png/decoder/
stream.rs

1use std::convert::TryInto;
2use std::error;
3use std::fmt;
4use std::io;
5use std::{borrow::Cow, cmp::min};
6
7use crc32fast::Hasher as Crc32;
8
9use super::zlib::UnfilterBuf;
10use super::zlib::ZlibStream;
11use crate::chunk::is_critical;
12use crate::chunk::{self, ChunkType, IDAT, IEND, IHDR};
13use crate::common::{
14    AnimationControl, BitDepth, BlendOp, ColorType, ContentLightLevelInfo, DisposeOp, FrameControl,
15    Info, MasteringDisplayColorVolume, ParameterError, ParameterErrorKind, PixelDimensions,
16    ScaledFloat, SourceChromaticities, Unit,
17};
18use crate::text_metadata::{ITXtChunk, TEXtChunk, TextDecodingError, ZTXtChunk};
19use crate::traits::ReadBytesExt;
20use crate::{CodingIndependentCodePoints, Limits};
21
22pub const CHUNK_BUFFER_SIZE: usize = 128;
23
24/// Determines if checksum checks should be disabled globally.
25///
26/// This is used only in fuzzing. `afl` automatically adds `--cfg fuzzing` to RUSTFLAGS which can
27/// be used to detect that build.
28#[allow(unexpected_cfgs)]
29const CHECKSUM_DISABLED: bool = cfg!(fuzzing);
30
31/// Kind of `u32` value that is being read via `State::U32`.
32#[derive(Debug)]
33enum U32ValueKind {
34    /// First 4 bytes of the PNG signature - see
35    /// http://www.libpng.org/pub/png/spec/1.2/PNG-Structure.html#PNG-file-signature
36    Signature1stU32,
37    /// Second 4 bytes of the PNG signature - see
38    /// http://www.libpng.org/pub/png/spec/1.2/PNG-Structure.html#PNG-file-signature
39    Signature2ndU32,
40    /// Chunk length - see
41    /// http://www.libpng.org/pub/png/spec/1.2/PNG-Structure.html#Chunk-layout
42    Length,
43    /// Chunk type - see
44    /// http://www.libpng.org/pub/png/spec/1.2/PNG-Structure.html#Chunk-layout
45    Type { length: u32 },
46    /// Chunk checksum - see
47    /// http://www.libpng.org/pub/png/spec/1.2/PNG-Structure.html#Chunk-layout
48    Crc(ChunkType),
49    /// Sequence number from an `fdAT` chunk - see
50    /// https://wiki.mozilla.org/APNG_Specification#.60fdAT.60:_The_Frame_Data_Chunk
51    ApngSequenceNumber,
52}
53
54#[derive(Debug)]
55enum State {
56    /// In this state we are reading a u32 value from external input.  We start with
57    /// `accumulated_count` set to `0`. After reading or accumulating the required 4 bytes we will
58    /// call `parse_32` which will then move onto the next state.
59    U32 {
60        kind: U32ValueKind,
61        bytes: [u8; 4],
62        accumulated_count: usize,
63    },
64    /// In this state we are reading chunk data from external input, and appending it to
65    /// `ChunkState::raw_bytes`. Then if all data has been read, we parse the chunk.
66    ReadChunkData(ChunkType),
67    /// In this state we are reading image data from external input and feeding it directly into
68    /// `StreamingDecoder::inflater`.
69    ImageData(ChunkType),
70}
71
72impl State {
73    fn new_u32(kind: U32ValueKind) -> Self {
74        Self::U32 {
75            kind,
76            bytes: [0; 4],
77            accumulated_count: 0,
78        }
79    }
80}
81
82#[derive(Debug)]
83/// Result of the decoding process
84pub enum Decoded {
85    /// Nothing decoded yet
86    Nothing,
87
88    /// A chunk header (length and type fields) has been read.
89    ChunkBegin(u32, ChunkType),
90
91    /// Chunk has been read successfully.
92    ChunkComplete(ChunkType),
93
94    /// An ancillary chunk has been read but it was in the wrong place, had corrupt contents, or had
95    /// an invalid CRC.
96    BadAncillaryChunk(ChunkType),
97
98    /// Skipped an ancillary chunk because it was unrecognized or the decoder was configured to skip
99    /// this type of chunk.
100    SkippedAncillaryChunk(ChunkType),
101
102    /// Decoded raw image data.
103    ImageData,
104
105    /// The last of a consecutive chunk of IDAT was done.
106    /// This is distinct from ChunkComplete which only marks that some IDAT chunk was completed but
107    /// not that no additional IDAT chunk follows.
108    ImageDataFlushed,
109}
110
111/// Any kind of error during PNG decoding.
112///
113/// This enumeration provides a very rough analysis on the origin of the failure. That is, each
114/// variant corresponds to one kind of actor causing the error. It should not be understood as a
115/// direct blame but can inform the search for a root cause or if such a search is required.
116#[derive(Debug)]
117pub enum DecodingError {
118    /// An error in IO of the underlying reader.
119    ///
120    /// Note that some IO errors may be recoverable - decoding may be retried after the
121    /// error is resolved.  For example, decoding from a slow stream of data (e.g. decoding from a
122    /// network stream) may occasionally result in [std::io::ErrorKind::UnexpectedEof] kind of
123    /// error, but decoding can resume when more data becomes available.
124    IoError(io::Error),
125    /// The input image was not a valid PNG.
126    ///
127    /// There isn't a lot that can be done here, except if the program itself was responsible for
128    /// creating this image then investigate the generator. This is internally implemented with a
129    /// large Enum. If You are interested in accessing some of the more exact information on the
130    /// variant then we can discuss in an issue.
131    Format(FormatError),
132    /// An interface was used incorrectly.
133    ///
134    /// This is used in cases where it's expected that the programmer might trip up and stability
135    /// could be affected. For example when:
136    ///
137    /// * The decoder is polled for more animation frames despite being done (or not being animated
138    ///   in the first place).
139    /// * The output buffer does not have the required size.
140    ///
141    /// As a rough guideline for introducing new variants parts of the requirements are dynamically
142    /// derived from the (untrusted) input data while the other half is from the caller. In the
143    /// above cases the number of frames respectively the size is determined by the file while the
144    /// number of calls
145    ///
146    /// If you're an application you might want to signal that a bug report is appreciated.
147    Parameter(ParameterError),
148    /// The image would have required exceeding the limits configured with the decoder.
149    ///
150    /// Note that Your allocations, e.g. when reading into a pre-allocated buffer, is __NOT__
151    /// considered part of the limits. Nevertheless, required intermediate buffers such as for
152    /// singular lines is checked against the limit.
153    ///
154    /// Note that this is a best-effort basis.
155    LimitsExceeded,
156}
157
158#[derive(Debug)]
159pub struct FormatError {
160    inner: FormatErrorInner,
161}
162
163#[derive(Debug)]
164pub(crate) enum FormatErrorInner {
165    /// Bad framing.
166    CrcMismatch {
167        /// Stored CRC32 value
168        crc_val: u32,
169        /// Calculated CRC32 sum
170        crc_sum: u32,
171        /// The chunk type that has the CRC mismatch.
172        chunk: ChunkType,
173    },
174    /// Not a PNG, the magic signature is missing.
175    InvalidSignature,
176    // Errors of chunk level ordering, missing etc.
177    /// Fctl must occur if an animated chunk occurs.
178    MissingFctl,
179    /// Image data that was indicated in IHDR or acTL is missing.
180    MissingImageData,
181    /// 4.3., Must be first.
182    ChunkBeforeIhdr {
183        kind: ChunkType,
184    },
185    /// 4.3., some chunks must be before IDAT.
186    AfterIdat {
187        kind: ChunkType,
188    },
189    // 4.3., Some chunks must be after PLTE.
190    BeforePlte {
191        kind: ChunkType,
192    },
193    /// 4.3., some chunks must be before PLTE.
194    AfterPlte {
195        kind: ChunkType,
196    },
197    /// 4.3., some chunks must be between PLTE and IDAT.
198    OutsidePlteIdat {
199        kind: ChunkType,
200    },
201    /// 4.3., some chunks must be unique.
202    DuplicateChunk {
203        kind: ChunkType,
204    },
205    /// Specifically for fdat there is an embedded sequence number for chunks.
206    ApngOrder {
207        /// The sequence number in the chunk.
208        present: u32,
209        /// The one that should have been present.
210        expected: u32,
211    },
212    // Errors specific to particular chunk data to be validated.
213    /// The palette did not even contain a single pixel data.
214    ShortPalette {
215        expected: usize,
216        len: usize,
217    },
218    /// sBIT chunk size based on color type.
219    InvalidSbitChunkSize {
220        color_type: ColorType,
221        expected: usize,
222        len: usize,
223    },
224    InvalidSbit {
225        sample_depth: BitDepth,
226        sbit: u8,
227    },
228    /// A palletized image did not have a palette.
229    PaletteRequired,
230    /// The color-depth combination is not valid according to Table 11.1.
231    InvalidColorBitDepth {
232        color_type: ColorType,
233        bit_depth: BitDepth,
234    },
235    ColorWithBadTrns(ColorType),
236    /// The image width or height is zero.
237    InvalidDimensions,
238    InvalidBitDepth(u8),
239    InvalidColorType(u8),
240    InvalidDisposeOp(u8),
241    InvalidBlendOp(u8),
242    InvalidUnit(u8),
243    /// The rendering intent of the sRGB chunk is invalid.
244    InvalidSrgbRenderingIntent(u8),
245    UnknownCompressionMethod(u8),
246    UnknownFilterMethod(u8),
247    UnknownInterlaceMethod(u8),
248    /// The subframe is not in bounds of the image.
249    /// TODO: fields with relevant data.
250    BadSubFrameBounds {},
251    // Errors specific to the IDAT/fdAT chunks.
252    /// The compression of the data stream was faulty.
253    CorruptFlateStream {
254        err: fdeflate::DecompressionError,
255    },
256    /// The image data chunk was too short for the expected pixel count.
257    NoMoreImageData,
258    /// Bad text encoding
259    BadTextEncoding(TextDecodingError),
260    /// fdAT shorter than 4 bytes
261    FdatShorterThanFourBytes,
262    /// "11.2.4 IDAT Image data" section of the PNG spec says: There may be multiple IDAT chunks;
263    /// if so, they shall appear consecutively with no other intervening chunks.
264    /// `UnexpectedRestartOfDataChunkSequence{kind: IDAT}` indicates that there were "intervening
265    /// chunks".
266    ///
267    /// The APNG spec doesn't directly describe an error similar to `CantInterleaveIdatChunks`,
268    /// but we require that a new sequence of consecutive `fdAT` chunks cannot appear unless we've
269    /// seen an `fcTL` chunk.
270    UnexpectedRestartOfDataChunkSequence {
271        kind: ChunkType,
272    },
273    /// Failure to parse a chunk, because the chunk didn't contain enough bytes.
274    ChunkTooShort {
275        kind: ChunkType,
276    },
277    UnrecognizedCriticalChunk {
278        /// The type of the unrecognized critical chunk.
279        type_str: ChunkType,
280    },
281    BadGammaValue,
282}
283
284impl error::Error for DecodingError {
285    fn cause(&self) -> Option<&(dyn error::Error + 'static)> {
286        match self {
287            DecodingError::IoError(err) => Some(err),
288            _ => None,
289        }
290    }
291}
292
293impl fmt::Display for DecodingError {
294    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
295        use self::DecodingError::*;
296        match self {
297            IoError(err) => write!(fmt, "{}", err),
298            Parameter(desc) => write!(fmt, "{}", &desc),
299            Format(desc) => write!(fmt, "{}", desc),
300            LimitsExceeded => write!(fmt, "limits are exceeded"),
301        }
302    }
303}
304
305impl fmt::Display for FormatError {
306    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
307        use FormatErrorInner::*;
308        match &self.inner {
309            CrcMismatch {
310                crc_val,
311                crc_sum,
312                chunk,
313                ..
314            } => write!(
315                fmt,
316                "CRC error: expected 0x{:x} have 0x{:x} while decoding {:?} chunk.",
317                crc_val, crc_sum, chunk
318            ),
319            MissingFctl => write!(fmt, "fcTL chunk missing before fdAT chunk."),
320            MissingImageData => write!(fmt, "IDAT or fdAT chunk is missing."),
321            ChunkBeforeIhdr { kind } => write!(fmt, "{:?} chunk appeared before IHDR chunk", kind),
322            AfterIdat { kind } => write!(fmt, "Chunk {:?} is invalid after IDAT chunk.", kind),
323            BeforePlte { kind } => write!(fmt, "Chunk {:?} is invalid before PLTE chunk.", kind),
324            AfterPlte { kind } => write!(fmt, "Chunk {:?} is invalid after PLTE chunk.", kind),
325            OutsidePlteIdat { kind } => write!(
326                fmt,
327                "Chunk {:?} must appear between PLTE and IDAT chunks.",
328                kind
329            ),
330            DuplicateChunk { kind } => write!(fmt, "Chunk {:?} must appear at most once.", kind),
331            ApngOrder { present, expected } => write!(
332                fmt,
333                "Sequence is not in order, expected #{} got #{}.",
334                expected, present,
335            ),
336            ShortPalette { expected, len } => write!(
337                fmt,
338                "Not enough palette entries, expect {} got {}.",
339                expected, len
340            ),
341            InvalidSbitChunkSize {color_type, expected, len} => write!(
342                fmt,
343                "The size of the sBIT chunk should be {} byte(s), but {} byte(s) were provided for the {:?} color type.",
344                expected, len, color_type
345            ),
346            InvalidSbit {sample_depth, sbit} => write!(
347                fmt,
348                "Invalid sBIT value {}. It must be greater than zero and less than the sample depth {:?}.",
349                sbit, sample_depth
350            ),
351            PaletteRequired => write!(fmt, "Missing palette of indexed image."),
352            InvalidDimensions => write!(fmt, "Invalid image dimensions"),
353            InvalidColorBitDepth {
354                color_type,
355                bit_depth,
356            } => write!(
357                fmt,
358                "Invalid color/depth combination in header: {:?}/{:?}",
359                color_type, bit_depth,
360            ),
361            ColorWithBadTrns(color_type) => write!(
362                fmt,
363                "Transparency chunk found for color type {:?}.",
364                color_type
365            ),
366            InvalidBitDepth(nr) => write!(fmt, "Invalid bit depth {}.", nr),
367            InvalidColorType(nr) => write!(fmt, "Invalid color type {}.", nr),
368            InvalidDisposeOp(nr) => write!(fmt, "Invalid dispose op {}.", nr),
369            InvalidBlendOp(nr) => write!(fmt, "Invalid blend op {}.", nr),
370            InvalidUnit(nr) => write!(fmt, "Invalid physical pixel size unit {}.", nr),
371            InvalidSrgbRenderingIntent(nr) => write!(fmt, "Invalid sRGB rendering intent {}.", nr),
372            UnknownCompressionMethod(nr) => write!(fmt, "Unknown compression method {}.", nr),
373            UnknownFilterMethod(nr) => write!(fmt, "Unknown filter method {}.", nr),
374            UnknownInterlaceMethod(nr) => write!(fmt, "Unknown interlace method {}.", nr),
375            BadSubFrameBounds {} => write!(fmt, "Sub frame is out-of-bounds."),
376            InvalidSignature => write!(fmt, "Invalid PNG signature."),
377            NoMoreImageData => write!(
378                fmt,
379                "IDAT or fDAT chunk does not have enough data for image."
380            ),
381            CorruptFlateStream { err } => {
382                write!(fmt, "Corrupt deflate stream. ")?;
383                write!(fmt, "{:?}", err)
384            }
385            // TODO: Wrap more info in the enum variant
386            BadTextEncoding(tde) => {
387                match tde {
388                    TextDecodingError::Unrepresentable => {
389                        write!(fmt, "Unrepresentable data in tEXt chunk.")
390                    }
391                    TextDecodingError::InvalidKeywordSize => {
392                        write!(fmt, "Keyword empty or longer than 79 bytes.")
393                    }
394                    TextDecodingError::MissingNullSeparator => {
395                        write!(fmt, "No null separator in tEXt chunk.")
396                    }
397                    TextDecodingError::InflationError => {
398                        write!(fmt, "Invalid compressed text data.")
399                    }
400                    TextDecodingError::OutOfDecompressionSpace => {
401                        write!(fmt, "Out of decompression space. Try with a larger limit.")
402                    }
403                    TextDecodingError::InvalidCompressionMethod => {
404                        write!(fmt, "Using an unrecognized byte as compression method.")
405                    }
406                    TextDecodingError::InvalidCompressionFlag => {
407                        write!(fmt, "Using a flag that is not 0 or 255 as a compression flag for iTXt chunk.")
408                    }
409                    TextDecodingError::MissingCompressionFlag => {
410                        write!(fmt, "No compression flag in the iTXt chunk.")
411                    }
412                }
413            }
414            FdatShorterThanFourBytes => write!(fmt, "fdAT chunk shorter than 4 bytes"),
415            UnexpectedRestartOfDataChunkSequence { kind } => {
416                write!(fmt, "Unexpected restart of {:?} chunk sequence", kind)
417            }
418            ChunkTooShort { kind } => {
419                write!(fmt, "Chunk is too short: {:?}", kind)
420            }
421            UnrecognizedCriticalChunk { type_str } => {
422                write!(fmt, "Unrecognized critical chunk: {:?}", type_str)
423            }
424            BadGammaValue => write!(fmt, "Bad gamma value."),
425        }
426    }
427}
428
429impl From<io::Error> for DecodingError {
430    fn from(err: io::Error) -> DecodingError {
431        DecodingError::IoError(err)
432    }
433}
434
435impl From<FormatError> for DecodingError {
436    fn from(err: FormatError) -> DecodingError {
437        DecodingError::Format(err)
438    }
439}
440
441impl From<FormatErrorInner> for FormatError {
442    fn from(inner: FormatErrorInner) -> Self {
443        FormatError { inner }
444    }
445}
446
447impl From<DecodingError> for io::Error {
448    fn from(err: DecodingError) -> io::Error {
449        match err {
450            DecodingError::IoError(err) => err,
451            err => io::Error::new(io::ErrorKind::Other, err.to_string()),
452        }
453    }
454}
455
456impl From<TextDecodingError> for DecodingError {
457    fn from(tbe: TextDecodingError) -> Self {
458        DecodingError::Format(FormatError {
459            inner: FormatErrorInner::BadTextEncoding(tbe),
460        })
461    }
462}
463
464/// Decoder configuration options
465#[derive(Clone)]
466pub struct DecodeOptions {
467    ignore_adler32: bool,
468    ignore_crc: bool,
469    ignore_text_chunk: bool,
470    ignore_iccp_chunk: bool,
471    skip_ancillary_crc_failures: bool,
472}
473
474impl Default for DecodeOptions {
475    fn default() -> Self {
476        Self {
477            ignore_adler32: true,
478            ignore_crc: false,
479            ignore_text_chunk: false,
480            ignore_iccp_chunk: false,
481            skip_ancillary_crc_failures: true,
482        }
483    }
484}
485
486impl DecodeOptions {
487    /// When set, the decoder will not compute and verify the Adler-32 checksum.
488    ///
489    /// Defaults to `true`.
490    pub fn set_ignore_adler32(&mut self, ignore_adler32: bool) {
491        self.ignore_adler32 = ignore_adler32;
492    }
493
494    /// When set, the decoder will not compute and verify the CRC code.
495    ///
496    /// Defaults to `false`.
497    pub fn set_ignore_crc(&mut self, ignore_crc: bool) {
498        self.ignore_crc = ignore_crc;
499    }
500
501    /// Flag to ignore computing and verifying the Adler-32 checksum and CRC
502    /// code.
503    pub fn set_ignore_checksums(&mut self, ignore_checksums: bool) {
504        self.ignore_adler32 = ignore_checksums;
505        self.ignore_crc = ignore_checksums;
506    }
507
508    /// Ignore text chunks while decoding.
509    ///
510    /// Defaults to `false`.
511    pub fn set_ignore_text_chunk(&mut self, ignore_text_chunk: bool) {
512        self.ignore_text_chunk = ignore_text_chunk;
513    }
514
515    /// Ignore ICCP chunks while decoding.
516    ///
517    /// Defaults to `false`.
518    pub fn set_ignore_iccp_chunk(&mut self, ignore_iccp_chunk: bool) {
519        self.ignore_iccp_chunk = ignore_iccp_chunk;
520    }
521
522    /// Ignore ancillary chunks if CRC fails
523    ///
524    /// Defaults to `true`
525    pub fn set_skip_ancillary_crc_failures(&mut self, skip_ancillary_crc_failures: bool) {
526        self.skip_ancillary_crc_failures = skip_ancillary_crc_failures;
527    }
528}
529
530/// PNG StreamingDecoder (low-level interface)
531///
532/// By default, the decoder does not verify Adler-32 checksum computation. To
533/// enable checksum verification, set it with [`StreamingDecoder::set_ignore_adler32`]
534/// before starting decompression.
535pub struct StreamingDecoder {
536    state: Option<State>,
537    current_chunk: ChunkState,
538    /// The inflater state handling consecutive `IDAT` and `fdAT` chunks.
539    inflater: ZlibStream,
540    /// The complete image info read from all prior chunks.
541    pub(crate) info: Option<Info<'static>>,
542    /// The animation chunk sequence number.
543    current_seq_no: Option<u32>,
544    /// Whether we have already seen a start of an IDAT chunk.  (Used to validate chunk ordering -
545    /// some chunk types can only appear before or after an IDAT chunk.)
546    have_idat: bool,
547    /// Whether we are ready for a start of an `IDAT` chunk sequence.  Initially `true` and set to
548    /// `false` when the first sequence of consecutive `IDAT` chunks ends.
549    ready_for_idat_chunks: bool,
550    /// Whether we are ready for a start of an `fdAT` chunk sequence.  Initially `false`.  Set to
551    /// `true` after encountering an `fcTL` chunk. Set to `false` when a sequence of consecutive
552    /// `fdAT` chunks ends.
553    ready_for_fdat_chunks: bool,
554    /// Whether we have already seen an iCCP chunk. Used to prevent parsing of duplicate iCCP chunks.
555    have_iccp: bool,
556    decode_options: DecodeOptions,
557    pub(crate) limits: Limits,
558}
559
560struct ChunkState {
561    /// The type of the current chunk.
562    /// Relevant for `IDAT` and `fdAT` which aggregate consecutive chunks of their own type.
563    type_: ChunkType,
564
565    /// Partial crc until now.
566    crc: Crc32,
567
568    /// Remaining bytes to be read.
569    remaining: u32,
570
571    /// Non-decoded bytes in the chunk.
572    raw_bytes: Vec<u8>,
573}
574
575impl StreamingDecoder {
576    /// Creates a new StreamingDecoder
577    ///
578    /// Allocates the internal buffers.
579    pub fn new() -> StreamingDecoder {
580        StreamingDecoder::new_with_options(DecodeOptions::default())
581    }
582
583    pub fn new_with_options(decode_options: DecodeOptions) -> StreamingDecoder {
584        let mut inflater = ZlibStream::new();
585        inflater.set_ignore_adler32(decode_options.ignore_adler32);
586
587        StreamingDecoder {
588            state: Some(State::new_u32(U32ValueKind::Signature1stU32)),
589            current_chunk: ChunkState {
590                type_: ChunkType([0; 4]),
591                crc: Crc32::new(),
592                remaining: 0,
593                raw_bytes: Vec::with_capacity(CHUNK_BUFFER_SIZE),
594            },
595            inflater,
596            info: None,
597            current_seq_no: None,
598            have_idat: false,
599            have_iccp: false,
600            ready_for_idat_chunks: true,
601            ready_for_fdat_chunks: false,
602            decode_options,
603            limits: Limits { bytes: usize::MAX },
604        }
605    }
606
607    /// Resets the StreamingDecoder
608    pub fn reset(&mut self) {
609        self.state = Some(State::new_u32(U32ValueKind::Signature1stU32));
610        self.current_chunk.crc = Crc32::new();
611        self.current_chunk.remaining = 0;
612        self.current_chunk.raw_bytes.clear();
613        self.inflater.reset();
614        self.info = None;
615        self.current_seq_no = None;
616        self.have_idat = false;
617    }
618
619    /// Provides access to the inner `info` field
620    pub fn info(&self) -> Option<&Info<'static>> {
621        self.info.as_ref()
622    }
623
624    pub fn set_ignore_text_chunk(&mut self, ignore_text_chunk: bool) {
625        self.decode_options.set_ignore_text_chunk(ignore_text_chunk);
626    }
627
628    pub fn set_ignore_iccp_chunk(&mut self, ignore_iccp_chunk: bool) {
629        self.decode_options.set_ignore_iccp_chunk(ignore_iccp_chunk);
630    }
631
632    /// Return whether the decoder is set to ignore the Adler-32 checksum.
633    pub fn ignore_adler32(&self) -> bool {
634        self.inflater.ignore_adler32()
635    }
636
637    /// Set whether to compute and verify the Adler-32 checksum during
638    /// decompression. Return `true` if the flag was successfully set.
639    ///
640    /// The decoder defaults to `true`.
641    ///
642    /// This flag cannot be modified after decompression has started until the
643    /// [`StreamingDecoder`] is reset.
644    pub fn set_ignore_adler32(&mut self, ignore_adler32: bool) -> bool {
645        self.inflater.set_ignore_adler32(ignore_adler32)
646    }
647
648    /// Set whether to compute and verify the Adler-32 checksum during
649    /// decompression.
650    ///
651    /// The decoder defaults to `false`.
652    pub fn set_ignore_crc(&mut self, ignore_crc: bool) {
653        self.decode_options.set_ignore_crc(ignore_crc)
654    }
655
656    /// Ignore ancillary chunks if CRC fails
657    ///
658    /// Defaults to `true`
659    pub fn set_skip_ancillary_crc_failures(&mut self, skip_ancillary_crc_failures: bool) {
660        self.decode_options
661            .set_skip_ancillary_crc_failures(skip_ancillary_crc_failures)
662    }
663
664    /// Low level StreamingDecoder interface.
665    ///
666    /// Allows to stream partial data to the encoder. Returns a tuple containing the bytes that have
667    /// been consumed from the input buffer and the current decoding result. If the decoded chunk
668    /// was an image data chunk, it also appends the read data to `image_data`.
669    pub fn update(
670        &mut self,
671        mut buf: &[u8],
672        mut image_data: Option<&mut UnfilterBuf<'_>>,
673    ) -> Result<(usize, Decoded), DecodingError> {
674        if self.state.is_none() {
675            return Err(DecodingError::Parameter(
676                ParameterErrorKind::PolledAfterFatalError.into(),
677            ));
678        }
679
680        let len = buf.len();
681        while !buf.is_empty() {
682            let image_data = image_data.as_deref_mut();
683
684            match self.next_state(buf, image_data) {
685                Ok((bytes, Decoded::Nothing)) => buf = &buf[bytes..],
686                Ok((bytes, result)) => {
687                    buf = &buf[bytes..];
688                    return Ok((len - buf.len(), result));
689                }
690                Err(err) => {
691                    debug_assert!(self.state.is_none());
692                    return Err(err);
693                }
694            }
695        }
696        Ok((len - buf.len(), Decoded::Nothing))
697    }
698
699    fn next_state(
700        &mut self,
701        buf: &[u8],
702        image_data: Option<&mut UnfilterBuf<'_>>,
703    ) -> Result<(usize, Decoded), DecodingError> {
704        use self::State::*;
705
706        // Driver should ensure that state is never None
707        let state = self.state.take().unwrap();
708
709        match state {
710            U32 {
711                kind,
712                mut bytes,
713                mut accumulated_count,
714            } => {
715                debug_assert!(accumulated_count <= 4);
716                if accumulated_count == 0 && buf.len() >= 4 {
717                    // Handling these `accumulated_count` and `buf.len()` values in a separate `if`
718                    // branch is not strictly necessary - the `else` statement below is already
719                    // capable of handling these values.  The main reason for special-casing these
720                    // values is that they occur fairly frequently and special-casing them results
721                    // in performance gains.
722                    const CONSUMED_BYTES: usize = 4;
723                    self.parse_u32(kind, &buf[0..4], image_data)
724                        .map(|decoded| (CONSUMED_BYTES, decoded))
725                } else {
726                    let remaining_count = 4 - accumulated_count;
727                    let consumed_bytes = {
728                        let available_count = min(remaining_count, buf.len());
729                        bytes[accumulated_count..accumulated_count + available_count]
730                            .copy_from_slice(&buf[0..available_count]);
731                        accumulated_count += available_count;
732                        available_count
733                    };
734
735                    if accumulated_count < 4 {
736                        self.state = Some(U32 {
737                            kind,
738                            bytes,
739                            accumulated_count,
740                        });
741                        Ok((consumed_bytes, Decoded::Nothing))
742                    } else {
743                        debug_assert_eq!(accumulated_count, 4);
744                        self.parse_u32(kind, &bytes, image_data)
745                            .map(|decoded| (consumed_bytes, decoded))
746                    }
747                }
748            }
749            ReadChunkData(type_str) => {
750                debug_assert!(type_str != IDAT && type_str != chunk::fdAT);
751                if self.current_chunk.remaining == 0 {
752                    self.state = Some(State::new_u32(U32ValueKind::Crc(type_str)));
753                    Ok((0, Decoded::Nothing))
754                } else {
755                    let ChunkState {
756                        crc,
757                        remaining,
758                        raw_bytes,
759                        type_: _,
760                    } = &mut self.current_chunk;
761
762                    if raw_bytes.len() == raw_bytes.capacity() {
763                        if self.limits.bytes == 0 {
764                            return Err(DecodingError::LimitsExceeded);
765                        }
766
767                        // Double the size of the Vec, but not beyond the allocation limit.
768                        debug_assert!(raw_bytes.capacity() > 0);
769                        let reserve_size = raw_bytes.capacity().min(self.limits.bytes);
770
771                        self.limits.reserve_bytes(reserve_size)?;
772                        raw_bytes.reserve_exact(reserve_size);
773                    }
774
775                    let buf_avail = raw_bytes.capacity() - raw_bytes.len();
776                    let bytes_avail = min(buf.len(), buf_avail);
777                    let n = min(*remaining, bytes_avail as u32);
778
779                    let buf = &buf[..n as usize];
780                    if !self.decode_options.ignore_crc {
781                        crc.update(buf);
782                    }
783                    raw_bytes.extend_from_slice(buf);
784
785                    *remaining -= n;
786                    if *remaining == 0 {
787                        debug_assert!(type_str != IDAT && type_str != chunk::fdAT);
788                        self.state = Some(State::new_u32(U32ValueKind::Crc(type_str)));
789                    } else {
790                        self.state = Some(ReadChunkData(type_str));
791                    }
792                    Ok((n as usize, Decoded::Nothing))
793                }
794            }
795            ImageData(type_str) => {
796                debug_assert!(type_str == IDAT || type_str == chunk::fdAT);
797                let len = std::cmp::min(buf.len(), self.current_chunk.remaining as usize);
798                let buf = &buf[..len];
799
800                let consumed = if let Some(image_data) = image_data {
801                    self.inflater.decompress(buf, image_data)?
802                } else {
803                    len
804                };
805
806                if !self.decode_options.ignore_crc {
807                    self.current_chunk.crc.update(&buf[..consumed]);
808                }
809
810                self.current_chunk.remaining -= consumed as u32;
811                if self.current_chunk.remaining == 0 {
812                    self.state = Some(State::new_u32(U32ValueKind::Crc(type_str)));
813                } else {
814                    self.state = Some(ImageData(type_str));
815                }
816                Ok((consumed, Decoded::ImageData))
817            }
818        }
819    }
820
821    fn parse_u32(
822        &mut self,
823        kind: U32ValueKind,
824        u32_be_bytes: &[u8],
825        image_data: Option<&mut UnfilterBuf<'_>>,
826    ) -> Result<Decoded, DecodingError> {
827        debug_assert_eq!(u32_be_bytes.len(), 4);
828        let bytes = u32_be_bytes.try_into().unwrap();
829        let val = u32::from_be_bytes(bytes);
830
831        match kind {
832            U32ValueKind::Signature1stU32 => {
833                if bytes == [137, 80, 78, 71] {
834                    self.state = Some(State::new_u32(U32ValueKind::Signature2ndU32));
835                    Ok(Decoded::Nothing)
836                } else {
837                    Err(DecodingError::Format(
838                        FormatErrorInner::InvalidSignature.into(),
839                    ))
840                }
841            }
842            U32ValueKind::Signature2ndU32 => {
843                if bytes == [13, 10, 26, 10] {
844                    self.state = Some(State::new_u32(U32ValueKind::Length));
845                    Ok(Decoded::Nothing)
846                } else {
847                    Err(DecodingError::Format(
848                        FormatErrorInner::InvalidSignature.into(),
849                    ))
850                }
851            }
852            U32ValueKind::Length => {
853                self.state = Some(State::new_u32(U32ValueKind::Type { length: val }));
854                Ok(Decoded::Nothing)
855            }
856            U32ValueKind::Type { length } => {
857                let type_str = ChunkType(bytes);
858                if self.info.is_none() && type_str != IHDR {
859                    return Err(DecodingError::Format(
860                        FormatErrorInner::ChunkBeforeIhdr { kind: type_str }.into(),
861                    ));
862                }
863                if type_str != self.current_chunk.type_
864                    && (self.current_chunk.type_ == IDAT || self.current_chunk.type_ == chunk::fdAT)
865                {
866                    self.current_chunk.type_ = type_str;
867                    if let Some(image_data) = image_data {
868                        self.inflater.finish_compressed_chunks(image_data)?;
869                    }
870
871                    self.ready_for_idat_chunks = false;
872                    self.ready_for_fdat_chunks = false;
873                    self.state = Some(State::U32 {
874                        kind,
875                        bytes,
876                        accumulated_count: 4,
877                    });
878                    return Ok(Decoded::ImageDataFlushed);
879                }
880                self.state = match type_str {
881                    chunk::fdAT => {
882                        if !self.ready_for_fdat_chunks {
883                            return Err(DecodingError::Format(
884                                FormatErrorInner::UnexpectedRestartOfDataChunkSequence {
885                                    kind: chunk::fdAT,
886                                }
887                                .into(),
888                            ));
889                        }
890                        if length < 4 {
891                            return Err(DecodingError::Format(
892                                FormatErrorInner::FdatShorterThanFourBytes.into(),
893                            ));
894                        }
895                        Some(State::new_u32(U32ValueKind::ApngSequenceNumber))
896                    }
897                    IDAT => {
898                        if !self.ready_for_idat_chunks {
899                            return Err(DecodingError::Format(
900                                FormatErrorInner::UnexpectedRestartOfDataChunkSequence {
901                                    kind: IDAT,
902                                }
903                                .into(),
904                            ));
905                        }
906                        self.have_idat = true;
907                        Some(State::ImageData(type_str))
908                    }
909                    _ => Some(State::ReadChunkData(type_str)),
910                };
911                self.current_chunk.type_ = type_str;
912                if !self.decode_options.ignore_crc {
913                    self.current_chunk.crc.reset();
914                    self.current_chunk.crc.update(&type_str.0);
915                }
916                self.current_chunk.remaining = length;
917                self.current_chunk.raw_bytes.clear();
918                Ok(Decoded::ChunkBegin(length, type_str))
919            }
920            U32ValueKind::Crc(type_str) => {
921                // If ignore_crc is set, do not calculate CRC. We set
922                // sum=val so that it short-circuits to true in the next
923                // if-statement block
924                let sum = if self.decode_options.ignore_crc {
925                    val
926                } else {
927                    self.current_chunk.crc.clone().finalize()
928                };
929
930                if val == sum || CHECKSUM_DISABLED {
931                    // A fatal error in chunk parsing leaves the decoder in state 'None' to enforce
932                    // that parsing can't continue after an error.
933                    debug_assert!(self.state.is_none());
934                    let decoded = self.parse_chunk(type_str)?;
935
936                    if type_str != IEND {
937                        self.state = Some(State::new_u32(U32ValueKind::Length));
938                    }
939                    Ok(decoded)
940                } else if self.decode_options.skip_ancillary_crc_failures
941                    && !chunk::is_critical(type_str)
942                {
943                    // Ignore ancillary chunk with invalid CRC
944                    self.state = Some(State::new_u32(U32ValueKind::Length));
945                    Ok(Decoded::BadAncillaryChunk(type_str))
946                } else {
947                    Err(DecodingError::Format(
948                        FormatErrorInner::CrcMismatch {
949                            crc_val: val,
950                            crc_sum: sum,
951                            chunk: type_str,
952                        }
953                        .into(),
954                    ))
955                }
956            }
957            U32ValueKind::ApngSequenceNumber => {
958                debug_assert_eq!(self.current_chunk.type_, chunk::fdAT);
959                let next_seq_no = val;
960
961                // Should be verified by the FdatShorterThanFourBytes check earlier.
962                debug_assert!(self.current_chunk.remaining >= 4);
963                self.current_chunk.remaining -= 4;
964
965                if let Some(seq_no) = self.current_seq_no {
966                    if next_seq_no != seq_no + 1 {
967                        return Err(DecodingError::Format(
968                            FormatErrorInner::ApngOrder {
969                                present: next_seq_no,
970                                expected: seq_no + 1,
971                            }
972                            .into(),
973                        ));
974                    }
975                    self.current_seq_no = Some(next_seq_no);
976                } else {
977                    return Err(DecodingError::Format(FormatErrorInner::MissingFctl.into()));
978                }
979
980                if !self.decode_options.ignore_crc {
981                    let data = next_seq_no.to_be_bytes();
982                    self.current_chunk.crc.update(&data);
983                }
984
985                self.state = Some(State::ImageData(chunk::fdAT));
986                Ok(Decoded::Nothing)
987            }
988        }
989    }
990
991    fn parse_chunk(&mut self, type_str: ChunkType) -> Result<Decoded, DecodingError> {
992        let mut parse_result = match type_str {
993            // Critical non-data chunks.
994            IHDR => self.parse_ihdr(),
995            chunk::PLTE => self.parse_plte(),
996            chunk::IEND => Ok(()), // TODO: Check chunk size.
997
998            // Data chunks handled separately.
999            chunk::IDAT => Ok(()),
1000            chunk::fdAT => Ok(()),
1001
1002            // Recognized bounded-size ancillary chunks.
1003            chunk::sBIT => self.parse_sbit(),
1004            chunk::tRNS => self.parse_trns(),
1005            chunk::pHYs => self.parse_phys(),
1006            chunk::gAMA => self.parse_gama(),
1007            chunk::acTL => self.parse_actl(),
1008            chunk::fcTL => self.parse_fctl(),
1009            chunk::cHRM => self.parse_chrm(),
1010            chunk::sRGB => self.parse_srgb(),
1011            chunk::cICP => self.parse_cicp(),
1012            chunk::mDCV => self.parse_mdcv(),
1013            chunk::cLLI => self.parse_clli(),
1014            chunk::bKGD => self.parse_bkgd(),
1015
1016            // Ancillary chunks with unbounded size.
1017            chunk::eXIf => self.parse_exif(), // TODO: allow skipping.
1018            chunk::iCCP if !self.decode_options.ignore_iccp_chunk => self.parse_iccp(),
1019            chunk::tEXt if !self.decode_options.ignore_text_chunk => self.parse_text(),
1020            chunk::zTXt if !self.decode_options.ignore_text_chunk => self.parse_ztxt(),
1021            chunk::iTXt if !self.decode_options.ignore_text_chunk => self.parse_itxt(),
1022
1023            // Unrecognized chunks.
1024            _ => {
1025                if is_critical(type_str) {
1026                    return Err(DecodingError::Format(
1027                        FormatErrorInner::UnrecognizedCriticalChunk { type_str }.into(),
1028                    ));
1029                } else {
1030                    return Ok(Decoded::SkippedAncillaryChunk(type_str));
1031                }
1032            }
1033        };
1034
1035        parse_result = parse_result.map_err(|e| {
1036            match e {
1037                // `parse_chunk` is invoked after gathering **all** bytes of a chunk, so
1038                // `UnexpectedEof` from something like `read_be` is permanent and indicates an
1039                // invalid PNG that should be represented as a `FormatError`, rather than as a
1040                // (potentially recoverable) `IoError` / `UnexpectedEof`.
1041                DecodingError::IoError(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => {
1042                    let fmt_err: FormatError =
1043                        FormatErrorInner::ChunkTooShort { kind: type_str }.into();
1044                    fmt_err.into()
1045                }
1046                e => e,
1047            }
1048        });
1049
1050        match parse_result {
1051            Ok(()) => Ok(Decoded::ChunkComplete(type_str)),
1052            Err(DecodingError::Format(_))
1053                if type_str != chunk::fcTL && !chunk::is_critical(type_str) =>
1054            {
1055                // Ignore benign errors in most auxiliary chunks. `LimitsExceeded`, `Parameter` and
1056                // other error kinds are *not* treated as benign. We don't ignore errors in `fcTL`
1057                // chunks because the fallback to the static/non-animated image has to be
1058                // implemented *on top* of the `StreamingDecoder` API.
1059                //
1060                // TODO: Consider supporting a strict mode where even benign errors are reported up.
1061                // See https://github.com/image-rs/image-png/pull/569#issuecomment-2642062285
1062                Ok(Decoded::BadAncillaryChunk(type_str))
1063            }
1064            Err(e) => Err(e),
1065        }
1066    }
1067
1068    fn parse_fctl(&mut self) -> Result<(), DecodingError> {
1069        let mut buf = &self.current_chunk.raw_bytes[..];
1070        let next_seq_no = buf.read_be()?;
1071
1072        // Assuming that fcTL is required before *every* fdAT-sequence
1073        self.current_seq_no = Some(if let Some(seq_no) = self.current_seq_no {
1074            if next_seq_no != seq_no + 1 {
1075                return Err(DecodingError::Format(
1076                    FormatErrorInner::ApngOrder {
1077                        expected: seq_no + 1,
1078                        present: next_seq_no,
1079                    }
1080                    .into(),
1081                ));
1082            }
1083            next_seq_no
1084        } else {
1085            if next_seq_no != 0 {
1086                return Err(DecodingError::Format(
1087                    FormatErrorInner::ApngOrder {
1088                        expected: 0,
1089                        present: next_seq_no,
1090                    }
1091                    .into(),
1092                ));
1093            }
1094            0
1095        });
1096        self.inflater.reset();
1097        self.ready_for_fdat_chunks = true;
1098        let fc = FrameControl {
1099            sequence_number: next_seq_no,
1100            width: buf.read_be()?,
1101            height: buf.read_be()?,
1102            x_offset: buf.read_be()?,
1103            y_offset: buf.read_be()?,
1104            delay_num: buf.read_be()?,
1105            delay_den: buf.read_be()?,
1106            dispose_op: {
1107                let dispose_op = buf.read_be()?;
1108                match DisposeOp::from_u8(dispose_op) {
1109                    Some(dispose_op) => dispose_op,
1110                    None => {
1111                        return Err(DecodingError::Format(
1112                            FormatErrorInner::InvalidDisposeOp(dispose_op).into(),
1113                        ))
1114                    }
1115                }
1116            },
1117            blend_op: {
1118                let blend_op = buf.read_be()?;
1119                match BlendOp::from_u8(blend_op) {
1120                    Some(blend_op) => blend_op,
1121                    None => {
1122                        return Err(DecodingError::Format(
1123                            FormatErrorInner::InvalidBlendOp(blend_op).into(),
1124                        ))
1125                    }
1126                }
1127            },
1128        };
1129        self.info.as_ref().unwrap().validate(&fc)?;
1130        if !self.have_idat {
1131            self.info.as_ref().unwrap().validate_default_image(&fc)?;
1132        }
1133        self.info.as_mut().unwrap().frame_control = Some(fc);
1134        Ok(())
1135    }
1136
1137    fn parse_actl(&mut self) -> Result<(), DecodingError> {
1138        if self.have_idat {
1139            Err(DecodingError::Format(
1140                FormatErrorInner::AfterIdat { kind: chunk::acTL }.into(),
1141            ))
1142        } else {
1143            let mut buf = &self.current_chunk.raw_bytes[..];
1144            let actl = AnimationControl {
1145                num_frames: buf.read_be()?,
1146                num_plays: buf.read_be()?,
1147            };
1148            // The spec says that "0 is not a valid value" for `num_frames`.
1149            // So let's ignore such malformed `acTL` chunks.
1150            if actl.num_frames == 0 {
1151                return Ok(());
1152            }
1153            self.info.as_mut().unwrap().animation_control = Some(actl);
1154            Ok(())
1155        }
1156    }
1157
1158    fn parse_plte(&mut self) -> Result<(), DecodingError> {
1159        let info = self.info.as_mut().unwrap();
1160        if info.palette.is_some() {
1161            // Only one palette is allowed
1162            Err(DecodingError::Format(
1163                FormatErrorInner::DuplicateChunk { kind: chunk::PLTE }.into(),
1164            ))
1165        } else {
1166            self.limits
1167                .reserve_bytes(self.current_chunk.raw_bytes.len())?;
1168            info.palette = Some(Cow::Owned(self.current_chunk.raw_bytes.clone()));
1169            Ok(())
1170        }
1171    }
1172
1173    fn parse_sbit(&mut self) -> Result<(), DecodingError> {
1174        let info = self.info.as_mut().unwrap();
1175        if info.palette.is_some() {
1176            return Err(DecodingError::Format(
1177                FormatErrorInner::AfterPlte { kind: chunk::sBIT }.into(),
1178            ));
1179        }
1180
1181        if self.have_idat {
1182            return Err(DecodingError::Format(
1183                FormatErrorInner::AfterIdat { kind: chunk::sBIT }.into(),
1184            ));
1185        }
1186
1187        if info.sbit.is_some() {
1188            return Err(DecodingError::Format(
1189                FormatErrorInner::DuplicateChunk { kind: chunk::sBIT }.into(),
1190            ));
1191        }
1192
1193        let (color_type, bit_depth) = { (info.color_type, info.bit_depth) };
1194        // The sample depth for color type 3 is fixed at eight bits.
1195        let sample_depth = if color_type == ColorType::Indexed {
1196            BitDepth::Eight
1197        } else {
1198            bit_depth
1199        };
1200        self.limits
1201            .reserve_bytes(self.current_chunk.raw_bytes.len())?;
1202        let vec = self.current_chunk.raw_bytes.clone();
1203        let len = vec.len();
1204
1205        // expected lenth of the chunk
1206        let expected = match color_type {
1207            ColorType::Grayscale => 1,
1208            ColorType::Rgb | ColorType::Indexed => 3,
1209            ColorType::GrayscaleAlpha => 2,
1210            ColorType::Rgba => 4,
1211        };
1212
1213        // Check if the sbit chunk size is valid.
1214        if expected != len {
1215            return Err(DecodingError::Format(
1216                FormatErrorInner::InvalidSbitChunkSize {
1217                    color_type,
1218                    expected,
1219                    len,
1220                }
1221                .into(),
1222            ));
1223        }
1224
1225        for sbit in &vec {
1226            if *sbit < 1 || *sbit > sample_depth as u8 {
1227                return Err(DecodingError::Format(
1228                    FormatErrorInner::InvalidSbit {
1229                        sample_depth,
1230                        sbit: *sbit,
1231                    }
1232                    .into(),
1233                ));
1234            }
1235        }
1236        info.sbit = Some(Cow::Owned(vec));
1237        Ok(())
1238    }
1239
1240    fn parse_trns(&mut self) -> Result<(), DecodingError> {
1241        let info = self.info.as_mut().unwrap();
1242        if info.trns.is_some() {
1243            return Err(DecodingError::Format(
1244                FormatErrorInner::DuplicateChunk { kind: chunk::PLTE }.into(),
1245            ));
1246        }
1247        let (color_type, bit_depth) = { (info.color_type, info.bit_depth as u8) };
1248        self.limits
1249            .reserve_bytes(self.current_chunk.raw_bytes.len())?;
1250        let mut vec = self.current_chunk.raw_bytes.clone();
1251        let len = vec.len();
1252        match color_type {
1253            ColorType::Grayscale => {
1254                if len < 2 {
1255                    return Err(DecodingError::Format(
1256                        FormatErrorInner::ShortPalette { expected: 2, len }.into(),
1257                    ));
1258                }
1259                if bit_depth < 16 {
1260                    vec[0] = vec[1];
1261                    vec.truncate(1);
1262                }
1263                info.trns = Some(Cow::Owned(vec));
1264                Ok(())
1265            }
1266            ColorType::Rgb => {
1267                if len < 6 {
1268                    return Err(DecodingError::Format(
1269                        FormatErrorInner::ShortPalette { expected: 6, len }.into(),
1270                    ));
1271                }
1272                if bit_depth < 16 {
1273                    vec[0] = vec[1];
1274                    vec[1] = vec[3];
1275                    vec[2] = vec[5];
1276                    vec.truncate(3);
1277                }
1278                info.trns = Some(Cow::Owned(vec));
1279                Ok(())
1280            }
1281            ColorType::Indexed => {
1282                // The transparency chunk must be after the palette chunk and
1283                // before the data chunk.
1284                if info.palette.is_none() {
1285                    return Err(DecodingError::Format(
1286                        FormatErrorInner::BeforePlte { kind: chunk::tRNS }.into(),
1287                    ));
1288                } else if self.have_idat {
1289                    return Err(DecodingError::Format(
1290                        FormatErrorInner::OutsidePlteIdat { kind: chunk::tRNS }.into(),
1291                    ));
1292                }
1293
1294                info.trns = Some(Cow::Owned(vec));
1295                Ok(())
1296            }
1297            c => Err(DecodingError::Format(
1298                FormatErrorInner::ColorWithBadTrns(c).into(),
1299            )),
1300        }
1301    }
1302
1303    fn parse_phys(&mut self) -> Result<(), DecodingError> {
1304        let info = self.info.as_mut().unwrap();
1305        if self.have_idat {
1306            Err(DecodingError::Format(
1307                FormatErrorInner::AfterIdat { kind: chunk::pHYs }.into(),
1308            ))
1309        } else if info.pixel_dims.is_some() {
1310            Err(DecodingError::Format(
1311                FormatErrorInner::DuplicateChunk { kind: chunk::pHYs }.into(),
1312            ))
1313        } else {
1314            let mut buf = &self.current_chunk.raw_bytes[..];
1315            let xppu = buf.read_be()?;
1316            let yppu = buf.read_be()?;
1317            let unit = buf.read_be()?;
1318            let unit = match Unit::from_u8(unit) {
1319                Some(unit) => unit,
1320                None => {
1321                    return Err(DecodingError::Format(
1322                        FormatErrorInner::InvalidUnit(unit).into(),
1323                    ))
1324                }
1325            };
1326            let pixel_dims = PixelDimensions { xppu, yppu, unit };
1327            info.pixel_dims = Some(pixel_dims);
1328            Ok(())
1329        }
1330    }
1331
1332    fn parse_chrm(&mut self) -> Result<(), DecodingError> {
1333        let info = self.info.as_mut().unwrap();
1334        if self.have_idat {
1335            Err(DecodingError::Format(
1336                FormatErrorInner::AfterIdat { kind: chunk::cHRM }.into(),
1337            ))
1338        } else if info.chrm_chunk.is_some() {
1339            Err(DecodingError::Format(
1340                FormatErrorInner::DuplicateChunk { kind: chunk::cHRM }.into(),
1341            ))
1342        } else {
1343            let mut buf = &self.current_chunk.raw_bytes[..];
1344            let white_x: u32 = buf.read_be()?;
1345            let white_y: u32 = buf.read_be()?;
1346            let red_x: u32 = buf.read_be()?;
1347            let red_y: u32 = buf.read_be()?;
1348            let green_x: u32 = buf.read_be()?;
1349            let green_y: u32 = buf.read_be()?;
1350            let blue_x: u32 = buf.read_be()?;
1351            let blue_y: u32 = buf.read_be()?;
1352
1353            let source_chromaticities = SourceChromaticities {
1354                white: (
1355                    ScaledFloat::from_scaled(white_x),
1356                    ScaledFloat::from_scaled(white_y),
1357                ),
1358                red: (
1359                    ScaledFloat::from_scaled(red_x),
1360                    ScaledFloat::from_scaled(red_y),
1361                ),
1362                green: (
1363                    ScaledFloat::from_scaled(green_x),
1364                    ScaledFloat::from_scaled(green_y),
1365                ),
1366                blue: (
1367                    ScaledFloat::from_scaled(blue_x),
1368                    ScaledFloat::from_scaled(blue_y),
1369                ),
1370            };
1371
1372            info.chrm_chunk = Some(source_chromaticities);
1373            Ok(())
1374        }
1375    }
1376
1377    fn parse_gama(&mut self) -> Result<(), DecodingError> {
1378        let info = self.info.as_mut().unwrap();
1379        if self.have_idat {
1380            Err(DecodingError::Format(
1381                FormatErrorInner::AfterIdat { kind: chunk::gAMA }.into(),
1382            ))
1383        } else if info.gama_chunk.is_some() {
1384            Err(DecodingError::Format(
1385                FormatErrorInner::DuplicateChunk { kind: chunk::gAMA }.into(),
1386            ))
1387        } else {
1388            let mut buf = &self.current_chunk.raw_bytes[..];
1389            let source_gamma: u32 = buf.read_be()?;
1390            if source_gamma == 0 {
1391                return Err(DecodingError::Format(
1392                    FormatErrorInner::BadGammaValue.into(),
1393                ));
1394            }
1395
1396            let source_gamma = ScaledFloat::from_scaled(source_gamma);
1397            info.gama_chunk = Some(source_gamma);
1398            Ok(())
1399        }
1400    }
1401
1402    fn parse_srgb(&mut self) -> Result<(), DecodingError> {
1403        let info = self.info.as_mut().unwrap();
1404        if self.have_idat {
1405            Err(DecodingError::Format(
1406                FormatErrorInner::AfterIdat { kind: chunk::sRGB }.into(),
1407            ))
1408        } else if info.srgb.is_some() {
1409            Err(DecodingError::Format(
1410                FormatErrorInner::DuplicateChunk { kind: chunk::sRGB }.into(),
1411            ))
1412        } else {
1413            let mut buf = &self.current_chunk.raw_bytes[..];
1414            let raw: u8 = buf.read_be()?; // BE is is nonsense for single bytes, but this way the size is checked.
1415            let rendering_intent = crate::SrgbRenderingIntent::from_raw(raw).ok_or_else(|| {
1416                FormatError::from(FormatErrorInner::InvalidSrgbRenderingIntent(raw))
1417            })?;
1418
1419            // Set srgb and override source gamma and chromaticities.
1420            info.srgb = Some(rendering_intent);
1421            Ok(())
1422        }
1423    }
1424
1425    fn parse_cicp(&mut self) -> Result<(), DecodingError> {
1426        let info = self.info.as_mut().unwrap();
1427
1428        // The spec requires that the cICP chunk MUST come before the PLTE and IDAT chunks.
1429        if info.coding_independent_code_points.is_some() {
1430            return Err(DecodingError::Format(
1431                FormatErrorInner::DuplicateChunk { kind: chunk::cICP }.into(),
1432            ));
1433        } else if info.palette.is_some() {
1434            return Err(DecodingError::Format(
1435                FormatErrorInner::AfterPlte { kind: chunk::cICP }.into(),
1436            ));
1437        } else if self.have_idat {
1438            return Err(DecodingError::Format(
1439                FormatErrorInner::AfterIdat { kind: chunk::cICP }.into(),
1440            ));
1441        }
1442
1443        let mut buf = &*self.current_chunk.raw_bytes;
1444        let color_primaries: u8 = buf.read_be()?;
1445        let transfer_function: u8 = buf.read_be()?;
1446        let matrix_coefficients: u8 = buf.read_be()?;
1447        let is_video_full_range_image = {
1448            let flag: u8 = buf.read_be()?;
1449            match flag {
1450                0 => false,
1451                1 => true,
1452                _ => {
1453                    return Err(DecodingError::IoError(
1454                        std::io::ErrorKind::InvalidData.into(),
1455                    ));
1456                }
1457            }
1458        };
1459
1460        // RGB is currently the only supported color model in PNG, and as
1461        // such Matrix Coefficients shall be set to 0.
1462        if matrix_coefficients != 0 {
1463            return Err(DecodingError::IoError(
1464                std::io::ErrorKind::InvalidData.into(),
1465            ));
1466        }
1467
1468        if !buf.is_empty() {
1469            return Err(DecodingError::IoError(
1470                std::io::ErrorKind::InvalidData.into(),
1471            ));
1472        }
1473
1474        info.coding_independent_code_points = Some(CodingIndependentCodePoints {
1475            color_primaries,
1476            transfer_function,
1477            matrix_coefficients,
1478            is_video_full_range_image,
1479        });
1480
1481        Ok(())
1482    }
1483
1484    fn parse_mdcv(&mut self) -> Result<(), DecodingError> {
1485        let info = self.info.as_mut().unwrap();
1486
1487        // The spec requires that the mDCV chunk MUST come before the PLTE and IDAT chunks.
1488        if info.mastering_display_color_volume.is_some() {
1489            return Err(DecodingError::Format(
1490                FormatErrorInner::DuplicateChunk { kind: chunk::mDCV }.into(),
1491            ));
1492        } else if info.palette.is_some() {
1493            return Err(DecodingError::Format(
1494                FormatErrorInner::AfterPlte { kind: chunk::mDCV }.into(),
1495            ));
1496        } else if self.have_idat {
1497            return Err(DecodingError::Format(
1498                FormatErrorInner::AfterIdat { kind: chunk::mDCV }.into(),
1499            ));
1500        }
1501
1502        let mut buf = &*self.current_chunk.raw_bytes;
1503        let red_x: u16 = buf.read_be()?;
1504        let red_y: u16 = buf.read_be()?;
1505        let green_x: u16 = buf.read_be()?;
1506        let green_y: u16 = buf.read_be()?;
1507        let blue_x: u16 = buf.read_be()?;
1508        let blue_y: u16 = buf.read_be()?;
1509        let white_x: u16 = buf.read_be()?;
1510        let white_y: u16 = buf.read_be()?;
1511        fn scale(chunk: u16) -> ScaledFloat {
1512            // `ScaledFloat::SCALING` is hardcoded to 100_000, which works
1513            // well for the `cHRM` chunk where the spec says that "a value
1514            // of 0.3127 would be stored as the integer 31270".  In the
1515            // `mDCV` chunk the spec says that "0.708, 0.292)" is stored as
1516            // "{ 35400, 14600 }", using a scaling factor of 50_000, so we
1517            // multiply by 2 before converting.
1518            ScaledFloat::from_scaled((chunk as u32) * 2)
1519        }
1520        let chromaticities = SourceChromaticities {
1521            white: (scale(white_x), scale(white_y)),
1522            red: (scale(red_x), scale(red_y)),
1523            green: (scale(green_x), scale(green_y)),
1524            blue: (scale(blue_x), scale(blue_y)),
1525        };
1526        let max_luminance: u32 = buf.read_be()?;
1527        let min_luminance: u32 = buf.read_be()?;
1528        if !buf.is_empty() {
1529            return Err(DecodingError::IoError(
1530                std::io::ErrorKind::InvalidData.into(),
1531            ));
1532        }
1533        info.mastering_display_color_volume = Some(MasteringDisplayColorVolume {
1534            chromaticities,
1535            max_luminance,
1536            min_luminance,
1537        });
1538
1539        Ok(())
1540    }
1541
1542    fn parse_clli(&mut self) -> Result<(), DecodingError> {
1543        let info = self.info.as_mut().unwrap();
1544        if info.content_light_level.is_some() {
1545            return Err(DecodingError::Format(
1546                FormatErrorInner::DuplicateChunk { kind: chunk::cLLI }.into(),
1547            ));
1548        }
1549
1550        let mut buf = &*self.current_chunk.raw_bytes;
1551        let max_content_light_level: u32 = buf.read_be()?;
1552        let max_frame_average_light_level: u32 = buf.read_be()?;
1553        if !buf.is_empty() {
1554            return Err(DecodingError::IoError(
1555                std::io::ErrorKind::InvalidData.into(),
1556            ));
1557        }
1558        info.content_light_level = Some(ContentLightLevelInfo {
1559            max_content_light_level,
1560            max_frame_average_light_level,
1561        });
1562
1563        Ok(())
1564    }
1565
1566    fn parse_exif(&mut self) -> Result<(), DecodingError> {
1567        let info = self.info.as_mut().unwrap();
1568        if info.exif_metadata.is_some() {
1569            return Err(DecodingError::Format(
1570                FormatErrorInner::DuplicateChunk { kind: chunk::eXIf }.into(),
1571            ));
1572        }
1573
1574        info.exif_metadata = Some(self.current_chunk.raw_bytes.clone().into());
1575        Ok(())
1576    }
1577
1578    fn parse_iccp(&mut self) -> Result<(), DecodingError> {
1579        if self.have_idat {
1580            Err(DecodingError::Format(
1581                FormatErrorInner::AfterIdat { kind: chunk::iCCP }.into(),
1582            ))
1583        } else if self.have_iccp {
1584            Err(DecodingError::Format(
1585                FormatErrorInner::DuplicateChunk { kind: chunk::iCCP }.into(),
1586            ))
1587        } else {
1588            self.have_iccp = true;
1589            let _ = self.parse_iccp_raw();
1590            Ok(())
1591        }
1592    }
1593
1594    fn parse_iccp_raw(&mut self) -> Result<(), DecodingError> {
1595        let info = self.info.as_mut().unwrap();
1596        let mut buf = &self.current_chunk.raw_bytes[..];
1597
1598        // read profile name
1599        for len in 0..=80 {
1600            let raw: u8 = buf.read_be()?;
1601            if (raw == 0 && len == 0) || (raw != 0 && len == 80) {
1602                return Err(DecodingError::from(TextDecodingError::InvalidKeywordSize));
1603            }
1604            if raw == 0 {
1605                break;
1606            }
1607        }
1608
1609        match buf.read_be()? {
1610            // compression method
1611            0u8 => (),
1612            n => {
1613                return Err(DecodingError::Format(
1614                    FormatErrorInner::UnknownCompressionMethod(n).into(),
1615                ))
1616            }
1617        }
1618
1619        match fdeflate::decompress_to_vec_bounded(buf, self.limits.bytes) {
1620            Ok(profile) => {
1621                self.limits.reserve_bytes(profile.len())?;
1622                info.icc_profile = Some(Cow::Owned(profile));
1623            }
1624            Err(fdeflate::BoundedDecompressionError::DecompressionError { inner: err }) => {
1625                return Err(DecodingError::Format(
1626                    FormatErrorInner::CorruptFlateStream { err }.into(),
1627                ))
1628            }
1629            Err(fdeflate::BoundedDecompressionError::OutputTooLarge { .. }) => {
1630                return Err(DecodingError::LimitsExceeded);
1631            }
1632        }
1633
1634        Ok(())
1635    }
1636
1637    fn parse_ihdr(&mut self) -> Result<(), DecodingError> {
1638        if self.info.is_some() {
1639            return Err(DecodingError::Format(
1640                FormatErrorInner::DuplicateChunk { kind: IHDR }.into(),
1641            ));
1642        }
1643        let mut buf = &self.current_chunk.raw_bytes[..];
1644        let width = buf.read_be()?;
1645        let height = buf.read_be()?;
1646        if width == 0 || height == 0 {
1647            return Err(DecodingError::Format(
1648                FormatErrorInner::InvalidDimensions.into(),
1649            ));
1650        }
1651        let bit_depth = buf.read_be()?;
1652        let bit_depth = match BitDepth::from_u8(bit_depth) {
1653            Some(bits) => bits,
1654            None => {
1655                return Err(DecodingError::Format(
1656                    FormatErrorInner::InvalidBitDepth(bit_depth).into(),
1657                ))
1658            }
1659        };
1660        let color_type = buf.read_be()?;
1661        let color_type = match ColorType::from_u8(color_type) {
1662            Some(color_type) => {
1663                if color_type.is_combination_invalid(bit_depth) {
1664                    return Err(DecodingError::Format(
1665                        FormatErrorInner::InvalidColorBitDepth {
1666                            color_type,
1667                            bit_depth,
1668                        }
1669                        .into(),
1670                    ));
1671                } else {
1672                    color_type
1673                }
1674            }
1675            None => {
1676                return Err(DecodingError::Format(
1677                    FormatErrorInner::InvalidColorType(color_type).into(),
1678                ))
1679            }
1680        };
1681        match buf.read_be()? {
1682            // compression method
1683            0u8 => (),
1684            n => {
1685                return Err(DecodingError::Format(
1686                    FormatErrorInner::UnknownCompressionMethod(n).into(),
1687                ))
1688            }
1689        }
1690        match buf.read_be()? {
1691            // filter method
1692            0u8 => (),
1693            n => {
1694                return Err(DecodingError::Format(
1695                    FormatErrorInner::UnknownFilterMethod(n).into(),
1696                ))
1697            }
1698        }
1699        let interlaced = match buf.read_be()? {
1700            0u8 => false,
1701            1 => true,
1702            n => {
1703                return Err(DecodingError::Format(
1704                    FormatErrorInner::UnknownInterlaceMethod(n).into(),
1705                ))
1706            }
1707        };
1708
1709        self.info = Some(Info {
1710            width,
1711            height,
1712            bit_depth,
1713            color_type,
1714            interlaced,
1715            ..Default::default()
1716        });
1717
1718        Ok(())
1719    }
1720
1721    fn split_keyword(buf: &[u8]) -> Result<(&[u8], &[u8]), DecodingError> {
1722        let null_byte_index = buf
1723            .iter()
1724            .position(|&b| b == 0)
1725            .ok_or_else(|| DecodingError::from(TextDecodingError::MissingNullSeparator))?;
1726
1727        if null_byte_index == 0 || null_byte_index > 79 {
1728            return Err(DecodingError::from(TextDecodingError::InvalidKeywordSize));
1729        }
1730
1731        Ok((&buf[..null_byte_index], &buf[null_byte_index + 1..]))
1732    }
1733
1734    fn parse_text(&mut self) -> Result<(), DecodingError> {
1735        let buf = &self.current_chunk.raw_bytes[..];
1736        self.limits.reserve_bytes(buf.len())?;
1737
1738        let (keyword_slice, value_slice) = Self::split_keyword(buf)?;
1739
1740        self.info
1741            .as_mut()
1742            .unwrap()
1743            .uncompressed_latin1_text
1744            .push(TEXtChunk::decode(keyword_slice, value_slice).map_err(DecodingError::from)?);
1745
1746        Ok(())
1747    }
1748
1749    fn parse_ztxt(&mut self) -> Result<(), DecodingError> {
1750        let buf = &self.current_chunk.raw_bytes[..];
1751        self.limits.reserve_bytes(buf.len())?;
1752
1753        let (keyword_slice, value_slice) = Self::split_keyword(buf)?;
1754
1755        let compression_method = *value_slice
1756            .first()
1757            .ok_or_else(|| DecodingError::from(TextDecodingError::InvalidCompressionMethod))?;
1758
1759        let text_slice = &value_slice[1..];
1760
1761        self.info.as_mut().unwrap().compressed_latin1_text.push(
1762            ZTXtChunk::decode(keyword_slice, compression_method, text_slice)
1763                .map_err(DecodingError::from)?,
1764        );
1765
1766        Ok(())
1767    }
1768
1769    fn parse_itxt(&mut self) -> Result<(), DecodingError> {
1770        let buf = &self.current_chunk.raw_bytes[..];
1771        self.limits.reserve_bytes(buf.len())?;
1772
1773        let (keyword_slice, value_slice) = Self::split_keyword(buf)?;
1774
1775        let compression_flag = *value_slice
1776            .first()
1777            .ok_or_else(|| DecodingError::from(TextDecodingError::MissingCompressionFlag))?;
1778
1779        let compression_method = *value_slice
1780            .get(1)
1781            .ok_or_else(|| DecodingError::from(TextDecodingError::InvalidCompressionMethod))?;
1782
1783        let second_null_byte_index = value_slice[2..]
1784            .iter()
1785            .position(|&b| b == 0)
1786            .ok_or_else(|| DecodingError::from(TextDecodingError::MissingNullSeparator))?
1787            + 2;
1788
1789        let language_tag_slice = &value_slice[2..second_null_byte_index];
1790
1791        let third_null_byte_index = value_slice[second_null_byte_index + 1..]
1792            .iter()
1793            .position(|&b| b == 0)
1794            .ok_or_else(|| DecodingError::from(TextDecodingError::MissingNullSeparator))?
1795            + (second_null_byte_index + 1);
1796
1797        let translated_keyword_slice =
1798            &value_slice[second_null_byte_index + 1..third_null_byte_index];
1799
1800        let text_slice = &value_slice[third_null_byte_index + 1..];
1801
1802        self.info.as_mut().unwrap().utf8_text.push(
1803            ITXtChunk::decode(
1804                keyword_slice,
1805                compression_flag,
1806                compression_method,
1807                language_tag_slice,
1808                translated_keyword_slice,
1809                text_slice,
1810            )
1811            .map_err(DecodingError::from)?,
1812        );
1813
1814        Ok(())
1815    }
1816
1817    fn parse_bkgd(&mut self) -> Result<(), DecodingError> {
1818        let info = self.info.as_mut().unwrap();
1819        if info.bkgd.is_some() {
1820            // Only one bKGD chunk is allowed
1821            return Err(DecodingError::Format(
1822                FormatErrorInner::DuplicateChunk { kind: chunk::bKGD }.into(),
1823            ));
1824        } else if self.have_idat {
1825            return Err(DecodingError::Format(
1826                FormatErrorInner::AfterIdat { kind: chunk::bKGD }.into(),
1827            ));
1828        }
1829
1830        let expected = match info.color_type {
1831            ColorType::Indexed => {
1832                if info.palette.is_none() {
1833                    return Err(DecodingError::IoError(
1834                        std::io::ErrorKind::InvalidData.into(),
1835                    ));
1836                };
1837                1
1838            }
1839            ColorType::Grayscale | ColorType::GrayscaleAlpha => 2,
1840            ColorType::Rgb | ColorType::Rgba => 6,
1841        };
1842        let vec = self.current_chunk.raw_bytes.clone();
1843        if vec.len() != expected {
1844            return Err(DecodingError::Format(
1845                FormatErrorInner::ChunkTooShort { kind: chunk::bKGD }.into(),
1846            ));
1847        }
1848
1849        info.bkgd = Some(Cow::Owned(vec));
1850        Ok(())
1851    }
1852}
1853
1854impl Info<'_> {
1855    fn validate_default_image(&self, fc: &FrameControl) -> Result<(), DecodingError> {
1856        // https://www.w3.org/TR/png-3/#fcTL-chunk says that:
1857        //
1858        // > The fcTL chunk corresponding to the default image, if it exists, has these
1859        // > restrictions:
1860        // >
1861        // > * The x_offset and y_offset fields must be 0.
1862        // > * The width and height fields must equal
1863        // >   the corresponding fields from the IHDR chunk.
1864        if fc.x_offset != 0
1865            || fc.y_offset != 0
1866            || fc.width != self.width
1867            || fc.height != self.height
1868        {
1869            return Err(DecodingError::Format(
1870                FormatErrorInner::BadSubFrameBounds {}.into(),
1871            ));
1872        }
1873        Ok(())
1874    }
1875
1876    fn validate(&self, fc: &FrameControl) -> Result<(), DecodingError> {
1877        if fc.width == 0 || fc.height == 0 {
1878            return Err(DecodingError::Format(
1879                FormatErrorInner::InvalidDimensions.into(),
1880            ));
1881        }
1882
1883        // Validate mathematically: fc.width + fc.x_offset <= self.width
1884        let in_x_bounds = Some(fc.width) <= self.width.checked_sub(fc.x_offset);
1885        // Validate mathematically: fc.height + fc.y_offset <= self.height
1886        let in_y_bounds = Some(fc.height) <= self.height.checked_sub(fc.y_offset);
1887
1888        if !in_x_bounds || !in_y_bounds {
1889            return Err(DecodingError::Format(
1890                // TODO: do we want to display the bad bounds?
1891                FormatErrorInner::BadSubFrameBounds {}.into(),
1892            ));
1893        }
1894
1895        Ok(())
1896    }
1897}
1898
1899impl Default for StreamingDecoder {
1900    fn default() -> Self {
1901        Self::new()
1902    }
1903}
1904
1905#[cfg(test)]
1906mod tests {
1907    use super::ScaledFloat;
1908    use super::SourceChromaticities;
1909    use crate::test_utils::*;
1910    use crate::{Decoder, DecodingError, Reader, SrgbRenderingIntent, Unit};
1911    use approx::assert_relative_eq;
1912    use byteorder::WriteBytesExt;
1913    use std::borrow::Cow;
1914    use std::cell::RefCell;
1915
1916    use std::fs::File;
1917    use std::io::BufRead;
1918    use std::io::Cursor;
1919    use std::io::Seek;
1920    use std::io::{BufReader, ErrorKind, Read, Write};
1921    use std::rc::Rc;
1922
1923    #[test]
1924    fn image_gamma() -> Result<(), ()> {
1925        fn trial(path: &str, expected: Option<ScaledFloat>) {
1926            let decoder = crate::Decoder::new(BufReader::new(File::open(path).unwrap()));
1927            let reader = decoder.read_info().unwrap();
1928            let actual: Option<ScaledFloat> = reader.info().gamma();
1929            assert!(actual == expected);
1930        }
1931        trial("tests/pngsuite/f00n0g08.png", None);
1932        trial("tests/pngsuite/f00n2c08.png", None);
1933        trial("tests/pngsuite/f01n0g08.png", None);
1934        trial("tests/pngsuite/f01n2c08.png", None);
1935        trial("tests/pngsuite/f02n0g08.png", None);
1936        trial("tests/pngsuite/f02n2c08.png", None);
1937        trial("tests/pngsuite/f03n0g08.png", None);
1938        trial("tests/pngsuite/f03n2c08.png", None);
1939        trial("tests/pngsuite/f04n0g08.png", None);
1940        trial("tests/pngsuite/f04n2c08.png", None);
1941        trial("tests/pngsuite/f99n0g04.png", None);
1942        trial("tests/pngsuite/tm3n3p02.png", None);
1943        trial("tests/pngsuite/g03n0g16.png", Some(ScaledFloat::new(0.35)));
1944        trial("tests/pngsuite/g03n2c08.png", Some(ScaledFloat::new(0.35)));
1945        trial("tests/pngsuite/g03n3p04.png", Some(ScaledFloat::new(0.35)));
1946        trial("tests/pngsuite/g04n0g16.png", Some(ScaledFloat::new(0.45)));
1947        trial("tests/pngsuite/g04n2c08.png", Some(ScaledFloat::new(0.45)));
1948        trial("tests/pngsuite/g04n3p04.png", Some(ScaledFloat::new(0.45)));
1949        trial("tests/pngsuite/g05n0g16.png", Some(ScaledFloat::new(0.55)));
1950        trial("tests/pngsuite/g05n2c08.png", Some(ScaledFloat::new(0.55)));
1951        trial("tests/pngsuite/g05n3p04.png", Some(ScaledFloat::new(0.55)));
1952        trial("tests/pngsuite/g07n0g16.png", Some(ScaledFloat::new(0.7)));
1953        trial("tests/pngsuite/g07n2c08.png", Some(ScaledFloat::new(0.7)));
1954        trial("tests/pngsuite/g07n3p04.png", Some(ScaledFloat::new(0.7)));
1955        trial("tests/pngsuite/g10n0g16.png", Some(ScaledFloat::new(1.0)));
1956        trial("tests/pngsuite/g10n2c08.png", Some(ScaledFloat::new(1.0)));
1957        trial("tests/pngsuite/g10n3p04.png", Some(ScaledFloat::new(1.0)));
1958        trial("tests/pngsuite/g25n0g16.png", Some(ScaledFloat::new(2.5)));
1959        trial("tests/pngsuite/g25n2c08.png", Some(ScaledFloat::new(2.5)));
1960        trial("tests/pngsuite/g25n3p04.png", Some(ScaledFloat::new(2.5)));
1961        Ok(())
1962    }
1963
1964    #[test]
1965    fn image_source_chromaticities() -> Result<(), ()> {
1966        fn trial(path: &str, expected: Option<SourceChromaticities>) {
1967            let decoder = crate::Decoder::new(BufReader::new(File::open(path).unwrap()));
1968            let reader = decoder.read_info().unwrap();
1969            let actual: Option<SourceChromaticities> = reader.info().chromaticities();
1970            assert!(actual == expected);
1971        }
1972        trial(
1973            "tests/pngsuite/ccwn2c08.png",
1974            Some(SourceChromaticities::new(
1975                (0.3127, 0.3290),
1976                (0.64, 0.33),
1977                (0.30, 0.60),
1978                (0.15, 0.06),
1979            )),
1980        );
1981        trial(
1982            "tests/pngsuite/ccwn3p08.png",
1983            Some(SourceChromaticities::new(
1984                (0.3127, 0.3290),
1985                (0.64, 0.33),
1986                (0.30, 0.60),
1987                (0.15, 0.06),
1988            )),
1989        );
1990        trial("tests/pngsuite/basi0g01.png", None);
1991        trial("tests/pngsuite/basi0g02.png", None);
1992        trial("tests/pngsuite/basi0g04.png", None);
1993        trial("tests/pngsuite/basi0g08.png", None);
1994        trial("tests/pngsuite/basi0g16.png", None);
1995        trial("tests/pngsuite/basi2c08.png", None);
1996        trial("tests/pngsuite/basi2c16.png", None);
1997        trial("tests/pngsuite/basi3p01.png", None);
1998        trial("tests/pngsuite/basi3p02.png", None);
1999        trial("tests/pngsuite/basi3p04.png", None);
2000        trial("tests/pngsuite/basi3p08.png", None);
2001        trial("tests/pngsuite/basi4a08.png", None);
2002        trial("tests/pngsuite/basi4a16.png", None);
2003        trial("tests/pngsuite/basi6a08.png", None);
2004        trial("tests/pngsuite/basi6a16.png", None);
2005        trial("tests/pngsuite/basn0g01.png", None);
2006        trial("tests/pngsuite/basn0g02.png", None);
2007        trial("tests/pngsuite/basn0g04.png", None);
2008        trial("tests/pngsuite/basn0g08.png", None);
2009        trial("tests/pngsuite/basn0g16.png", None);
2010        trial("tests/pngsuite/basn2c08.png", None);
2011        trial("tests/pngsuite/basn2c16.png", None);
2012        trial("tests/pngsuite/basn3p01.png", None);
2013        trial("tests/pngsuite/basn3p02.png", None);
2014        trial("tests/pngsuite/basn3p04.png", None);
2015        trial("tests/pngsuite/basn3p08.png", None);
2016        trial("tests/pngsuite/basn4a08.png", None);
2017        trial("tests/pngsuite/basn4a16.png", None);
2018        trial("tests/pngsuite/basn6a08.png", None);
2019        trial("tests/pngsuite/basn6a16.png", None);
2020        trial("tests/pngsuite/bgai4a08.png", None);
2021        trial("tests/pngsuite/bgai4a16.png", None);
2022        trial("tests/pngsuite/bgan6a08.png", None);
2023        trial("tests/pngsuite/bgan6a16.png", None);
2024        trial("tests/pngsuite/bgbn4a08.png", None);
2025        trial("tests/pngsuite/bggn4a16.png", None);
2026        trial("tests/pngsuite/bgwn6a08.png", None);
2027        trial("tests/pngsuite/bgyn6a16.png", None);
2028        trial("tests/pngsuite/cdfn2c08.png", None);
2029        trial("tests/pngsuite/cdhn2c08.png", None);
2030        trial("tests/pngsuite/cdsn2c08.png", None);
2031        trial("tests/pngsuite/cdun2c08.png", None);
2032        trial("tests/pngsuite/ch1n3p04.png", None);
2033        trial("tests/pngsuite/ch2n3p08.png", None);
2034        trial("tests/pngsuite/cm0n0g04.png", None);
2035        trial("tests/pngsuite/cm7n0g04.png", None);
2036        trial("tests/pngsuite/cm9n0g04.png", None);
2037        trial("tests/pngsuite/cs3n2c16.png", None);
2038        trial("tests/pngsuite/cs3n3p08.png", None);
2039        trial("tests/pngsuite/cs5n2c08.png", None);
2040        trial("tests/pngsuite/cs5n3p08.png", None);
2041        trial("tests/pngsuite/cs8n2c08.png", None);
2042        trial("tests/pngsuite/cs8n3p08.png", None);
2043        trial("tests/pngsuite/ct0n0g04.png", None);
2044        trial("tests/pngsuite/ct1n0g04.png", None);
2045        trial("tests/pngsuite/cten0g04.png", None);
2046        trial("tests/pngsuite/ctfn0g04.png", None);
2047        trial("tests/pngsuite/ctgn0g04.png", None);
2048        trial("tests/pngsuite/cthn0g04.png", None);
2049        trial("tests/pngsuite/ctjn0g04.png", None);
2050        trial("tests/pngsuite/ctzn0g04.png", None);
2051        trial("tests/pngsuite/f00n0g08.png", None);
2052        trial("tests/pngsuite/f00n2c08.png", None);
2053        trial("tests/pngsuite/f01n0g08.png", None);
2054        trial("tests/pngsuite/f01n2c08.png", None);
2055        trial("tests/pngsuite/f02n0g08.png", None);
2056        trial("tests/pngsuite/f02n2c08.png", None);
2057        trial("tests/pngsuite/f03n0g08.png", None);
2058        trial("tests/pngsuite/f03n2c08.png", None);
2059        trial("tests/pngsuite/f04n0g08.png", None);
2060        trial("tests/pngsuite/f04n2c08.png", None);
2061        trial("tests/pngsuite/f99n0g04.png", None);
2062        trial("tests/pngsuite/g03n0g16.png", None);
2063        trial("tests/pngsuite/g03n2c08.png", None);
2064        trial("tests/pngsuite/g03n3p04.png", None);
2065        trial("tests/pngsuite/g04n0g16.png", None);
2066        trial("tests/pngsuite/g04n2c08.png", None);
2067        trial("tests/pngsuite/g04n3p04.png", None);
2068        trial("tests/pngsuite/g05n0g16.png", None);
2069        trial("tests/pngsuite/g05n2c08.png", None);
2070        trial("tests/pngsuite/g05n3p04.png", None);
2071        trial("tests/pngsuite/g07n0g16.png", None);
2072        trial("tests/pngsuite/g07n2c08.png", None);
2073        trial("tests/pngsuite/g07n3p04.png", None);
2074        trial("tests/pngsuite/g10n0g16.png", None);
2075        trial("tests/pngsuite/g10n2c08.png", None);
2076        trial("tests/pngsuite/g10n3p04.png", None);
2077        trial("tests/pngsuite/g25n0g16.png", None);
2078        trial("tests/pngsuite/g25n2c08.png", None);
2079        trial("tests/pngsuite/g25n3p04.png", None);
2080        trial("tests/pngsuite/oi1n0g16.png", None);
2081        trial("tests/pngsuite/oi1n2c16.png", None);
2082        trial("tests/pngsuite/oi2n0g16.png", None);
2083        trial("tests/pngsuite/oi2n2c16.png", None);
2084        trial("tests/pngsuite/oi4n0g16.png", None);
2085        trial("tests/pngsuite/oi4n2c16.png", None);
2086        trial("tests/pngsuite/oi9n0g16.png", None);
2087        trial("tests/pngsuite/oi9n2c16.png", None);
2088        trial("tests/pngsuite/PngSuite.png", None);
2089        trial("tests/pngsuite/pp0n2c16.png", None);
2090        trial("tests/pngsuite/pp0n6a08.png", None);
2091        trial("tests/pngsuite/ps1n0g08.png", None);
2092        trial("tests/pngsuite/ps1n2c16.png", None);
2093        trial("tests/pngsuite/ps2n0g08.png", None);
2094        trial("tests/pngsuite/ps2n2c16.png", None);
2095        trial("tests/pngsuite/s01i3p01.png", None);
2096        trial("tests/pngsuite/s01n3p01.png", None);
2097        trial("tests/pngsuite/s02i3p01.png", None);
2098        trial("tests/pngsuite/s02n3p01.png", None);
2099        trial("tests/pngsuite/s03i3p01.png", None);
2100        trial("tests/pngsuite/s03n3p01.png", None);
2101        trial("tests/pngsuite/s04i3p01.png", None);
2102        trial("tests/pngsuite/s04n3p01.png", None);
2103        trial("tests/pngsuite/s05i3p02.png", None);
2104        trial("tests/pngsuite/s05n3p02.png", None);
2105        trial("tests/pngsuite/s06i3p02.png", None);
2106        trial("tests/pngsuite/s06n3p02.png", None);
2107        trial("tests/pngsuite/s07i3p02.png", None);
2108        trial("tests/pngsuite/s07n3p02.png", None);
2109        trial("tests/pngsuite/s08i3p02.png", None);
2110        trial("tests/pngsuite/s08n3p02.png", None);
2111        trial("tests/pngsuite/s09i3p02.png", None);
2112        trial("tests/pngsuite/s09n3p02.png", None);
2113        trial("tests/pngsuite/s32i3p04.png", None);
2114        trial("tests/pngsuite/s32n3p04.png", None);
2115        trial("tests/pngsuite/s33i3p04.png", None);
2116        trial("tests/pngsuite/s33n3p04.png", None);
2117        trial("tests/pngsuite/s34i3p04.png", None);
2118        trial("tests/pngsuite/s34n3p04.png", None);
2119        trial("tests/pngsuite/s35i3p04.png", None);
2120        trial("tests/pngsuite/s35n3p04.png", None);
2121        trial("tests/pngsuite/s36i3p04.png", None);
2122        trial("tests/pngsuite/s36n3p04.png", None);
2123        trial("tests/pngsuite/s37i3p04.png", None);
2124        trial("tests/pngsuite/s37n3p04.png", None);
2125        trial("tests/pngsuite/s38i3p04.png", None);
2126        trial("tests/pngsuite/s38n3p04.png", None);
2127        trial("tests/pngsuite/s39i3p04.png", None);
2128        trial("tests/pngsuite/s39n3p04.png", None);
2129        trial("tests/pngsuite/s40i3p04.png", None);
2130        trial("tests/pngsuite/s40n3p04.png", None);
2131        trial("tests/pngsuite/tbbn0g04.png", None);
2132        trial("tests/pngsuite/tbbn2c16.png", None);
2133        trial("tests/pngsuite/tbbn3p08.png", None);
2134        trial("tests/pngsuite/tbgn2c16.png", None);
2135        trial("tests/pngsuite/tbgn3p08.png", None);
2136        trial("tests/pngsuite/tbrn2c08.png", None);
2137        trial("tests/pngsuite/tbwn0g16.png", None);
2138        trial("tests/pngsuite/tbwn3p08.png", None);
2139        trial("tests/pngsuite/tbyn3p08.png", None);
2140        trial("tests/pngsuite/tm3n3p02.png", None);
2141        trial("tests/pngsuite/tp0n0g08.png", None);
2142        trial("tests/pngsuite/tp0n2c08.png", None);
2143        trial("tests/pngsuite/tp0n3p08.png", None);
2144        trial("tests/pngsuite/tp1n3p08.png", None);
2145        trial("tests/pngsuite/z00n2c08.png", None);
2146        trial("tests/pngsuite/z03n2c08.png", None);
2147        trial("tests/pngsuite/z06n2c08.png", None);
2148        Ok(())
2149    }
2150
2151    #[test]
2152    fn image_source_sbit() {
2153        fn trial(path: &str, expected: Option<Cow<[u8]>>) {
2154            let decoder = crate::Decoder::new(BufReader::new(File::open(path).unwrap()));
2155            let reader = decoder.read_info().unwrap();
2156            let actual: Option<Cow<[u8]>> = reader.info().sbit.clone();
2157            assert!(actual == expected);
2158        }
2159
2160        trial("tests/sbit/g.png", Some(Cow::Owned(vec![5u8])));
2161        trial("tests/sbit/ga.png", Some(Cow::Owned(vec![5u8, 3u8])));
2162        trial(
2163            "tests/sbit/indexed.png",
2164            Some(Cow::Owned(vec![5u8, 6u8, 5u8])),
2165        );
2166        trial("tests/sbit/rgb.png", Some(Cow::Owned(vec![5u8, 6u8, 5u8])));
2167        trial(
2168            "tests/sbit/rgba.png",
2169            Some(Cow::Owned(vec![5u8, 6u8, 5u8, 8u8])),
2170        );
2171    }
2172
2173    /// Test handling of a PNG file that contains *two* iCCP chunks.
2174    /// This is a regression test for https://github.com/image-rs/image/issues/1825.
2175    #[test]
2176    fn test_two_iccp_chunks() {
2177        // The test file has been taken from
2178        // https://github.com/image-rs/image/issues/1825#issuecomment-1321798639,
2179        // but the 2nd iCCP chunk has been altered manually (see the 2nd comment below for more
2180        // details).
2181        let decoder = crate::Decoder::new(BufReader::new(
2182            File::open("tests/bugfixes/issue#1825.png").unwrap(),
2183        ));
2184        let reader = decoder.read_info().unwrap();
2185        let icc_profile = reader.info().icc_profile.clone().unwrap().into_owned();
2186
2187        // Assert that the contents of the *first* iCCP chunk are returned.
2188        //
2189        // Note that the 2nd chunk in the test file has been manually altered to have a different
2190        // content (`b"test iccp contents"`) which would have a different CRC (797351983).
2191        assert_eq!(4070462061, crc32fast::hash(&icc_profile));
2192    }
2193
2194    #[test]
2195    fn test_iccp_roundtrip() {
2196        let dummy_icc = b"I'm a profile";
2197
2198        let mut info = crate::Info::with_size(1, 1);
2199        info.icc_profile = Some(dummy_icc.into());
2200        let mut encoded_image = Vec::new();
2201        let enc = crate::Encoder::with_info(&mut encoded_image, info).unwrap();
2202        let mut enc = enc.write_header().unwrap();
2203        enc.write_image_data(&[0]).unwrap();
2204        enc.finish().unwrap();
2205
2206        let dec = crate::Decoder::new(Cursor::new(&encoded_image));
2207        let dec = dec.read_info().unwrap();
2208        assert_eq!(dummy_icc, &**dec.info().icc_profile.as_ref().unwrap());
2209    }
2210
2211    #[test]
2212    fn test_phys_roundtrip() {
2213        let mut info = crate::Info::with_size(1, 1);
2214        info.pixel_dims = Some(crate::PixelDimensions {
2215            xppu: 12,
2216            yppu: 34,
2217            unit: Unit::Meter,
2218        });
2219        let mut encoded_image = Vec::new();
2220        let enc = crate::Encoder::with_info(&mut encoded_image, info).unwrap();
2221        let mut enc = enc.write_header().unwrap();
2222        enc.write_image_data(&[0]).unwrap();
2223        enc.finish().unwrap();
2224
2225        let dec = crate::Decoder::new(Cursor::new(&encoded_image));
2226        let dec = dec.read_info().unwrap();
2227        let phys = dec.info().pixel_dims.as_ref().unwrap();
2228        assert_eq!(phys.xppu, 12);
2229        assert_eq!(phys.yppu, 34);
2230        assert_eq!(phys.unit, Unit::Meter);
2231    }
2232
2233    #[test]
2234    fn test_srgb_roundtrip() {
2235        let mut info = crate::Info::with_size(1, 1);
2236        info.srgb = Some(SrgbRenderingIntent::Saturation);
2237        let mut encoded_image = Vec::new();
2238        let enc = crate::Encoder::with_info(&mut encoded_image, info).unwrap();
2239        let mut enc = enc.write_header().unwrap();
2240        enc.write_image_data(&[0]).unwrap();
2241        enc.finish().unwrap();
2242
2243        let dec = crate::Decoder::new(Cursor::new(&encoded_image));
2244        let dec = dec.read_info().unwrap();
2245        assert_eq!(dec.info().srgb.unwrap(), SrgbRenderingIntent::Saturation);
2246    }
2247
2248    #[test]
2249    fn test_png_with_broken_iccp() {
2250        let decoder = crate::Decoder::new(BufReader::new(
2251            File::open("tests/iccp/broken_iccp.png").unwrap(),
2252        ));
2253        assert!(decoder.read_info().is_ok());
2254        let mut decoder = crate::Decoder::new(BufReader::new(
2255            File::open("tests/iccp/broken_iccp.png").unwrap(),
2256        ));
2257        decoder.set_ignore_iccp_chunk(true);
2258        assert!(decoder.read_info().is_ok());
2259    }
2260
2261    /// Test handling of `cICP`, `mDCV`, and `cLLI` chunks.
2262    #[test]
2263    fn test_cicp_mdcv_and_clli_chunks() {
2264        let mut decoder = crate::Decoder::new(BufReader::new(
2265            File::open("tests/bugfixes/cicp_pq.png").unwrap(),
2266        ));
2267        decoder.ignore_checksums(true);
2268        let reader = decoder.read_info().unwrap();
2269        let info = reader.info();
2270
2271        let cicp = info.coding_independent_code_points.unwrap();
2272        assert_eq!(cicp.color_primaries, 9);
2273        assert_eq!(cicp.transfer_function, 16);
2274        assert_eq!(cicp.matrix_coefficients, 0);
2275        assert!(cicp.is_video_full_range_image);
2276
2277        let mdcv = info.mastering_display_color_volume.unwrap();
2278        assert_relative_eq!(mdcv.chromaticities.red.0.into_value(), 0.680);
2279        assert_relative_eq!(mdcv.chromaticities.red.1.into_value(), 0.320);
2280        assert_relative_eq!(mdcv.chromaticities.green.0.into_value(), 0.265);
2281        assert_relative_eq!(mdcv.chromaticities.green.1.into_value(), 0.690);
2282        assert_relative_eq!(mdcv.chromaticities.blue.0.into_value(), 0.150);
2283        assert_relative_eq!(mdcv.chromaticities.blue.1.into_value(), 0.060);
2284        assert_relative_eq!(mdcv.chromaticities.white.0.into_value(), 0.3127);
2285        assert_relative_eq!(mdcv.chromaticities.white.1.into_value(), 0.3290);
2286        assert_relative_eq!(mdcv.min_luminance as f32 / 10_000.0, 0.01);
2287        assert_relative_eq!(mdcv.max_luminance as f32 / 10_000.0, 5000.0);
2288
2289        let clli = info.content_light_level.unwrap();
2290        assert_relative_eq!(clli.max_content_light_level as f32 / 10_000.0, 4000.0);
2291        assert_relative_eq!(clli.max_frame_average_light_level as f32 / 10_000.0, 2627.0);
2292    }
2293
2294    /// Test handling of `eXIf` chunk.
2295    #[test]
2296    fn test_exif_chunk() {
2297        let decoder = crate::Decoder::new(BufReader::new(
2298            File::open("tests/bugfixes/F-exif-chunk-early.png").unwrap(),
2299        ));
2300        let reader = decoder.read_info().unwrap();
2301        let info = reader.info();
2302        let exif = info.exif_metadata.as_ref().unwrap().as_ref();
2303        assert_eq!(exif.len(), 90);
2304    }
2305
2306    /// Tests what happens then [`Reader.finish`] is called twice.
2307    #[test]
2308    fn test_finishing_twice() {
2309        let mut png = Vec::new();
2310        write_noncompressed_png(&mut png, 16, 1024);
2311        let decoder = Decoder::new(Cursor::new(&png));
2312        let mut reader = decoder.read_info().unwrap();
2313
2314        // First call to `finish` - expecting success.
2315        reader.finish().unwrap();
2316
2317        // Second call to `finish` - expecting an error.
2318        let err = reader.finish().unwrap_err();
2319        assert!(matches!(&err, DecodingError::Parameter(_)));
2320        assert_eq!("End of image has been reached", format!("{err}"));
2321    }
2322
2323    /// Writes an acTL chunk.
2324    /// See https://wiki.mozilla.org/APNG_Specification#.60acTL.60:_The_Animation_Control_Chunk
2325    fn write_actl(w: &mut impl Write, animation: &crate::AnimationControl) {
2326        let mut data = Vec::new();
2327        data.write_u32::<byteorder::BigEndian>(animation.num_frames)
2328            .unwrap();
2329        data.write_u32::<byteorder::BigEndian>(animation.num_plays)
2330            .unwrap();
2331        write_chunk(w, b"acTL", &data);
2332    }
2333
2334    /// Writes an fcTL chunk.
2335    /// See https://wiki.mozilla.org/APNG_Specification#.60fcTL.60:_The_Frame_Control_Chunk
2336    fn write_fctl(w: &mut impl Write, frame: &crate::FrameControl) {
2337        let mut data = Vec::new();
2338        data.write_u32::<byteorder::BigEndian>(frame.sequence_number)
2339            .unwrap();
2340        data.write_u32::<byteorder::BigEndian>(frame.width).unwrap();
2341        data.write_u32::<byteorder::BigEndian>(frame.height)
2342            .unwrap();
2343        data.write_u32::<byteorder::BigEndian>(frame.x_offset)
2344            .unwrap();
2345        data.write_u32::<byteorder::BigEndian>(frame.y_offset)
2346            .unwrap();
2347        data.write_u16::<byteorder::BigEndian>(frame.delay_num)
2348            .unwrap();
2349        data.write_u16::<byteorder::BigEndian>(frame.delay_den)
2350            .unwrap();
2351        data.write_u8(frame.dispose_op as u8).unwrap();
2352        data.write_u8(frame.blend_op as u8).unwrap();
2353        write_chunk(w, b"fcTL", &data);
2354    }
2355
2356    /// Writes an fdAT chunk.
2357    /// See https://wiki.mozilla.org/APNG_Specification#.60fdAT.60:_The_Frame_Data_Chunk
2358    fn write_fdat(w: &mut impl Write, sequence_number: u32, image_data: &[u8]) {
2359        let mut data = Vec::new();
2360        data.write_u32::<byteorder::BigEndian>(sequence_number)
2361            .unwrap();
2362        data.write_all(image_data).unwrap();
2363        write_chunk(w, b"fdAT", &data);
2364    }
2365
2366    /// Writes PNG signature and chunks that can precede an fdAT chunk that is expected
2367    /// to have
2368    /// - `sequence_number` set to 0
2369    /// - image data with rgba8 pixels in a `width` by `width` image
2370    fn write_fdat_prefix(w: &mut impl Write, num_frames: u32, width: u32) {
2371        write_png_sig(w);
2372        write_rgba8_ihdr_with_width(w, width);
2373        write_actl(
2374            w,
2375            &crate::AnimationControl {
2376                num_frames,
2377                num_plays: 0,
2378            },
2379        );
2380
2381        let mut fctl = crate::FrameControl {
2382            width,
2383            height: width,
2384            ..Default::default()
2385        };
2386        write_fctl(w, &fctl);
2387        write_rgba8_idats(w, width, 0x7fffffff);
2388
2389        fctl.sequence_number += 1;
2390        write_fctl(w, &fctl);
2391    }
2392
2393    #[test]
2394    fn test_fdat_chunk_payload_length_0() {
2395        let mut png = Vec::new();
2396        write_fdat_prefix(&mut png, 2, 8);
2397        write_chunk(&mut png, b"fdAT", &[]);
2398
2399        let decoder = Decoder::new(Cursor::new(&png));
2400        let mut reader = decoder.read_info().unwrap();
2401        let mut buf = vec![0; reader.output_buffer_size().unwrap()];
2402        reader.next_frame(&mut buf).unwrap();
2403
2404        // 0-length fdAT should result in an error.
2405        let err = reader.next_frame(&mut buf).unwrap_err();
2406        assert!(matches!(&err, DecodingError::Format(_)));
2407        assert_eq!("fdAT chunk shorter than 4 bytes", format!("{err}"));
2408
2409        // Calling `next_frame` again should return an error.  Same error as above would be nice,
2410        // but it is probably unnecessary and infeasible (`DecodingError` can't derive `Clone`
2411        // because `std::io::Error` doesn't implement `Clone`)..  But it definitely shouldn't enter
2412        // an infinite loop.
2413        let err2 = reader.next_frame(&mut buf).unwrap_err();
2414        assert!(matches!(&err2, DecodingError::Parameter(_)));
2415        assert_eq!(
2416            "A fatal decoding error has been encounted earlier",
2417            format!("{err2}")
2418        );
2419    }
2420
2421    #[test]
2422    fn test_fdat_chunk_payload_length_3() {
2423        let mut png = Vec::new();
2424        write_fdat_prefix(&mut png, 2, 8);
2425        write_chunk(&mut png, b"fdAT", &[1, 0, 0]);
2426
2427        let decoder = Decoder::new(Cursor::new(&png));
2428        let mut reader = decoder.read_info().unwrap();
2429        let mut buf = vec![0; reader.output_buffer_size().unwrap()];
2430        reader.next_frame(&mut buf).unwrap();
2431
2432        // 3-bytes-long fdAT should result in an error.
2433        let err = reader.next_frame(&mut buf).unwrap_err();
2434        assert!(matches!(&err, DecodingError::Format(_)));
2435        assert_eq!("fdAT chunk shorter than 4 bytes", format!("{err}"));
2436    }
2437
2438    #[test]
2439    fn test_frame_split_across_two_fdat_chunks() {
2440        // Generate test data where the 2nd animation frame is split across 2 fdAT chunks.
2441        //
2442        // This is similar to the example given in
2443        // https://wiki.mozilla.org/APNG_Specification#Chunk_Sequence_Numbers:
2444        //
2445        // ```
2446        //    Sequence number    Chunk
2447        //    (none)             `acTL`
2448        //    0                  `fcTL` first frame
2449        //    (none)             `IDAT` first frame / default image
2450        //    1                  `fcTL` second frame
2451        //    2                  first `fdAT` for second frame
2452        //    3                  second `fdAT` for second frame
2453        // ```
2454        let png = {
2455            let mut png = Vec::new();
2456            write_fdat_prefix(&mut png, 2, 8);
2457            let image_data = generate_rgba8_with_width_and_height(8, 8);
2458            write_fdat(&mut png, 2, &image_data[..30]);
2459            write_fdat(&mut png, 3, &image_data[30..]);
2460            write_iend(&mut png);
2461            png
2462        };
2463
2464        // Start decoding.
2465        let decoder = Decoder::new(Cursor::new(&png));
2466        let mut reader = decoder.read_info().unwrap();
2467        let mut buf = vec![0; reader.output_buffer_size().unwrap()];
2468        let Some(animation_control) = reader.info().animation_control else {
2469            panic!("No acTL");
2470        };
2471        assert_eq!(animation_control.num_frames, 2);
2472
2473        // Process the 1st animation frame.
2474        let first_frame: Vec<u8>;
2475        {
2476            reader.next_frame(&mut buf).unwrap();
2477            first_frame = buf.clone();
2478
2479            // Note that the doc comment of `Reader::next_frame` says that "[...]
2480            // can be checked afterwards by calling `info` **after** a successful call and
2481            // inspecting the `frame_control` data.".  (Note the **emphasis** on "after".)
2482            let Some(frame_control) = reader.info().frame_control else {
2483                panic!("No fcTL (1st frame)");
2484            };
2485            // The sequence number is taken from the `fcTL` chunk that comes before the `IDAT`
2486            // chunk.
2487            assert_eq!(frame_control.sequence_number, 0);
2488        }
2489
2490        // Process the 2nd animation frame.
2491        let second_frame: Vec<u8>;
2492        {
2493            reader.next_frame(&mut buf).unwrap();
2494            second_frame = buf.clone();
2495
2496            // Same as above - updated `frame_control` is available *after* the `next_frame` call.
2497            let Some(frame_control) = reader.info().frame_control else {
2498                panic!("No fcTL (2nd frame)");
2499            };
2500            // The sequence number is taken from the `fcTL` chunk that comes before the two `fdAT`
2501            // chunks.  Note that sequence numbers inside `fdAT` chunks are not publicly exposed
2502            // (but they are still checked when decoding to verify that they are sequential).
2503            assert_eq!(frame_control.sequence_number, 1);
2504        }
2505
2506        assert_eq!(first_frame, second_frame);
2507    }
2508
2509    #[test]
2510    fn test_idat_bigger_than_image_size_from_ihdr() {
2511        let png = {
2512            let mut png = Vec::new();
2513            write_png_sig(&mut png);
2514            write_rgba8_ihdr_with_width(&mut png, 8);
2515
2516            // Here we want to test an invalid image where the `IDAT` chunk contains more data
2517            // (data for 8x256 image) than declared in the `IHDR` chunk (which only describes an
2518            // 8x8 image).
2519            write_chunk(
2520                &mut png,
2521                b"IDAT",
2522                &generate_rgba8_with_width_and_height(8, 256),
2523            );
2524
2525            write_iend(&mut png);
2526            png
2527        };
2528        let decoder = Decoder::new(Cursor::new(&png));
2529        let mut reader = decoder.read_info().unwrap();
2530        let mut buf = vec![0; reader.output_buffer_size().unwrap()];
2531
2532        // TODO: Should this return an error instead?  For now let's just have test assertions for
2533        // the current behavior.
2534        reader.next_frame(&mut buf).unwrap();
2535        assert_eq!(3093270825, crc32fast::hash(&buf));
2536    }
2537
2538    #[test]
2539    fn test_only_idat_chunk_in_input_stream() {
2540        let png = {
2541            let mut png = Vec::new();
2542            write_png_sig(&mut png);
2543            write_chunk(&mut png, b"IDAT", &[]);
2544            png
2545        };
2546        let decoder = Decoder::new(Cursor::new(&png));
2547        let Err(err) = decoder.read_info() else {
2548            panic!("Expected an error")
2549        };
2550        assert!(matches!(&err, DecodingError::Format(_)));
2551        assert_eq!(
2552            "ChunkType { type: IDAT, \
2553                         critical: true, \
2554                         private: false, \
2555                         reserved: false, \
2556                         safecopy: false \
2557             } chunk appeared before IHDR chunk",
2558            format!("{err}"),
2559        );
2560    }
2561
2562    /// `StreamingInput` can be used by tests to simulate a streaming input
2563    /// (e.g. a slow http response, where all bytes are not immediately available).
2564    #[derive(Clone)]
2565    struct StreamingInput {
2566        full_input: Vec<u8>,
2567        state: Rc<RefCell<StreamingInputState>>,
2568    }
2569
2570    struct StreamingInputState {
2571        current_pos: usize,
2572        available_len: usize,
2573    }
2574
2575    impl StreamingInput {
2576        fn new(full_input: Vec<u8>) -> Self {
2577            Self {
2578                full_input,
2579                state: Rc::new(RefCell::new(StreamingInputState {
2580                    current_pos: 0,
2581                    available_len: 0,
2582                })),
2583            }
2584        }
2585
2586        fn with_noncompressed_png(width: u32, idat_size: usize) -> Self {
2587            let mut png = Vec::new();
2588            write_noncompressed_png(&mut png, width, idat_size);
2589            Self::new(png)
2590        }
2591
2592        fn expose_next_byte(&self) {
2593            let mut state = self.state.borrow_mut();
2594            assert!(state.available_len < self.full_input.len());
2595            state.available_len += 1;
2596        }
2597
2598        fn stream_input_until_reader_is_available(&self) -> Reader<StreamingInput> {
2599            loop {
2600                self.state.borrow_mut().current_pos = 0;
2601                match Decoder::new(self.clone()).read_info() {
2602                    Ok(reader) => {
2603                        break reader;
2604                    }
2605                    Err(DecodingError::IoError(e)) if e.kind() == ErrorKind::UnexpectedEof => {
2606                        self.expose_next_byte();
2607                    }
2608                    _ => panic!("Unexpected error"),
2609                }
2610            }
2611        }
2612
2613        fn decode_full_input<F, R>(&self, f: F) -> R
2614        where
2615            F: FnOnce(Reader<Cursor<&[u8]>>) -> R,
2616        {
2617            let decoder = Decoder::new(Cursor::new(&*self.full_input));
2618            f(decoder.read_info().unwrap())
2619        }
2620    }
2621
2622    impl Read for StreamingInput {
2623        fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
2624            let mut state = self.state.borrow_mut();
2625            let mut available_bytes = &self.full_input[state.current_pos..state.available_len];
2626            let number_of_read_bytes = available_bytes.read(buf)?;
2627            state.current_pos += number_of_read_bytes;
2628            assert!(state.current_pos <= state.available_len);
2629            Ok(number_of_read_bytes)
2630        }
2631    }
2632    impl BufRead for StreamingInput {
2633        fn fill_buf(&mut self) -> std::io::Result<&[u8]> {
2634            let state = self.state.borrow();
2635            Ok(&self.full_input[state.current_pos..state.available_len])
2636        }
2637
2638        fn consume(&mut self, amt: usize) {
2639            let mut state = self.state.borrow_mut();
2640            state.current_pos += amt;
2641            assert!(state.current_pos <= state.available_len);
2642        }
2643    }
2644    impl Seek for StreamingInput {
2645        fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> {
2646            let mut state = self.state.borrow_mut();
2647            state.current_pos = match pos {
2648                std::io::SeekFrom::Start(n) => n as usize,
2649                std::io::SeekFrom::End(n) => (self.full_input.len() as i64 + n) as usize,
2650                std::io::SeekFrom::Current(n) => (state.current_pos as i64 + n) as usize,
2651            } as usize;
2652            Ok(state.current_pos as u64)
2653        }
2654        fn stream_position(&mut self) -> std::io::Result<u64> {
2655            Ok(self.state.borrow().current_pos as u64)
2656        }
2657    }
2658
2659    /// Test resuming/retrying `Reader.next_frame` after `UnexpectedEof`.
2660    #[test]
2661    fn test_streaming_input_and_decoding_via_next_frame() {
2662        const WIDTH: u32 = 16;
2663        const IDAT_SIZE: usize = 512;
2664        let streaming_input = StreamingInput::with_noncompressed_png(WIDTH, IDAT_SIZE);
2665
2666        let (whole_output_info, decoded_from_whole_input) =
2667            streaming_input.decode_full_input(|mut r| {
2668                let mut buf = vec![0; r.output_buffer_size().unwrap()];
2669                let output_info = r.next_frame(&mut buf).unwrap();
2670                (output_info, buf)
2671            });
2672
2673        let mut png_reader = streaming_input.stream_input_until_reader_is_available();
2674        let mut decoded_from_streaming_input = vec![0; png_reader.output_buffer_size().unwrap()];
2675        let streaming_output_info = loop {
2676            match png_reader.next_frame(decoded_from_streaming_input.as_mut_slice()) {
2677                Ok(output_info) => break output_info,
2678                Err(DecodingError::IoError(e)) if e.kind() == ErrorKind::UnexpectedEof => {
2679                    streaming_input.expose_next_byte()
2680                }
2681                e => panic!("Unexpected error: {:?}", e),
2682            }
2683        };
2684        assert_eq!(whole_output_info, streaming_output_info);
2685        assert_eq!(
2686            crc32fast::hash(&decoded_from_whole_input),
2687            crc32fast::hash(&decoded_from_streaming_input)
2688        );
2689    }
2690
2691    /// Test resuming/retrying `Reader.next_row` after `UnexpectedEof`.
2692    #[test]
2693    fn test_streaming_input_and_decoding_via_next_row() {
2694        const WIDTH: u32 = 16;
2695        const IDAT_SIZE: usize = 512;
2696        let streaming_input = StreamingInput::with_noncompressed_png(WIDTH, IDAT_SIZE);
2697
2698        let decoded_from_whole_input = streaming_input.decode_full_input(|mut r| {
2699            let mut buf = vec![0; r.output_buffer_size().unwrap()];
2700            r.next_frame(&mut buf).unwrap();
2701            buf
2702        });
2703
2704        let mut png_reader = streaming_input.stream_input_until_reader_is_available();
2705        let mut decoded_from_streaming_input = Vec::new();
2706        loop {
2707            match png_reader.next_row() {
2708                Ok(None) => break,
2709                Ok(Some(row)) => decoded_from_streaming_input.extend_from_slice(row.data()),
2710                Err(DecodingError::IoError(e)) if e.kind() == ErrorKind::UnexpectedEof => {
2711                    streaming_input.expose_next_byte()
2712                }
2713                e => panic!("Unexpected error: {:?}", e),
2714            }
2715        }
2716        assert_eq!(
2717            crc32fast::hash(&decoded_from_whole_input),
2718            crc32fast::hash(&decoded_from_streaming_input)
2719        );
2720    }
2721
2722    /// Test resuming/retrying `Decoder.read_header_info` after `UnexpectedEof`.
2723    #[test]
2724    fn test_streaming_input_and_reading_header_info() {
2725        const WIDTH: u32 = 16;
2726        const IDAT_SIZE: usize = 512;
2727        let streaming_input = StreamingInput::with_noncompressed_png(WIDTH, IDAT_SIZE);
2728
2729        let info_from_whole_input = streaming_input.decode_full_input(|r| r.info().clone());
2730
2731        let mut decoder = Decoder::new(streaming_input.clone());
2732        let info_from_streaming_input = loop {
2733            match decoder.read_header_info() {
2734                Ok(info) => break info.clone(),
2735                Err(DecodingError::IoError(e)) if e.kind() == ErrorKind::UnexpectedEof => {
2736                    streaming_input.expose_next_byte()
2737                }
2738                e => panic!("Unexpected error: {:?}", e),
2739            }
2740        };
2741
2742        assert_eq!(info_from_whole_input.width, info_from_streaming_input.width);
2743        assert_eq!(
2744            info_from_whole_input.height,
2745            info_from_streaming_input.height
2746        );
2747        assert_eq!(
2748            info_from_whole_input.bit_depth,
2749            info_from_streaming_input.bit_depth
2750        );
2751        assert_eq!(
2752            info_from_whole_input.color_type,
2753            info_from_streaming_input.color_type
2754        );
2755        assert_eq!(
2756            info_from_whole_input.interlaced,
2757            info_from_streaming_input.interlaced
2758        );
2759    }
2760
2761    /// Creates a ready-to-test [`Reader`] which decodes a PNG that contains:
2762    /// IHDR, IDAT, IEND.
2763    fn create_reader_of_ihdr_idat() -> Reader<Cursor<Vec<u8>>> {
2764        let mut png = Vec::new();
2765        write_noncompressed_png(&mut png, /* width = */ 16, /* idat_size = */ 1024);
2766        Decoder::new(Cursor::new(png)).read_info().unwrap()
2767    }
2768
2769    /// Creates a ready-to-test [`Reader`] which decodes an animated PNG that contains:
2770    /// IHDR, acTL, fcTL, IDAT, fcTL, fdAT, IEND.  (i.e. IDAT is part of the animation)
2771    fn create_reader_of_ihdr_actl_fctl_idat_fctl_fdat() -> Reader<Cursor<Vec<u8>>> {
2772        let width = 16;
2773        let mut fctl = crate::FrameControl {
2774            width,
2775            height: width,
2776            ..Default::default()
2777        };
2778
2779        let mut png = Vec::new();
2780        write_png_sig(&mut png);
2781        write_rgba8_ihdr_with_width(&mut png, width);
2782        write_actl(
2783            &mut png,
2784            &crate::AnimationControl {
2785                num_frames: 2,
2786                num_plays: 0,
2787            },
2788        );
2789        fctl.sequence_number = 0;
2790        write_fctl(&mut png, &fctl);
2791        // Using `fctl.height + 1` means that the `IDAT` will have "left-over" data after
2792        // processing.  This helps to verify that `Reader.read_until_image_data` discards the
2793        // left-over data when resetting `UnfilteredRowsBuffer`.
2794        let idat_data = generate_rgba8_with_width_and_height(fctl.width, fctl.height + 1);
2795        write_chunk(&mut png, b"IDAT", &idat_data);
2796
2797        let fdat_width = 10;
2798        fctl.sequence_number = 1;
2799        // Using different width in `IDAT` and `fDAT` frames helps to catch problems that
2800        // may arise when `Reader.read_until_image_data` doesn't properly reset
2801        // `UnfilteredRowsBuffer`.
2802        fctl.width = fdat_width;
2803        write_fctl(&mut png, &fctl);
2804        let fdat_data = generate_rgba8_with_width_and_height(fctl.width, fctl.height);
2805        write_fdat(&mut png, 2, &fdat_data);
2806        write_iend(&mut png);
2807
2808        Decoder::new(Cursor::new(png)).read_info().unwrap()
2809    }
2810
2811    /// Creates a ready-to-test [`Reader`] which decodes an animated PNG that contains: IHDR, acTL,
2812    /// IDAT, fcTL, fdAT, fcTL, fdAT, IEND.  (i.e. IDAT is *not* part of the animation)
2813    fn create_reader_of_ihdr_actl_idat_fctl_fdat_fctl_fdat() -> Reader<Cursor<Vec<u8>>> {
2814        let width = 16;
2815        let frame_data = generate_rgba8_with_width_and_height(width, width);
2816        let mut fctl = crate::FrameControl {
2817            width,
2818            height: width,
2819            ..Default::default()
2820        };
2821
2822        let mut png = Vec::new();
2823        write_png_sig(&mut png);
2824        write_rgba8_ihdr_with_width(&mut png, width);
2825        write_actl(
2826            &mut png,
2827            &crate::AnimationControl {
2828                num_frames: 2,
2829                num_plays: 0,
2830            },
2831        );
2832        write_chunk(&mut png, b"IDAT", &frame_data);
2833        fctl.sequence_number = 0;
2834        write_fctl(&mut png, &fctl);
2835        write_fdat(&mut png, 1, &frame_data);
2836        fctl.sequence_number = 2;
2837        write_fctl(&mut png, &fctl);
2838        write_fdat(&mut png, 3, &frame_data);
2839        write_iend(&mut png);
2840
2841        Decoder::new(Cursor::new(png)).read_info().unwrap()
2842    }
2843
2844    fn get_fctl_sequence_number(reader: &Reader<impl BufRead + Seek>) -> u32 {
2845        reader
2846            .info()
2847            .frame_control
2848            .as_ref()
2849            .unwrap()
2850            .sequence_number
2851    }
2852
2853    /// Tests that [`Reader.next_frame`] will report a `PolledAfterEndOfImage` error when called
2854    /// after already decoding a single frame in a non-animated PNG.
2855    #[test]
2856    fn test_next_frame_polling_after_end_non_animated() {
2857        let mut reader = create_reader_of_ihdr_idat();
2858        let mut buf = vec![0; reader.output_buffer_size().unwrap()];
2859        reader
2860            .next_frame(&mut buf)
2861            .expect("Expecting no error for IDAT frame");
2862
2863        let err = reader
2864            .next_frame(&mut buf)
2865            .expect_err("Main test - expecting error");
2866        assert!(
2867            matches!(&err, DecodingError::Parameter(_)),
2868            "Unexpected kind of error: {:?}",
2869            &err,
2870        );
2871    }
2872
2873    /// Tests that [`Reader.next_frame_info`] will report a `PolledAfterEndOfImage` error when
2874    /// called when decoding a PNG that only contains a single frame.
2875    #[test]
2876    fn test_next_frame_info_polling_after_end_non_animated() {
2877        let mut reader = create_reader_of_ihdr_idat();
2878
2879        let err = reader
2880            .next_frame_info()
2881            .expect_err("Main test - expecting error");
2882        assert!(
2883            matches!(&err, DecodingError::Parameter(_)),
2884            "Unexpected kind of error: {:?}",
2885            &err,
2886        );
2887    }
2888
2889    /// Tests that [`Reader.next_frame`] will report a `PolledAfterEndOfImage` error when called
2890    /// after already decoding a single frame in an animated PNG where IDAT is part of the
2891    /// animation.
2892    #[test]
2893    fn test_next_frame_polling_after_end_idat_part_of_animation() {
2894        let mut reader = create_reader_of_ihdr_actl_fctl_idat_fctl_fdat();
2895        let mut buf = vec![0; reader.output_buffer_size().unwrap()];
2896
2897        assert_eq!(get_fctl_sequence_number(&reader), 0);
2898        reader
2899            .next_frame(&mut buf)
2900            .expect("Expecting no error for IDAT frame");
2901
2902        // `next_frame` doesn't advance to the next `fcTL`.
2903        assert_eq!(get_fctl_sequence_number(&reader), 0);
2904
2905        reader
2906            .next_frame(&mut buf)
2907            .expect("Expecting no error for fdAT frame");
2908        assert_eq!(get_fctl_sequence_number(&reader), 1);
2909
2910        let err = reader
2911            .next_frame(&mut buf)
2912            .expect_err("Main test - expecting error");
2913        assert!(
2914            matches!(&err, DecodingError::Parameter(_)),
2915            "Unexpected kind of error: {:?}",
2916            &err,
2917        );
2918    }
2919
2920    /// Tests that [`Reader.next_frame`] will report a `PolledAfterEndOfImage` error when called
2921    /// after already decoding a single frame in an animated PNG where IDAT is *not* part of the
2922    /// animation.
2923    #[test]
2924    fn test_next_frame_polling_after_end_idat_not_part_of_animation() {
2925        let mut reader = create_reader_of_ihdr_actl_idat_fctl_fdat_fctl_fdat();
2926        let mut buf = vec![0; reader.output_buffer_size().unwrap()];
2927
2928        assert!(reader.info().frame_control.is_none());
2929        reader
2930            .next_frame(&mut buf)
2931            .expect("Expecting no error for IDAT frame");
2932
2933        // `next_frame` doesn't advance to the next `fcTL`.
2934        assert!(reader.info().frame_control.is_none());
2935
2936        reader
2937            .next_frame(&mut buf)
2938            .expect("Expecting no error for 1st fdAT frame");
2939        assert_eq!(get_fctl_sequence_number(&reader), 0);
2940
2941        reader
2942            .next_frame(&mut buf)
2943            .expect("Expecting no error for 2nd fdAT frame");
2944        assert_eq!(get_fctl_sequence_number(&reader), 2);
2945
2946        let err = reader
2947            .next_frame(&mut buf)
2948            .expect_err("Main test - expecting error");
2949        assert!(
2950            matches!(&err, DecodingError::Parameter(_)),
2951            "Unexpected kind of error: {:?}",
2952            &err,
2953        );
2954    }
2955
2956    /// Tests that after decoding a whole frame via [`Reader.next_row`] the call to
2957    /// [`Reader.next_frame`] will decode the **next** frame.
2958    #[test]
2959    fn test_row_by_row_then_next_frame() {
2960        let mut reader = create_reader_of_ihdr_actl_fctl_idat_fctl_fdat();
2961        let mut buf = vec![0; reader.output_buffer_size().unwrap()];
2962
2963        assert_eq!(get_fctl_sequence_number(&reader), 0);
2964        while let Some(_) = reader.next_row().unwrap() {}
2965        assert_eq!(get_fctl_sequence_number(&reader), 0);
2966
2967        buf.fill(0x0f);
2968        reader
2969            .next_frame(&mut buf)
2970            .expect("Expecting no error from next_frame call");
2971
2972        // Verify if we have read the next `fcTL` chunk + repopulated `buf`:
2973        assert_eq!(get_fctl_sequence_number(&reader), 1);
2974        assert!(buf.iter().any(|byte| *byte != 0x0f));
2975    }
2976
2977    /// Tests that after decoding a whole frame via [`Reader.next_row`] it is possible
2978    /// to use [`Reader.next_row`] to decode the next frame (by using the `next_frame_info` API to
2979    /// advance to the next frame when `next_row` returns `None`).
2980    #[test]
2981    fn test_row_by_row_of_two_frames() {
2982        let mut reader = create_reader_of_ihdr_actl_fctl_idat_fctl_fdat();
2983
2984        let mut rows_of_frame1 = 0;
2985        assert_eq!(get_fctl_sequence_number(&reader), 0);
2986        while let Some(_) = reader.next_row().unwrap() {
2987            rows_of_frame1 += 1;
2988        }
2989        assert_eq!(rows_of_frame1, 16);
2990        assert_eq!(get_fctl_sequence_number(&reader), 0);
2991
2992        let mut rows_of_frame2 = 0;
2993        assert_eq!(reader.next_frame_info().unwrap().sequence_number, 1);
2994        assert_eq!(get_fctl_sequence_number(&reader), 1);
2995        while let Some(_) = reader.next_row().unwrap() {
2996            rows_of_frame2 += 1;
2997        }
2998        assert_eq!(rows_of_frame2, 16);
2999        assert_eq!(get_fctl_sequence_number(&reader), 1);
3000
3001        let err = reader
3002            .next_frame_info()
3003            .expect_err("No more frames - expecting error");
3004        assert!(
3005            matches!(&err, DecodingError::Parameter(_)),
3006            "Unexpected kind of error: {:?}",
3007            &err,
3008        );
3009    }
3010
3011    /// This test is similar to `test_next_frame_polling_after_end_idat_part_of_animation`, but it
3012    /// uses `next_frame_info` calls to read to the next `fcTL` earlier - before the next call to
3013    /// `next_frame` (knowing `fcTL` before calling `next_frame` may be helpful to determine the
3014    /// size of the output buffer and/or to prepare the buffer based on the `DisposeOp` of the
3015    /// previous frames).
3016    #[test]
3017    fn test_next_frame_info_after_next_frame() {
3018        let mut reader = create_reader_of_ihdr_actl_fctl_idat_fctl_fdat();
3019        let mut buf = vec![0; reader.output_buffer_size().unwrap()];
3020
3021        assert_eq!(get_fctl_sequence_number(&reader), 0);
3022        reader
3023            .next_frame(&mut buf)
3024            .expect("Expecting no error for IDAT frame");
3025
3026        // `next_frame` doesn't advance to the next `fcTL`.
3027        assert_eq!(get_fctl_sequence_number(&reader), 0);
3028
3029        // But `next_frame_info` can be used to go to the next `fcTL`.
3030        assert_eq!(reader.next_frame_info().unwrap().sequence_number, 1);
3031        assert_eq!(get_fctl_sequence_number(&reader), 1);
3032
3033        reader
3034            .next_frame(&mut buf)
3035            .expect("Expecting no error for fdAT frame");
3036        assert_eq!(get_fctl_sequence_number(&reader), 1);
3037
3038        let err = reader
3039            .next_frame_info()
3040            .expect_err("Main test - expecting error");
3041        assert!(
3042            matches!(&err, DecodingError::Parameter(_)),
3043            "Unexpected kind of error: {:?}",
3044            &err,
3045        );
3046    }
3047
3048    /// This test is similar to `test_next_frame_polling_after_end_idat_not_part_of_animation`, but
3049    /// it uses `next_frame_info` to skip the `IDAT` frame entirely + to move between frames.
3050    #[test]
3051    fn test_next_frame_info_to_skip_first_frame() {
3052        let mut reader = create_reader_of_ihdr_actl_idat_fctl_fdat_fctl_fdat();
3053        let mut buf = vec![0; reader.output_buffer_size().unwrap()];
3054
3055        // First (IDAT) frame doesn't have frame control info, which means
3056        // that it is not part of the animation.
3057        assert!(reader.info().frame_control.is_none());
3058
3059        // `next_frame_info` can be used to skip the IDAT frame (without first having to separately
3060        // discard the image data - e.g. by also calling `next_frame` first).
3061        assert_eq!(reader.next_frame_info().unwrap().sequence_number, 0);
3062        assert_eq!(get_fctl_sequence_number(&reader), 0);
3063        reader
3064            .next_frame(&mut buf)
3065            .expect("Expecting no error for 1st fdAT frame");
3066        assert_eq!(get_fctl_sequence_number(&reader), 0);
3067
3068        // Get the `fcTL` for the 2nd frame.
3069        assert_eq!(reader.next_frame_info().unwrap().sequence_number, 2);
3070        reader
3071            .next_frame(&mut buf)
3072            .expect("Expecting no error for 2nd fdAT frame");
3073        assert_eq!(get_fctl_sequence_number(&reader), 2);
3074
3075        let err = reader
3076            .next_frame_info()
3077            .expect_err("Main test - expecting error");
3078        assert!(
3079            matches!(&err, DecodingError::Parameter(_)),
3080            "Unexpected kind of error: {:?}",
3081            &err,
3082        );
3083    }
3084
3085    #[test]
3086    fn test_incorrect_trns_chunk_is_ignored() {
3087        let png = {
3088            let mut png = Vec::new();
3089            write_png_sig(&mut png);
3090            write_rgba8_ihdr_with_width(&mut png, 8);
3091            write_chunk(&mut png, b"tRNS", &[12, 34, 56]);
3092            write_chunk(
3093                &mut png,
3094                b"IDAT",
3095                &generate_rgba8_with_width_and_height(8, 8),
3096            );
3097            write_iend(&mut png);
3098            png
3099        };
3100        let decoder = Decoder::new(Cursor::new(&png));
3101        let mut reader = decoder.read_info().unwrap();
3102        let mut buf = vec![0; reader.output_buffer_size().unwrap()];
3103        assert!(reader.info().trns.is_none());
3104        reader.next_frame(&mut buf).unwrap();
3105        assert_eq!(3093270825, crc32fast::hash(&buf));
3106        assert!(reader.info().trns.is_none());
3107    }
3108
3109    /// This is a regression test for https://crbug.com/422421347
3110    #[test]
3111    fn test_actl_num_frames_zero() {
3112        let width = 16;
3113        let frame_data = generate_rgba8_with_width_and_height(width, width);
3114
3115        let mut png = Vec::new();
3116        write_png_sig(&mut png);
3117        write_rgba8_ihdr_with_width(&mut png, width);
3118        write_actl(
3119            &mut png,
3120            &crate::AnimationControl {
3121                num_frames: 0, // <= spec violation needed by this test
3122                num_plays: 0,
3123            },
3124        );
3125        // Presence of an `fcTL` chunk will prevent incrementing
3126        // `num_frames` when calculating `remaining_frames` in
3127        // `Decoder::read_info`.  So the test writes an `fcTL` chunk
3128        // to end up with `remaining_frames == 0` if `parse_actl` allows
3129        // `num_frames == 0`.
3130        write_fctl(
3131            &mut png,
3132            &crate::FrameControl {
3133                width,
3134                height: width,
3135                ..Default::default()
3136            },
3137        );
3138        write_chunk(&mut png, b"IDAT", &frame_data);
3139        write_iend(&mut png);
3140
3141        let mut reader = Decoder::new(Cursor::new(png)).read_info().unwrap();
3142
3143        // Using `next_interlaced_row` in the test, because it doesn't check
3144        // `Reader::remaining_frames` (unlike `next_frame`), because it assumes that either
3145        // `read_info` or `next_frame` leave `Reader` in a valid state.
3146        //
3147        // The test passes if these `next_interlaced_row` calls don't hit any `assert!` failures.
3148        while let Some(_row) = reader.next_interlaced_row().unwrap() {}
3149    }
3150
3151    #[test]
3152    fn test_small_fctl() {
3153        const FCTL_SIZE: u32 = 30;
3154        const IHDR_SIZE: u32 = 50;
3155        let mut png = Vec::new();
3156        write_png_sig(&mut png);
3157        write_rgba8_ihdr_with_width(&mut png, IHDR_SIZE);
3158        write_actl(
3159            &mut png,
3160            &crate::AnimationControl {
3161                num_frames: 1,
3162                num_plays: 1,
3163            },
3164        );
3165        write_fctl(
3166            &mut png,
3167            &crate::FrameControl {
3168                width: FCTL_SIZE,
3169                height: FCTL_SIZE,
3170                x_offset: 10,
3171                y_offset: 10,
3172                sequence_number: 0,
3173                ..Default::default()
3174            },
3175        );
3176        write_chunk(
3177            &mut png,
3178            b"IDAT",
3179            &generate_rgba8_with_width_and_height(IHDR_SIZE, IHDR_SIZE),
3180        );
3181        write_iend(&mut png);
3182
3183        let reader = Decoder::new(Cursor::new(png)).read_info();
3184        let err = reader.err().unwrap();
3185        assert!(matches!(&err, DecodingError::Format(_)));
3186        assert_eq!("Sub frame is out-of-bounds.", format!("{err}"));
3187    }
3188
3189    #[test]
3190    fn test_invalid_text_chunk() {
3191        // The spec requires a NUL character (separating keyword from text) within the first 80
3192        // bytes of the chunk.  Here there is no NUL character in the first 100 bytes, so this
3193        // chunk is invalid and should trigger an error in `parse_text`.
3194        let invalid_text_chunk = vec![b'A'; 100];
3195
3196        const SIZE: u32 = 20;
3197        let mut png = Vec::new();
3198        write_png_sig(&mut png);
3199        write_rgba8_ihdr_with_width(&mut png, SIZE);
3200        write_chunk(&mut png, b"tEXt", invalid_text_chunk.as_slice());
3201        write_chunk(
3202            &mut png,
3203            b"IDAT",
3204            &generate_rgba8_with_width_and_height(SIZE, SIZE),
3205        );
3206        write_iend(&mut png);
3207
3208        let reader = Decoder::new(Cursor::new(png)).read_info().unwrap();
3209        let info = reader.info();
3210        assert_eq!(info.width, SIZE);
3211        assert_eq!(info.uncompressed_latin1_text.len(), 0);
3212    }
3213}