aboutsummaryrefslogtreecommitdiff
path: root/vendor/image/src/codecs/pnm
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/image/src/codecs/pnm')
-rw-r--r--vendor/image/src/codecs/pnm/autobreak.rs124
-rw-r--r--vendor/image/src/codecs/pnm/decoder.rs1272
-rw-r--r--vendor/image/src/codecs/pnm/encoder.rs673
-rw-r--r--vendor/image/src/codecs/pnm/header.rs354
-rw-r--r--vendor/image/src/codecs/pnm/mod.rs184
5 files changed, 0 insertions, 2607 deletions
diff --git a/vendor/image/src/codecs/pnm/autobreak.rs b/vendor/image/src/codecs/pnm/autobreak.rs
deleted file mode 100644
index cea2cd8..0000000
--- a/vendor/image/src/codecs/pnm/autobreak.rs
+++ /dev/null
@@ -1,124 +0,0 @@
-//! Insert line breaks between written buffers when they would overflow the line length.
-use std::io;
-
-// The pnm standard says to insert line breaks after 70 characters. Assumes that no line breaks
-// are actually written. We have to be careful to fully commit buffers or not commit them at all,
-// otherwise we might insert a newline in the middle of a token.
-pub(crate) struct AutoBreak<W: io::Write> {
- wrapped: W,
- line_capacity: usize,
- line: Vec<u8>,
- has_newline: bool,
- panicked: bool, // see https://github.com/rust-lang/rust/issues/30888
-}
-
-impl<W: io::Write> AutoBreak<W> {
- pub(crate) fn new(writer: W, line_capacity: usize) -> Self {
- AutoBreak {
- wrapped: writer,
- line_capacity,
- line: Vec::with_capacity(line_capacity + 1),
- has_newline: false,
- panicked: false,
- }
- }
-
- fn flush_buf(&mut self) -> io::Result<()> {
- // from BufWriter
- let mut written = 0;
- let len = self.line.len();
- let mut ret = Ok(());
- while written < len {
- self.panicked = true;
- let r = self.wrapped.write(&self.line[written..]);
- self.panicked = false;
- match r {
- Ok(0) => {
- ret = Err(io::Error::new(
- io::ErrorKind::WriteZero,
- "failed to write the buffered data",
- ));
- break;
- }
- Ok(n) => written += n,
- Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
- Err(e) => {
- ret = Err(e);
- break;
- }
- }
- }
- if written > 0 {
- self.line.drain(..written);
- }
- ret
- }
-}
-
-impl<W: io::Write> io::Write for AutoBreak<W> {
- fn write(&mut self, buffer: &[u8]) -> io::Result<usize> {
- if self.has_newline {
- self.flush()?;
- self.has_newline = false;
- }
-
- if !self.line.is_empty() && self.line.len() + buffer.len() > self.line_capacity {
- self.line.push(b'\n');
- self.has_newline = true;
- self.flush()?;
- self.has_newline = false;
- }
-
- self.line.extend_from_slice(buffer);
- Ok(buffer.len())
- }
-
- fn flush(&mut self) -> io::Result<()> {
- self.flush_buf()?;
- self.wrapped.flush()
- }
-}
-
-impl<W: io::Write> Drop for AutoBreak<W> {
- fn drop(&mut self) {
- if !self.panicked {
- let _r = self.flush_buf();
- // internal writer flushed automatically by Drop
- }
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use std::io::Write;
-
- #[test]
- fn test_aligned_writes() {
- let mut output = Vec::new();
-
- {
- let mut writer = AutoBreak::new(&mut output, 10);
- writer.write_all(b"0123456789").unwrap();
- writer.write_all(b"0123456789").unwrap();
- }
-
- assert_eq!(output.as_slice(), b"0123456789\n0123456789");
- }
-
- #[test]
- fn test_greater_writes() {
- let mut output = Vec::new();
-
- {
- let mut writer = AutoBreak::new(&mut output, 10);
- writer.write_all(b"012").unwrap();
- writer.write_all(b"345").unwrap();
- writer.write_all(b"0123456789").unwrap();
- writer.write_all(b"012345678910").unwrap();
- writer.write_all(b"_").unwrap();
- }
-
- assert_eq!(output.as_slice(), b"012345\n0123456789\n012345678910\n_");
- }
-}
diff --git a/vendor/image/src/codecs/pnm/decoder.rs b/vendor/image/src/codecs/pnm/decoder.rs
deleted file mode 100644
index a495871..0000000
--- a/vendor/image/src/codecs/pnm/decoder.rs
+++ /dev/null
@@ -1,1272 +0,0 @@
-use std::convert::TryFrom;
-use std::convert::TryInto;
-use std::error;
-use std::fmt::{self, Display};
-use std::io::{self, BufRead, Cursor, Read};
-use std::marker::PhantomData;
-use std::mem;
-use std::num::ParseIntError;
-use std::str::{self, FromStr};
-
-use super::{ArbitraryHeader, ArbitraryTuplType, BitmapHeader, GraymapHeader, PixmapHeader};
-use super::{HeaderRecord, PnmHeader, PnmSubtype, SampleEncoding};
-use crate::color::{ColorType, ExtendedColorType};
-use crate::error::{
- DecodingError, ImageError, ImageResult, UnsupportedError, UnsupportedErrorKind,
-};
-use crate::image::{self, ImageDecoder, ImageFormat};
-use crate::utils;
-
-use byteorder::{BigEndian, ByteOrder, NativeEndian};
-
-/// All errors that can occur when attempting to parse a PNM
-#[derive(Debug, Clone)]
-enum DecoderError {
- /// PNM's "P[123456]" signature wrong or missing
- PnmMagicInvalid([u8; 2]),
- /// Couldn't parse the specified string as an integer from the specified source
- UnparsableValue(ErrorDataSource, String, ParseIntError),
-
- /// More than the exactly one allowed plane specified by the format
- NonAsciiByteInHeader(u8),
- /// The PAM header contained a non-ASCII byte
- NonAsciiLineInPamHeader,
- /// A sample string contained a non-ASCII byte
- NonAsciiSample,
-
- /// The byte after the P7 magic was not 0x0A NEWLINE
- NotNewlineAfterP7Magic(u8),
- /// The PNM header had too few lines
- UnexpectedPnmHeaderEnd,
-
- /// The specified line was specified twice
- HeaderLineDuplicated(PnmHeaderLine),
- /// The line with the specified ID was not understood
- HeaderLineUnknown(String),
- /// At least one of the required lines were missing from the header (are `None` here)
- ///
- /// Same names as [`PnmHeaderLine`](enum.PnmHeaderLine.html)
- #[allow(missing_docs)]
- HeaderLineMissing {
- height: Option<u32>,
- width: Option<u32>,
- depth: Option<u32>,
- maxval: Option<u32>,
- },
-
- /// Not enough data was provided to the Decoder to decode the image
- InputTooShort,
- /// Sample raster contained unexpected byte
- UnexpectedByteInRaster(u8),
- /// Specified sample was out of bounds (e.g. >1 in B&W)
- SampleOutOfBounds(u8),
- /// The image's maxval exceeds 0xFFFF
- MaxvalTooBig(u32),
-
- /// The specified tuple type supports restricted depths and maxvals, those restrictions were not met
- InvalidDepthOrMaxval {
- tuple_type: ArbitraryTuplType,
- depth: u32,
- maxval: u32,
- },
- /// The specified tuple type supports restricted depths, those restrictions were not met
- InvalidDepth {
- tuple_type: ArbitraryTuplType,
- depth: u32,
- },
- /// The tuple type was not recognised by the parser
- TupleTypeUnrecognised,
-
- /// Overflowed the specified value when parsing
- Overflow,
-}
-
-impl Display for DecoderError {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self {
- DecoderError::PnmMagicInvalid(magic) => f.write_fmt(format_args!(
- "Expected magic constant for PNM: P1..P7, got [{:#04X?}, {:#04X?}]",
- magic[0], magic[1]
- )),
- DecoderError::UnparsableValue(src, data, err) => {
- f.write_fmt(format_args!("Error parsing {:?} as {}: {}", data, src, err))
- }
-
- DecoderError::NonAsciiByteInHeader(c) => {
- f.write_fmt(format_args!("Non-ASCII character {:#04X?} in header", c))
- }
- DecoderError::NonAsciiLineInPamHeader => f.write_str("Non-ASCII line in PAM header"),
- DecoderError::NonAsciiSample => {
- f.write_str("Non-ASCII character where sample value was expected")
- }
-
- DecoderError::NotNewlineAfterP7Magic(c) => f.write_fmt(format_args!(
- "Expected newline after P7 magic, got {:#04X?}",
- c
- )),
- DecoderError::UnexpectedPnmHeaderEnd => f.write_str("Unexpected end of PNM header"),
-
- DecoderError::HeaderLineDuplicated(line) => {
- f.write_fmt(format_args!("Duplicate {} line", line))
- }
- DecoderError::HeaderLineUnknown(identifier) => f.write_fmt(format_args!(
- "Unknown header line with identifier {:?}",
- identifier
- )),
- DecoderError::HeaderLineMissing {
- height,
- width,
- depth,
- maxval,
- } => f.write_fmt(format_args!(
- "Missing header line: have height={:?}, width={:?}, depth={:?}, maxval={:?}",
- height, width, depth, maxval
- )),
-
- DecoderError::InputTooShort => {
- f.write_str("Not enough data was provided to the Decoder to decode the image")
- }
- DecoderError::UnexpectedByteInRaster(c) => f.write_fmt(format_args!(
- "Unexpected character {:#04X?} within sample raster",
- c
- )),
- DecoderError::SampleOutOfBounds(val) => {
- f.write_fmt(format_args!("Sample value {} outside of bounds", val))
- }
- DecoderError::MaxvalTooBig(maxval) => {
- f.write_fmt(format_args!("Image MAXVAL exceeds {}: {}", 0xFFFF, maxval))
- }
-
- DecoderError::InvalidDepthOrMaxval {
- tuple_type,
- depth,
- maxval,
- } => f.write_fmt(format_args!(
- "Invalid depth ({}) or maxval ({}) for tuple type {}",
- depth,
- maxval,
- tuple_type.name()
- )),
- DecoderError::InvalidDepth { tuple_type, depth } => f.write_fmt(format_args!(
- "Invalid depth ({}) for tuple type {}",
- depth,
- tuple_type.name()
- )),
- DecoderError::TupleTypeUnrecognised => f.write_str("Tuple type not recognized"),
- DecoderError::Overflow => f.write_str("Overflow when parsing value"),
- }
- }
-}
-
-/// Note: should `pnm` be extracted into a separate crate,
-/// this will need to be hidden until that crate hits version `1.0`.
-impl From<DecoderError> for ImageError {
- fn from(e: DecoderError) -> ImageError {
- ImageError::Decoding(DecodingError::new(ImageFormat::Pnm.into(), e))
- }
-}
-
-impl error::Error for DecoderError {
- fn source(&self) -> Option<&(dyn error::Error + 'static)> {
- match self {
- DecoderError::UnparsableValue(_, _, err) => Some(err),
- _ => None,
- }
- }
-}
-
-/// Single-value lines in a PNM header
-#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
-enum PnmHeaderLine {
- /// "HEIGHT"
- Height,
- /// "WIDTH"
- Width,
- /// "DEPTH"
- Depth,
- /// "MAXVAL", a.k.a. `maxwhite`
- Maxval,
-}
-
-impl Display for PnmHeaderLine {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.write_str(match self {
- PnmHeaderLine::Height => "HEIGHT",
- PnmHeaderLine::Width => "WIDTH",
- PnmHeaderLine::Depth => "DEPTH",
- PnmHeaderLine::Maxval => "MAXVAL",
- })
- }
-}
-
-/// Single-value lines in a PNM header
-#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
-enum ErrorDataSource {
- /// One of the header lines
- Line(PnmHeaderLine),
- /// Value in the preamble
- Preamble,
- /// Sample/pixel data
- Sample,
-}
-
-impl Display for ErrorDataSource {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self {
- ErrorDataSource::Line(l) => l.fmt(f),
- ErrorDataSource::Preamble => f.write_str("number in preamble"),
- ErrorDataSource::Sample => f.write_str("sample"),
- }
- }
-}
-
-/// Dynamic representation, represents all decodable (sample, depth) combinations.
-#[derive(Clone, Copy)]
-enum TupleType {
- PbmBit,
- BWBit,
- GrayU8,
- GrayU16,
- RGBU8,
- RGBU16,
-}
-
-trait Sample {
- fn bytelen(width: u32, height: u32, samples: u32) -> ImageResult<usize>;
- fn from_bytes(bytes: &[u8], row_size: usize, output_buf: &mut [u8]) -> ImageResult<()>;
- fn from_ascii(reader: &mut dyn Read, output_buf: &mut [u8]) -> ImageResult<()>;
-}
-
-struct U8;
-struct U16;
-struct PbmBit;
-struct BWBit;
-
-trait DecodableImageHeader {
- fn tuple_type(&self) -> ImageResult<TupleType>;
-}
-
-/// PNM decoder
-pub struct PnmDecoder<R> {
- reader: R,
- header: PnmHeader,
- tuple: TupleType,
-}
-
-impl<R: BufRead> PnmDecoder<R> {
- /// Create a new decoder that decodes from the stream ```read```
- pub fn new(mut buffered_read: R) -> ImageResult<PnmDecoder<R>> {
- let magic = buffered_read.read_magic_constant()?;
-
- let subtype = match magic {
- [b'P', b'1'] => PnmSubtype::Bitmap(SampleEncoding::Ascii),
- [b'P', b'2'] => PnmSubtype::Graymap(SampleEncoding::Ascii),
- [b'P', b'3'] => PnmSubtype::Pixmap(SampleEncoding::Ascii),
- [b'P', b'4'] => PnmSubtype::Bitmap(SampleEncoding::Binary),
- [b'P', b'5'] => PnmSubtype::Graymap(SampleEncoding::Binary),
- [b'P', b'6'] => PnmSubtype::Pixmap(SampleEncoding::Binary),
- [b'P', b'7'] => PnmSubtype::ArbitraryMap,
- _ => return Err(DecoderError::PnmMagicInvalid(magic).into()),
- };
-
- let decoder = match subtype {
- PnmSubtype::Bitmap(enc) => PnmDecoder::read_bitmap_header(buffered_read, enc),
- PnmSubtype::Graymap(enc) => PnmDecoder::read_graymap_header(buffered_read, enc),
- PnmSubtype::Pixmap(enc) => PnmDecoder::read_pixmap_header(buffered_read, enc),
- PnmSubtype::ArbitraryMap => PnmDecoder::read_arbitrary_header(buffered_read),
- }?;
-
- if utils::check_dimension_overflow(
- decoder.dimensions().0,
- decoder.dimensions().1,
- decoder.color_type().bytes_per_pixel(),
- ) {
- return Err(ImageError::Unsupported(
- UnsupportedError::from_format_and_kind(
- ImageFormat::Pnm.into(),
- UnsupportedErrorKind::GenericFeature(format!(
- "Image dimensions ({}x{}) are too large",
- decoder.dimensions().0,
- decoder.dimensions().1
- )),
- ),
- ));
- }
-
- Ok(decoder)
- }
-
- /// Extract the reader and header after an image has been read.
- pub fn into_inner(self) -> (R, PnmHeader) {
- (self.reader, self.header)
- }
-
- fn read_bitmap_header(mut reader: R, encoding: SampleEncoding) -> ImageResult<PnmDecoder<R>> {
- let header = reader.read_bitmap_header(encoding)?;
- Ok(PnmDecoder {
- reader,
- tuple: TupleType::PbmBit,
- header: PnmHeader {
- decoded: HeaderRecord::Bitmap(header),
- encoded: None,
- },
- })
- }
-
- fn read_graymap_header(mut reader: R, encoding: SampleEncoding) -> ImageResult<PnmDecoder<R>> {
- let header = reader.read_graymap_header(encoding)?;
- let tuple_type = header.tuple_type()?;
- Ok(PnmDecoder {
- reader,
- tuple: tuple_type,
- header: PnmHeader {
- decoded: HeaderRecord::Graymap(header),
- encoded: None,
- },
- })
- }
-
- fn read_pixmap_header(mut reader: R, encoding: SampleEncoding) -> ImageResult<PnmDecoder<R>> {
- let header = reader.read_pixmap_header(encoding)?;
- let tuple_type = header.tuple_type()?;
- Ok(PnmDecoder {
- reader,
- tuple: tuple_type,
- header: PnmHeader {
- decoded: HeaderRecord::Pixmap(header),
- encoded: None,
- },
- })
- }
-
- fn read_arbitrary_header(mut reader: R) -> ImageResult<PnmDecoder<R>> {
- let header = reader.read_arbitrary_header()?;
- let tuple_type = header.tuple_type()?;
- Ok(PnmDecoder {
- reader,
- tuple: tuple_type,
- header: PnmHeader {
- decoded: HeaderRecord::Arbitrary(header),
- encoded: None,
- },
- })
- }
-}
-
-trait HeaderReader: BufRead {
- /// Reads the two magic constant bytes
- fn read_magic_constant(&mut self) -> ImageResult<[u8; 2]> {
- let mut magic: [u8; 2] = [0, 0];
- self.read_exact(&mut magic)?;
- Ok(magic)
- }
-
- /// Reads a string as well as a single whitespace after it, ignoring comments
- fn read_next_string(&mut self) -> ImageResult<String> {
- let mut bytes = Vec::new();
-
- // pair input bytes with a bool mask to remove comments
- let mark_comments = self.bytes().scan(true, |partof, read| {
- let byte = match read {
- Err(err) => return Some((*partof, Err(err))),
- Ok(byte) => byte,
- };
- let cur_enabled = *partof && byte != b'#';
- let next_enabled = cur_enabled || (byte == b'\r' || byte == b'\n');
- *partof = next_enabled;
- Some((cur_enabled, Ok(byte)))
- });
-
- for (_, byte) in mark_comments.filter(|e| e.0) {
- match byte {
- Ok(b'\t') | Ok(b'\n') | Ok(b'\x0b') | Ok(b'\x0c') | Ok(b'\r') | Ok(b' ') => {
- if !bytes.is_empty() {
- break; // We're done as we already have some content
- }
- }
- Ok(byte) if !byte.is_ascii() => {
- return Err(DecoderError::NonAsciiByteInHeader(byte).into())
- }
- Ok(byte) => {
- bytes.push(byte);
- }
- Err(_) => break,
- }
- }
-
- if bytes.is_empty() {
- return Err(ImageError::IoError(io::ErrorKind::UnexpectedEof.into()));
- }
-
- if !bytes.as_slice().is_ascii() {
- // We have only filled the buffer with characters for which `byte.is_ascii()` holds.
- unreachable!("Non-ASCII character should have returned sooner")
- }
-
- let string = String::from_utf8(bytes)
- // We checked the precondition ourselves a few lines before, `bytes.as_slice().is_ascii()`.
- .unwrap_or_else(|_| unreachable!("Only ASCII characters should be decoded"));
-
- Ok(string)
- }
-
- /// Read the next line
- fn read_next_line(&mut self) -> ImageResult<String> {
- let mut buffer = String::new();
- self.read_line(&mut buffer)?;
- Ok(buffer)
- }
-
- fn read_next_u32(&mut self) -> ImageResult<u32> {
- let s = self.read_next_string()?;
- s.parse::<u32>()
- .map_err(|err| DecoderError::UnparsableValue(ErrorDataSource::Preamble, s, err).into())
- }
-
- fn read_bitmap_header(&mut self, encoding: SampleEncoding) -> ImageResult<BitmapHeader> {
- let width = self.read_next_u32()?;
- let height = self.read_next_u32()?;
- Ok(BitmapHeader {
- encoding,
- width,
- height,
- })
- }
-
- fn read_graymap_header(&mut self, encoding: SampleEncoding) -> ImageResult<GraymapHeader> {
- self.read_pixmap_header(encoding).map(
- |PixmapHeader {
- encoding,
- width,
- height,
- maxval,
- }| GraymapHeader {
- encoding,
- width,
- height,
- maxwhite: maxval,
- },
- )
- }
-
- fn read_pixmap_header(&mut self, encoding: SampleEncoding) -> ImageResult<PixmapHeader> {
- let width = self.read_next_u32()?;
- let height = self.read_next_u32()?;
- let maxval = self.read_next_u32()?;
- Ok(PixmapHeader {
- encoding,
- width,
- height,
- maxval,
- })
- }
-
- fn read_arbitrary_header(&mut self) -> ImageResult<ArbitraryHeader> {
- fn parse_single_value_line(
- line_val: &mut Option<u32>,
- rest: &str,
- line: PnmHeaderLine,
- ) -> ImageResult<()> {
- if line_val.is_some() {
- Err(DecoderError::HeaderLineDuplicated(line).into())
- } else {
- let v = rest.trim().parse().map_err(|err| {
- DecoderError::UnparsableValue(ErrorDataSource::Line(line), rest.to_owned(), err)
- })?;
- *line_val = Some(v);
- Ok(())
- }
- }
-
- match self.bytes().next() {
- None => return Err(ImageError::IoError(io::ErrorKind::UnexpectedEof.into())),
- Some(Err(io)) => return Err(ImageError::IoError(io)),
- Some(Ok(b'\n')) => (),
- Some(Ok(c)) => return Err(DecoderError::NotNewlineAfterP7Magic(c).into()),
- }
-
- let mut line = String::new();
- let mut height: Option<u32> = None;
- let mut width: Option<u32> = None;
- let mut depth: Option<u32> = None;
- let mut maxval: Option<u32> = None;
- let mut tupltype: Option<String> = None;
- loop {
- line.truncate(0);
- let len = self.read_line(&mut line)?;
- if len == 0 {
- return Err(DecoderError::UnexpectedPnmHeaderEnd.into());
- }
- if line.as_bytes()[0] == b'#' {
- continue;
- }
- if !line.is_ascii() {
- return Err(DecoderError::NonAsciiLineInPamHeader.into());
- }
- #[allow(deprecated)]
- let (identifier, rest) = line
- .trim_left()
- .split_at(line.find(char::is_whitespace).unwrap_or(line.len()));
- match identifier {
- "ENDHDR" => break,
- "HEIGHT" => parse_single_value_line(&mut height, rest, PnmHeaderLine::Height)?,
- "WIDTH" => parse_single_value_line(&mut width, rest, PnmHeaderLine::Width)?,
- "DEPTH" => parse_single_value_line(&mut depth, rest, PnmHeaderLine::Depth)?,
- "MAXVAL" => parse_single_value_line(&mut maxval, rest, PnmHeaderLine::Maxval)?,
- "TUPLTYPE" => {
- let identifier = rest.trim();
- if tupltype.is_some() {
- let appended = tupltype.take().map(|mut v| {
- v.push(' ');
- v.push_str(identifier);
- v
- });
- tupltype = appended;
- } else {
- tupltype = Some(identifier.to_string());
- }
- }
- _ => return Err(DecoderError::HeaderLineUnknown(identifier.to_string()).into()),
- }
- }
-
- let (h, w, d, m) = match (height, width, depth, maxval) {
- (Some(h), Some(w), Some(d), Some(m)) => (h, w, d, m),
- _ => {
- return Err(DecoderError::HeaderLineMissing {
- height,
- width,
- depth,
- maxval,
- }
- .into())
- }
- };
-
- let tupltype = match tupltype {
- None => None,
- Some(ref t) if t == "BLACKANDWHITE" => Some(ArbitraryTuplType::BlackAndWhite),
- Some(ref t) if t == "BLACKANDWHITE_ALPHA" => {
- Some(ArbitraryTuplType::BlackAndWhiteAlpha)
- }
- Some(ref t) if t == "GRAYSCALE" => Some(ArbitraryTuplType::Grayscale),
- Some(ref t) if t == "GRAYSCALE_ALPHA" => Some(ArbitraryTuplType::GrayscaleAlpha),
- Some(ref t) if t == "RGB" => Some(ArbitraryTuplType::RGB),
- Some(ref t) if t == "RGB_ALPHA" => Some(ArbitraryTuplType::RGBAlpha),
- Some(other) => Some(ArbitraryTuplType::Custom(other)),
- };
-
- Ok(ArbitraryHeader {
- height: h,
- width: w,
- depth: d,
- maxval: m,
- tupltype,
- })
- }
-}
-
-impl<R> HeaderReader for R where R: BufRead {}
-
-/// Wrapper struct around a `Cursor<Vec<u8>>`
-pub struct PnmReader<R>(Cursor<Vec<u8>>, PhantomData<R>);
-impl<R> Read for PnmReader<R> {
- fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
- self.0.read(buf)
- }
- fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
- if self.0.position() == 0 && buf.is_empty() {
- mem::swap(buf, self.0.get_mut());
- Ok(buf.len())
- } else {
- self.0.read_to_end(buf)
- }
- }
-}
-
-impl<'a, R: 'a + Read> ImageDecoder<'a> for PnmDecoder<R> {
- type Reader = PnmReader<R>;
-
- fn dimensions(&self) -> (u32, u32) {
- (self.header.width(), self.header.height())
- }
-
- fn color_type(&self) -> ColorType {
- match self.tuple {
- TupleType::PbmBit => ColorType::L8,
- TupleType::BWBit => ColorType::L8,
- TupleType::GrayU8 => ColorType::L8,
- TupleType::GrayU16 => ColorType::L16,
- TupleType::RGBU8 => ColorType::Rgb8,
- TupleType::RGBU16 => ColorType::Rgb16,
- }
- }
-
- fn original_color_type(&self) -> ExtendedColorType {
- match self.tuple {
- TupleType::PbmBit => ExtendedColorType::L1,
- TupleType::BWBit => ExtendedColorType::L1,
- TupleType::GrayU8 => ExtendedColorType::L8,
- TupleType::GrayU16 => ExtendedColorType::L16,
- TupleType::RGBU8 => ExtendedColorType::Rgb8,
- TupleType::RGBU16 => ExtendedColorType::Rgb16,
- }
- }
-
- fn into_reader(self) -> ImageResult<Self::Reader> {
- Ok(PnmReader(
- Cursor::new(image::decoder_to_vec(self)?),
- PhantomData,
- ))
- }
-
- fn read_image(mut self, buf: &mut [u8]) -> ImageResult<()> {
- assert_eq!(u64::try_from(buf.len()), Ok(self.total_bytes()));
- match self.tuple {
- TupleType::PbmBit => self.read_samples::<PbmBit>(1, buf),
- TupleType::BWBit => self.read_samples::<BWBit>(1, buf),
- TupleType::RGBU8 => self.read_samples::<U8>(3, buf),
- TupleType::RGBU16 => self.read_samples::<U16>(3, buf),
- TupleType::GrayU8 => self.read_samples::<U8>(1, buf),
- TupleType::GrayU16 => self.read_samples::<U16>(1, buf),
- }
- }
-}
-
-impl<R: Read> PnmDecoder<R> {
- fn read_samples<S: Sample>(&mut self, components: u32, buf: &mut [u8]) -> ImageResult<()> {
- match self.subtype().sample_encoding() {
- SampleEncoding::Binary => {
- let width = self.header.width();
- let height = self.header.height();
- let bytecount = S::bytelen(width, height, components)?;
-
- let mut bytes = vec![];
- self.reader
- .by_ref()
- // This conversion is potentially lossy but unlikely and in that case we error
- // later anyways.
- .take(bytecount as u64)
- .read_to_end(&mut bytes)?;
- if bytes.len() != bytecount {
- return Err(DecoderError::InputTooShort.into());
- }
-
- let width: usize = width.try_into().map_err(|_| DecoderError::Overflow)?;
- let components: usize =
- components.try_into().map_err(|_| DecoderError::Overflow)?;
- let row_size = width
- .checked_mul(components)
- .ok_or(DecoderError::Overflow)?;
-
- S::from_bytes(&bytes, row_size, buf)
- }
- SampleEncoding::Ascii => self.read_ascii::<S>(buf),
- }
- }
-
- fn read_ascii<Basic: Sample>(&mut self, output_buf: &mut [u8]) -> ImageResult<()> {
- Basic::from_ascii(&mut self.reader, output_buf)
- }
-
- /// Get the pnm subtype, depending on the magic constant contained in the header
- pub fn subtype(&self) -> PnmSubtype {
- self.header.subtype()
- }
-}
-
-fn read_separated_ascii<T: FromStr<Err = ParseIntError>>(reader: &mut dyn Read) -> ImageResult<T>
-where
- T::Err: Display,
-{
- let is_separator = |v: &u8| matches! { *v, b'\t' | b'\n' | b'\x0b' | b'\x0c' | b'\r' | b' ' };
-
- let token = reader
- .bytes()
- .skip_while(|v| v.as_ref().ok().map(is_separator).unwrap_or(false))
- .take_while(|v| v.as_ref().ok().map(|c| !is_separator(c)).unwrap_or(false))
- .collect::<Result<Vec<u8>, _>>()?;
-
- if !token.is_ascii() {
- return Err(DecoderError::NonAsciiSample.into());
- }
-
- let string = str::from_utf8(&token)
- // We checked the precondition ourselves a few lines before with `token.is_ascii()`.
- .unwrap_or_else(|_| unreachable!("Only ASCII characters should be decoded"));
-
- string.parse().map_err(|err| {
- DecoderError::UnparsableValue(ErrorDataSource::Sample, string.to_owned(), err).into()
- })
-}
-
-impl Sample for U8 {
- fn bytelen(width: u32, height: u32, samples: u32) -> ImageResult<usize> {
- Ok((width * height * samples) as usize)
- }
-
- fn from_bytes(bytes: &[u8], _row_size: usize, output_buf: &mut [u8]) -> ImageResult<()> {
- output_buf.copy_from_slice(bytes);
- Ok(())
- }
-
- fn from_ascii(reader: &mut dyn Read, output_buf: &mut [u8]) -> ImageResult<()> {
- for b in output_buf {
- *b = read_separated_ascii(reader)?;
- }
- Ok(())
- }
-}
-
-impl Sample for U16 {
- fn bytelen(width: u32, height: u32, samples: u32) -> ImageResult<usize> {
- Ok((width * height * samples * 2) as usize)
- }
-
- fn from_bytes(bytes: &[u8], _row_size: usize, output_buf: &mut [u8]) -> ImageResult<()> {
- output_buf.copy_from_slice(bytes);
- for chunk in output_buf.chunks_exact_mut(2) {
- let v = BigEndian::read_u16(chunk);
- NativeEndian::write_u16(chunk, v);
- }
- Ok(())
- }
-
- fn from_ascii(reader: &mut dyn Read, output_buf: &mut [u8]) -> ImageResult<()> {
- for chunk in output_buf.chunks_exact_mut(2) {
- let v = read_separated_ascii::<u16>(reader)?;
- NativeEndian::write_u16(chunk, v);
- }
- Ok(())
- }
-}
-
-// The image is encoded in rows of bits, high order bits first. Any bits beyond the row bits should
-// be ignored. Also, contrary to rgb, black pixels are encoded as a 1 while white is 0. This will
-// need to be reversed for the grayscale output.
-impl Sample for PbmBit {
- fn bytelen(width: u32, height: u32, samples: u32) -> ImageResult<usize> {
- let count = width * samples;
- let linelen = (count / 8) + ((count % 8) != 0) as u32;
- Ok((linelen * height) as usize)
- }
-
- fn from_bytes(bytes: &[u8], row_size: usize, output_buf: &mut [u8]) -> ImageResult<()> {
- let mut expanded = utils::expand_bits(1, row_size.try_into().unwrap(), bytes);
- for b in expanded.iter_mut() {
- *b = !*b;
- }
- output_buf.copy_from_slice(&expanded);
- Ok(())
- }
-
- fn from_ascii(reader: &mut dyn Read, output_buf: &mut [u8]) -> ImageResult<()> {
- let mut bytes = reader.bytes();
- for b in output_buf {
- loop {
- let byte = bytes
- .next()
- .ok_or_else::<ImageError, _>(|| DecoderError::InputTooShort.into())??;
- match byte {
- b'\t' | b'\n' | b'\x0b' | b'\x0c' | b'\r' | b' ' => continue,
- b'0' => *b = 255,
- b'1' => *b = 0,
- c => return Err(DecoderError::UnexpectedByteInRaster(c).into()),
- }
- break;
- }
- }
-
- Ok(())
- }
-}
-
-// Encoded just like a normal U8 but we check the values.
-impl Sample for BWBit {
- fn bytelen(width: u32, height: u32, samples: u32) -> ImageResult<usize> {
- U8::bytelen(width, height, samples)
- }
-
- fn from_bytes(bytes: &[u8], row_size: usize, output_buf: &mut [u8]) -> ImageResult<()> {
- U8::from_bytes(bytes, row_size, output_buf)?;
- if let Some(val) = output_buf.iter().find(|&val| *val > 1) {
- return Err(DecoderError::SampleOutOfBounds(*val).into());
- }
- Ok(())
- }
-
- fn from_ascii(_reader: &mut dyn Read, _output_buf: &mut [u8]) -> ImageResult<()> {
- unreachable!("BW bits from anymaps are never encoded as ASCII")
- }
-}
-
-impl DecodableImageHeader for BitmapHeader {
- fn tuple_type(&self) -> ImageResult<TupleType> {
- Ok(TupleType::PbmBit)
- }
-}
-
-impl DecodableImageHeader for GraymapHeader {
- fn tuple_type(&self) -> ImageResult<TupleType> {
- match self.maxwhite {
- v if v <= 0xFF => Ok(TupleType::GrayU8),
- v if v <= 0xFFFF => Ok(TupleType::GrayU16),
- _ => Err(DecoderError::MaxvalTooBig(self.maxwhite).into()),
- }
- }
-}
-
-impl DecodableImageHeader for PixmapHeader {
- fn tuple_type(&self) -> ImageResult<TupleType> {
- match self.maxval {
- v if v <= 0xFF => Ok(TupleType::RGBU8),
- v if v <= 0xFFFF => Ok(TupleType::RGBU16),
- _ => Err(DecoderError::MaxvalTooBig(self.maxval).into()),
- }
- }
-}
-
-impl DecodableImageHeader for ArbitraryHeader {
- fn tuple_type(&self) -> ImageResult<TupleType> {
- match self.tupltype {
- None if self.depth == 1 => Ok(TupleType::GrayU8),
- None if self.depth == 2 => Err(ImageError::Unsupported(
- UnsupportedError::from_format_and_kind(
- ImageFormat::Pnm.into(),
- UnsupportedErrorKind::Color(ExtendedColorType::La8),
- ),
- )),
- None if self.depth == 3 => Ok(TupleType::RGBU8),
- None if self.depth == 4 => Err(ImageError::Unsupported(
- UnsupportedError::from_format_and_kind(
- ImageFormat::Pnm.into(),
- UnsupportedErrorKind::Color(ExtendedColorType::Rgba8),
- ),
- )),
-
- Some(ArbitraryTuplType::BlackAndWhite) if self.maxval == 1 && self.depth == 1 => {
- Ok(TupleType::BWBit)
- }
- Some(ArbitraryTuplType::BlackAndWhite) => Err(DecoderError::InvalidDepthOrMaxval {
- tuple_type: ArbitraryTuplType::BlackAndWhite,
- maxval: self.maxval,
- depth: self.depth,
- }
- .into()),
-
- Some(ArbitraryTuplType::Grayscale) if self.depth == 1 && self.maxval <= 0xFF => {
- Ok(TupleType::GrayU8)
- }
- Some(ArbitraryTuplType::Grayscale) if self.depth <= 1 && self.maxval <= 0xFFFF => {
- Ok(TupleType::GrayU16)
- }
- Some(ArbitraryTuplType::Grayscale) => Err(DecoderError::InvalidDepthOrMaxval {
- tuple_type: ArbitraryTuplType::Grayscale,
- maxval: self.maxval,
- depth: self.depth,
- }
- .into()),
-
- Some(ArbitraryTuplType::RGB) if self.depth == 3 && self.maxval <= 0xFF => {
- Ok(TupleType::RGBU8)
- }
- Some(ArbitraryTuplType::RGB) if self.depth == 3 && self.maxval <= 0xFFFF => {
- Ok(TupleType::RGBU16)
- }
- Some(ArbitraryTuplType::RGB) => Err(DecoderError::InvalidDepth {
- tuple_type: ArbitraryTuplType::RGB,
- depth: self.depth,
- }
- .into()),
-
- Some(ArbitraryTuplType::BlackAndWhiteAlpha) => Err(ImageError::Unsupported(
- UnsupportedError::from_format_and_kind(
- ImageFormat::Pnm.into(),
- UnsupportedErrorKind::GenericFeature(format!(
- "Color type {}",
- ArbitraryTuplType::BlackAndWhiteAlpha.name()
- )),
- ),
- )),
- Some(ArbitraryTuplType::GrayscaleAlpha) => Err(ImageError::Unsupported(
- UnsupportedError::from_format_and_kind(
- ImageFormat::Pnm.into(),
- UnsupportedErrorKind::Color(ExtendedColorType::La8),
- ),
- )),
- Some(ArbitraryTuplType::RGBAlpha) => Err(ImageError::Unsupported(
- UnsupportedError::from_format_and_kind(
- ImageFormat::Pnm.into(),
- UnsupportedErrorKind::Color(ExtendedColorType::Rgba8),
- ),
- )),
- Some(ArbitraryTuplType::Custom(ref custom)) => Err(ImageError::Unsupported(
- UnsupportedError::from_format_and_kind(
- ImageFormat::Pnm.into(),
- UnsupportedErrorKind::GenericFeature(format!("Tuple type {:?}", custom)),
- ),
- )),
- None => Err(DecoderError::TupleTypeUnrecognised.into()),
- }
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- /// Tests reading of a valid blackandwhite pam
- #[test]
- fn pam_blackandwhite() {
- let pamdata = b"P7
-WIDTH 4
-HEIGHT 4
-DEPTH 1
-MAXVAL 1
-TUPLTYPE BLACKANDWHITE
-# Comment line
-ENDHDR
-\x01\x00\x00\x01\x01\x00\x00\x01\x01\x00\x00\x01\x01\x00\x00\x01";
- let decoder = PnmDecoder::new(&pamdata[..]).unwrap();
- assert_eq!(decoder.color_type(), ColorType::L8);
- assert_eq!(decoder.original_color_type(), ExtendedColorType::L1);
- assert_eq!(decoder.dimensions(), (4, 4));
- assert_eq!(decoder.subtype(), PnmSubtype::ArbitraryMap);
-
- let mut image = vec![0; decoder.total_bytes() as usize];
- decoder.read_image(&mut image).unwrap();
- assert_eq!(
- image,
- vec![
- 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00,
- 0x00, 0x01
- ]
- );
- match PnmDecoder::new(&pamdata[..]).unwrap().into_inner() {
- (
- _,
- PnmHeader {
- decoded:
- HeaderRecord::Arbitrary(ArbitraryHeader {
- width: 4,
- height: 4,
- maxval: 1,
- depth: 1,
- tupltype: Some(ArbitraryTuplType::BlackAndWhite),
- }),
- encoded: _,
- },
- ) => (),
- _ => panic!("Decoded header is incorrect"),
- }
- }
-
- /// Tests reading of a valid grayscale pam
- #[test]
- fn pam_grayscale() {
- let pamdata = b"P7
-WIDTH 4
-HEIGHT 4
-DEPTH 1
-MAXVAL 255
-TUPLTYPE GRAYSCALE
-# Comment line
-ENDHDR
-\xde\xad\xbe\xef\xde\xad\xbe\xef\xde\xad\xbe\xef\xde\xad\xbe\xef";
- let decoder = PnmDecoder::new(&pamdata[..]).unwrap();
- assert_eq!(decoder.color_type(), ColorType::L8);
- assert_eq!(decoder.dimensions(), (4, 4));
- assert_eq!(decoder.subtype(), PnmSubtype::ArbitraryMap);
-
- let mut image = vec![0; decoder.total_bytes() as usize];
- decoder.read_image(&mut image).unwrap();
- assert_eq!(
- image,
- vec![
- 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad,
- 0xbe, 0xef
- ]
- );
- match PnmDecoder::new(&pamdata[..]).unwrap().into_inner() {
- (
- _,
- PnmHeader {
- decoded:
- HeaderRecord::Arbitrary(ArbitraryHeader {
- width: 4,
- height: 4,
- depth: 1,
- maxval: 255,
- tupltype: Some(ArbitraryTuplType::Grayscale),
- }),
- encoded: _,
- },
- ) => (),
- _ => panic!("Decoded header is incorrect"),
- }
- }
-
- /// Tests reading of a valid rgb pam
- #[test]
- fn pam_rgb() {
- let pamdata = b"P7
-# Comment line
-MAXVAL 255
-TUPLTYPE RGB
-DEPTH 3
-WIDTH 2
-HEIGHT 2
-ENDHDR
-\xde\xad\xbe\xef\xde\xad\xbe\xef\xde\xad\xbe\xef";
- let decoder = PnmDecoder::new(&pamdata[..]).unwrap();
- assert_eq!(decoder.color_type(), ColorType::Rgb8);
- assert_eq!(decoder.dimensions(), (2, 2));
- assert_eq!(decoder.subtype(), PnmSubtype::ArbitraryMap);
-
- let mut image = vec![0; decoder.total_bytes() as usize];
- decoder.read_image(&mut image).unwrap();
- assert_eq!(
- image,
- vec![0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef]
- );
- match PnmDecoder::new(&pamdata[..]).unwrap().into_inner() {
- (
- _,
- PnmHeader {
- decoded:
- HeaderRecord::Arbitrary(ArbitraryHeader {
- maxval: 255,
- tupltype: Some(ArbitraryTuplType::RGB),
- depth: 3,
- width: 2,
- height: 2,
- }),
- encoded: _,
- },
- ) => (),
- _ => panic!("Decoded header is incorrect"),
- }
- }
-
- #[test]
- fn pbm_binary() {
- // The data contains two rows of the image (each line is padded to the full byte). For
- // comments on its format, see documentation of `impl SampleType for PbmBit`.
- let pbmbinary = [&b"P4 6 2\n"[..], &[0b01101100 as u8, 0b10110111]].concat();
- let decoder = PnmDecoder::new(&pbmbinary[..]).unwrap();
- assert_eq!(decoder.color_type(), ColorType::L8);
- assert_eq!(decoder.original_color_type(), ExtendedColorType::L1);
- assert_eq!(decoder.dimensions(), (6, 2));
- assert_eq!(
- decoder.subtype(),
- PnmSubtype::Bitmap(SampleEncoding::Binary)
- );
- let mut image = vec![0; decoder.total_bytes() as usize];
- decoder.read_image(&mut image).unwrap();
- assert_eq!(image, vec![255, 0, 0, 255, 0, 0, 0, 255, 0, 0, 255, 0]);
- match PnmDecoder::new(&pbmbinary[..]).unwrap().into_inner() {
- (
- _,
- PnmHeader {
- decoded:
- HeaderRecord::Bitmap(BitmapHeader {
- encoding: SampleEncoding::Binary,
- width: 6,
- height: 2,
- }),
- encoded: _,
- },
- ) => (),
- _ => panic!("Decoded header is incorrect"),
- }
- }
-
- /// A previous infinite loop.
- #[test]
- fn pbm_binary_ascii_termination() {
- use std::io::{BufReader, Cursor, Error, ErrorKind, Read, Result};
- struct FailRead(Cursor<&'static [u8]>);
-
- impl Read for FailRead {
- fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
- match self.0.read(buf) {
- Ok(n) if n > 0 => Ok(n),
- _ => Err(Error::new(
- ErrorKind::BrokenPipe,
- "Simulated broken pipe error",
- )),
- }
- }
- }
-
- let pbmbinary = BufReader::new(FailRead(Cursor::new(b"P1 1 1\n")));
-
- let decoder = PnmDecoder::new(pbmbinary).unwrap();
- let mut image = vec![0; decoder.total_bytes() as usize];
- decoder
- .read_image(&mut image)
- .expect_err("Image is malformed");
- }
-
- #[test]
- fn pbm_ascii() {
- // The data contains two rows of the image (each line is padded to the full byte). For
- // comments on its format, see documentation of `impl SampleType for PbmBit`. Tests all
- // whitespace characters that should be allowed (the 6 characters according to POSIX).
- let pbmbinary = b"P1 6 2\n 0 1 1 0 1 1\n1 0 1 1 0\t\n\x0b\x0c\r1";
- let decoder = PnmDecoder::new(&pbmbinary[..]).unwrap();
- assert_eq!(decoder.color_type(), ColorType::L8);
- assert_eq!(decoder.original_color_type(), ExtendedColorType::L1);
- assert_eq!(decoder.dimensions(), (6, 2));
- assert_eq!(decoder.subtype(), PnmSubtype::Bitmap(SampleEncoding::Ascii));
-
- let mut image = vec![0; decoder.total_bytes() as usize];
- decoder.read_image(&mut image).unwrap();
- assert_eq!(image, vec![255, 0, 0, 255, 0, 0, 0, 255, 0, 0, 255, 0]);
- match PnmDecoder::new(&pbmbinary[..]).unwrap().into_inner() {
- (
- _,
- PnmHeader {
- decoded:
- HeaderRecord::Bitmap(BitmapHeader {
- encoding: SampleEncoding::Ascii,
- width: 6,
- height: 2,
- }),
- encoded: _,
- },
- ) => (),
- _ => panic!("Decoded header is incorrect"),
- }
- }
-
- #[test]
- fn pbm_ascii_nospace() {
- // The data contains two rows of the image (each line is padded to the full byte). Notably,
- // it is completely within specification for the ascii data not to contain separating
- // whitespace for the pbm format or any mix.
- let pbmbinary = b"P1 6 2\n011011101101";
- let decoder = PnmDecoder::new(&pbmbinary[..]).unwrap();
- assert_eq!(decoder.color_type(), ColorType::L8);
- assert_eq!(decoder.original_color_type(), ExtendedColorType::L1);
- assert_eq!(decoder.dimensions(), (6, 2));
- assert_eq!(decoder.subtype(), PnmSubtype::Bitmap(SampleEncoding::Ascii));
-
- let mut image = vec![0; decoder.total_bytes() as usize];
- decoder.read_image(&mut image).unwrap();
- assert_eq!(image, vec![255, 0, 0, 255, 0, 0, 0, 255, 0, 0, 255, 0]);
- match PnmDecoder::new(&pbmbinary[..]).unwrap().into_inner() {
- (
- _,
- PnmHeader {
- decoded:
- HeaderRecord::Bitmap(BitmapHeader {
- encoding: SampleEncoding::Ascii,
- width: 6,
- height: 2,
- }),
- encoded: _,
- },
- ) => (),
- _ => panic!("Decoded header is incorrect"),
- }
- }
-
- #[test]
- fn pgm_binary() {
- // The data contains two rows of the image (each line is padded to the full byte). For
- // comments on its format, see documentation of `impl SampleType for PbmBit`.
- let elements = (0..16).collect::<Vec<_>>();
- let pbmbinary = [&b"P5 4 4 255\n"[..], &elements].concat();
- let decoder = PnmDecoder::new(&pbmbinary[..]).unwrap();
- assert_eq!(decoder.color_type(), ColorType::L8);
- assert_eq!(decoder.dimensions(), (4, 4));
- assert_eq!(
- decoder.subtype(),
- PnmSubtype::Graymap(SampleEncoding::Binary)
- );
- let mut image = vec![0; decoder.total_bytes() as usize];
- decoder.read_image(&mut image).unwrap();
- assert_eq!(image, elements);
- match PnmDecoder::new(&pbmbinary[..]).unwrap().into_inner() {
- (
- _,
- PnmHeader {
- decoded:
- HeaderRecord::Graymap(GraymapHeader {
- encoding: SampleEncoding::Binary,
- width: 4,
- height: 4,
- maxwhite: 255,
- }),
- encoded: _,
- },
- ) => (),
- _ => panic!("Decoded header is incorrect"),
- }
- }
-
- #[test]
- fn pgm_ascii() {
- // The data contains two rows of the image (each line is padded to the full byte). For
- // comments on its format, see documentation of `impl SampleType for PbmBit`.
- let pbmbinary = b"P2 4 4 255\n 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15";
- let decoder = PnmDecoder::new(&pbmbinary[..]).unwrap();
- assert_eq!(decoder.color_type(), ColorType::L8);
- assert_eq!(decoder.dimensions(), (4, 4));
- assert_eq!(
- decoder.subtype(),
- PnmSubtype::Graymap(SampleEncoding::Ascii)
- );
- let mut image = vec![0; decoder.total_bytes() as usize];
- decoder.read_image(&mut image).unwrap();
- assert_eq!(image, (0..16).collect::<Vec<_>>());
- match PnmDecoder::new(&pbmbinary[..]).unwrap().into_inner() {
- (
- _,
- PnmHeader {
- decoded:
- HeaderRecord::Graymap(GraymapHeader {
- encoding: SampleEncoding::Ascii,
- width: 4,
- height: 4,
- maxwhite: 255,
- }),
- encoded: _,
- },
- ) => (),
- _ => panic!("Decoded header is incorrect"),
- }
- }
-
- #[test]
- fn dimension_overflow() {
- let pamdata = b"P7
-# Comment line
-MAXVAL 255
-TUPLTYPE RGB
-DEPTH 3
-WIDTH 4294967295
-HEIGHT 4294967295
-ENDHDR
-\xde\xad\xbe\xef\xde\xad\xbe\xef\xde\xad\xbe\xef";
-
- assert!(PnmDecoder::new(&pamdata[..]).is_err());
- }
-
- #[test]
- fn issue_1508() {
- let _ = crate::load_from_memory(b"P391919 16999 1 1 9 919 16999 1 9999 999* 99999 N");
- }
-
- #[test]
- fn issue_1616_overflow() {
- let data = vec![
- 80, 54, 10, 52, 50, 57, 52, 56, 50, 57, 52, 56, 35, 56, 10, 52, 10, 48, 10, 12, 12, 56,
- ];
- // Validate: we have a header. Note: we might already calculate that this will fail but
- // then we could not return information about the header to the caller.
- let decoder = PnmDecoder::new(&data[..]).unwrap();
- let mut image = vec![0; decoder.total_bytes() as usize];
- let _ = decoder.read_image(&mut image);
- }
-}
diff --git a/vendor/image/src/codecs/pnm/encoder.rs b/vendor/image/src/codecs/pnm/encoder.rs
deleted file mode 100644
index 9f823d0..0000000
--- a/vendor/image/src/codecs/pnm/encoder.rs
+++ /dev/null
@@ -1,673 +0,0 @@
-//! Encoding of PNM Images
-use std::fmt;
-use std::io;
-
-use std::io::Write;
-
-use super::AutoBreak;
-use super::{ArbitraryHeader, ArbitraryTuplType, BitmapHeader, GraymapHeader, PixmapHeader};
-use super::{HeaderRecord, PnmHeader, PnmSubtype, SampleEncoding};
-use crate::color::{ColorType, ExtendedColorType};
-use crate::error::{
- ImageError, ImageResult, ParameterError, ParameterErrorKind, UnsupportedError,
- UnsupportedErrorKind,
-};
-use crate::image::{ImageEncoder, ImageFormat};
-
-use byteorder::{BigEndian, WriteBytesExt};
-
-enum HeaderStrategy {
- Dynamic,
- Subtype(PnmSubtype),
- Chosen(PnmHeader),
-}
-
-#[derive(Clone, Copy)]
-pub enum FlatSamples<'a> {
- U8(&'a [u8]),
- U16(&'a [u16]),
-}
-
-/// Encodes images to any of the `pnm` image formats.
-pub struct PnmEncoder<W: Write> {
- writer: W,
- header: HeaderStrategy,
-}
-
-/// Encapsulate the checking system in the type system. Non of the fields are actually accessed
-/// but requiring them forces us to validly construct the struct anyways.
-struct CheckedImageBuffer<'a> {
- _image: FlatSamples<'a>,
- _width: u32,
- _height: u32,
- _color: ExtendedColorType,
-}
-
-// Check the header against the buffer. Each struct produces the next after a check.
-struct UncheckedHeader<'a> {
- header: &'a PnmHeader,
-}
-
-struct CheckedDimensions<'a> {
- unchecked: UncheckedHeader<'a>,
- width: u32,
- height: u32,
-}
-
-struct CheckedHeaderColor<'a> {
- dimensions: CheckedDimensions<'a>,
- color: ExtendedColorType,
-}
-
-struct CheckedHeader<'a> {
- color: CheckedHeaderColor<'a>,
- encoding: TupleEncoding<'a>,
- _image: CheckedImageBuffer<'a>,
-}
-
-enum TupleEncoding<'a> {
- PbmBits {
- samples: FlatSamples<'a>,
- width: u32,
- },
- Ascii {
- samples: FlatSamples<'a>,
- },
- Bytes {
- samples: FlatSamples<'a>,
- },
-}
-
-impl<W: Write> PnmEncoder<W> {
- /// Create new PnmEncoder from the `writer`.
- ///
- /// The encoded images will have some `pnm` format. If more control over the image type is
- /// required, use either one of `with_subtype` or `with_header`. For more information on the
- /// behaviour, see `with_dynamic_header`.
- pub fn new(writer: W) -> Self {
- PnmEncoder {
- writer,
- header: HeaderStrategy::Dynamic,
- }
- }
-
- /// Encode a specific pnm subtype image.
- ///
- /// The magic number and encoding type will be chosen as provided while the rest of the header
- /// data will be generated dynamically. Trying to encode incompatible images (e.g. encoding an
- /// RGB image as Graymap) will result in an error.
- ///
- /// This will overwrite the effect of earlier calls to `with_header` and `with_dynamic_header`.
- pub fn with_subtype(self, subtype: PnmSubtype) -> Self {
- PnmEncoder {
- writer: self.writer,
- header: HeaderStrategy::Subtype(subtype),
- }
- }
-
- /// Enforce the use of a chosen header.
- ///
- /// While this option gives the most control over the actual written data, the encoding process
- /// will error in case the header data and image parameters do not agree. It is the users
- /// obligation to ensure that the width and height are set accordingly, for example.
- ///
- /// Choose this option if you want a lossless decoding/encoding round trip.
- ///
- /// This will overwrite the effect of earlier calls to `with_subtype` and `with_dynamic_header`.
- pub fn with_header(self, header: PnmHeader) -> Self {
- PnmEncoder {
- writer: self.writer,
- header: HeaderStrategy::Chosen(header),
- }
- }
-
- /// Create the header dynamically for each image.
- ///
- /// This is the default option upon creation of the encoder. With this, most images should be
- /// encodable but the specific format chosen is out of the users control. The pnm subtype is
- /// chosen arbitrarily by the library.
- ///
- /// This will overwrite the effect of earlier calls to `with_subtype` and `with_header`.
- pub fn with_dynamic_header(self) -> Self {
- PnmEncoder {
- writer: self.writer,
- header: HeaderStrategy::Dynamic,
- }
- }
-
- /// Encode an image whose samples are represented as `u8`.
- ///
- /// Some `pnm` subtypes are incompatible with some color options, a chosen header most
- /// certainly with any deviation from the original decoded image.
- pub fn encode<'s, S>(
- &mut self,
- image: S,
- width: u32,
- height: u32,
- color: ColorType,
- ) -> ImageResult<()>
- where
- S: Into<FlatSamples<'s>>,
- {
- let image = image.into();
- match self.header {
- HeaderStrategy::Dynamic => {
- self.write_dynamic_header(image, width, height, color.into())
- }
- HeaderStrategy::Subtype(subtype) => {
- self.write_subtyped_header(subtype, image, width, height, color.into())
- }
- HeaderStrategy::Chosen(ref header) => Self::write_with_header(
- &mut self.writer,
- header,
- image,
- width,
- height,
- color.into(),
- ),
- }
- }
-
- /// Choose any valid pnm format that the image can be expressed in and write its header.
- ///
- /// Returns how the body should be written if successful.
- fn write_dynamic_header(
- &mut self,
- image: FlatSamples,
- width: u32,
- height: u32,
- color: ExtendedColorType,
- ) -> ImageResult<()> {
- let depth = u32::from(color.channel_count());
- let (maxval, tupltype) = match color {
- ExtendedColorType::L1 => (1, ArbitraryTuplType::BlackAndWhite),
- ExtendedColorType::L8 => (0xff, ArbitraryTuplType::Grayscale),
- ExtendedColorType::L16 => (0xffff, ArbitraryTuplType::Grayscale),
- ExtendedColorType::La1 => (1, ArbitraryTuplType::BlackAndWhiteAlpha),
- ExtendedColorType::La8 => (0xff, ArbitraryTuplType::GrayscaleAlpha),
- ExtendedColorType::La16 => (0xffff, ArbitraryTuplType::GrayscaleAlpha),
- ExtendedColorType::Rgb8 => (0xff, ArbitraryTuplType::RGB),
- ExtendedColorType::Rgb16 => (0xffff, ArbitraryTuplType::RGB),
- ExtendedColorType::Rgba8 => (0xff, ArbitraryTuplType::RGBAlpha),
- ExtendedColorType::Rgba16 => (0xffff, ArbitraryTuplType::RGBAlpha),
- _ => {
- return Err(ImageError::Unsupported(
- UnsupportedError::from_format_and_kind(
- ImageFormat::Pnm.into(),
- UnsupportedErrorKind::Color(color),
- ),
- ))
- }
- };
-
- let header = PnmHeader {
- decoded: HeaderRecord::Arbitrary(ArbitraryHeader {
- width,
- height,
- depth,
- maxval,
- tupltype: Some(tupltype),
- }),
- encoded: None,
- };
-
- Self::write_with_header(&mut self.writer, &header, image, width, height, color)
- }
-
- /// Try to encode the image with the chosen format, give its corresponding pixel encoding type.
- fn write_subtyped_header(
- &mut self,
- subtype: PnmSubtype,
- image: FlatSamples,
- width: u32,
- height: u32,
- color: ExtendedColorType,
- ) -> ImageResult<()> {
- let header = match (subtype, color) {
- (PnmSubtype::ArbitraryMap, color) => {
- return self.write_dynamic_header(image, width, height, color)
- }
- (PnmSubtype::Pixmap(encoding), ExtendedColorType::Rgb8) => PnmHeader {
- decoded: HeaderRecord::Pixmap(PixmapHeader {
- encoding,
- width,
- height,
- maxval: 255,
- }),
- encoded: None,
- },
- (PnmSubtype::Graymap(encoding), ExtendedColorType::L8) => PnmHeader {
- decoded: HeaderRecord::Graymap(GraymapHeader {
- encoding,
- width,
- height,
- maxwhite: 255,
- }),
- encoded: None,
- },
- (PnmSubtype::Bitmap(encoding), ExtendedColorType::L8)
- | (PnmSubtype::Bitmap(encoding), ExtendedColorType::L1) => PnmHeader {
- decoded: HeaderRecord::Bitmap(BitmapHeader {
- encoding,
- width,
- height,
- }),
- encoded: None,
- },
- (_, _) => {
- return Err(ImageError::Parameter(ParameterError::from_kind(
- ParameterErrorKind::Generic(
- "Color type can not be represented in the chosen format".to_owned(),
- ),
- )));
- }
- };
-
- Self::write_with_header(&mut self.writer, &header, image, width, height, color)
- }
-
- /// Try to encode the image with the chosen header, checking if values are correct.
- ///
- /// Returns how the body should be written if successful.
- fn write_with_header(
- writer: &mut dyn Write,
- header: &PnmHeader,
- image: FlatSamples,
- width: u32,
- height: u32,
- color: ExtendedColorType,
- ) -> ImageResult<()> {
- let unchecked = UncheckedHeader { header };
-
- unchecked
- .check_header_dimensions(width, height)?
- .check_header_color(color)?
- .check_sample_values(image)?
- .write_header(writer)?
- .write_image(writer)
- }
-}
-
-impl<W: Write> ImageEncoder for PnmEncoder<W> {
- fn write_image(
- mut self,
- buf: &[u8],
- width: u32,
- height: u32,
- color_type: ColorType,
- ) -> ImageResult<()> {
- self.encode(buf, width, height, color_type)
- }
-}
-
-impl<'a> CheckedImageBuffer<'a> {
- fn check(
- image: FlatSamples<'a>,
- width: u32,
- height: u32,
- color: ExtendedColorType,
- ) -> ImageResult<CheckedImageBuffer<'a>> {
- let components = color.channel_count() as usize;
- let uwidth = width as usize;
- let uheight = height as usize;
- let expected_len = components
- .checked_mul(uwidth)
- .and_then(|v| v.checked_mul(uheight));
- if Some(image.len()) != expected_len {
- // Image buffer does not correspond to size and colour.
- return Err(ImageError::Parameter(ParameterError::from_kind(
- ParameterErrorKind::DimensionMismatch,
- )));
- }
- Ok(CheckedImageBuffer {
- _image: image,
- _width: width,
- _height: height,
- _color: color,
- })
- }
-}
-
-impl<'a> UncheckedHeader<'a> {
- fn check_header_dimensions(
- self,
- width: u32,
- height: u32,
- ) -> ImageResult<CheckedDimensions<'a>> {
- if self.header.width() != width || self.header.height() != height {
- // Chosen header does not match Image dimensions.
- return Err(ImageError::Parameter(ParameterError::from_kind(
- ParameterErrorKind::DimensionMismatch,
- )));
- }
-
- Ok(CheckedDimensions {
- unchecked: self,
- width,
- height,
- })
- }
-}
-
-impl<'a> CheckedDimensions<'a> {
- // Check color compatibility with the header. This will only error when we are certain that
- // the combination is bogus (e.g. combining Pixmap and Palette) but allows uncertain
- // combinations (basically a ArbitraryTuplType::Custom with any color of fitting depth).
- fn check_header_color(self, color: ExtendedColorType) -> ImageResult<CheckedHeaderColor<'a>> {
- let components = u32::from(color.channel_count());
-
- match *self.unchecked.header {
- PnmHeader {
- decoded: HeaderRecord::Bitmap(_),
- ..
- } => match color {
- ExtendedColorType::L1 | ExtendedColorType::L8 | ExtendedColorType::L16 => (),
- _ => {
- return Err(ImageError::Parameter(ParameterError::from_kind(
- ParameterErrorKind::Generic(
- "PBM format only support luma color types".to_owned(),
- ),
- )))
- }
- },
- PnmHeader {
- decoded: HeaderRecord::Graymap(_),
- ..
- } => match color {
- ExtendedColorType::L1 | ExtendedColorType::L8 | ExtendedColorType::L16 => (),
- _ => {
- return Err(ImageError::Parameter(ParameterError::from_kind(
- ParameterErrorKind::Generic(
- "PGM format only support luma color types".to_owned(),
- ),
- )))
- }
- },
- PnmHeader {
- decoded: HeaderRecord::Pixmap(_),
- ..
- } => match color {
- ExtendedColorType::Rgb8 => (),
- _ => {
- return Err(ImageError::Parameter(ParameterError::from_kind(
- ParameterErrorKind::Generic(
- "PPM format only support ExtendedColorType::Rgb8".to_owned(),
- ),
- )))
- }
- },
- PnmHeader {
- decoded:
- HeaderRecord::Arbitrary(ArbitraryHeader {
- depth,
- ref tupltype,
- ..
- }),
- ..
- } => match (tupltype, color) {
- (&Some(ArbitraryTuplType::BlackAndWhite), ExtendedColorType::L1) => (),
- (&Some(ArbitraryTuplType::BlackAndWhiteAlpha), ExtendedColorType::La8) => (),
-
- (&Some(ArbitraryTuplType::Grayscale), ExtendedColorType::L1) => (),
- (&Some(ArbitraryTuplType::Grayscale), ExtendedColorType::L8) => (),
- (&Some(ArbitraryTuplType::Grayscale), ExtendedColorType::L16) => (),
- (&Some(ArbitraryTuplType::GrayscaleAlpha), ExtendedColorType::La8) => (),
-
- (&Some(ArbitraryTuplType::RGB), ExtendedColorType::Rgb8) => (),
- (&Some(ArbitraryTuplType::RGBAlpha), ExtendedColorType::Rgba8) => (),
-
- (&None, _) if depth == components => (),
- (&Some(ArbitraryTuplType::Custom(_)), _) if depth == components => (),
- _ if depth != components => {
- return Err(ImageError::Parameter(ParameterError::from_kind(
- ParameterErrorKind::Generic(format!(
- "Depth mismatch: header {} vs. color {}",
- depth, components
- )),
- )))
- }
- _ => {
- return Err(ImageError::Parameter(ParameterError::from_kind(
- ParameterErrorKind::Generic(
- "Invalid color type for selected PAM color type".to_owned(),
- ),
- )))
- }
- },
- }
-
- Ok(CheckedHeaderColor {
- dimensions: self,
- color,
- })
- }
-}
-
-impl<'a> CheckedHeaderColor<'a> {
- fn check_sample_values(self, image: FlatSamples<'a>) -> ImageResult<CheckedHeader<'a>> {
- let header_maxval = match self.dimensions.unchecked.header.decoded {
- HeaderRecord::Bitmap(_) => 1,
- HeaderRecord::Graymap(GraymapHeader { maxwhite, .. }) => maxwhite,
- HeaderRecord::Pixmap(PixmapHeader { maxval, .. }) => maxval,
- HeaderRecord::Arbitrary(ArbitraryHeader { maxval, .. }) => maxval,
- };
-
- // We trust the image color bit count to be correct at least.
- let max_sample = match self.color {
- ExtendedColorType::Unknown(n) if n <= 16 => (1 << n) - 1,
- ExtendedColorType::L1 => 1,
- ExtendedColorType::L8
- | ExtendedColorType::La8
- | ExtendedColorType::Rgb8
- | ExtendedColorType::Rgba8
- | ExtendedColorType::Bgr8
- | ExtendedColorType::Bgra8 => 0xff,
- ExtendedColorType::L16
- | ExtendedColorType::La16
- | ExtendedColorType::Rgb16
- | ExtendedColorType::Rgba16 => 0xffff,
- _ => {
- // Unsupported target color type.
- return Err(ImageError::Unsupported(
- UnsupportedError::from_format_and_kind(
- ImageFormat::Pnm.into(),
- UnsupportedErrorKind::Color(self.color),
- ),
- ));
- }
- };
-
- // Avoid the performance heavy check if possible, e.g. if the header has been chosen by us.
- if header_maxval < max_sample && !image.all_smaller(header_maxval) {
- // Sample value greater than allowed for chosen header.
- return Err(ImageError::Unsupported(
- UnsupportedError::from_format_and_kind(
- ImageFormat::Pnm.into(),
- UnsupportedErrorKind::GenericFeature(
- "Sample value greater than allowed for chosen header".to_owned(),
- ),
- ),
- ));
- }
-
- let encoding = image.encoding_for(&self.dimensions.unchecked.header.decoded);
-
- let image = CheckedImageBuffer::check(
- image,
- self.dimensions.width,
- self.dimensions.height,
- self.color,
- )?;
-
- Ok(CheckedHeader {
- color: self,
- encoding,
- _image: image,
- })
- }
-}
-
-impl<'a> CheckedHeader<'a> {
- fn write_header(self, writer: &mut dyn Write) -> ImageResult<TupleEncoding<'a>> {
- self.header().write(writer)?;
- Ok(self.encoding)
- }
-
- fn header(&self) -> &PnmHeader {
- self.color.dimensions.unchecked.header
- }
-}
-
-struct SampleWriter<'a>(&'a mut dyn Write);
-
-impl<'a> SampleWriter<'a> {
- fn write_samples_ascii<V>(self, samples: V) -> io::Result<()>
- where
- V: Iterator,
- V::Item: fmt::Display,
- {
- let mut auto_break_writer = AutoBreak::new(self.0, 70);
- for value in samples {
- write!(auto_break_writer, "{} ", value)?;
- }
- auto_break_writer.flush()
- }
-
- fn write_pbm_bits<V>(self, samples: &[V], width: u32) -> io::Result<()>
- /* Default gives 0 for all primitives. TODO: replace this with `Zeroable` once it hits stable */
- where
- V: Default + Eq + Copy,
- {
- // The length of an encoded scanline
- let line_width = (width - 1) / 8 + 1;
-
- // We'll be writing single bytes, so buffer
- let mut line_buffer = Vec::with_capacity(line_width as usize);
-
- for line in samples.chunks(width as usize) {
- for byte_bits in line.chunks(8) {
- let mut byte = 0u8;
- for i in 0..8 {
- // Black pixels are encoded as 1s
- if let Some(&v) = byte_bits.get(i) {
- if v == V::default() {
- byte |= 1u8 << (7 - i)
- }
- }
- }
- line_buffer.push(byte)
- }
- self.0.write_all(line_buffer.as_slice())?;
- line_buffer.clear();
- }
-
- self.0.flush()
- }
-}
-
-impl<'a> FlatSamples<'a> {
- fn len(&self) -> usize {
- match *self {
- FlatSamples::U8(arr) => arr.len(),
- FlatSamples::U16(arr) => arr.len(),
- }
- }
-
- fn all_smaller(&self, max_val: u32) -> bool {
- match *self {
- FlatSamples::U8(arr) => arr.iter().any(|&val| u32::from(val) > max_val),
- FlatSamples::U16(arr) => arr.iter().any(|&val| u32::from(val) > max_val),
- }
- }
-
- fn encoding_for(&self, header: &HeaderRecord) -> TupleEncoding<'a> {
- match *header {
- HeaderRecord::Bitmap(BitmapHeader {
- encoding: SampleEncoding::Binary,
- width,
- ..
- }) => TupleEncoding::PbmBits {
- samples: *self,
- width,
- },
-
- HeaderRecord::Bitmap(BitmapHeader {
- encoding: SampleEncoding::Ascii,
- ..
- }) => TupleEncoding::Ascii { samples: *self },
-
- HeaderRecord::Arbitrary(_) => TupleEncoding::Bytes { samples: *self },
-
- HeaderRecord::Graymap(GraymapHeader {
- encoding: SampleEncoding::Ascii,
- ..
- })
- | HeaderRecord::Pixmap(PixmapHeader {
- encoding: SampleEncoding::Ascii,
- ..
- }) => TupleEncoding::Ascii { samples: *self },
-
- HeaderRecord::Graymap(GraymapHeader {
- encoding: SampleEncoding::Binary,
- ..
- })
- | HeaderRecord::Pixmap(PixmapHeader {
- encoding: SampleEncoding::Binary,
- ..
- }) => TupleEncoding::Bytes { samples: *self },
- }
- }
-}
-
-impl<'a> From<&'a [u8]> for FlatSamples<'a> {
- fn from(samples: &'a [u8]) -> Self {
- FlatSamples::U8(samples)
- }
-}
-
-impl<'a> From<&'a [u16]> for FlatSamples<'a> {
- fn from(samples: &'a [u16]) -> Self {
- FlatSamples::U16(samples)
- }
-}
-
-impl<'a> TupleEncoding<'a> {
- fn write_image(&self, writer: &mut dyn Write) -> ImageResult<()> {
- match *self {
- TupleEncoding::PbmBits {
- samples: FlatSamples::U8(samples),
- width,
- } => SampleWriter(writer)
- .write_pbm_bits(samples, width)
- .map_err(ImageError::IoError),
- TupleEncoding::PbmBits {
- samples: FlatSamples::U16(samples),
- width,
- } => SampleWriter(writer)
- .write_pbm_bits(samples, width)
- .map_err(ImageError::IoError),
-
- TupleEncoding::Bytes {
- samples: FlatSamples::U8(samples),
- } => writer.write_all(samples).map_err(ImageError::IoError),
- TupleEncoding::Bytes {
- samples: FlatSamples::U16(samples),
- } => samples.iter().try_for_each(|&sample| {
- writer
- .write_u16::<BigEndian>(sample)
- .map_err(ImageError::IoError)
- }),
-
- TupleEncoding::Ascii {
- samples: FlatSamples::U8(samples),
- } => SampleWriter(writer)
- .write_samples_ascii(samples.iter())
- .map_err(ImageError::IoError),
- TupleEncoding::Ascii {
- samples: FlatSamples::U16(samples),
- } => SampleWriter(writer)
- .write_samples_ascii(samples.iter())
- .map_err(ImageError::IoError),
- }
- }
-}
diff --git a/vendor/image/src/codecs/pnm/header.rs b/vendor/image/src/codecs/pnm/header.rs
deleted file mode 100644
index 443a701..0000000
--- a/vendor/image/src/codecs/pnm/header.rs
+++ /dev/null
@@ -1,354 +0,0 @@
-use std::{fmt, io};
-
-/// The kind of encoding used to store sample values
-#[derive(Clone, Copy, PartialEq, Eq, Debug)]
-pub enum SampleEncoding {
- /// Samples are unsigned binary integers in big endian
- Binary,
-
- /// Samples are encoded as decimal ascii strings separated by whitespace
- Ascii,
-}
-
-/// Denotes the category of the magic number
-#[derive(Clone, Copy, PartialEq, Eq, Debug)]
-pub enum PnmSubtype {
- /// Magic numbers P1 and P4
- Bitmap(SampleEncoding),
-
- /// Magic numbers P2 and P5
- Graymap(SampleEncoding),
-
- /// Magic numbers P3 and P6
- Pixmap(SampleEncoding),
-
- /// Magic number P7
- ArbitraryMap,
-}
-
-/// Stores the complete header data of a file.
-///
-/// Internally, provides mechanisms for lossless reencoding. After reading a file with the decoder
-/// it is possible to recover the header and construct an encoder. Using the encoder on the just
-/// loaded image should result in a byte copy of the original file (for single image pnms without
-/// additional trailing data).
-pub struct PnmHeader {
- pub(crate) decoded: HeaderRecord,
- pub(crate) encoded: Option<Vec<u8>>,
-}
-
-pub(crate) enum HeaderRecord {
- Bitmap(BitmapHeader),
- Graymap(GraymapHeader),
- Pixmap(PixmapHeader),
- Arbitrary(ArbitraryHeader),
-}
-
-/// Header produced by a `pbm` file ("Portable Bit Map")
-#[derive(Clone, Copy, Debug)]
-pub struct BitmapHeader {
- /// Binary or Ascii encoded file
- pub encoding: SampleEncoding,
-
- /// Height of the image file
- pub height: u32,
-
- /// Width of the image file
- pub width: u32,
-}
-
-/// Header produced by a `pgm` file ("Portable Gray Map")
-#[derive(Clone, Copy, Debug)]
-pub struct GraymapHeader {
- /// Binary or Ascii encoded file
- pub encoding: SampleEncoding,
-
- /// Height of the image file
- pub height: u32,
-
- /// Width of the image file
- pub width: u32,
-
- /// Maximum sample value within the image
- pub maxwhite: u32,
-}
-
-/// Header produced by a `ppm` file ("Portable Pixel Map")
-#[derive(Clone, Copy, Debug)]
-pub struct PixmapHeader {
- /// Binary or Ascii encoded file
- pub encoding: SampleEncoding,
-
- /// Height of the image file
- pub height: u32,
-
- /// Width of the image file
- pub width: u32,
-
- /// Maximum sample value within the image
- pub maxval: u32,
-}
-
-/// Header produced by a `pam` file ("Portable Arbitrary Map")
-#[derive(Clone, Debug)]
-pub struct ArbitraryHeader {
- /// Height of the image file
- pub height: u32,
-
- /// Width of the image file
- pub width: u32,
-
- /// Number of color channels
- pub depth: u32,
-
- /// Maximum sample value within the image
- pub maxval: u32,
-
- /// Color interpretation of image pixels
- pub tupltype: Option<ArbitraryTuplType>,
-}
-
-/// Standardized tuple type specifiers in the header of a `pam`.
-#[derive(Clone, Debug)]
-pub enum ArbitraryTuplType {
- /// Pixels are either black (0) or white (1)
- BlackAndWhite,
-
- /// Pixels are either black (0) or white (1) and a second alpha channel
- BlackAndWhiteAlpha,
-
- /// Pixels represent the amount of white
- Grayscale,
-
- /// Grayscale with an additional alpha channel
- GrayscaleAlpha,
-
- /// Three channels: Red, Green, Blue
- RGB,
-
- /// Four channels: Red, Green, Blue, Alpha
- RGBAlpha,
-
- /// An image format which is not standardized
- Custom(String),
-}
-
-impl ArbitraryTuplType {
- pub(crate) fn name(&self) -> &str {
- match self {
- ArbitraryTuplType::BlackAndWhite => "BLACKANDWHITE",
- ArbitraryTuplType::BlackAndWhiteAlpha => "BLACKANDWHITE_ALPHA",
- ArbitraryTuplType::Grayscale => "GRAYSCALE",
- ArbitraryTuplType::GrayscaleAlpha => "GRAYSCALE_ALPHA",
- ArbitraryTuplType::RGB => "RGB",
- ArbitraryTuplType::RGBAlpha => "RGB_ALPHA",
- ArbitraryTuplType::Custom(custom) => custom,
- }
- }
-}
-
-impl PnmSubtype {
- /// Get the two magic constant bytes corresponding to this format subtype.
- pub fn magic_constant(self) -> &'static [u8; 2] {
- match self {
- PnmSubtype::Bitmap(SampleEncoding::Ascii) => b"P1",
- PnmSubtype::Graymap(SampleEncoding::Ascii) => b"P2",
- PnmSubtype::Pixmap(SampleEncoding::Ascii) => b"P3",
- PnmSubtype::Bitmap(SampleEncoding::Binary) => b"P4",
- PnmSubtype::Graymap(SampleEncoding::Binary) => b"P5",
- PnmSubtype::Pixmap(SampleEncoding::Binary) => b"P6",
- PnmSubtype::ArbitraryMap => b"P7",
- }
- }
-
- /// Whether samples are stored as binary or as decimal ascii
- pub fn sample_encoding(self) -> SampleEncoding {
- match self {
- PnmSubtype::ArbitraryMap => SampleEncoding::Binary,
- PnmSubtype::Bitmap(enc) => enc,
- PnmSubtype::Graymap(enc) => enc,
- PnmSubtype::Pixmap(enc) => enc,
- }
- }
-}
-
-impl PnmHeader {
- /// Retrieve the format subtype from which the header was created.
- pub fn subtype(&self) -> PnmSubtype {
- match self.decoded {
- HeaderRecord::Bitmap(BitmapHeader { encoding, .. }) => PnmSubtype::Bitmap(encoding),
- HeaderRecord::Graymap(GraymapHeader { encoding, .. }) => PnmSubtype::Graymap(encoding),
- HeaderRecord::Pixmap(PixmapHeader { encoding, .. }) => PnmSubtype::Pixmap(encoding),
- HeaderRecord::Arbitrary(ArbitraryHeader { .. }) => PnmSubtype::ArbitraryMap,
- }
- }
-
- /// The width of the image this header is for.
- pub fn width(&self) -> u32 {
- match self.decoded {
- HeaderRecord::Bitmap(BitmapHeader { width, .. }) => width,
- HeaderRecord::Graymap(GraymapHeader { width, .. }) => width,
- HeaderRecord::Pixmap(PixmapHeader { width, .. }) => width,
- HeaderRecord::Arbitrary(ArbitraryHeader { width, .. }) => width,
- }
- }
-
- /// The height of the image this header is for.
- pub fn height(&self) -> u32 {
- match self.decoded {
- HeaderRecord::Bitmap(BitmapHeader { height, .. }) => height,
- HeaderRecord::Graymap(GraymapHeader { height, .. }) => height,
- HeaderRecord::Pixmap(PixmapHeader { height, .. }) => height,
- HeaderRecord::Arbitrary(ArbitraryHeader { height, .. }) => height,
- }
- }
-
- /// The biggest value a sample can have. In other words, the colour resolution.
- pub fn maximal_sample(&self) -> u32 {
- match self.decoded {
- HeaderRecord::Bitmap(BitmapHeader { .. }) => 1,
- HeaderRecord::Graymap(GraymapHeader { maxwhite, .. }) => maxwhite,
- HeaderRecord::Pixmap(PixmapHeader { maxval, .. }) => maxval,
- HeaderRecord::Arbitrary(ArbitraryHeader { maxval, .. }) => maxval,
- }
- }
-
- /// Retrieve the underlying bitmap header if any
- pub fn as_bitmap(&self) -> Option<&BitmapHeader> {
- match self.decoded {
- HeaderRecord::Bitmap(ref bitmap) => Some(bitmap),
- _ => None,
- }
- }
-
- /// Retrieve the underlying graymap header if any
- pub fn as_graymap(&self) -> Option<&GraymapHeader> {
- match self.decoded {
- HeaderRecord::Graymap(ref graymap) => Some(graymap),
- _ => None,
- }
- }
-
- /// Retrieve the underlying pixmap header if any
- pub fn as_pixmap(&self) -> Option<&PixmapHeader> {
- match self.decoded {
- HeaderRecord::Pixmap(ref pixmap) => Some(pixmap),
- _ => None,
- }
- }
-
- /// Retrieve the underlying arbitrary header if any
- pub fn as_arbitrary(&self) -> Option<&ArbitraryHeader> {
- match self.decoded {
- HeaderRecord::Arbitrary(ref arbitrary) => Some(arbitrary),
- _ => None,
- }
- }
-
- /// Write the header back into a binary stream
- pub fn write(&self, writer: &mut dyn io::Write) -> io::Result<()> {
- writer.write_all(self.subtype().magic_constant())?;
- match *self {
- PnmHeader {
- encoded: Some(ref content),
- ..
- } => writer.write_all(content),
- PnmHeader {
- decoded:
- HeaderRecord::Bitmap(BitmapHeader {
- encoding: _encoding,
- width,
- height,
- }),
- ..
- } => writeln!(writer, "\n{} {}", width, height),
- PnmHeader {
- decoded:
- HeaderRecord::Graymap(GraymapHeader {
- encoding: _encoding,
- width,
- height,
- maxwhite,
- }),
- ..
- } => writeln!(writer, "\n{} {} {}", width, height, maxwhite),
- PnmHeader {
- decoded:
- HeaderRecord::Pixmap(PixmapHeader {
- encoding: _encoding,
- width,
- height,
- maxval,
- }),
- ..
- } => writeln!(writer, "\n{} {} {}", width, height, maxval),
- PnmHeader {
- decoded:
- HeaderRecord::Arbitrary(ArbitraryHeader {
- width,
- height,
- depth,
- maxval,
- ref tupltype,
- }),
- ..
- } => {
- struct TupltypeWriter<'a>(&'a Option<ArbitraryTuplType>);
- impl<'a> fmt::Display for TupltypeWriter<'a> {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self.0 {
- Some(tt) => writeln!(f, "TUPLTYPE {}", tt.name()),
- None => Ok(()),
- }
- }
- }
-
- writeln!(
- writer,
- "\nWIDTH {}\nHEIGHT {}\nDEPTH {}\nMAXVAL {}\n{}ENDHDR",
- width,
- height,
- depth,
- maxval,
- TupltypeWriter(tupltype)
- )
- }
- }
- }
-}
-
-impl From<BitmapHeader> for PnmHeader {
- fn from(header: BitmapHeader) -> Self {
- PnmHeader {
- decoded: HeaderRecord::Bitmap(header),
- encoded: None,
- }
- }
-}
-
-impl From<GraymapHeader> for PnmHeader {
- fn from(header: GraymapHeader) -> Self {
- PnmHeader {
- decoded: HeaderRecord::Graymap(header),
- encoded: None,
- }
- }
-}
-
-impl From<PixmapHeader> for PnmHeader {
- fn from(header: PixmapHeader) -> Self {
- PnmHeader {
- decoded: HeaderRecord::Pixmap(header),
- encoded: None,
- }
- }
-}
-
-impl From<ArbitraryHeader> for PnmHeader {
- fn from(header: ArbitraryHeader) -> Self {
- PnmHeader {
- decoded: HeaderRecord::Arbitrary(header),
- encoded: None,
- }
- }
-}
diff --git a/vendor/image/src/codecs/pnm/mod.rs b/vendor/image/src/codecs/pnm/mod.rs
deleted file mode 100644
index de8612d..0000000
--- a/vendor/image/src/codecs/pnm/mod.rs
+++ /dev/null
@@ -1,184 +0,0 @@
-//! Decoding of netpbm image formats (pbm, pgm, ppm and pam).
-//!
-//! The formats pbm, pgm and ppm are fully supported. The pam decoder recognizes the tuple types
-//! `BLACKANDWHITE`, `GRAYSCALE` and `RGB` and explicitly recognizes but rejects their `_ALPHA`
-//! variants for now as alpha color types are unsupported.
-use self::autobreak::AutoBreak;
-pub use self::decoder::PnmDecoder;
-pub use self::encoder::PnmEncoder;
-use self::header::HeaderRecord;
-pub use self::header::{
- ArbitraryHeader, ArbitraryTuplType, BitmapHeader, GraymapHeader, PixmapHeader,
-};
-pub use self::header::{PnmHeader, PnmSubtype, SampleEncoding};
-
-mod autobreak;
-mod decoder;
-mod encoder;
-mod header;
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use crate::color::ColorType;
- use crate::image::ImageDecoder;
- use byteorder::{ByteOrder, NativeEndian};
-
- fn execute_roundtrip_default(buffer: &[u8], width: u32, height: u32, color: ColorType) {
- let mut encoded_buffer = Vec::new();
-
- {
- let mut encoder = PnmEncoder::new(&mut encoded_buffer);
- encoder
- .encode(buffer, width, height, color)
- .expect("Failed to encode the image buffer");
- }
-
- let (header, loaded_color, loaded_image) = {
- let decoder = PnmDecoder::new(&encoded_buffer[..]).unwrap();
- let color_type = decoder.color_type();
- let mut image = vec![0; decoder.total_bytes() as usize];
- decoder
- .read_image(&mut image)
- .expect("Failed to decode the image");
- let (_, header) = PnmDecoder::new(&encoded_buffer[..]).unwrap().into_inner();
- (header, color_type, image)
- };
-
- assert_eq!(header.width(), width);
- assert_eq!(header.height(), height);
- assert_eq!(loaded_color, color);
- assert_eq!(loaded_image.as_slice(), buffer);
- }
-
- fn execute_roundtrip_with_subtype(
- buffer: &[u8],
- width: u32,
- height: u32,
- color: ColorType,
- subtype: PnmSubtype,
- ) {
- let mut encoded_buffer = Vec::new();
-
- {
- let mut encoder = PnmEncoder::new(&mut encoded_buffer).with_subtype(subtype);
- encoder
- .encode(buffer, width, height, color)
- .expect("Failed to encode the image buffer");
- }
-
- let (header, loaded_color, loaded_image) = {
- let decoder = PnmDecoder::new(&encoded_buffer[..]).unwrap();
- let color_type = decoder.color_type();
- let mut image = vec![0; decoder.total_bytes() as usize];
- decoder
- .read_image(&mut image)
- .expect("Failed to decode the image");
- let (_, header) = PnmDecoder::new(&encoded_buffer[..]).unwrap().into_inner();
- (header, color_type, image)
- };
-
- assert_eq!(header.width(), width);
- assert_eq!(header.height(), height);
- assert_eq!(header.subtype(), subtype);
- assert_eq!(loaded_color, color);
- assert_eq!(loaded_image.as_slice(), buffer);
- }
-
- fn execute_roundtrip_u16(buffer: &[u16], width: u32, height: u32, color: ColorType) {
- let mut encoded_buffer = Vec::new();
-
- {
- let mut encoder = PnmEncoder::new(&mut encoded_buffer);
- encoder
- .encode(buffer, width, height, color)
- .expect("Failed to encode the image buffer");
- }
-
- let (header, loaded_color, loaded_image) = {
- let decoder = PnmDecoder::new(&encoded_buffer[..]).unwrap();
- let color_type = decoder.color_type();
- let mut image = vec![0; decoder.total_bytes() as usize];
- decoder
- .read_image(&mut image)
- .expect("Failed to decode the image");
- let (_, header) = PnmDecoder::new(&encoded_buffer[..]).unwrap().into_inner();
- (header, color_type, image)
- };
-
- let mut buffer_u8 = vec![0; buffer.len() * 2];
- NativeEndian::write_u16_into(buffer, &mut buffer_u8[..]);
-
- assert_eq!(header.width(), width);
- assert_eq!(header.height(), height);
- assert_eq!(loaded_color, color);
- assert_eq!(loaded_image, buffer_u8);
- }
-
- #[test]
- fn roundtrip_gray() {
- #[rustfmt::skip]
- let buf: [u8; 16] = [
- 0, 0, 0, 255,
- 255, 255, 255, 255,
- 255, 0, 255, 0,
- 255, 0, 0, 0,
- ];
-
- execute_roundtrip_default(&buf, 4, 4, ColorType::L8);
- execute_roundtrip_with_subtype(&buf, 4, 4, ColorType::L8, PnmSubtype::ArbitraryMap);
- execute_roundtrip_with_subtype(
- &buf,
- 4,
- 4,
- ColorType::L8,
- PnmSubtype::Graymap(SampleEncoding::Ascii),
- );
- execute_roundtrip_with_subtype(
- &buf,
- 4,
- 4,
- ColorType::L8,
- PnmSubtype::Graymap(SampleEncoding::Binary),
- );
- }
-
- #[test]
- fn roundtrip_rgb() {
- #[rustfmt::skip]
- let buf: [u8; 27] = [
- 0, 0, 0,
- 0, 0, 255,
- 0, 255, 0,
- 0, 255, 255,
- 255, 0, 0,
- 255, 0, 255,
- 255, 255, 0,
- 255, 255, 255,
- 255, 255, 255,
- ];
- execute_roundtrip_default(&buf, 3, 3, ColorType::Rgb8);
- execute_roundtrip_with_subtype(&buf, 3, 3, ColorType::Rgb8, PnmSubtype::ArbitraryMap);
- execute_roundtrip_with_subtype(
- &buf,
- 3,
- 3,
- ColorType::Rgb8,
- PnmSubtype::Pixmap(SampleEncoding::Binary),
- );
- execute_roundtrip_with_subtype(
- &buf,
- 3,
- 3,
- ColorType::Rgb8,
- PnmSubtype::Pixmap(SampleEncoding::Ascii),
- );
- }
-
- #[test]
- fn roundtrip_u16() {
- let buf: [u16; 6] = [0, 1, 0xFFFF, 0x1234, 0x3412, 0xBEAF];
-
- execute_roundtrip_u16(&buf, 6, 1, ColorType::L16);
- }
-}