Skip to main content

ai_image/codecs/pnm/
decoder.rs

1use alloc::{borrow::ToOwned, boxed::Box, format, string::String, string::ToString, vec::Vec};
2use core::error;
3use core::fmt::{self, Display};
4use core::mem::size_of;
5use core::num::ParseIntError;
6use core::str;
7use no_std_io::io::{self, Read};
8#[cfg(not(feature = "std"))]
9use num_traits::float::FloatCore as _;
10
11use super::{ArbitraryHeader, ArbitraryTuplType, BitmapHeader, GraymapHeader, PixmapHeader};
12use super::{HeaderRecord, PnmHeader, PnmSubtype, SampleEncoding};
13use crate::color::{ColorType, ExtendedColorType};
14use crate::error::{
15    DecodingError, ImageError, ImageResult, UnsupportedError, UnsupportedErrorKind,
16};
17use crate::{utils, ImageDecoder, ImageFormat};
18
19/// All errors that can occur when attempting to parse a PNM
20#[derive(Debug, Clone)]
21enum DecoderError {
22    /// PNM's "P[123456]" signature wrong or missing
23    PnmMagicInvalid([u8; 2]),
24    /// Couldn't parse the specified string as an integer from the specified source
25    UnparsableValue(ErrorDataSource, String, ParseIntError),
26
27    /// More than the exactly one allowed plane specified by the format
28    NonAsciiByteInHeader(u8),
29    /// The PAM header contained a non-ASCII byte
30    NonAsciiLineInPamHeader,
31    /// Couldn't parse an integer: expected but did not get an ASCII digit
32    InvalidDigit(ErrorDataSource),
33
34    /// The byte after the P7 magic was not 0x0A NEWLINE
35    NotNewlineAfterP7Magic(u8),
36    /// The PNM header had too few lines
37    UnexpectedPnmHeaderEnd,
38
39    /// The specified line was specified twice
40    HeaderLineDuplicated(PnmHeaderLine),
41    /// The line with the specified ID was not understood
42    HeaderLineUnknown(String),
43    /// At least one of the required lines were missing from the header (are `None` here)
44    ///
45    /// Same names as [`PnmHeaderLine`](enum.PnmHeaderLine.html)
46    #[allow(missing_docs)]
47    HeaderLineMissing {
48        height: Option<u32>,
49        width: Option<u32>,
50        depth: Option<u32>,
51        maxval: Option<u32>,
52    },
53
54    /// Not enough data was provided to the Decoder to decode the image
55    InputTooShort,
56    /// Sample raster contained unexpected byte
57    UnexpectedByteInRaster(u8),
58    /// Specified sample was out of bounds (e.g. >1 in B&W)
59    SampleOutOfBounds(u8),
60    /// The image's maxval is zero
61    MaxvalZero,
62    /// The image's maxval exceeds 0xFFFF
63    MaxvalTooBig(u32),
64
65    /// The specified tuple type supports restricted depths and maxvals, those restrictions were not met
66    InvalidDepthOrMaxval {
67        tuple_type: ArbitraryTuplType,
68        depth: u32,
69        maxval: u32,
70    },
71    /// The specified tuple type supports restricted depths, those restrictions were not met
72    InvalidDepth {
73        tuple_type: ArbitraryTuplType,
74        depth: u32,
75    },
76    /// The tuple type was not recognised by the parser
77    TupleTypeUnrecognised,
78
79    /// Overflowed the specified value when parsing
80    Overflow(ErrorDataSource),
81}
82
83impl Display for DecoderError {
84    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
85        match self {
86            DecoderError::PnmMagicInvalid(magic) => f.write_fmt(format_args!(
87                "Expected magic constant for PNM: P1..P7, got [{:#04X?}, {:#04X?}]",
88                magic[0], magic[1]
89            )),
90            DecoderError::UnparsableValue(src, data, err) => {
91                f.write_fmt(format_args!("Error parsing {data:?} as {src}: {err}"))
92            }
93
94            DecoderError::NonAsciiByteInHeader(c) => {
95                f.write_fmt(format_args!("Non-ASCII character {c:#04X?} in header"))
96            }
97            DecoderError::NonAsciiLineInPamHeader => f.write_str("Non-ASCII line in PAM header"),
98            DecoderError::InvalidDigit(src) => {
99                f.write_fmt(format_args!("Non-ASCII-digit character when parsing number in {src}"))
100            }
101
102            DecoderError::NotNewlineAfterP7Magic(c) => f.write_fmt(format_args!(
103                "Expected newline after P7 magic, got {c:#04X?}"
104            )),
105            DecoderError::UnexpectedPnmHeaderEnd => f.write_str("Unexpected end of PNM header"),
106
107            DecoderError::HeaderLineDuplicated(line) => {
108                f.write_fmt(format_args!("Duplicate {line} line"))
109            }
110            DecoderError::HeaderLineUnknown(identifier) => f.write_fmt(format_args!(
111                "Unknown header line with identifier {identifier:?}"
112            )),
113            DecoderError::HeaderLineMissing {
114                height,
115                width,
116                depth,
117                maxval,
118            } => f.write_fmt(format_args!(
119                "Missing header line: have height={height:?}, width={width:?}, depth={depth:?}, maxval={maxval:?}"
120            )),
121
122            DecoderError::InputTooShort => {
123                f.write_str("Not enough data was provided to the Decoder to decode the image")
124            }
125            DecoderError::UnexpectedByteInRaster(c) => f.write_fmt(format_args!(
126                "Unexpected character {c:#04X?} within sample raster"
127            )),
128            DecoderError::SampleOutOfBounds(val) => {
129                f.write_fmt(format_args!("Sample value {val} outside of bounds"))
130            }
131            DecoderError::MaxvalZero => f.write_str("Image MAXVAL is zero"),
132            DecoderError::MaxvalTooBig(maxval) => {
133                f.write_fmt(format_args!("Image MAXVAL exceeds {}: {}", 0xFFFF, maxval))
134            }
135
136            DecoderError::InvalidDepthOrMaxval {
137                tuple_type,
138                depth,
139                maxval,
140            } => f.write_fmt(format_args!(
141                "Invalid depth ({}) or maxval ({}) for tuple type {}",
142                depth,
143                maxval,
144                tuple_type.name()
145            )),
146            DecoderError::InvalidDepth { tuple_type, depth } => f.write_fmt(format_args!(
147                "Invalid depth ({}) for tuple type {}",
148                depth,
149                tuple_type.name()
150            )),
151            DecoderError::TupleTypeUnrecognised => f.write_str("Tuple type not recognized"),
152            DecoderError::Overflow(src) => f.write_fmt(format_args!(
153                "Overflow when parsing integer in {src}"
154            ))
155        }
156    }
157}
158
159/// Note: should `pnm` be extracted into a separate crate,
160/// this will need to be hidden until that crate hits version `1.0`.
161impl From<DecoderError> for ImageError {
162    fn from(e: DecoderError) -> ImageError {
163        ImageError::Decoding(DecodingError::new(ImageFormat::Pnm.into(), e))
164    }
165}
166
167impl error::Error for DecoderError {
168    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
169        match self {
170            DecoderError::UnparsableValue(_, _, err) => Some(err),
171            _ => None,
172        }
173    }
174}
175
176/// Single-value lines in a PNM header
177#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
178enum PnmHeaderLine {
179    /// "HEIGHT"
180    Height,
181    /// "WIDTH"
182    Width,
183    /// "DEPTH"
184    Depth,
185    /// "MAXVAL", a.k.a. `maxwhite`
186    Maxval,
187}
188
189impl Display for PnmHeaderLine {
190    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
191        f.write_str(match self {
192            PnmHeaderLine::Height => "HEIGHT",
193            PnmHeaderLine::Width => "WIDTH",
194            PnmHeaderLine::Depth => "DEPTH",
195            PnmHeaderLine::Maxval => "MAXVAL",
196        })
197    }
198}
199
200/// Single-value lines in a PNM header
201#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
202enum ErrorDataSource {
203    /// One of the header lines
204    Line(PnmHeaderLine),
205    /// Value in the preamble
206    Preamble,
207    /// Sample/pixel data
208    Sample,
209}
210
211impl Display for ErrorDataSource {
212    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
213        match self {
214            ErrorDataSource::Line(l) => l.fmt(f),
215            ErrorDataSource::Preamble => f.write_str("number in preamble"),
216            ErrorDataSource::Sample => f.write_str("sample"),
217        }
218    }
219}
220
221/// Dynamic representation, represents all decodable (sample, depth) combinations.
222#[derive(Clone, Copy)]
223enum TupleType {
224    PbmBit,
225    BWBit,
226    BWAlphaBit,
227    GrayU8,
228    GrayAlphaU8,
229    GrayU16,
230    GrayAlphaU16,
231    RGBU8,
232    RGBAlphaU8,
233    RGBU16,
234    RGBAlphaU16,
235}
236
237trait Sample {
238    type Representation;
239
240    /// Representation size in bytes
241    fn sample_size() -> u32 {
242        size_of::<Self::Representation>() as u32
243    }
244    fn from_bytes(
245        reader: &mut dyn Read,
246        output_buf: &mut [u8],
247        width: u32,
248        height: u32,
249        components: u32,
250    ) -> ImageResult<()>;
251    fn from_ascii(reader: &mut dyn Read, output_buf: &mut [u8]) -> ImageResult<()>;
252}
253
254struct U8;
255struct U16;
256struct PbmBit;
257struct BWBit;
258
259trait DecodableImageHeader {
260    fn tuple_type(&self) -> ImageResult<TupleType>;
261}
262
263/// PNM decoder
264pub struct PnmDecoder<R> {
265    reader: R,
266    header: PnmHeader,
267    tuple: TupleType,
268}
269
270impl<R: Read> PnmDecoder<R> {
271    /// Create a new decoder that decodes from the stream ```read```
272    pub fn new(mut buffered_read: R) -> ImageResult<PnmDecoder<R>> {
273        let magic = buffered_read.read_magic_constant()?;
274
275        let subtype = match magic {
276            [b'P', b'1'] => PnmSubtype::Bitmap(SampleEncoding::Ascii),
277            [b'P', b'2'] => PnmSubtype::Graymap(SampleEncoding::Ascii),
278            [b'P', b'3'] => PnmSubtype::Pixmap(SampleEncoding::Ascii),
279            [b'P', b'4'] => PnmSubtype::Bitmap(SampleEncoding::Binary),
280            [b'P', b'5'] => PnmSubtype::Graymap(SampleEncoding::Binary),
281            [b'P', b'6'] => PnmSubtype::Pixmap(SampleEncoding::Binary),
282            [b'P', b'7'] => PnmSubtype::ArbitraryMap,
283            _ => return Err(DecoderError::PnmMagicInvalid(magic).into()),
284        };
285
286        let decoder = match subtype {
287            PnmSubtype::Bitmap(enc) => PnmDecoder::read_bitmap_header(buffered_read, enc),
288            PnmSubtype::Graymap(enc) => PnmDecoder::read_graymap_header(buffered_read, enc),
289            PnmSubtype::Pixmap(enc) => PnmDecoder::read_pixmap_header(buffered_read, enc),
290            PnmSubtype::ArbitraryMap => PnmDecoder::read_arbitrary_header(buffered_read),
291        }?;
292
293        if utils::check_dimension_overflow(
294            decoder.dimensions().0,
295            decoder.dimensions().1,
296            decoder.color_type().bytes_per_pixel(),
297        ) {
298            return Err(ImageError::Unsupported(
299                UnsupportedError::from_format_and_kind(
300                    ImageFormat::Pnm.into(),
301                    UnsupportedErrorKind::GenericFeature(format!(
302                        "Image dimensions ({}x{}) are too large",
303                        decoder.dimensions().0,
304                        decoder.dimensions().1
305                    )),
306                ),
307            ));
308        }
309
310        Ok(decoder)
311    }
312
313    /// Get the header of the decoded image.
314    pub fn header(&self) -> &PnmHeader {
315        &self.header
316    }
317
318    /// Extract the reader and header after an image has been read.
319    pub fn into_inner(self) -> (R, PnmHeader) {
320        (self.reader, self.header)
321    }
322
323    fn read_bitmap_header(mut reader: R, encoding: SampleEncoding) -> ImageResult<PnmDecoder<R>> {
324        let header = reader.read_bitmap_header(encoding)?;
325        Ok(PnmDecoder {
326            reader,
327            tuple: TupleType::PbmBit,
328            header: PnmHeader {
329                decoded: HeaderRecord::Bitmap(header),
330                encoded: None,
331            },
332        })
333    }
334
335    fn read_graymap_header(mut reader: R, encoding: SampleEncoding) -> ImageResult<PnmDecoder<R>> {
336        let header = reader.read_graymap_header(encoding)?;
337        let tuple_type = header.tuple_type()?;
338        Ok(PnmDecoder {
339            reader,
340            tuple: tuple_type,
341            header: PnmHeader {
342                decoded: HeaderRecord::Graymap(header),
343                encoded: None,
344            },
345        })
346    }
347
348    fn read_pixmap_header(mut reader: R, encoding: SampleEncoding) -> ImageResult<PnmDecoder<R>> {
349        let header = reader.read_pixmap_header(encoding)?;
350        let tuple_type = header.tuple_type()?;
351        Ok(PnmDecoder {
352            reader,
353            tuple: tuple_type,
354            header: PnmHeader {
355                decoded: HeaderRecord::Pixmap(header),
356                encoded: None,
357            },
358        })
359    }
360
361    fn read_arbitrary_header(mut reader: R) -> ImageResult<PnmDecoder<R>> {
362        let header = reader.read_arbitrary_header()?;
363        let tuple_type = header.tuple_type()?;
364        Ok(PnmDecoder {
365            reader,
366            tuple: tuple_type,
367            header: PnmHeader {
368                decoded: HeaderRecord::Arbitrary(header),
369                encoded: None,
370            },
371        })
372    }
373}
374
375trait HeaderReader: Read {
376    /// Reads the two magic constant bytes
377    fn read_magic_constant(&mut self) -> ImageResult<[u8; 2]> {
378        let mut magic: [u8; 2] = [0, 0];
379        self.read_exact(&mut magic)?;
380        Ok(magic)
381    }
382
383    /// Reads an integer as well as a single whitespace after it, ignoring comments
384    /// and leading whitespace
385    fn read_next_u32(&mut self) -> ImageResult<u32> {
386        // pair input bytes with a bool mask to remove comments
387        #[allow(clippy::unbuffered_bytes)]
388        let mark_comments = self.bytes().scan(true, |partof, read| {
389            let byte = match read {
390                Err(err) => return Some((*partof, Err(err))),
391                Ok(byte) => byte,
392            };
393            let cur_enabled = *partof && byte != b'#';
394            let next_enabled = cur_enabled || (byte == b'\r' || byte == b'\n');
395            *partof = next_enabled;
396            Some((cur_enabled, Ok(byte)))
397        });
398
399        // Streaming parse of the integer. To match Netpbm, this accepts values
400        // with leading zeros, like 000005, but no leading + or -
401        let mut value: u32 = 0;
402        let mut found_digit = false;
403
404        for (_, byte) in mark_comments.filter(|e| e.0) {
405            match byte {
406                Ok(b'\t' | b'\n' | b'\x0b' | b'\x0c' | b'\r' | b' ') => {
407                    if found_digit {
408                        break; // We're done as we already have some content
409                    }
410                }
411                Ok(byte) if !byte.is_ascii() => {
412                    return Err(DecoderError::NonAsciiByteInHeader(byte).into())
413                }
414                Ok(byte) => {
415                    let digit = match byte {
416                        b'0'..=b'9' => u32::from(byte - b'0'),
417                        _ => {
418                            return Err(DecoderError::InvalidDigit(ErrorDataSource::Preamble).into())
419                        }
420                    };
421                    value = value
422                        .checked_mul(10)
423                        .ok_or(DecoderError::Overflow(ErrorDataSource::Preamble))?;
424                    value = value
425                        .checked_add(digit)
426                        .ok_or(DecoderError::Overflow(ErrorDataSource::Preamble))?;
427                    found_digit = true;
428                }
429                Err(_) => break,
430            }
431        }
432
433        if !found_digit {
434            return Err(ImageError::IoError(io::ErrorKind::UnexpectedEof.into()));
435        }
436
437        Ok(value)
438    }
439
440    fn read_next_line(&mut self) -> ImageResult<String> {
441        let mut buffer = Vec::new();
442        loop {
443            let mut byte = [0];
444            if self.read(&mut byte)? == 0 || byte[0] == b'\n' {
445                break;
446            }
447            buffer.push(byte[0]);
448        }
449
450        String::from_utf8(buffer)
451            .map_err(|e| ImageError::Decoding(DecodingError::new(ImageFormat::Pnm.into(), e)))
452    }
453
454    fn read_bitmap_header(&mut self, encoding: SampleEncoding) -> ImageResult<BitmapHeader> {
455        let width = self.read_next_u32()?;
456        let height = self.read_next_u32()?;
457        Ok(BitmapHeader {
458            encoding,
459            height,
460            width,
461        })
462    }
463
464    fn read_graymap_header(&mut self, encoding: SampleEncoding) -> ImageResult<GraymapHeader> {
465        self.read_pixmap_header(encoding).map(
466            |PixmapHeader {
467                 encoding,
468                 width,
469                 height,
470                 maxval,
471             }| GraymapHeader {
472                encoding,
473                width,
474                height,
475                maxwhite: maxval,
476            },
477        )
478    }
479
480    fn read_pixmap_header(&mut self, encoding: SampleEncoding) -> ImageResult<PixmapHeader> {
481        let width = self.read_next_u32()?;
482        let height = self.read_next_u32()?;
483        let maxval = self.read_next_u32()?;
484        Ok(PixmapHeader {
485            encoding,
486            height,
487            width,
488            maxval,
489        })
490    }
491
492    fn read_arbitrary_header(&mut self) -> ImageResult<ArbitraryHeader> {
493        fn parse_single_value_line(
494            line_val: &mut Option<u32>,
495            rest: &str,
496            line: PnmHeaderLine,
497        ) -> ImageResult<()> {
498            if line_val.is_some() {
499                Err(DecoderError::HeaderLineDuplicated(line).into())
500            } else {
501                let v = rest.trim().parse().map_err(|err| {
502                    DecoderError::UnparsableValue(ErrorDataSource::Line(line), rest.to_owned(), err)
503                })?;
504                *line_val = Some(v);
505                Ok(())
506            }
507        }
508
509        #[allow(clippy::unbuffered_bytes)]
510        match self.bytes().next() {
511            None => return Err(ImageError::IoError(io::ErrorKind::UnexpectedEof.into())),
512            Some(Err(io)) => return Err(ImageError::IoError(io)),
513            Some(Ok(b'\n')) => (),
514            Some(Ok(c)) => return Err(DecoderError::NotNewlineAfterP7Magic(c).into()),
515        }
516
517        let mut line;
518        let mut height: Option<u32> = None;
519        let mut width: Option<u32> = None;
520        let mut depth: Option<u32> = None;
521        let mut maxval: Option<u32> = None;
522        let mut tupltype: Option<String> = None;
523        loop {
524            line = self.read_next_line()?;
525            if line.is_empty() {
526                return Err(DecoderError::UnexpectedPnmHeaderEnd.into());
527            }
528            if line.as_bytes()[0] == b'#' {
529                continue;
530            }
531            if !line.is_ascii() {
532                return Err(DecoderError::NonAsciiLineInPamHeader.into());
533            }
534            #[allow(deprecated)]
535            let (identifier, rest) = line
536                .trim_left()
537                .split_at(line.find(char::is_whitespace).unwrap_or(line.len()));
538            match identifier {
539                "ENDHDR" => break,
540                "HEIGHT" => parse_single_value_line(&mut height, rest, PnmHeaderLine::Height)?,
541                "WIDTH" => parse_single_value_line(&mut width, rest, PnmHeaderLine::Width)?,
542                "DEPTH" => parse_single_value_line(&mut depth, rest, PnmHeaderLine::Depth)?,
543                "MAXVAL" => parse_single_value_line(&mut maxval, rest, PnmHeaderLine::Maxval)?,
544                "TUPLTYPE" => {
545                    let identifier = rest.trim();
546                    if tupltype.is_some() {
547                        let appended = tupltype.take().map(|mut v| {
548                            v.push(' ');
549                            v.push_str(identifier);
550                            v
551                        });
552                        tupltype = appended;
553                    } else {
554                        tupltype = Some(identifier.to_string());
555                    }
556                }
557                _ => return Err(DecoderError::HeaderLineUnknown(identifier.to_string()).into()),
558            }
559        }
560
561        let (Some(h), Some(w), Some(d), Some(m)) = (height, width, depth, maxval) else {
562            return Err(DecoderError::HeaderLineMissing {
563                height,
564                width,
565                depth,
566                maxval,
567            }
568            .into());
569        };
570
571        let tupltype = match tupltype {
572            None => None,
573            Some(ref t) if t == "BLACKANDWHITE" => Some(ArbitraryTuplType::BlackAndWhite),
574            Some(ref t) if t == "BLACKANDWHITE_ALPHA" => {
575                Some(ArbitraryTuplType::BlackAndWhiteAlpha)
576            }
577            Some(ref t) if t == "GRAYSCALE" => Some(ArbitraryTuplType::Grayscale),
578            Some(ref t) if t == "GRAYSCALE_ALPHA" => Some(ArbitraryTuplType::GrayscaleAlpha),
579            Some(ref t) if t == "RGB" => Some(ArbitraryTuplType::RGB),
580            Some(ref t) if t == "RGB_ALPHA" => Some(ArbitraryTuplType::RGBAlpha),
581            Some(other) => Some(ArbitraryTuplType::Custom(other)),
582        };
583
584        Ok(ArbitraryHeader {
585            height: h,
586            width: w,
587            depth: d,
588            maxval: m,
589            tupltype,
590        })
591    }
592}
593
594impl<R> HeaderReader for R where R: Read {}
595
596impl<R: Read> ImageDecoder for PnmDecoder<R> {
597    fn dimensions(&self) -> (u32, u32) {
598        (self.header.width(), self.header.height())
599    }
600
601    fn color_type(&self) -> ColorType {
602        match self.tuple {
603            TupleType::PbmBit => ColorType::L8,
604            TupleType::BWBit => ColorType::L8,
605            TupleType::BWAlphaBit => ColorType::La8,
606            TupleType::GrayU8 => ColorType::L8,
607            TupleType::GrayAlphaU8 => ColorType::La8,
608            TupleType::GrayU16 => ColorType::L16,
609            TupleType::GrayAlphaU16 => ColorType::La16,
610            TupleType::RGBU8 => ColorType::Rgb8,
611            TupleType::RGBAlphaU8 => ColorType::Rgba8,
612            TupleType::RGBU16 => ColorType::Rgb16,
613            TupleType::RGBAlphaU16 => ColorType::Rgba16,
614        }
615    }
616
617    fn original_color_type(&self) -> ExtendedColorType {
618        match self.tuple {
619            TupleType::PbmBit => ExtendedColorType::L1,
620            TupleType::BWBit => ExtendedColorType::L1,
621            TupleType::BWAlphaBit => ExtendedColorType::La1,
622            TupleType::GrayU8 => ExtendedColorType::L8,
623            TupleType::GrayAlphaU8 => ExtendedColorType::La8,
624            TupleType::GrayU16 => ExtendedColorType::L16,
625            TupleType::GrayAlphaU16 => ExtendedColorType::La16,
626            TupleType::RGBU8 => ExtendedColorType::Rgb8,
627            TupleType::RGBAlphaU8 => ExtendedColorType::Rgba8,
628            TupleType::RGBU16 => ExtendedColorType::Rgb16,
629            TupleType::RGBAlphaU16 => ExtendedColorType::Rgba16,
630        }
631    }
632
633    fn read_image(mut self, buf: &mut [u8]) -> ImageResult<()> {
634        assert_eq!(u64::try_from(buf.len()), Ok(self.total_bytes()));
635        match self.tuple {
636            TupleType::PbmBit => self.read_samples::<PbmBit>(1, buf),
637            TupleType::BWBit => self.read_samples::<BWBit>(1, buf),
638            TupleType::BWAlphaBit => self.read_samples::<BWBit>(2, buf),
639            TupleType::RGBU8 => self.read_samples::<U8>(3, buf),
640            TupleType::RGBAlphaU8 => self.read_samples::<U8>(4, buf),
641            TupleType::RGBU16 => self.read_samples::<U16>(3, buf),
642            TupleType::RGBAlphaU16 => self.read_samples::<U16>(4, buf),
643            TupleType::GrayU8 => self.read_samples::<U8>(1, buf),
644            TupleType::GrayAlphaU8 => self.read_samples::<U8>(2, buf),
645            TupleType::GrayU16 => self.read_samples::<U16>(1, buf),
646            TupleType::GrayAlphaU16 => self.read_samples::<U16>(2, buf),
647        }
648    }
649
650    fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()> {
651        (*self).read_image(buf)
652    }
653}
654
655impl<R: Read> PnmDecoder<R> {
656    fn read_samples<S: Sample>(&mut self, components: u32, buf: &mut [u8]) -> ImageResult<()> {
657        match self.subtype().sample_encoding() {
658            SampleEncoding::Binary => {
659                S::from_bytes(
660                    &mut self.reader,
661                    buf,
662                    self.header.width(),
663                    self.header.height(),
664                    components,
665                )?;
666            }
667            SampleEncoding::Ascii => {
668                S::from_ascii(&mut self.reader, buf)?;
669            }
670        }
671
672        // Scale samples if 8bit or 16bit is not saturated
673        let current_sample_max = self.header.maximal_sample();
674        let target_sample_max = 256_u32.pow(S::sample_size()) - 1;
675
676        if current_sample_max != target_sample_max {
677            let factor = target_sample_max as f32 / current_sample_max as f32;
678
679            if S::sample_size() == 1 {
680                for v in buf.iter_mut() {
681                    *v = (f32::from(*v) * factor).round() as u8;
682                }
683            } else if S::sample_size() == 2 {
684                for chunk in buf.as_chunks_mut::<2>().0.iter_mut() {
685                    let v = (f32::from(u16::from_ne_bytes(*chunk)) * factor).round() as u16;
686                    chunk.copy_from_slice(&v.to_ne_bytes());
687                }
688            }
689        }
690
691        Ok(())
692    }
693
694    /// Get the pnm subtype, depending on the magic constant contained in the header
695    pub fn subtype(&self) -> PnmSubtype {
696        self.header.subtype()
697    }
698}
699
700fn read_separated_ascii<T: TryFrom<u16>>(reader: &mut dyn Read) -> ImageResult<T> {
701    let is_separator = |v: &u8| matches!(*v, b'\t' | b'\n' | b'\x0b' | b'\x0c' | b'\r' | b' ');
702
703    let mut v: u16 = 0;
704    let mut had_any = false;
705    #[allow(clippy::unbuffered_bytes)]
706    for rc in reader
707        .bytes()
708        .skip_while(|v| v.as_ref().ok().is_some_and(is_separator))
709        .take_while(|v| v.as_ref().ok().is_some_and(|c| !is_separator(c)))
710    {
711        let c = rc?;
712        let digit = match c {
713            b'0'..=b'9' => u16::from(c - b'0'),
714            _ => return Err(DecoderError::InvalidDigit(ErrorDataSource::Sample).into()),
715        };
716        v = v
717            .checked_mul(10)
718            .ok_or(DecoderError::Overflow(ErrorDataSource::Sample))?;
719        v = v
720            .checked_add(digit)
721            .ok_or(DecoderError::Overflow(ErrorDataSource::Sample))?;
722        had_any = true;
723    }
724
725    if !had_any {
726        return Err(DecoderError::InputTooShort.into());
727    }
728
729    Ok(T::try_from(v).or(Err(DecoderError::Overflow(ErrorDataSource::Sample)))?)
730}
731
732impl Sample for U8 {
733    type Representation = u8;
734    fn from_bytes(
735        reader: &mut dyn Read,
736        output_buf: &mut [u8],
737        _width: u32,
738        _height: u32,
739        _components: u32,
740    ) -> ImageResult<()> {
741        reader.read_exact(output_buf)?;
742        Ok(())
743    }
744
745    fn from_ascii(reader: &mut dyn Read, output_buf: &mut [u8]) -> ImageResult<()> {
746        for b in output_buf {
747            *b = read_separated_ascii(reader)?;
748        }
749        Ok(())
750    }
751}
752
753impl Sample for U16 {
754    type Representation = u16;
755
756    fn from_bytes(
757        reader: &mut dyn Read,
758        output_buf: &mut [u8],
759        _width: u32,
760        _height: u32,
761        _components: u32,
762    ) -> ImageResult<()> {
763        reader.read_exact(output_buf)?;
764        for chunk in output_buf.as_chunks_mut::<2>().0.iter_mut() {
765            let v = u16::from_be_bytes(*chunk);
766            chunk.copy_from_slice(&v.to_ne_bytes());
767        }
768        Ok(())
769    }
770
771    fn from_ascii(reader: &mut dyn Read, output_buf: &mut [u8]) -> ImageResult<()> {
772        for chunk in output_buf.as_chunks_mut::<2>().0.iter_mut() {
773            let v = read_separated_ascii::<u16>(reader)?;
774            chunk.copy_from_slice(&v.to_ne_bytes());
775        }
776        Ok(())
777    }
778}
779
780// The image is encoded in rows of bits, high order bits first. Any bits beyond the row bits should
781// be ignored. Also, contrary to rgb, black pixels are encoded as a 1 while white is 0. This will
782// need to be reversed for the grayscale output.
783impl Sample for PbmBit {
784    type Representation = u8;
785
786    fn from_bytes(
787        reader: &mut dyn Read,
788        output_buf: &mut [u8],
789        width: u32,
790        height: u32,
791        components: u32,
792    ) -> ImageResult<()> {
793        assert!(components == 1);
794
795        let width: usize = width
796            .try_into()
797            .map_err(|_| DecoderError::Overflow(ErrorDataSource::Sample))?;
798        let height: usize = height
799            .try_into()
800            .map_err(|_| DecoderError::Overflow(ErrorDataSource::Sample))?;
801        assert!(width.checked_mul(height) == Some(output_buf.len()));
802
803        let linelen = width.div_ceil(8);
804        let bytecount = height
805            .checked_mul(linelen)
806            .filter(|l| *l <= output_buf.len())
807            .expect("PBM packed data is never longer than unpacked");
808
809        reader.read_exact(&mut output_buf[..bytecount])?;
810
811        // Expand the PBM data in place. This can be done byte by byte with a single
812        // backwards pass over the image. At all times, the position being read
813        // from will not be after the position being written to, so no data is lost.
814
815        for y in (0..height).rev() {
816            for x in (0..width).rev() {
817                let shift = 7 - (x % 8);
818                let v = (output_buf[y * linelen + x / 8] >> shift) & 0x1;
819                output_buf[y * width + x] = 1 - v;
820            }
821        }
822        Ok(())
823    }
824
825    fn from_ascii(reader: &mut dyn Read, output_buf: &mut [u8]) -> ImageResult<()> {
826        #[allow(clippy::unbuffered_bytes)]
827        let mut bytes = reader.bytes();
828        for b in output_buf {
829            loop {
830                let byte = bytes
831                    .next()
832                    .ok_or_else::<ImageError, _>(|| DecoderError::InputTooShort.into())??;
833                match byte {
834                    b'\t' | b'\n' | b'\x0b' | b'\x0c' | b'\r' | b' ' => continue,
835                    b'0' => *b = 255,
836                    b'1' => *b = 0,
837                    c => return Err(DecoderError::UnexpectedByteInRaster(c).into()),
838                }
839                break;
840            }
841        }
842
843        Ok(())
844    }
845}
846
847// Encoded just like a normal U8 but we check the values.
848impl Sample for BWBit {
849    type Representation = u8;
850
851    fn from_bytes(
852        reader: &mut dyn Read,
853        output_buf: &mut [u8],
854        width: u32,
855        height: u32,
856        components: u32,
857    ) -> ImageResult<()> {
858        U8::from_bytes(reader, output_buf, width, height, components)?;
859        if let Some(val) = output_buf.iter().find(|&val| *val > 1) {
860            return Err(DecoderError::SampleOutOfBounds(*val).into());
861        }
862        Ok(())
863    }
864
865    fn from_ascii(_reader: &mut dyn Read, _output_buf: &mut [u8]) -> ImageResult<()> {
866        unreachable!("BW bits from anymaps are never encoded as ASCII")
867    }
868}
869
870impl DecodableImageHeader for BitmapHeader {
871    fn tuple_type(&self) -> ImageResult<TupleType> {
872        Ok(TupleType::PbmBit)
873    }
874}
875
876impl DecodableImageHeader for GraymapHeader {
877    fn tuple_type(&self) -> ImageResult<TupleType> {
878        match self.maxwhite {
879            0 => Err(DecoderError::MaxvalZero.into()),
880            v if v <= 0xFF => Ok(TupleType::GrayU8),
881            v if v <= 0xFFFF => Ok(TupleType::GrayU16),
882            _ => Err(DecoderError::MaxvalTooBig(self.maxwhite).into()),
883        }
884    }
885}
886
887impl DecodableImageHeader for PixmapHeader {
888    fn tuple_type(&self) -> ImageResult<TupleType> {
889        match self.maxval {
890            0 => Err(DecoderError::MaxvalZero.into()),
891            v if v <= 0xFF => Ok(TupleType::RGBU8),
892            v if v <= 0xFFFF => Ok(TupleType::RGBU16),
893            _ => Err(DecoderError::MaxvalTooBig(self.maxval).into()),
894        }
895    }
896}
897
898impl DecodableImageHeader for ArbitraryHeader {
899    fn tuple_type(&self) -> ImageResult<TupleType> {
900        match self.tupltype {
901            _ if self.maxval == 0 => Err(DecoderError::MaxvalZero.into()),
902            None if self.depth == 1 => Ok(TupleType::GrayU8),
903            None if self.depth == 2 => Ok(TupleType::GrayAlphaU8),
904            None if self.depth == 3 => Ok(TupleType::RGBU8),
905            None if self.depth == 4 => Ok(TupleType::RGBAlphaU8),
906
907            Some(ArbitraryTuplType::BlackAndWhite) if self.maxval == 1 && self.depth == 1 => {
908                Ok(TupleType::BWBit)
909            }
910            Some(ArbitraryTuplType::BlackAndWhite) => Err(DecoderError::InvalidDepthOrMaxval {
911                tuple_type: ArbitraryTuplType::BlackAndWhite,
912                maxval: self.maxval,
913                depth: self.depth,
914            }
915            .into()),
916
917            Some(ArbitraryTuplType::Grayscale) if self.depth == 1 && self.maxval <= 0xFF => {
918                Ok(TupleType::GrayU8)
919            }
920            Some(ArbitraryTuplType::Grayscale) if self.depth <= 1 && self.maxval <= 0xFFFF => {
921                Ok(TupleType::GrayU16)
922            }
923            Some(ArbitraryTuplType::Grayscale) => Err(DecoderError::InvalidDepthOrMaxval {
924                tuple_type: ArbitraryTuplType::Grayscale,
925                maxval: self.maxval,
926                depth: self.depth,
927            }
928            .into()),
929
930            Some(ArbitraryTuplType::RGB) if self.depth == 3 && self.maxval <= 0xFF => {
931                Ok(TupleType::RGBU8)
932            }
933            Some(ArbitraryTuplType::RGB) if self.depth == 3 && self.maxval <= 0xFFFF => {
934                Ok(TupleType::RGBU16)
935            }
936            Some(ArbitraryTuplType::RGB) => Err(DecoderError::InvalidDepth {
937                tuple_type: ArbitraryTuplType::RGB,
938                depth: self.depth,
939            }
940            .into()),
941
942            Some(ArbitraryTuplType::BlackAndWhiteAlpha) if self.depth == 2 && self.maxval == 1 => {
943                Ok(TupleType::BWAlphaBit)
944            }
945            Some(ArbitraryTuplType::BlackAndWhiteAlpha) => {
946                Err(DecoderError::InvalidDepthOrMaxval {
947                    tuple_type: ArbitraryTuplType::BlackAndWhiteAlpha,
948                    maxval: self.maxval,
949                    depth: self.depth,
950                }
951                .into())
952            }
953
954            Some(ArbitraryTuplType::GrayscaleAlpha) if self.depth == 2 && self.maxval <= 0xFF => {
955                Ok(TupleType::GrayAlphaU8)
956            }
957            Some(ArbitraryTuplType::GrayscaleAlpha) if self.depth == 2 && self.maxval <= 0xFFFF => {
958                Ok(TupleType::GrayAlphaU16)
959            }
960            Some(ArbitraryTuplType::GrayscaleAlpha) => Err(DecoderError::InvalidDepth {
961                tuple_type: ArbitraryTuplType::GrayscaleAlpha,
962                depth: self.depth,
963            }
964            .into()),
965
966            Some(ArbitraryTuplType::RGBAlpha) if self.depth == 4 && self.maxval <= 0xFF => {
967                Ok(TupleType::RGBAlphaU8)
968            }
969            Some(ArbitraryTuplType::RGBAlpha) if self.depth == 4 && self.maxval <= 0xFFFF => {
970                Ok(TupleType::RGBAlphaU16)
971            }
972            Some(ArbitraryTuplType::RGBAlpha) => Err(DecoderError::InvalidDepth {
973                tuple_type: ArbitraryTuplType::RGBAlpha,
974                depth: self.depth,
975            }
976            .into()),
977
978            Some(ArbitraryTuplType::Custom(ref custom)) => Err(ImageError::Unsupported(
979                UnsupportedError::from_format_and_kind(
980                    ImageFormat::Pnm.into(),
981                    UnsupportedErrorKind::GenericFeature(format!("Tuple type {custom:?}")),
982                ),
983            )),
984            None => Err(DecoderError::TupleTypeUnrecognised.into()),
985        }
986    }
987}
988
989#[cfg(test)]
990mod tests {
991    use super::*;
992    /// Tests reading of a valid blackandwhite pam
993    #[test]
994    fn pam_blackandwhite() {
995        let pamdata = b"P7
996WIDTH 4
997HEIGHT 4
998DEPTH 1
999MAXVAL 1
1000TUPLTYPE BLACKANDWHITE
1001# Comment line
1002ENDHDR
1003\x01\x00\x00\x01\x01\x00\x00\x01\x01\x00\x00\x01\x01\x00\x00\x01";
1004        let decoder = PnmDecoder::new(&pamdata[..]).unwrap();
1005        assert_eq!(decoder.color_type(), ColorType::L8);
1006        assert_eq!(decoder.original_color_type(), ExtendedColorType::L1);
1007        assert_eq!(decoder.dimensions(), (4, 4));
1008        assert_eq!(decoder.subtype(), PnmSubtype::ArbitraryMap);
1009
1010        let mut image = vec![0; decoder.total_bytes() as usize];
1011        decoder.read_image(&mut image).unwrap();
1012        assert_eq!(
1013            image,
1014            vec![
1015                0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00,
1016                0x00, 0xFF
1017            ]
1018        );
1019        match PnmDecoder::new(&pamdata[..]).unwrap().into_inner() {
1020            (
1021                _,
1022                PnmHeader {
1023                    decoded:
1024                        HeaderRecord::Arbitrary(ArbitraryHeader {
1025                            width: 4,
1026                            height: 4,
1027                            maxval: 1,
1028                            depth: 1,
1029                            tupltype: Some(ArbitraryTuplType::BlackAndWhite),
1030                        }),
1031                    encoded: _,
1032                },
1033            ) => (),
1034            _ => panic!("Decoded header is incorrect"),
1035        }
1036    }
1037
1038    /// Tests reading of a valid blackandwhite_alpha pam
1039    #[test]
1040    fn pam_blackandwhite_alpha() {
1041        let pamdata = b"P7
1042WIDTH 2
1043HEIGHT 2
1044DEPTH 2
1045MAXVAL 1
1046TUPLTYPE BLACKANDWHITE_ALPHA
1047# Comment line
1048ENDHDR
1049\x01\x00\x00\x01\x01\x00\x00\x01";
1050        let decoder = PnmDecoder::new(&pamdata[..]).unwrap();
1051        assert_eq!(decoder.color_type(), ColorType::La8);
1052        assert_eq!(decoder.original_color_type(), ExtendedColorType::La1);
1053        assert_eq!(decoder.dimensions(), (2, 2));
1054        assert_eq!(decoder.subtype(), PnmSubtype::ArbitraryMap);
1055
1056        let mut image = vec![0; decoder.total_bytes() as usize];
1057        decoder.read_image(&mut image).unwrap();
1058        assert_eq!(image, vec![0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF,]);
1059        match PnmDecoder::new(&pamdata[..]).unwrap().into_inner() {
1060            (
1061                _,
1062                PnmHeader {
1063                    decoded:
1064                        HeaderRecord::Arbitrary(ArbitraryHeader {
1065                            width: 2,
1066                            height: 2,
1067                            maxval: 1,
1068                            depth: 2,
1069                            tupltype: Some(ArbitraryTuplType::BlackAndWhiteAlpha),
1070                        }),
1071                    encoded: _,
1072                },
1073            ) => (),
1074            _ => panic!("Decoded header is incorrect"),
1075        }
1076    }
1077
1078    /// Tests reading of a valid grayscale pam
1079    #[test]
1080    fn pam_grayscale() {
1081        let pamdata = b"P7
1082WIDTH 4
1083HEIGHT 4
1084DEPTH 1
1085MAXVAL 255
1086TUPLTYPE GRAYSCALE
1087# Comment line
1088ENDHDR
1089\xde\xad\xbe\xef\xde\xad\xbe\xef\xde\xad\xbe\xef\xde\xad\xbe\xef";
1090        let decoder = PnmDecoder::new(&pamdata[..]).unwrap();
1091        assert_eq!(decoder.color_type(), ColorType::L8);
1092        assert_eq!(decoder.dimensions(), (4, 4));
1093        assert_eq!(decoder.subtype(), PnmSubtype::ArbitraryMap);
1094
1095        let mut image = vec![0; decoder.total_bytes() as usize];
1096        decoder.read_image(&mut image).unwrap();
1097        assert_eq!(
1098            image,
1099            vec![
1100                0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad,
1101                0xbe, 0xef
1102            ]
1103        );
1104        match PnmDecoder::new(&pamdata[..]).unwrap().into_inner() {
1105            (
1106                _,
1107                PnmHeader {
1108                    decoded:
1109                        HeaderRecord::Arbitrary(ArbitraryHeader {
1110                            width: 4,
1111                            height: 4,
1112                            depth: 1,
1113                            maxval: 255,
1114                            tupltype: Some(ArbitraryTuplType::Grayscale),
1115                        }),
1116                    encoded: _,
1117                },
1118            ) => (),
1119            _ => panic!("Decoded header is incorrect"),
1120        }
1121    }
1122
1123    /// Tests reading of a valid grayscale_alpha pam
1124    #[test]
1125    fn pam_grayscale_alpha() {
1126        let pamdata = b"P7
1127HEIGHT 1
1128WIDTH 2
1129MAXVAL 65535
1130DEPTH 2
1131TUPLTYPE GRAYSCALE_ALPHA
1132# Comment line
1133ENDHDR
1134\xdc\xba\x32\x10\xdc\xba\x32\x10";
1135        let decoder = PnmDecoder::new(&pamdata[..]).unwrap();
1136        assert_eq!(decoder.color_type(), ColorType::La16);
1137        assert_eq!(decoder.original_color_type(), ExtendedColorType::La16);
1138        assert_eq!(decoder.dimensions(), (2, 1));
1139        assert_eq!(decoder.subtype(), PnmSubtype::ArbitraryMap);
1140
1141        let mut image = vec![0; decoder.total_bytes() as usize];
1142        decoder.read_image(&mut image).unwrap();
1143        assert_eq!(
1144            image,
1145            [
1146                u16::to_ne_bytes(0xdcba),
1147                u16::to_ne_bytes(0x3210),
1148                u16::to_ne_bytes(0xdcba),
1149                u16::to_ne_bytes(0x3210)
1150            ]
1151            .concat()
1152        );
1153        match PnmDecoder::new(&pamdata[..]).unwrap().into_inner() {
1154            (
1155                _,
1156                PnmHeader {
1157                    decoded:
1158                        HeaderRecord::Arbitrary(ArbitraryHeader {
1159                            width: 2,
1160                            height: 1,
1161                            maxval: 65535,
1162                            depth: 2,
1163                            tupltype: Some(ArbitraryTuplType::GrayscaleAlpha),
1164                        }),
1165                    encoded: _,
1166                },
1167            ) => (),
1168            _ => panic!("Decoded header is incorrect"),
1169        }
1170    }
1171
1172    /// Tests reading of a valid rgb pam
1173    #[test]
1174    fn pam_rgb() {
1175        let pamdata = b"P7
1176# Comment line
1177MAXVAL 255
1178TUPLTYPE RGB
1179DEPTH 3
1180WIDTH 2
1181HEIGHT 2
1182ENDHDR
1183\xde\xad\xbe\xef\xde\xad\xbe\xef\xde\xad\xbe\xef";
1184        let decoder = PnmDecoder::new(&pamdata[..]).unwrap();
1185        assert_eq!(decoder.color_type(), ColorType::Rgb8);
1186        assert_eq!(decoder.dimensions(), (2, 2));
1187        assert_eq!(decoder.subtype(), PnmSubtype::ArbitraryMap);
1188
1189        let mut image = vec![0; decoder.total_bytes() as usize];
1190        decoder.read_image(&mut image).unwrap();
1191        assert_eq!(
1192            image,
1193            vec![0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef]
1194        );
1195        match PnmDecoder::new(&pamdata[..]).unwrap().into_inner() {
1196            (
1197                _,
1198                PnmHeader {
1199                    decoded:
1200                        HeaderRecord::Arbitrary(ArbitraryHeader {
1201                            maxval: 255,
1202                            tupltype: Some(ArbitraryTuplType::RGB),
1203                            depth: 3,
1204                            width: 2,
1205                            height: 2,
1206                        }),
1207                    encoded: _,
1208                },
1209            ) => (),
1210            _ => panic!("Decoded header is incorrect"),
1211        }
1212    }
1213
1214    /// Tests reading of a valid rgb_alpha pam
1215    #[test]
1216    fn pam_rgb_alpha() {
1217        let pamdata = b"P7
1218WIDTH 1
1219HEIGHT 3
1220DEPTH 4
1221MAXVAL 15
1222TUPLTYPE RGB_ALPHA
1223# Comment line
1224ENDHDR
1225\x00\x01\x02\x03\x0a\x0b\x0c\x0d\x05\x06\x07\x08";
1226        let decoder = PnmDecoder::new(&pamdata[..]).unwrap();
1227        assert_eq!(decoder.color_type(), ColorType::Rgba8);
1228        assert_eq!(decoder.original_color_type(), ExtendedColorType::Rgba8);
1229        assert_eq!(decoder.dimensions(), (1, 3));
1230        assert_eq!(decoder.subtype(), PnmSubtype::ArbitraryMap);
1231
1232        let mut image = vec![0; decoder.total_bytes() as usize];
1233        decoder.read_image(&mut image).unwrap();
1234        assert_eq!(image, b"\x00\x11\x22\x33\xaa\xbb\xcc\xdd\x55\x66\x77\x88",);
1235        match PnmDecoder::new(&pamdata[..]).unwrap().into_inner() {
1236            (
1237                _,
1238                PnmHeader {
1239                    decoded:
1240                        HeaderRecord::Arbitrary(ArbitraryHeader {
1241                            width: 1,
1242                            height: 3,
1243                            maxval: 15,
1244                            depth: 4,
1245                            tupltype: Some(ArbitraryTuplType::RGBAlpha),
1246                        }),
1247                    encoded: _,
1248                },
1249            ) => (),
1250            _ => panic!("Decoded header is incorrect"),
1251        }
1252    }
1253
1254    #[test]
1255    fn pbm_binary() {
1256        // The data contains two rows of the image (each line is padded to the full byte). For
1257        // comments on its format, see documentation of `impl SampleType for PbmBit`.
1258        let pbmbinary = [&b"P4 6 2\n"[..], &[0b0110_1100_u8, 0b1011_0111]].concat();
1259        let decoder = PnmDecoder::new(&pbmbinary[..]).unwrap();
1260        assert_eq!(decoder.color_type(), ColorType::L8);
1261        assert_eq!(decoder.original_color_type(), ExtendedColorType::L1);
1262        assert_eq!(decoder.dimensions(), (6, 2));
1263        assert_eq!(
1264            decoder.subtype(),
1265            PnmSubtype::Bitmap(SampleEncoding::Binary)
1266        );
1267        let mut image = vec![0; decoder.total_bytes() as usize];
1268        decoder.read_image(&mut image).unwrap();
1269        assert_eq!(image, vec![255, 0, 0, 255, 0, 0, 0, 255, 0, 0, 255, 0]);
1270        match PnmDecoder::new(&pbmbinary[..]).unwrap().into_inner() {
1271            (
1272                _,
1273                PnmHeader {
1274                    decoded:
1275                        HeaderRecord::Bitmap(BitmapHeader {
1276                            encoding: SampleEncoding::Binary,
1277                            width: 6,
1278                            height: 2,
1279                        }),
1280                    encoded: _,
1281                },
1282            ) => (),
1283            _ => panic!("Decoded header is incorrect"),
1284        }
1285    }
1286
1287    /// A previous infinite loop.
1288    #[test]
1289    fn pbm_binary_ascii_termination() {
1290        use no_std_io::io::{BufReader, Cursor, Error, ErrorKind, Read, Result};
1291        struct FailRead(Cursor<&'static [u8]>);
1292
1293        impl Read for FailRead {
1294            fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
1295                match self.0.read(buf) {
1296                    Ok(n) if n > 0 => Ok(n),
1297                    _ => Err(Error::new(
1298                        ErrorKind::BrokenPipe,
1299                        "Simulated broken pipe error",
1300                    )),
1301                }
1302            }
1303        }
1304
1305        let pbmbinary = BufReader::new(FailRead(Cursor::new(b"P1 1 1\n")));
1306
1307        let decoder = PnmDecoder::new(pbmbinary).unwrap();
1308        let mut image = vec![0; decoder.total_bytes() as usize];
1309        decoder
1310            .read_image(&mut image)
1311            .expect_err("Image is malformed");
1312    }
1313
1314    #[test]
1315    fn pbm_ascii() {
1316        // The data contains two rows of the image (each line is padded to the full byte). For
1317        // comments on its format, see documentation of `impl SampleType for PbmBit`.  Tests all
1318        // whitespace characters that should be allowed (the 6 characters according to POSIX).
1319        let pbmbinary = b"P1 6 2\n 0 1 1 0 1 1\n1 0 1 1 0\t\n\x0b\x0c\r1";
1320        let decoder = PnmDecoder::new(&pbmbinary[..]).unwrap();
1321        assert_eq!(decoder.color_type(), ColorType::L8);
1322        assert_eq!(decoder.original_color_type(), ExtendedColorType::L1);
1323        assert_eq!(decoder.dimensions(), (6, 2));
1324        assert_eq!(decoder.subtype(), PnmSubtype::Bitmap(SampleEncoding::Ascii));
1325
1326        let mut image = vec![0; decoder.total_bytes() as usize];
1327        decoder.read_image(&mut image).unwrap();
1328        assert_eq!(image, vec![255, 0, 0, 255, 0, 0, 0, 255, 0, 0, 255, 0]);
1329        match PnmDecoder::new(&pbmbinary[..]).unwrap().into_inner() {
1330            (
1331                _,
1332                PnmHeader {
1333                    decoded:
1334                        HeaderRecord::Bitmap(BitmapHeader {
1335                            encoding: SampleEncoding::Ascii,
1336                            width: 6,
1337                            height: 2,
1338                        }),
1339                    encoded: _,
1340                },
1341            ) => (),
1342            _ => panic!("Decoded header is incorrect"),
1343        }
1344    }
1345
1346    #[test]
1347    fn pbm_ascii_nospace() {
1348        // The data contains two rows of the image (each line is padded to the full byte). Notably,
1349        // it is completely within specification for the ascii data not to contain separating
1350        // whitespace for the pbm format or any mix.
1351        let pbmbinary = b"P1 6 2\n011011101101";
1352        let decoder = PnmDecoder::new(&pbmbinary[..]).unwrap();
1353        assert_eq!(decoder.color_type(), ColorType::L8);
1354        assert_eq!(decoder.original_color_type(), ExtendedColorType::L1);
1355        assert_eq!(decoder.dimensions(), (6, 2));
1356        assert_eq!(decoder.subtype(), PnmSubtype::Bitmap(SampleEncoding::Ascii));
1357
1358        let mut image = vec![0; decoder.total_bytes() as usize];
1359        decoder.read_image(&mut image).unwrap();
1360        assert_eq!(image, vec![255, 0, 0, 255, 0, 0, 0, 255, 0, 0, 255, 0]);
1361        match PnmDecoder::new(&pbmbinary[..]).unwrap().into_inner() {
1362            (
1363                _,
1364                PnmHeader {
1365                    decoded:
1366                        HeaderRecord::Bitmap(BitmapHeader {
1367                            encoding: SampleEncoding::Ascii,
1368                            width: 6,
1369                            height: 2,
1370                        }),
1371                    encoded: _,
1372                },
1373            ) => (),
1374            _ => panic!("Decoded header is incorrect"),
1375        }
1376    }
1377
1378    #[test]
1379    fn pgm_binary() {
1380        // The data contains two rows of the image (each line is padded to the full byte). For
1381        // comments on its format, see documentation of `impl SampleType for PbmBit`.
1382        let elements = (0..16).collect::<Vec<_>>();
1383        let pbmbinary = [&b"P5 4 4 255\n"[..], &elements].concat();
1384        let decoder = PnmDecoder::new(&pbmbinary[..]).unwrap();
1385        assert_eq!(decoder.color_type(), ColorType::L8);
1386        assert_eq!(decoder.dimensions(), (4, 4));
1387        assert_eq!(
1388            decoder.subtype(),
1389            PnmSubtype::Graymap(SampleEncoding::Binary)
1390        );
1391        let mut image = vec![0; decoder.total_bytes() as usize];
1392        decoder.read_image(&mut image).unwrap();
1393        assert_eq!(image, elements);
1394        match PnmDecoder::new(&pbmbinary[..]).unwrap().into_inner() {
1395            (
1396                _,
1397                PnmHeader {
1398                    decoded:
1399                        HeaderRecord::Graymap(GraymapHeader {
1400                            encoding: SampleEncoding::Binary,
1401                            width: 4,
1402                            height: 4,
1403                            maxwhite: 255,
1404                        }),
1405                    encoded: _,
1406                },
1407            ) => (),
1408            _ => panic!("Decoded header is incorrect"),
1409        }
1410    }
1411
1412    #[test]
1413    fn pgm_ascii() {
1414        // The data contains two rows of the image (each line is padded to the full byte). For
1415        // comments on its format, see documentation of `impl SampleType for PbmBit`.
1416        let pbmbinary = b"P2 4 4 255\n 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15";
1417        let decoder = PnmDecoder::new(&pbmbinary[..]).unwrap();
1418        assert_eq!(decoder.color_type(), ColorType::L8);
1419        assert_eq!(decoder.dimensions(), (4, 4));
1420        assert_eq!(
1421            decoder.subtype(),
1422            PnmSubtype::Graymap(SampleEncoding::Ascii)
1423        );
1424        let mut image = vec![0; decoder.total_bytes() as usize];
1425        decoder.read_image(&mut image).unwrap();
1426        assert_eq!(image, (0..16).collect::<Vec<_>>());
1427        match PnmDecoder::new(&pbmbinary[..]).unwrap().into_inner() {
1428            (
1429                _,
1430                PnmHeader {
1431                    decoded:
1432                        HeaderRecord::Graymap(GraymapHeader {
1433                            encoding: SampleEncoding::Ascii,
1434                            width: 4,
1435                            height: 4,
1436                            maxwhite: 255,
1437                        }),
1438                    encoded: _,
1439                },
1440            ) => (),
1441            _ => panic!("Decoded header is incorrect"),
1442        }
1443    }
1444
1445    #[test]
1446    fn ppm_ascii() {
1447        let ascii = b"P3 1 1 2000\n0 1000 2000";
1448        let decoder = PnmDecoder::new(&ascii[..]).unwrap();
1449        let mut image = vec![0; decoder.total_bytes() as usize];
1450        decoder.read_image(&mut image).unwrap();
1451        assert_eq!(
1452            image,
1453            [
1454                0_u16.to_ne_bytes(),
1455                (u16::MAX / 2 + 1).to_ne_bytes(),
1456                u16::MAX.to_ne_bytes()
1457            ]
1458            .into_iter()
1459            .flatten()
1460            .collect::<Vec<_>>()
1461        );
1462    }
1463
1464    #[test]
1465    fn dimension_overflow() {
1466        let pamdata = b"P7
1467# Comment line
1468MAXVAL 255
1469TUPLTYPE RGB
1470DEPTH 3
1471WIDTH 4294967295
1472HEIGHT 4294967295
1473ENDHDR
1474\xde\xad\xbe\xef\xde\xad\xbe\xef\xde\xad\xbe\xef";
1475
1476        assert!(PnmDecoder::new(&pamdata[..]).is_err());
1477    }
1478
1479    #[test]
1480    fn issue_1508() {
1481        let _ = crate::load_from_memory(b"P391919 16999 1 1 9 919 16999 1 9999 999* 99999 N");
1482    }
1483
1484    #[test]
1485    fn issue_1616_overflow() {
1486        let data = [
1487            80, 54, 10, 52, 50, 57, 52, 56, 50, 57, 52, 56, 35, 56, 10, 52, 10, 48, 10, 12, 12, 56,
1488        ];
1489        // Validate: we have a header. Note: we might already calculate that this will fail but
1490        // then we could not return information about the header to the caller.
1491        let decoder = PnmDecoder::new(&data[..]).unwrap();
1492        let mut image = vec![0; decoder.total_bytes() as usize];
1493        let _ = decoder.read_image(&mut image);
1494    }
1495
1496    #[test]
1497    fn data_too_short() {
1498        let data = b"P3 16 16 1\n";
1499        let decoder = PnmDecoder::new(&data[..]).unwrap();
1500        let mut image = vec![0; decoder.total_bytes() as usize];
1501
1502        let _ = decoder.read_image(&mut image).unwrap_err();
1503    }
1504
1505    #[test]
1506    fn no_integers_with_plus() {
1507        let data = b"P3 +1 1 1\n";
1508        assert!(PnmDecoder::new(&data[..]).is_err());
1509    }
1510
1511    #[test]
1512    fn incomplete_pnm_header() {
1513        let data = b"P5 2 3 \n";
1514        assert!(PnmDecoder::new(&data[..]).is_err());
1515    }
1516
1517    #[test]
1518    fn leading_zeros() {
1519        let data = b"P2 03 00000000000002 00100\n011 22 033\n44 055 66\n";
1520        let decoder = PnmDecoder::new(&data[..]).unwrap();
1521        let mut image = vec![0; decoder.total_bytes() as usize];
1522        assert!(decoder.read_image(&mut image).is_ok());
1523    }
1524
1525    #[test]
1526    fn header_overflow() {
1527        let data = b"P1 4294967295 4294967297\n";
1528        assert!(PnmDecoder::new(&data[..]).is_err());
1529    }
1530
1531    #[test]
1532    fn header_large_dimension() {
1533        let data = b"P4 1 01234567890\n";
1534        let decoder = PnmDecoder::new(&data[..]).unwrap();
1535        assert!(decoder.dimensions() == (1, 1234567890));
1536    }
1537}