1extern crate crc32fast;
2
3use std::convert::{From, TryInto};
4use std::default::Default;
5use std::error;
6use std::fmt;
7use std::io;
8use std::{borrow::Cow, cmp::min};
9
10use crc32fast::Hasher as Crc32;
11
12use super::zlib::ZlibStream;
13use crate::chunk::{self, ChunkType, IDAT, IEND, IHDR};
14use crate::common::{
15 AnimationControl, BitDepth, BlendOp, ColorType, DisposeOp, FrameControl, Info, ParameterError,
16 PixelDimensions, ScaledFloat, SourceChromaticities, Unit,
17};
18use crate::text_metadata::{ITXtChunk, TEXtChunk, TextDecodingError, ZTXtChunk};
19use crate::traits::ReadBytesExt;
20use crate::Limits;
21
22/// TODO check if these size are reasonable
23pub const CHUNCK_BUFFER_SIZE: usize = 32 * 1024;
24
25/// Determines if checksum checks should be disabled globally.
26///
27/// This is used only in fuzzing. `afl` automatically adds `--cfg fuzzing` to RUSTFLAGS which can
28/// be used to detect that build.
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`.
66 ReadChunkData(ChunkType),
67 /// In this state we check if all chunk data has been already read into `ChunkState::raw_bytes`
68 /// and if so then we parse the chunk. Otherwise, we go back to the `ReadChunkData` state.
69 ParseChunkData(ChunkType),
70 /// In this state we are reading image data from external input and feeding it directly into
71 /// `StreamingDecoder::inflater`.
72 ImageData(ChunkType),
73}
74
75impl State {
76 fn new_u32(kind: U32ValueKind) -> Self {
77 Self::U32 {
78 kind,
79 bytes: [0; 4],
80 accumulated_count: 0,
81 }
82 }
83}
84
85#[derive(Debug)]
86/// Result of the decoding process
87pub enum Decoded {
88 /// Nothing decoded yet
89 Nothing,
90 Header(u32, u32, BitDepth, ColorType, bool),
91 ChunkBegin(u32, ChunkType),
92 ChunkComplete(u32, ChunkType),
93 PixelDimensions(PixelDimensions),
94 AnimationControl(AnimationControl),
95 FrameControl(FrameControl),
96 /// Decoded raw image data.
97 ImageData,
98 /// The last of a consecutive chunk of IDAT was done.
99 /// This is distinct from ChunkComplete which only marks that some IDAT chunk was completed but
100 /// not that no additional IDAT chunk follows.
101 ImageDataFlushed,
102 PartialChunk(ChunkType),
103 ImageEnd,
104}
105
106/// Any kind of error during PNG decoding.
107///
108/// This enumeration provides a very rough analysis on the origin of the failure. That is, each
109/// variant corresponds to one kind of actor causing the error. It should not be understood as a
110/// direct blame but can inform the search for a root cause or if such a search is required.
111#[derive(Debug)]
112pub enum DecodingError {
113 /// An error in IO of the underlying reader.
114 IoError(io::Error),
115 /// The input image was not a valid PNG.
116 ///
117 /// There isn't a lot that can be done here, except if the program itself was responsible for
118 /// creating this image then investigate the generator. This is internally implemented with a
119 /// large Enum. If You are interested in accessing some of the more exact information on the
120 /// variant then we can discuss in an issue.
121 Format(FormatError),
122 /// An interface was used incorrectly.
123 ///
124 /// This is used in cases where it's expected that the programmer might trip up and stability
125 /// could be affected. For example when:
126 ///
127 /// * The decoder is polled for more animation frames despite being done (or not being animated
128 /// in the first place).
129 /// * The output buffer does not have the required size.
130 ///
131 /// As a rough guideline for introducing new variants parts of the requirements are dynamically
132 /// derived from the (untrusted) input data while the other half is from the caller. In the
133 /// above cases the number of frames respectively the size is determined by the file while the
134 /// number of calls
135 ///
136 /// If you're an application you might want to signal that a bug report is appreciated.
137 Parameter(ParameterError),
138 /// The image would have required exceeding the limits configured with the decoder.
139 ///
140 /// Note that Your allocations, e.g. when reading into a pre-allocated buffer, is __NOT__
141 /// considered part of the limits. Nevertheless, required intermediate buffers such as for
142 /// singular lines is checked against the limit.
143 ///
144 /// Note that this is a best-effort basis.
145 LimitsExceeded,
146}
147
148#[derive(Debug)]
149pub struct FormatError {
150 inner: FormatErrorInner,
151}
152
153#[derive(Debug)]
154pub(crate) enum FormatErrorInner {
155 /// Bad framing.
156 CrcMismatch {
157 /// Stored CRC32 value
158 crc_val: u32,
159 /// Calculated CRC32 sum
160 crc_sum: u32,
161 /// The chunk type that has the CRC mismatch.
162 chunk: ChunkType,
163 },
164 /// Not a PNG, the magic signature is missing.
165 InvalidSignature,
166 /// End of file, within a chunk event.
167 UnexpectedEof,
168 /// End of file, while expecting more image data.
169 UnexpectedEndOfChunk,
170 // Errors of chunk level ordering, missing etc.
171 /// Ihdr must occur.
172 MissingIhdr,
173 /// Fctl must occur if an animated chunk occurs.
174 MissingFctl,
175 /// Image data that was indicated in IHDR or acTL is missing.
176 MissingImageData,
177 /// 4.3., Must be first.
178 ChunkBeforeIhdr {
179 kind: ChunkType,
180 },
181 /// 4.3., some chunks must be before IDAT.
182 AfterIdat {
183 kind: ChunkType,
184 },
185 /// 4.3., some chunks must be before PLTE.
186 AfterPlte {
187 kind: ChunkType,
188 },
189 /// 4.3., some chunks must be between PLTE and IDAT.
190 OutsidePlteIdat {
191 kind: ChunkType,
192 },
193 /// 4.3., some chunks must be unique.
194 DuplicateChunk {
195 kind: ChunkType,
196 },
197 /// Specifically for fdat there is an embedded sequence number for chunks.
198 ApngOrder {
199 /// The sequence number in the chunk.
200 present: u32,
201 /// The one that should have been present.
202 expected: u32,
203 },
204 // Errors specific to particular chunk data to be validated.
205 /// The palette did not even contain a single pixel data.
206 ShortPalette {
207 expected: usize,
208 len: usize,
209 },
210 /// A palletized image did not have a palette.
211 PaletteRequired,
212 /// The color-depth combination is not valid according to Table 11.1.
213 InvalidColorBitDepth {
214 color_type: ColorType,
215 bit_depth: BitDepth,
216 },
217 ColorWithBadTrns(ColorType),
218 /// The image width or height is zero.
219 InvalidDimensions,
220 InvalidBitDepth(u8),
221 InvalidColorType(u8),
222 InvalidDisposeOp(u8),
223 InvalidBlendOp(u8),
224 InvalidUnit(u8),
225 /// The rendering intent of the sRGB chunk is invalid.
226 InvalidSrgbRenderingIntent(u8),
227 UnknownCompressionMethod(u8),
228 UnknownFilterMethod(u8),
229 UnknownInterlaceMethod(u8),
230 /// The subframe is not in bounds of the image.
231 /// TODO: fields with relevant data.
232 BadSubFrameBounds {},
233 // Errors specific to the IDAT/fDAT chunks.
234 /// The compression of the data stream was faulty.
235 CorruptFlateStream {
236 err: fdeflate::DecompressionError,
237 },
238 /// The image data chunk was too short for the expected pixel count.
239 NoMoreImageData,
240 /// Bad text encoding
241 BadTextEncoding(TextDecodingError),
242 /// fdAT shorter than 4 bytes
243 FdatShorterThanFourBytes,
244}
245
246impl error::Error for DecodingError {
247 fn cause(&self) -> Option<&(dyn error::Error + 'static)> {
248 match self {
249 DecodingError::IoError(err: &Error) => Some(err),
250 _ => None,
251 }
252 }
253}
254
255impl fmt::Display for DecodingError {
256 fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
257 use self::DecodingError::*;
258 match self {
259 IoError(err: &Error) => write!(fmt, "{}", err),
260 Parameter(desc: &ParameterError) => write!(fmt, "{}", &desc),
261 Format(desc: &FormatError) => write!(fmt, "{}", desc),
262 LimitsExceeded => write!(fmt, "limits are exceeded"),
263 }
264 }
265}
266
267impl fmt::Display for FormatError {
268 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
269 use FormatErrorInner::*;
270 match &self.inner {
271 CrcMismatch {
272 crc_val,
273 crc_sum,
274 chunk,
275 ..
276 } => write!(
277 fmt,
278 "CRC error: expected 0x{:x} have 0x{:x} while decoding {:?} chunk.",
279 crc_val, crc_sum, chunk
280 ),
281 MissingIhdr => write!(fmt, "IHDR chunk missing"),
282 MissingFctl => write!(fmt, "fcTL chunk missing before fdAT chunk."),
283 MissingImageData => write!(fmt, "IDAT or fDAT chunk is missing."),
284 ChunkBeforeIhdr { kind } => write!(fmt, "{:?} chunk appeared before IHDR chunk", kind),
285 AfterIdat { kind } => write!(fmt, "Chunk {:?} is invalid after IDAT chunk.", kind),
286 AfterPlte { kind } => write!(fmt, "Chunk {:?} is invalid after PLTE chunk.", kind),
287 OutsidePlteIdat { kind } => write!(
288 fmt,
289 "Chunk {:?} must appear between PLTE and IDAT chunks.",
290 kind
291 ),
292 DuplicateChunk { kind } => write!(fmt, "Chunk {:?} must appear at most once.", kind),
293 ApngOrder { present, expected } => write!(
294 fmt,
295 "Sequence is not in order, expected #{} got #{}.",
296 expected, present,
297 ),
298 ShortPalette { expected, len } => write!(
299 fmt,
300 "Not enough palette entries, expect {} got {}.",
301 expected, len
302 ),
303 PaletteRequired => write!(fmt, "Missing palette of indexed image."),
304 InvalidDimensions => write!(fmt, "Invalid image dimensions"),
305 InvalidColorBitDepth {
306 color_type,
307 bit_depth,
308 } => write!(
309 fmt,
310 "Invalid color/depth combination in header: {:?}/{:?}",
311 color_type, bit_depth,
312 ),
313 ColorWithBadTrns(color_type) => write!(
314 fmt,
315 "Transparency chunk found for color type {:?}.",
316 color_type
317 ),
318 InvalidBitDepth(nr) => write!(fmt, "Invalid dispose operation {}.", nr),
319 InvalidColorType(nr) => write!(fmt, "Invalid color type {}.", nr),
320 InvalidDisposeOp(nr) => write!(fmt, "Invalid dispose op {}.", nr),
321 InvalidBlendOp(nr) => write!(fmt, "Invalid blend op {}.", nr),
322 InvalidUnit(nr) => write!(fmt, "Invalid physical pixel size unit {}.", nr),
323 InvalidSrgbRenderingIntent(nr) => write!(fmt, "Invalid sRGB rendering intent {}.", nr),
324 UnknownCompressionMethod(nr) => write!(fmt, "Unknown compression method {}.", nr),
325 UnknownFilterMethod(nr) => write!(fmt, "Unknown filter method {}.", nr),
326 UnknownInterlaceMethod(nr) => write!(fmt, "Unknown interlace method {}.", nr),
327 BadSubFrameBounds {} => write!(fmt, "Sub frame is out-of-bounds."),
328 InvalidSignature => write!(fmt, "Invalid PNG signature."),
329 UnexpectedEof => write!(fmt, "Unexpected end of data before image end."),
330 UnexpectedEndOfChunk => write!(fmt, "Unexpected end of data within a chunk."),
331 NoMoreImageData => write!(fmt, "IDAT or fDAT chunk is has not enough data for image."),
332 CorruptFlateStream { err } => {
333 write!(fmt, "Corrupt deflate stream. ")?;
334 write!(fmt, "{:?}", err)
335 }
336 // TODO: Wrap more info in the enum variant
337 BadTextEncoding(tde) => {
338 match tde {
339 TextDecodingError::Unrepresentable => {
340 write!(fmt, "Unrepresentable data in tEXt chunk.")
341 }
342 TextDecodingError::InvalidKeywordSize => {
343 write!(fmt, "Keyword empty or longer than 79 bytes.")
344 }
345 TextDecodingError::MissingNullSeparator => {
346 write!(fmt, "No null separator in tEXt chunk.")
347 }
348 TextDecodingError::InflationError => {
349 write!(fmt, "Invalid compressed text data.")
350 }
351 TextDecodingError::OutOfDecompressionSpace => {
352 write!(fmt, "Out of decompression space. Try with a larger limit.")
353 }
354 TextDecodingError::InvalidCompressionMethod => {
355 write!(fmt, "Using an unrecognized byte as compression method.")
356 }
357 TextDecodingError::InvalidCompressionFlag => {
358 write!(fmt, "Using a flag that is not 0 or 255 as a compression flag for iTXt chunk.")
359 }
360 TextDecodingError::MissingCompressionFlag => {
361 write!(fmt, "No compression flag in the iTXt chunk.")
362 }
363 }
364 }
365 FdatShorterThanFourBytes => write!(fmt, "fdAT chunk shorter than 4 bytes"),
366 }
367 }
368}
369
370impl From<io::Error> for DecodingError {
371 fn from(err: io::Error) -> DecodingError {
372 DecodingError::IoError(err)
373 }
374}
375
376impl From<FormatError> for DecodingError {
377 fn from(err: FormatError) -> DecodingError {
378 DecodingError::Format(err)
379 }
380}
381
382impl From<FormatErrorInner> for FormatError {
383 fn from(inner: FormatErrorInner) -> Self {
384 FormatError { inner }
385 }
386}
387
388impl From<DecodingError> for io::Error {
389 fn from(err: DecodingError) -> io::Error {
390 match err {
391 DecodingError::IoError(err: Error) => err,
392 err: DecodingError => io::Error::new(kind:io::ErrorKind::Other, error:err.to_string()),
393 }
394 }
395}
396
397impl From<TextDecodingError> for DecodingError {
398 fn from(tbe: TextDecodingError) -> Self {
399 DecodingError::Format(FormatError {
400 inner: FormatErrorInner::BadTextEncoding(tbe),
401 })
402 }
403}
404
405/// Decoder configuration options
406#[derive(Clone)]
407pub struct DecodeOptions {
408 ignore_adler32: bool,
409 ignore_crc: bool,
410 ignore_text_chunk: bool,
411 skip_ancillary_crc_failures: bool,
412}
413
414impl Default for DecodeOptions {
415 fn default() -> Self {
416 Self {
417 ignore_adler32: true,
418 ignore_crc: false,
419 ignore_text_chunk: false,
420 skip_ancillary_crc_failures: true,
421 }
422 }
423}
424
425impl DecodeOptions {
426 /// When set, the decoder will not compute and verify the Adler-32 checksum.
427 ///
428 /// Defaults to `true`.
429 pub fn set_ignore_adler32(&mut self, ignore_adler32: bool) {
430 self.ignore_adler32 = ignore_adler32;
431 }
432
433 /// When set, the decoder will not compute and verify the CRC code.
434 ///
435 /// Defaults to `false`.
436 pub fn set_ignore_crc(&mut self, ignore_crc: bool) {
437 self.ignore_crc = ignore_crc;
438 }
439
440 /// Flag to ignore computing and verifying the Adler-32 checksum and CRC
441 /// code.
442 pub fn set_ignore_checksums(&mut self, ignore_checksums: bool) {
443 self.ignore_adler32 = ignore_checksums;
444 self.ignore_crc = ignore_checksums;
445 }
446
447 /// Ignore text chunks while decoding.
448 ///
449 /// Defaults to `false`.
450 pub fn set_ignore_text_chunk(&mut self, ignore_text_chunk: bool) {
451 self.ignore_text_chunk = ignore_text_chunk;
452 }
453
454 /// Ignore ancillary chunks if CRC fails
455 ///
456 /// Defaults to `true`
457 pub fn set_skip_ancillary_crc_failures(&mut self, skip_ancillary_crc_failures: bool) {
458 self.skip_ancillary_crc_failures = skip_ancillary_crc_failures;
459 }
460}
461
462/// PNG StreamingDecoder (low-level interface)
463///
464/// By default, the decoder does not verify Adler-32 checksum computation. To
465/// enable checksum verification, set it with [`StreamingDecoder::set_ignore_adler32`]
466/// before starting decompression.
467pub struct StreamingDecoder {
468 state: Option<State>,
469 current_chunk: ChunkState,
470 /// The inflater state handling consecutive `IDAT` and `fdAT` chunks.
471 inflater: ZlibStream,
472 /// The complete image info read from all prior chunks.
473 pub(crate) info: Option<Info<'static>>,
474 /// The animation chunk sequence number.
475 current_seq_no: Option<u32>,
476 /// Whether we have already seen a start of an IDAT chunk. (Used to validate chunk ordering -
477 /// some chunk types can only appear before or after an IDAT chunk.)
478 have_idat: bool,
479 decode_options: DecodeOptions,
480 pub(crate) limits: Limits,
481}
482
483struct ChunkState {
484 /// The type of the current chunk.
485 /// Relevant for `IDAT` and `fdAT` which aggregate consecutive chunks of their own type.
486 type_: ChunkType,
487
488 /// Partial crc until now.
489 crc: Crc32,
490
491 /// Remaining bytes to be read.
492 remaining: u32,
493
494 /// Non-decoded bytes in the chunk.
495 raw_bytes: Vec<u8>,
496}
497
498impl StreamingDecoder {
499 /// Creates a new StreamingDecoder
500 ///
501 /// Allocates the internal buffers.
502 pub fn new() -> StreamingDecoder {
503 StreamingDecoder::new_with_options(DecodeOptions::default())
504 }
505
506 pub fn new_with_options(decode_options: DecodeOptions) -> StreamingDecoder {
507 let mut inflater = ZlibStream::new();
508 inflater.set_ignore_adler32(decode_options.ignore_adler32);
509
510 StreamingDecoder {
511 state: Some(State::new_u32(U32ValueKind::Signature1stU32)),
512 current_chunk: ChunkState::default(),
513 inflater,
514 info: None,
515 current_seq_no: None,
516 have_idat: false,
517 decode_options,
518 limits: Limits { bytes: usize::MAX },
519 }
520 }
521
522 /// Resets the StreamingDecoder
523 pub fn reset(&mut self) {
524 self.state = Some(State::new_u32(U32ValueKind::Signature1stU32));
525 self.current_chunk.crc = Crc32::new();
526 self.current_chunk.remaining = 0;
527 self.current_chunk.raw_bytes.clear();
528 self.inflater.reset();
529 self.info = None;
530 self.current_seq_no = None;
531 self.have_idat = false;
532 }
533
534 /// Provides access to the inner `info` field
535 pub fn info(&self) -> Option<&Info<'static>> {
536 self.info.as_ref()
537 }
538
539 pub fn set_ignore_text_chunk(&mut self, ignore_text_chunk: bool) {
540 self.decode_options.set_ignore_text_chunk(ignore_text_chunk);
541 }
542
543 /// Return whether the decoder is set to ignore the Adler-32 checksum.
544 pub fn ignore_adler32(&self) -> bool {
545 self.inflater.ignore_adler32()
546 }
547
548 /// Set whether to compute and verify the Adler-32 checksum during
549 /// decompression. Return `true` if the flag was successfully set.
550 ///
551 /// The decoder defaults to `true`.
552 ///
553 /// This flag cannot be modified after decompression has started until the
554 /// [`StreamingDecoder`] is reset.
555 pub fn set_ignore_adler32(&mut self, ignore_adler32: bool) -> bool {
556 self.inflater.set_ignore_adler32(ignore_adler32)
557 }
558
559 /// Set whether to compute and verify the Adler-32 checksum during
560 /// decompression.
561 ///
562 /// The decoder defaults to `false`.
563 pub fn set_ignore_crc(&mut self, ignore_crc: bool) {
564 self.decode_options.set_ignore_crc(ignore_crc)
565 }
566
567 /// Ignore ancillary chunks if CRC fails
568 ///
569 /// Defaults to `true`
570 pub fn set_skip_ancillary_crc_failures(&mut self, skip_ancillary_crc_failures: bool) {
571 self.decode_options
572 .set_skip_ancillary_crc_failures(skip_ancillary_crc_failures)
573 }
574
575 /// Low level StreamingDecoder interface.
576 ///
577 /// Allows to stream partial data to the encoder. Returns a tuple containing the bytes that have
578 /// been consumed from the input buffer and the current decoding result. If the decoded chunk
579 /// was an image data chunk, it also appends the read data to `image_data`.
580 pub fn update(
581 &mut self,
582 mut buf: &[u8],
583 image_data: &mut Vec<u8>,
584 ) -> Result<(usize, Decoded), DecodingError> {
585 let len = buf.len();
586 while !buf.is_empty() && self.state.is_some() {
587 match self.next_state(buf, image_data) {
588 Ok((bytes, Decoded::Nothing)) => buf = &buf[bytes..],
589 Ok((bytes, result)) => {
590 buf = &buf[bytes..];
591 return Ok((len - buf.len(), result));
592 }
593 Err(err) => return Err(err),
594 }
595 }
596 Ok((len - buf.len(), Decoded::Nothing))
597 }
598
599 fn next_state(
600 &mut self,
601 buf: &[u8],
602 image_data: &mut Vec<u8>,
603 ) -> Result<(usize, Decoded), DecodingError> {
604 use self::State::*;
605
606 // Driver should ensure that state is never None
607 let state = self.state.take().unwrap();
608
609 match state {
610 U32 {
611 kind,
612 mut bytes,
613 mut accumulated_count,
614 } => {
615 debug_assert!(accumulated_count <= 4);
616 if accumulated_count == 0 && buf.len() >= 4 {
617 // Handling these `accumulated_count` and `buf.len()` values in a separate `if`
618 // branch is not strictly necessary - the `else` statement below is already
619 // capable of handling these values. The main reason for special-casing these
620 // values is that they occur fairly frequently and special-casing them results
621 // in performance gains.
622 const CONSUMED_BYTES: usize = 4;
623 self.parse_u32(kind, &buf[0..4], image_data)
624 .map(|decoded| (CONSUMED_BYTES, decoded))
625 } else {
626 let remaining_count = 4 - accumulated_count;
627 let consumed_bytes = {
628 let available_count = min(remaining_count, buf.len());
629 bytes[accumulated_count..accumulated_count + available_count]
630 .copy_from_slice(&buf[0..available_count]);
631 accumulated_count += available_count;
632 available_count
633 };
634
635 if accumulated_count < 4 {
636 self.state = Some(U32 {
637 kind,
638 bytes,
639 accumulated_count,
640 });
641 Ok((consumed_bytes, Decoded::Nothing))
642 } else {
643 debug_assert_eq!(accumulated_count, 4);
644 self.parse_u32(kind, &bytes, image_data)
645 .map(|decoded| (consumed_bytes, decoded))
646 }
647 }
648 }
649 ParseChunkData(type_str) => {
650 debug_assert!(type_str != IDAT && type_str != chunk::fdAT);
651 if self.current_chunk.remaining == 0 {
652 // Got complete chunk.
653 Ok((0, self.parse_chunk(type_str)?))
654 } else {
655 // Make sure we have room to read more of the chunk.
656 // We need it fully before parsing.
657 self.reserve_current_chunk()?;
658
659 self.state = Some(ReadChunkData(type_str));
660 Ok((0, Decoded::PartialChunk(type_str)))
661 }
662 }
663 ReadChunkData(type_str) => {
664 debug_assert!(type_str != IDAT && type_str != chunk::fdAT);
665 if self.current_chunk.remaining == 0 {
666 self.state = Some(State::new_u32(U32ValueKind::Crc(type_str)));
667 Ok((0, Decoded::Nothing))
668 } else {
669 let ChunkState {
670 crc,
671 remaining,
672 raw_bytes,
673 type_: _,
674 } = &mut self.current_chunk;
675
676 let buf_avail = raw_bytes.capacity() - raw_bytes.len();
677 let bytes_avail = min(buf.len(), buf_avail);
678 let n = min(*remaining, bytes_avail as u32);
679 if buf_avail == 0 {
680 self.state = Some(ParseChunkData(type_str));
681 Ok((0, Decoded::Nothing))
682 } else {
683 let buf = &buf[..n as usize];
684 if !self.decode_options.ignore_crc {
685 crc.update(buf);
686 }
687 raw_bytes.extend_from_slice(buf);
688
689 *remaining -= n;
690 if *remaining == 0 {
691 self.state = Some(ParseChunkData(type_str));
692 } else {
693 self.state = Some(ReadChunkData(type_str));
694 }
695 Ok((n as usize, Decoded::Nothing))
696 }
697 }
698 }
699 ImageData(type_str) => {
700 debug_assert!(type_str == IDAT || type_str == chunk::fdAT);
701 let len = std::cmp::min(buf.len(), self.current_chunk.remaining as usize);
702 let buf = &buf[..len];
703 let consumed = self.inflater.decompress(buf, image_data)?;
704 self.current_chunk.crc.update(&buf[..consumed]);
705 self.current_chunk.remaining -= consumed as u32;
706 if self.current_chunk.remaining == 0 {
707 self.state = Some(State::new_u32(U32ValueKind::Crc(type_str)));
708 } else {
709 self.state = Some(ImageData(type_str));
710 }
711 Ok((consumed, Decoded::ImageData))
712 }
713 }
714 }
715
716 fn parse_u32(
717 &mut self,
718 kind: U32ValueKind,
719 u32_be_bytes: &[u8],
720 image_data: &mut Vec<u8>,
721 ) -> Result<Decoded, DecodingError> {
722 debug_assert_eq!(u32_be_bytes.len(), 4);
723 let bytes = u32_be_bytes.try_into().unwrap();
724 let val = u32::from_be_bytes(bytes);
725
726 match kind {
727 U32ValueKind::Signature1stU32 => {
728 if bytes == [137, 80, 78, 71] {
729 self.state = Some(State::new_u32(U32ValueKind::Signature2ndU32));
730 Ok(Decoded::Nothing)
731 } else {
732 Err(DecodingError::Format(
733 FormatErrorInner::InvalidSignature.into(),
734 ))
735 }
736 }
737 U32ValueKind::Signature2ndU32 => {
738 if bytes == [13, 10, 26, 10] {
739 self.state = Some(State::new_u32(U32ValueKind::Length));
740 Ok(Decoded::Nothing)
741 } else {
742 Err(DecodingError::Format(
743 FormatErrorInner::InvalidSignature.into(),
744 ))
745 }
746 }
747 U32ValueKind::Length => {
748 self.state = Some(State::new_u32(U32ValueKind::Type { length: val }));
749 Ok(Decoded::Nothing)
750 }
751 U32ValueKind::Type { length } => {
752 let type_str = ChunkType(bytes);
753 if self.info.is_none() && type_str != IHDR {
754 return Err(DecodingError::Format(
755 FormatErrorInner::ChunkBeforeIhdr { kind: type_str }.into(),
756 ));
757 }
758 if type_str != self.current_chunk.type_
759 && (self.current_chunk.type_ == IDAT || self.current_chunk.type_ == chunk::fdAT)
760 {
761 self.current_chunk.type_ = type_str;
762 self.inflater.finish_compressed_chunks(image_data)?;
763 self.inflater.reset();
764 self.state = Some(State::U32 {
765 kind,
766 bytes,
767 accumulated_count: 4,
768 });
769 return Ok(Decoded::ImageDataFlushed);
770 }
771 self.current_chunk.type_ = type_str;
772 if !self.decode_options.ignore_crc {
773 self.current_chunk.crc.reset();
774 self.current_chunk.crc.update(&type_str.0);
775 }
776 self.current_chunk.remaining = length;
777 self.current_chunk.raw_bytes.clear();
778 self.state = match type_str {
779 chunk::fdAT => {
780 if length < 4 {
781 return Err(DecodingError::Format(
782 FormatErrorInner::FdatShorterThanFourBytes.into(),
783 ));
784 }
785 Some(State::new_u32(U32ValueKind::ApngSequenceNumber))
786 }
787 IDAT => {
788 self.have_idat = true;
789 Some(State::ImageData(type_str))
790 }
791 _ => Some(State::ReadChunkData(type_str)),
792 };
793 Ok(Decoded::ChunkBegin(length, type_str))
794 }
795 U32ValueKind::Crc(type_str) => {
796 // If ignore_crc is set, do not calculate CRC. We set
797 // sum=val so that it short-circuits to true in the next
798 // if-statement block
799 let sum = if self.decode_options.ignore_crc {
800 val
801 } else {
802 self.current_chunk.crc.clone().finalize()
803 };
804
805 if val == sum || CHECKSUM_DISABLED {
806 self.state = Some(State::new_u32(U32ValueKind::Length));
807 if type_str == IEND {
808 Ok(Decoded::ImageEnd)
809 } else {
810 Ok(Decoded::ChunkComplete(val, type_str))
811 }
812 } else if self.decode_options.skip_ancillary_crc_failures
813 && !chunk::is_critical(type_str)
814 {
815 // Ignore ancillary chunk with invalid CRC
816 self.state = Some(State::new_u32(U32ValueKind::Length));
817 Ok(Decoded::Nothing)
818 } else {
819 Err(DecodingError::Format(
820 FormatErrorInner::CrcMismatch {
821 crc_val: val,
822 crc_sum: sum,
823 chunk: type_str,
824 }
825 .into(),
826 ))
827 }
828 }
829 U32ValueKind::ApngSequenceNumber => {
830 debug_assert_eq!(self.current_chunk.type_, chunk::fdAT);
831 let next_seq_no = val;
832
833 // Should be verified by the FdatShorterThanFourBytes check earlier.
834 debug_assert!(self.current_chunk.remaining >= 4);
835 self.current_chunk.remaining -= 4;
836
837 if let Some(seq_no) = self.current_seq_no {
838 if next_seq_no != seq_no + 1 {
839 return Err(DecodingError::Format(
840 FormatErrorInner::ApngOrder {
841 present: next_seq_no,
842 expected: seq_no + 1,
843 }
844 .into(),
845 ));
846 }
847 self.current_seq_no = Some(next_seq_no);
848 } else {
849 return Err(DecodingError::Format(FormatErrorInner::MissingFctl.into()));
850 }
851
852 if !self.decode_options.ignore_crc {
853 let data = next_seq_no.to_be_bytes();
854 self.current_chunk.crc.update(&data);
855 }
856
857 self.state = Some(State::ImageData(chunk::fdAT));
858 Ok(Decoded::PartialChunk(chunk::fdAT))
859 }
860 }
861 }
862
863 fn reserve_current_chunk(&mut self) -> Result<(), DecodingError> {
864 let max = self.limits.bytes;
865 let buffer = &mut self.current_chunk.raw_bytes;
866
867 // Double if necessary, but no more than until the limit is reached.
868 let reserve_size = max.saturating_sub(buffer.capacity()).min(buffer.len());
869 self.limits.reserve_bytes(reserve_size)?;
870 buffer.reserve_exact(reserve_size);
871
872 if buffer.capacity() == buffer.len() {
873 Err(DecodingError::LimitsExceeded)
874 } else {
875 Ok(())
876 }
877 }
878
879 fn parse_chunk(&mut self, type_str: ChunkType) -> Result<Decoded, DecodingError> {
880 self.state = Some(State::new_u32(U32ValueKind::Crc(type_str)));
881 let parse_result = match type_str {
882 IHDR => self.parse_ihdr(),
883 chunk::PLTE => self.parse_plte(),
884 chunk::tRNS => self.parse_trns(),
885 chunk::pHYs => self.parse_phys(),
886 chunk::gAMA => self.parse_gama(),
887 chunk::acTL => self.parse_actl(),
888 chunk::fcTL => self.parse_fctl(),
889 chunk::cHRM => self.parse_chrm(),
890 chunk::sRGB => self.parse_srgb(),
891 chunk::iCCP => self.parse_iccp(),
892 chunk::tEXt if !self.decode_options.ignore_text_chunk => self.parse_text(),
893 chunk::zTXt if !self.decode_options.ignore_text_chunk => self.parse_ztxt(),
894 chunk::iTXt if !self.decode_options.ignore_text_chunk => self.parse_itxt(),
895 _ => Ok(Decoded::PartialChunk(type_str)),
896 };
897
898 if parse_result.is_err() {
899 self.state = None;
900 }
901 parse_result
902 }
903
904 fn parse_fctl(&mut self) -> Result<Decoded, DecodingError> {
905 let mut buf = &self.current_chunk.raw_bytes[..];
906 let next_seq_no = buf.read_be()?;
907
908 // Assuming that fcTL is required before *every* fdAT-sequence
909 self.current_seq_no = Some(if let Some(seq_no) = self.current_seq_no {
910 if next_seq_no != seq_no + 1 {
911 return Err(DecodingError::Format(
912 FormatErrorInner::ApngOrder {
913 expected: seq_no + 1,
914 present: next_seq_no,
915 }
916 .into(),
917 ));
918 }
919 next_seq_no
920 } else {
921 if next_seq_no != 0 {
922 return Err(DecodingError::Format(
923 FormatErrorInner::ApngOrder {
924 expected: 0,
925 present: next_seq_no,
926 }
927 .into(),
928 ));
929 }
930 0
931 });
932 self.inflater.reset();
933 let fc = FrameControl {
934 sequence_number: next_seq_no,
935 width: buf.read_be()?,
936 height: buf.read_be()?,
937 x_offset: buf.read_be()?,
938 y_offset: buf.read_be()?,
939 delay_num: buf.read_be()?,
940 delay_den: buf.read_be()?,
941 dispose_op: {
942 let dispose_op = buf.read_be()?;
943 match DisposeOp::from_u8(dispose_op) {
944 Some(dispose_op) => dispose_op,
945 None => {
946 return Err(DecodingError::Format(
947 FormatErrorInner::InvalidDisposeOp(dispose_op).into(),
948 ))
949 }
950 }
951 },
952 blend_op: {
953 let blend_op = buf.read_be()?;
954 match BlendOp::from_u8(blend_op) {
955 Some(blend_op) => blend_op,
956 None => {
957 return Err(DecodingError::Format(
958 FormatErrorInner::InvalidBlendOp(blend_op).into(),
959 ))
960 }
961 }
962 },
963 };
964 self.info.as_ref().unwrap().validate(&fc)?;
965 self.info.as_mut().unwrap().frame_control = Some(fc);
966 Ok(Decoded::FrameControl(fc))
967 }
968
969 fn parse_actl(&mut self) -> Result<Decoded, DecodingError> {
970 if self.have_idat {
971 Err(DecodingError::Format(
972 FormatErrorInner::AfterIdat { kind: chunk::acTL }.into(),
973 ))
974 } else {
975 let mut buf = &self.current_chunk.raw_bytes[..];
976 let actl = AnimationControl {
977 num_frames: buf.read_be()?,
978 num_plays: buf.read_be()?,
979 };
980 self.info.as_mut().unwrap().animation_control = Some(actl);
981 Ok(Decoded::AnimationControl(actl))
982 }
983 }
984
985 fn parse_plte(&mut self) -> Result<Decoded, DecodingError> {
986 let info = self.info.as_mut().unwrap();
987 if info.palette.is_some() {
988 // Only one palette is allowed
989 Err(DecodingError::Format(
990 FormatErrorInner::DuplicateChunk { kind: chunk::PLTE }.into(),
991 ))
992 } else {
993 self.limits
994 .reserve_bytes(self.current_chunk.raw_bytes.len())?;
995 info.palette = Some(Cow::Owned(self.current_chunk.raw_bytes.clone()));
996 Ok(Decoded::Nothing)
997 }
998 }
999
1000 fn parse_trns(&mut self) -> Result<Decoded, DecodingError> {
1001 let info = self.info.as_mut().unwrap();
1002 if info.trns.is_some() {
1003 return Err(DecodingError::Format(
1004 FormatErrorInner::DuplicateChunk { kind: chunk::PLTE }.into(),
1005 ));
1006 }
1007 let (color_type, bit_depth) = { (info.color_type, info.bit_depth as u8) };
1008 self.limits
1009 .reserve_bytes(self.current_chunk.raw_bytes.len())?;
1010 let mut vec = self.current_chunk.raw_bytes.clone();
1011 let len = vec.len();
1012 match color_type {
1013 ColorType::Grayscale => {
1014 if len < 2 {
1015 return Err(DecodingError::Format(
1016 FormatErrorInner::ShortPalette { expected: 2, len }.into(),
1017 ));
1018 }
1019 if bit_depth < 16 {
1020 vec[0] = vec[1];
1021 vec.truncate(1);
1022 }
1023 info.trns = Some(Cow::Owned(vec));
1024 Ok(Decoded::Nothing)
1025 }
1026 ColorType::Rgb => {
1027 if len < 6 {
1028 return Err(DecodingError::Format(
1029 FormatErrorInner::ShortPalette { expected: 6, len }.into(),
1030 ));
1031 }
1032 if bit_depth < 16 {
1033 vec[0] = vec[1];
1034 vec[1] = vec[3];
1035 vec[2] = vec[5];
1036 vec.truncate(3);
1037 }
1038 info.trns = Some(Cow::Owned(vec));
1039 Ok(Decoded::Nothing)
1040 }
1041 ColorType::Indexed => {
1042 // The transparency chunk must be after the palette chunk and
1043 // before the data chunk.
1044 if info.palette.is_none() {
1045 return Err(DecodingError::Format(
1046 FormatErrorInner::AfterPlte { kind: chunk::tRNS }.into(),
1047 ));
1048 } else if self.have_idat {
1049 return Err(DecodingError::Format(
1050 FormatErrorInner::OutsidePlteIdat { kind: chunk::tRNS }.into(),
1051 ));
1052 }
1053
1054 info.trns = Some(Cow::Owned(vec));
1055 Ok(Decoded::Nothing)
1056 }
1057 c => Err(DecodingError::Format(
1058 FormatErrorInner::ColorWithBadTrns(c).into(),
1059 )),
1060 }
1061 }
1062
1063 fn parse_phys(&mut self) -> Result<Decoded, DecodingError> {
1064 let info = self.info.as_mut().unwrap();
1065 if self.have_idat {
1066 Err(DecodingError::Format(
1067 FormatErrorInner::AfterIdat { kind: chunk::pHYs }.into(),
1068 ))
1069 } else if info.pixel_dims.is_some() {
1070 Err(DecodingError::Format(
1071 FormatErrorInner::DuplicateChunk { kind: chunk::pHYs }.into(),
1072 ))
1073 } else {
1074 let mut buf = &self.current_chunk.raw_bytes[..];
1075 let xppu = buf.read_be()?;
1076 let yppu = buf.read_be()?;
1077 let unit = buf.read_be()?;
1078 let unit = match Unit::from_u8(unit) {
1079 Some(unit) => unit,
1080 None => {
1081 return Err(DecodingError::Format(
1082 FormatErrorInner::InvalidUnit(unit).into(),
1083 ))
1084 }
1085 };
1086 let pixel_dims = PixelDimensions { xppu, yppu, unit };
1087 info.pixel_dims = Some(pixel_dims);
1088 Ok(Decoded::PixelDimensions(pixel_dims))
1089 }
1090 }
1091
1092 fn parse_chrm(&mut self) -> Result<Decoded, DecodingError> {
1093 let info = self.info.as_mut().unwrap();
1094 if self.have_idat {
1095 Err(DecodingError::Format(
1096 FormatErrorInner::AfterIdat { kind: chunk::cHRM }.into(),
1097 ))
1098 } else if info.chrm_chunk.is_some() {
1099 Err(DecodingError::Format(
1100 FormatErrorInner::DuplicateChunk { kind: chunk::cHRM }.into(),
1101 ))
1102 } else {
1103 let mut buf = &self.current_chunk.raw_bytes[..];
1104 let white_x: u32 = buf.read_be()?;
1105 let white_y: u32 = buf.read_be()?;
1106 let red_x: u32 = buf.read_be()?;
1107 let red_y: u32 = buf.read_be()?;
1108 let green_x: u32 = buf.read_be()?;
1109 let green_y: u32 = buf.read_be()?;
1110 let blue_x: u32 = buf.read_be()?;
1111 let blue_y: u32 = buf.read_be()?;
1112
1113 let source_chromaticities = SourceChromaticities {
1114 white: (
1115 ScaledFloat::from_scaled(white_x),
1116 ScaledFloat::from_scaled(white_y),
1117 ),
1118 red: (
1119 ScaledFloat::from_scaled(red_x),
1120 ScaledFloat::from_scaled(red_y),
1121 ),
1122 green: (
1123 ScaledFloat::from_scaled(green_x),
1124 ScaledFloat::from_scaled(green_y),
1125 ),
1126 blue: (
1127 ScaledFloat::from_scaled(blue_x),
1128 ScaledFloat::from_scaled(blue_y),
1129 ),
1130 };
1131
1132 info.chrm_chunk = Some(source_chromaticities);
1133 // Ignore chromaticities if sRGB profile is used.
1134 if info.srgb.is_none() {
1135 info.source_chromaticities = Some(source_chromaticities);
1136 }
1137
1138 Ok(Decoded::Nothing)
1139 }
1140 }
1141
1142 fn parse_gama(&mut self) -> Result<Decoded, DecodingError> {
1143 let info = self.info.as_mut().unwrap();
1144 if self.have_idat {
1145 Err(DecodingError::Format(
1146 FormatErrorInner::AfterIdat { kind: chunk::gAMA }.into(),
1147 ))
1148 } else if info.gama_chunk.is_some() {
1149 Err(DecodingError::Format(
1150 FormatErrorInner::DuplicateChunk { kind: chunk::gAMA }.into(),
1151 ))
1152 } else {
1153 let mut buf = &self.current_chunk.raw_bytes[..];
1154 let source_gamma: u32 = buf.read_be()?;
1155 let source_gamma = ScaledFloat::from_scaled(source_gamma);
1156
1157 info.gama_chunk = Some(source_gamma);
1158 // Ignore chromaticities if sRGB profile is used.
1159 if info.srgb.is_none() {
1160 info.source_gamma = Some(source_gamma);
1161 }
1162
1163 Ok(Decoded::Nothing)
1164 }
1165 }
1166
1167 fn parse_srgb(&mut self) -> Result<Decoded, DecodingError> {
1168 let info = self.info.as_mut().unwrap();
1169 if self.have_idat {
1170 Err(DecodingError::Format(
1171 FormatErrorInner::AfterIdat { kind: chunk::acTL }.into(),
1172 ))
1173 } else if info.srgb.is_some() {
1174 Err(DecodingError::Format(
1175 FormatErrorInner::DuplicateChunk { kind: chunk::sRGB }.into(),
1176 ))
1177 } else {
1178 let mut buf = &self.current_chunk.raw_bytes[..];
1179 let raw: u8 = buf.read_be()?; // BE is is nonsense for single bytes, but this way the size is checked.
1180 let rendering_intent = crate::SrgbRenderingIntent::from_raw(raw).ok_or_else(|| {
1181 FormatError::from(FormatErrorInner::InvalidSrgbRenderingIntent(raw))
1182 })?;
1183
1184 // Set srgb and override source gamma and chromaticities.
1185 info.srgb = Some(rendering_intent);
1186 info.source_gamma = Some(crate::srgb::substitute_gamma());
1187 info.source_chromaticities = Some(crate::srgb::substitute_chromaticities());
1188 Ok(Decoded::Nothing)
1189 }
1190 }
1191
1192 fn parse_iccp(&mut self) -> Result<Decoded, DecodingError> {
1193 let info = self.info.as_mut().unwrap();
1194 if self.have_idat {
1195 Err(DecodingError::Format(
1196 FormatErrorInner::AfterIdat { kind: chunk::iCCP }.into(),
1197 ))
1198 } else if info.icc_profile.is_some() {
1199 // We have already encountered an iCCP chunk before.
1200 //
1201 // Section "4.2.2.4. iCCP Embedded ICC profile" of the spec says:
1202 // > A file should contain at most one embedded profile,
1203 // > whether explicit like iCCP or implicit like sRGB.
1204 // but
1205 // * This is a "should", not a "must"
1206 // * The spec also says that "All ancillary chunks are optional, in the sense that
1207 // [...] decoders can ignore them."
1208 // * The reference implementation (libpng) ignores the subsequent iCCP chunks
1209 // (treating them as a benign error).
1210 Ok(Decoded::Nothing)
1211 } else {
1212 let mut buf = &self.current_chunk.raw_bytes[..];
1213
1214 // read profile name
1215 let _: u8 = buf.read_be()?;
1216 for _ in 1..80 {
1217 let raw: u8 = buf.read_be()?;
1218 if raw == 0 {
1219 break;
1220 }
1221 }
1222
1223 match buf.read_be()? {
1224 // compression method
1225 0u8 => (),
1226 n => {
1227 return Err(DecodingError::Format(
1228 FormatErrorInner::UnknownCompressionMethod(n).into(),
1229 ))
1230 }
1231 }
1232
1233 match fdeflate::decompress_to_vec_bounded(buf, self.limits.bytes) {
1234 Ok(profile) => {
1235 self.limits.reserve_bytes(profile.len())?;
1236 info.icc_profile = Some(Cow::Owned(profile));
1237 }
1238 Err(fdeflate::BoundedDecompressionError::DecompressionError { inner: err }) => {
1239 return Err(DecodingError::Format(
1240 FormatErrorInner::CorruptFlateStream { err }.into(),
1241 ))
1242 }
1243 Err(fdeflate::BoundedDecompressionError::OutputTooLarge { .. }) => {
1244 return Err(DecodingError::LimitsExceeded);
1245 }
1246 }
1247
1248 Ok(Decoded::Nothing)
1249 }
1250 }
1251
1252 fn parse_ihdr(&mut self) -> Result<Decoded, DecodingError> {
1253 if self.info.is_some() {
1254 return Err(DecodingError::Format(
1255 FormatErrorInner::DuplicateChunk { kind: IHDR }.into(),
1256 ));
1257 }
1258 let mut buf = &self.current_chunk.raw_bytes[..];
1259 let width = buf.read_be()?;
1260 let height = buf.read_be()?;
1261 if width == 0 || height == 0 {
1262 return Err(DecodingError::Format(
1263 FormatErrorInner::InvalidDimensions.into(),
1264 ));
1265 }
1266 let bit_depth = buf.read_be()?;
1267 let bit_depth = match BitDepth::from_u8(bit_depth) {
1268 Some(bits) => bits,
1269 None => {
1270 return Err(DecodingError::Format(
1271 FormatErrorInner::InvalidBitDepth(bit_depth).into(),
1272 ))
1273 }
1274 };
1275 let color_type = buf.read_be()?;
1276 let color_type = match ColorType::from_u8(color_type) {
1277 Some(color_type) => {
1278 if color_type.is_combination_invalid(bit_depth) {
1279 return Err(DecodingError::Format(
1280 FormatErrorInner::InvalidColorBitDepth {
1281 color_type,
1282 bit_depth,
1283 }
1284 .into(),
1285 ));
1286 } else {
1287 color_type
1288 }
1289 }
1290 None => {
1291 return Err(DecodingError::Format(
1292 FormatErrorInner::InvalidColorType(color_type).into(),
1293 ))
1294 }
1295 };
1296 match buf.read_be()? {
1297 // compression method
1298 0u8 => (),
1299 n => {
1300 return Err(DecodingError::Format(
1301 FormatErrorInner::UnknownCompressionMethod(n).into(),
1302 ))
1303 }
1304 }
1305 match buf.read_be()? {
1306 // filter method
1307 0u8 => (),
1308 n => {
1309 return Err(DecodingError::Format(
1310 FormatErrorInner::UnknownFilterMethod(n).into(),
1311 ))
1312 }
1313 }
1314 let interlaced = match buf.read_be()? {
1315 0u8 => false,
1316 1 => true,
1317 n => {
1318 return Err(DecodingError::Format(
1319 FormatErrorInner::UnknownInterlaceMethod(n).into(),
1320 ))
1321 }
1322 };
1323
1324 if let Some(mut raw_row_len) = color_type.checked_raw_row_length(bit_depth, width) {
1325 if interlaced {
1326 // This overshoots, but overestimating should be fine.
1327 // TODO: Calculate **exact** IDAT size for interlaced images.
1328 raw_row_len = raw_row_len.saturating_mul(2);
1329 }
1330 self.inflater
1331 .set_max_total_output((height as usize).saturating_mul(raw_row_len));
1332 }
1333
1334 self.info = Some(Info {
1335 width,
1336 height,
1337 bit_depth,
1338 color_type,
1339 interlaced,
1340 ..Default::default()
1341 });
1342
1343 Ok(Decoded::Header(
1344 width, height, bit_depth, color_type, interlaced,
1345 ))
1346 }
1347
1348 fn split_keyword(buf: &[u8]) -> Result<(&[u8], &[u8]), DecodingError> {
1349 let null_byte_index = buf
1350 .iter()
1351 .position(|&b| b == 0)
1352 .ok_or_else(|| DecodingError::from(TextDecodingError::MissingNullSeparator))?;
1353
1354 if null_byte_index == 0 || null_byte_index > 79 {
1355 return Err(DecodingError::from(TextDecodingError::InvalidKeywordSize));
1356 }
1357
1358 Ok((&buf[..null_byte_index], &buf[null_byte_index + 1..]))
1359 }
1360
1361 fn parse_text(&mut self) -> Result<Decoded, DecodingError> {
1362 let buf = &self.current_chunk.raw_bytes[..];
1363 self.limits.reserve_bytes(buf.len())?;
1364
1365 let (keyword_slice, value_slice) = Self::split_keyword(buf)?;
1366
1367 self.info
1368 .as_mut()
1369 .unwrap()
1370 .uncompressed_latin1_text
1371 .push(TEXtChunk::decode(keyword_slice, value_slice).map_err(DecodingError::from)?);
1372
1373 Ok(Decoded::Nothing)
1374 }
1375
1376 fn parse_ztxt(&mut self) -> Result<Decoded, DecodingError> {
1377 let buf = &self.current_chunk.raw_bytes[..];
1378 self.limits.reserve_bytes(buf.len())?;
1379
1380 let (keyword_slice, value_slice) = Self::split_keyword(buf)?;
1381
1382 let compression_method = *value_slice
1383 .first()
1384 .ok_or_else(|| DecodingError::from(TextDecodingError::InvalidCompressionMethod))?;
1385
1386 let text_slice = &value_slice[1..];
1387
1388 self.info.as_mut().unwrap().compressed_latin1_text.push(
1389 ZTXtChunk::decode(keyword_slice, compression_method, text_slice)
1390 .map_err(DecodingError::from)?,
1391 );
1392
1393 Ok(Decoded::Nothing)
1394 }
1395
1396 fn parse_itxt(&mut self) -> Result<Decoded, DecodingError> {
1397 let buf = &self.current_chunk.raw_bytes[..];
1398 self.limits.reserve_bytes(buf.len())?;
1399
1400 let (keyword_slice, value_slice) = Self::split_keyword(buf)?;
1401
1402 let compression_flag = *value_slice
1403 .first()
1404 .ok_or_else(|| DecodingError::from(TextDecodingError::MissingCompressionFlag))?;
1405
1406 let compression_method = *value_slice
1407 .get(1)
1408 .ok_or_else(|| DecodingError::from(TextDecodingError::InvalidCompressionMethod))?;
1409
1410 let second_null_byte_index = value_slice[2..]
1411 .iter()
1412 .position(|&b| b == 0)
1413 .ok_or_else(|| DecodingError::from(TextDecodingError::MissingNullSeparator))?
1414 + 2;
1415
1416 let language_tag_slice = &value_slice[2..second_null_byte_index];
1417
1418 let third_null_byte_index = value_slice[second_null_byte_index + 1..]
1419 .iter()
1420 .position(|&b| b == 0)
1421 .ok_or_else(|| DecodingError::from(TextDecodingError::MissingNullSeparator))?
1422 + (second_null_byte_index + 1);
1423
1424 let translated_keyword_slice =
1425 &value_slice[second_null_byte_index + 1..third_null_byte_index];
1426
1427 let text_slice = &value_slice[third_null_byte_index + 1..];
1428
1429 self.info.as_mut().unwrap().utf8_text.push(
1430 ITXtChunk::decode(
1431 keyword_slice,
1432 compression_flag,
1433 compression_method,
1434 language_tag_slice,
1435 translated_keyword_slice,
1436 text_slice,
1437 )
1438 .map_err(DecodingError::from)?,
1439 );
1440
1441 Ok(Decoded::Nothing)
1442 }
1443}
1444
1445impl Info<'_> {
1446 fn validate(&self, fc: &FrameControl) -> Result<(), DecodingError> {
1447 if fc.width == 0 || fc.height == 0 {
1448 return Err(DecodingError::Format(
1449 FormatErrorInner::InvalidDimensions.into(),
1450 ));
1451 }
1452
1453 // Validate mathematically: fc.width + fc.x_offset <= self.width
1454 let in_x_bounds: bool = Some(fc.width) <= self.width.checked_sub(fc.x_offset);
1455 // Validate mathematically: fc.height + fc.y_offset <= self.height
1456 let in_y_bounds: bool = Some(fc.height) <= self.height.checked_sub(fc.y_offset);
1457
1458 if !in_x_bounds || !in_y_bounds {
1459 return Err(DecodingError::Format(
1460 // TODO: do we want to display the bad bounds?
1461 FormatErrorInner::BadSubFrameBounds {}.into(),
1462 ));
1463 }
1464
1465 Ok(())
1466 }
1467}
1468
1469impl Default for StreamingDecoder {
1470 fn default() -> Self {
1471 Self::new()
1472 }
1473}
1474
1475impl Default for ChunkState {
1476 fn default() -> Self {
1477 ChunkState {
1478 type_: ChunkType([0; 4]),
1479 crc: Crc32::new(),
1480 remaining: 0,
1481 raw_bytes: Vec::with_capacity(CHUNCK_BUFFER_SIZE),
1482 }
1483 }
1484}
1485
1486#[cfg(test)]
1487mod tests {
1488 use super::ScaledFloat;
1489 use super::SourceChromaticities;
1490 use crate::test_utils::*;
1491 use crate::{Decoder, DecodingError};
1492 use byteorder::WriteBytesExt;
1493 use std::fs::File;
1494 use std::io::Write;
1495
1496 #[test]
1497 fn image_gamma() -> Result<(), ()> {
1498 fn trial(path: &str, expected: Option<ScaledFloat>) {
1499 let decoder = crate::Decoder::new(File::open(path).unwrap());
1500 let reader = decoder.read_info().unwrap();
1501 let actual: Option<ScaledFloat> = reader.info().source_gamma;
1502 assert!(actual == expected);
1503 }
1504 trial("tests/pngsuite/f00n0g08.png", None);
1505 trial("tests/pngsuite/f00n2c08.png", None);
1506 trial("tests/pngsuite/f01n0g08.png", None);
1507 trial("tests/pngsuite/f01n2c08.png", None);
1508 trial("tests/pngsuite/f02n0g08.png", None);
1509 trial("tests/pngsuite/f02n2c08.png", None);
1510 trial("tests/pngsuite/f03n0g08.png", None);
1511 trial("tests/pngsuite/f03n2c08.png", None);
1512 trial("tests/pngsuite/f04n0g08.png", None);
1513 trial("tests/pngsuite/f04n2c08.png", None);
1514 trial("tests/pngsuite/f99n0g04.png", None);
1515 trial("tests/pngsuite/tm3n3p02.png", None);
1516 trial("tests/pngsuite/g03n0g16.png", Some(ScaledFloat::new(0.35)));
1517 trial("tests/pngsuite/g03n2c08.png", Some(ScaledFloat::new(0.35)));
1518 trial("tests/pngsuite/g03n3p04.png", Some(ScaledFloat::new(0.35)));
1519 trial("tests/pngsuite/g04n0g16.png", Some(ScaledFloat::new(0.45)));
1520 trial("tests/pngsuite/g04n2c08.png", Some(ScaledFloat::new(0.45)));
1521 trial("tests/pngsuite/g04n3p04.png", Some(ScaledFloat::new(0.45)));
1522 trial("tests/pngsuite/g05n0g16.png", Some(ScaledFloat::new(0.55)));
1523 trial("tests/pngsuite/g05n2c08.png", Some(ScaledFloat::new(0.55)));
1524 trial("tests/pngsuite/g05n3p04.png", Some(ScaledFloat::new(0.55)));
1525 trial("tests/pngsuite/g07n0g16.png", Some(ScaledFloat::new(0.7)));
1526 trial("tests/pngsuite/g07n2c08.png", Some(ScaledFloat::new(0.7)));
1527 trial("tests/pngsuite/g07n3p04.png", Some(ScaledFloat::new(0.7)));
1528 trial("tests/pngsuite/g10n0g16.png", Some(ScaledFloat::new(1.0)));
1529 trial("tests/pngsuite/g10n2c08.png", Some(ScaledFloat::new(1.0)));
1530 trial("tests/pngsuite/g10n3p04.png", Some(ScaledFloat::new(1.0)));
1531 trial("tests/pngsuite/g25n0g16.png", Some(ScaledFloat::new(2.5)));
1532 trial("tests/pngsuite/g25n2c08.png", Some(ScaledFloat::new(2.5)));
1533 trial("tests/pngsuite/g25n3p04.png", Some(ScaledFloat::new(2.5)));
1534 Ok(())
1535 }
1536
1537 #[test]
1538 fn image_source_chromaticities() -> Result<(), ()> {
1539 fn trial(path: &str, expected: Option<SourceChromaticities>) {
1540 let decoder = crate::Decoder::new(File::open(path).unwrap());
1541 let reader = decoder.read_info().unwrap();
1542 let actual: Option<SourceChromaticities> = reader.info().source_chromaticities;
1543 assert!(actual == expected);
1544 }
1545 trial(
1546 "tests/pngsuite/ccwn2c08.png",
1547 Some(SourceChromaticities::new(
1548 (0.3127, 0.3290),
1549 (0.64, 0.33),
1550 (0.30, 0.60),
1551 (0.15, 0.06),
1552 )),
1553 );
1554 trial(
1555 "tests/pngsuite/ccwn3p08.png",
1556 Some(SourceChromaticities::new(
1557 (0.3127, 0.3290),
1558 (0.64, 0.33),
1559 (0.30, 0.60),
1560 (0.15, 0.06),
1561 )),
1562 );
1563 trial("tests/pngsuite/basi0g01.png", None);
1564 trial("tests/pngsuite/basi0g02.png", None);
1565 trial("tests/pngsuite/basi0g04.png", None);
1566 trial("tests/pngsuite/basi0g08.png", None);
1567 trial("tests/pngsuite/basi0g16.png", None);
1568 trial("tests/pngsuite/basi2c08.png", None);
1569 trial("tests/pngsuite/basi2c16.png", None);
1570 trial("tests/pngsuite/basi3p01.png", None);
1571 trial("tests/pngsuite/basi3p02.png", None);
1572 trial("tests/pngsuite/basi3p04.png", None);
1573 trial("tests/pngsuite/basi3p08.png", None);
1574 trial("tests/pngsuite/basi4a08.png", None);
1575 trial("tests/pngsuite/basi4a16.png", None);
1576 trial("tests/pngsuite/basi6a08.png", None);
1577 trial("tests/pngsuite/basi6a16.png", None);
1578 trial("tests/pngsuite/basn0g01.png", None);
1579 trial("tests/pngsuite/basn0g02.png", None);
1580 trial("tests/pngsuite/basn0g04.png", None);
1581 trial("tests/pngsuite/basn0g08.png", None);
1582 trial("tests/pngsuite/basn0g16.png", None);
1583 trial("tests/pngsuite/basn2c08.png", None);
1584 trial("tests/pngsuite/basn2c16.png", None);
1585 trial("tests/pngsuite/basn3p01.png", None);
1586 trial("tests/pngsuite/basn3p02.png", None);
1587 trial("tests/pngsuite/basn3p04.png", None);
1588 trial("tests/pngsuite/basn3p08.png", None);
1589 trial("tests/pngsuite/basn4a08.png", None);
1590 trial("tests/pngsuite/basn4a16.png", None);
1591 trial("tests/pngsuite/basn6a08.png", None);
1592 trial("tests/pngsuite/basn6a16.png", None);
1593 trial("tests/pngsuite/bgai4a08.png", None);
1594 trial("tests/pngsuite/bgai4a16.png", None);
1595 trial("tests/pngsuite/bgan6a08.png", None);
1596 trial("tests/pngsuite/bgan6a16.png", None);
1597 trial("tests/pngsuite/bgbn4a08.png", None);
1598 trial("tests/pngsuite/bggn4a16.png", None);
1599 trial("tests/pngsuite/bgwn6a08.png", None);
1600 trial("tests/pngsuite/bgyn6a16.png", None);
1601 trial("tests/pngsuite/cdfn2c08.png", None);
1602 trial("tests/pngsuite/cdhn2c08.png", None);
1603 trial("tests/pngsuite/cdsn2c08.png", None);
1604 trial("tests/pngsuite/cdun2c08.png", None);
1605 trial("tests/pngsuite/ch1n3p04.png", None);
1606 trial("tests/pngsuite/ch2n3p08.png", None);
1607 trial("tests/pngsuite/cm0n0g04.png", None);
1608 trial("tests/pngsuite/cm7n0g04.png", None);
1609 trial("tests/pngsuite/cm9n0g04.png", None);
1610 trial("tests/pngsuite/cs3n2c16.png", None);
1611 trial("tests/pngsuite/cs3n3p08.png", None);
1612 trial("tests/pngsuite/cs5n2c08.png", None);
1613 trial("tests/pngsuite/cs5n3p08.png", None);
1614 trial("tests/pngsuite/cs8n2c08.png", None);
1615 trial("tests/pngsuite/cs8n3p08.png", None);
1616 trial("tests/pngsuite/ct0n0g04.png", None);
1617 trial("tests/pngsuite/ct1n0g04.png", None);
1618 trial("tests/pngsuite/cten0g04.png", None);
1619 trial("tests/pngsuite/ctfn0g04.png", None);
1620 trial("tests/pngsuite/ctgn0g04.png", None);
1621 trial("tests/pngsuite/cthn0g04.png", None);
1622 trial("tests/pngsuite/ctjn0g04.png", None);
1623 trial("tests/pngsuite/ctzn0g04.png", None);
1624 trial("tests/pngsuite/f00n0g08.png", None);
1625 trial("tests/pngsuite/f00n2c08.png", None);
1626 trial("tests/pngsuite/f01n0g08.png", None);
1627 trial("tests/pngsuite/f01n2c08.png", None);
1628 trial("tests/pngsuite/f02n0g08.png", None);
1629 trial("tests/pngsuite/f02n2c08.png", None);
1630 trial("tests/pngsuite/f03n0g08.png", None);
1631 trial("tests/pngsuite/f03n2c08.png", None);
1632 trial("tests/pngsuite/f04n0g08.png", None);
1633 trial("tests/pngsuite/f04n2c08.png", None);
1634 trial("tests/pngsuite/f99n0g04.png", None);
1635 trial("tests/pngsuite/g03n0g16.png", None);
1636 trial("tests/pngsuite/g03n2c08.png", None);
1637 trial("tests/pngsuite/g03n3p04.png", None);
1638 trial("tests/pngsuite/g04n0g16.png", None);
1639 trial("tests/pngsuite/g04n2c08.png", None);
1640 trial("tests/pngsuite/g04n3p04.png", None);
1641 trial("tests/pngsuite/g05n0g16.png", None);
1642 trial("tests/pngsuite/g05n2c08.png", None);
1643 trial("tests/pngsuite/g05n3p04.png", None);
1644 trial("tests/pngsuite/g07n0g16.png", None);
1645 trial("tests/pngsuite/g07n2c08.png", None);
1646 trial("tests/pngsuite/g07n3p04.png", None);
1647 trial("tests/pngsuite/g10n0g16.png", None);
1648 trial("tests/pngsuite/g10n2c08.png", None);
1649 trial("tests/pngsuite/g10n3p04.png", None);
1650 trial("tests/pngsuite/g25n0g16.png", None);
1651 trial("tests/pngsuite/g25n2c08.png", None);
1652 trial("tests/pngsuite/g25n3p04.png", None);
1653 trial("tests/pngsuite/oi1n0g16.png", None);
1654 trial("tests/pngsuite/oi1n2c16.png", None);
1655 trial("tests/pngsuite/oi2n0g16.png", None);
1656 trial("tests/pngsuite/oi2n2c16.png", None);
1657 trial("tests/pngsuite/oi4n0g16.png", None);
1658 trial("tests/pngsuite/oi4n2c16.png", None);
1659 trial("tests/pngsuite/oi9n0g16.png", None);
1660 trial("tests/pngsuite/oi9n2c16.png", None);
1661 trial("tests/pngsuite/PngSuite.png", None);
1662 trial("tests/pngsuite/pp0n2c16.png", None);
1663 trial("tests/pngsuite/pp0n6a08.png", None);
1664 trial("tests/pngsuite/ps1n0g08.png", None);
1665 trial("tests/pngsuite/ps1n2c16.png", None);
1666 trial("tests/pngsuite/ps2n0g08.png", None);
1667 trial("tests/pngsuite/ps2n2c16.png", None);
1668 trial("tests/pngsuite/s01i3p01.png", None);
1669 trial("tests/pngsuite/s01n3p01.png", None);
1670 trial("tests/pngsuite/s02i3p01.png", None);
1671 trial("tests/pngsuite/s02n3p01.png", None);
1672 trial("tests/pngsuite/s03i3p01.png", None);
1673 trial("tests/pngsuite/s03n3p01.png", None);
1674 trial("tests/pngsuite/s04i3p01.png", None);
1675 trial("tests/pngsuite/s04n3p01.png", None);
1676 trial("tests/pngsuite/s05i3p02.png", None);
1677 trial("tests/pngsuite/s05n3p02.png", None);
1678 trial("tests/pngsuite/s06i3p02.png", None);
1679 trial("tests/pngsuite/s06n3p02.png", None);
1680 trial("tests/pngsuite/s07i3p02.png", None);
1681 trial("tests/pngsuite/s07n3p02.png", None);
1682 trial("tests/pngsuite/s08i3p02.png", None);
1683 trial("tests/pngsuite/s08n3p02.png", None);
1684 trial("tests/pngsuite/s09i3p02.png", None);
1685 trial("tests/pngsuite/s09n3p02.png", None);
1686 trial("tests/pngsuite/s32i3p04.png", None);
1687 trial("tests/pngsuite/s32n3p04.png", None);
1688 trial("tests/pngsuite/s33i3p04.png", None);
1689 trial("tests/pngsuite/s33n3p04.png", None);
1690 trial("tests/pngsuite/s34i3p04.png", None);
1691 trial("tests/pngsuite/s34n3p04.png", None);
1692 trial("tests/pngsuite/s35i3p04.png", None);
1693 trial("tests/pngsuite/s35n3p04.png", None);
1694 trial("tests/pngsuite/s36i3p04.png", None);
1695 trial("tests/pngsuite/s36n3p04.png", None);
1696 trial("tests/pngsuite/s37i3p04.png", None);
1697 trial("tests/pngsuite/s37n3p04.png", None);
1698 trial("tests/pngsuite/s38i3p04.png", None);
1699 trial("tests/pngsuite/s38n3p04.png", None);
1700 trial("tests/pngsuite/s39i3p04.png", None);
1701 trial("tests/pngsuite/s39n3p04.png", None);
1702 trial("tests/pngsuite/s40i3p04.png", None);
1703 trial("tests/pngsuite/s40n3p04.png", None);
1704 trial("tests/pngsuite/tbbn0g04.png", None);
1705 trial("tests/pngsuite/tbbn2c16.png", None);
1706 trial("tests/pngsuite/tbbn3p08.png", None);
1707 trial("tests/pngsuite/tbgn2c16.png", None);
1708 trial("tests/pngsuite/tbgn3p08.png", None);
1709 trial("tests/pngsuite/tbrn2c08.png", None);
1710 trial("tests/pngsuite/tbwn0g16.png", None);
1711 trial("tests/pngsuite/tbwn3p08.png", None);
1712 trial("tests/pngsuite/tbyn3p08.png", None);
1713 trial("tests/pngsuite/tm3n3p02.png", None);
1714 trial("tests/pngsuite/tp0n0g08.png", None);
1715 trial("tests/pngsuite/tp0n2c08.png", None);
1716 trial("tests/pngsuite/tp0n3p08.png", None);
1717 trial("tests/pngsuite/tp1n3p08.png", None);
1718 trial("tests/pngsuite/z00n2c08.png", None);
1719 trial("tests/pngsuite/z03n2c08.png", None);
1720 trial("tests/pngsuite/z06n2c08.png", None);
1721 Ok(())
1722 }
1723
1724 /// Test handling of a PNG file that contains *two* iCCP chunks.
1725 /// This is a regression test for https://github.com/image-rs/image/issues/1825.
1726 #[test]
1727 fn test_two_iccp_chunks() {
1728 // The test file has been taken from
1729 // https://github.com/image-rs/image/issues/1825#issuecomment-1321798639,
1730 // but the 2nd iCCP chunk has been altered manually (see the 2nd comment below for more
1731 // details).
1732 let decoder = crate::Decoder::new(File::open("tests/bugfixes/issue#1825.png").unwrap());
1733 let reader = decoder.read_info().unwrap();
1734 let icc_profile = reader.info().icc_profile.clone().unwrap().into_owned();
1735
1736 // Assert that the contents of the *first* iCCP chunk are returned.
1737 //
1738 // Note that the 2nd chunk in the test file has been manually altered to have a different
1739 // content (`b"test iccp contents"`) which would have a different CRC (797351983).
1740 assert_eq!(4070462061, crc32fast::hash(&icc_profile));
1741 }
1742
1743 /// Writes an acTL chunk.
1744 /// See https://wiki.mozilla.org/APNG_Specification#.60acTL.60:_The_Animation_Control_Chunk
1745 fn write_actl(w: &mut impl Write, animation: &crate::AnimationControl) {
1746 let mut data = Vec::new();
1747 data.write_u32::<byteorder::BigEndian>(animation.num_frames)
1748 .unwrap();
1749 data.write_u32::<byteorder::BigEndian>(animation.num_plays)
1750 .unwrap();
1751 write_chunk(w, b"acTL", &data);
1752 }
1753
1754 /// Writes an fcTL chunk.
1755 /// See https://wiki.mozilla.org/APNG_Specification#.60fcTL.60:_The_Frame_Control_Chunk
1756 fn write_fctl(w: &mut impl Write, frame: &crate::FrameControl) {
1757 let mut data = Vec::new();
1758 data.write_u32::<byteorder::BigEndian>(frame.sequence_number)
1759 .unwrap();
1760 data.write_u32::<byteorder::BigEndian>(frame.width).unwrap();
1761 data.write_u32::<byteorder::BigEndian>(frame.height)
1762 .unwrap();
1763 data.write_u32::<byteorder::BigEndian>(frame.x_offset)
1764 .unwrap();
1765 data.write_u32::<byteorder::BigEndian>(frame.y_offset)
1766 .unwrap();
1767 data.write_u16::<byteorder::BigEndian>(frame.delay_num)
1768 .unwrap();
1769 data.write_u16::<byteorder::BigEndian>(frame.delay_den)
1770 .unwrap();
1771 data.write_u8(frame.dispose_op as u8).unwrap();
1772 data.write_u8(frame.blend_op as u8).unwrap();
1773 write_chunk(w, b"fcTL", &data);
1774 }
1775
1776 /// Writes an fdAT chunk.
1777 /// See https://wiki.mozilla.org/APNG_Specification#.60fdAT.60:_The_Frame_Data_Chunk
1778 fn write_fdat(w: &mut impl Write, sequence_number: u32, image_data: &[u8]) {
1779 let mut data = Vec::new();
1780 data.write_u32::<byteorder::BigEndian>(sequence_number)
1781 .unwrap();
1782 data.write_all(image_data).unwrap();
1783 write_chunk(w, b"fdAT", &data);
1784 }
1785
1786 /// Writes PNG signature and chunks that can precede an fdAT chunk that is expected
1787 /// to have
1788 /// - `sequence_number` set to 0
1789 /// - image data with rgba8 pixels in a `width` by `width` image
1790 fn write_fdat_prefix(w: &mut impl Write, num_frames: u32, width: u32) {
1791 write_png_sig(w);
1792 write_rgba8_ihdr_with_width(w, width);
1793 write_actl(
1794 w,
1795 &crate::AnimationControl {
1796 num_frames,
1797 num_plays: 0,
1798 },
1799 );
1800
1801 let mut fctl = crate::FrameControl {
1802 width,
1803 height: width,
1804 ..Default::default()
1805 };
1806 write_fctl(w, &fctl);
1807 write_rgba8_idats(w, width, 0x7fffffff);
1808
1809 fctl.sequence_number += 1;
1810 write_fctl(w, &fctl);
1811 }
1812
1813 #[test]
1814 fn test_fdat_chunk_payload_length_0() {
1815 let mut png = Vec::new();
1816 write_fdat_prefix(&mut png, 2, 8);
1817 write_chunk(&mut png, b"fdAT", &[]);
1818
1819 let decoder = Decoder::new(png.as_slice());
1820 let mut reader = decoder.read_info().unwrap();
1821 let mut buf = vec![0; reader.output_buffer_size()];
1822 reader.next_frame(&mut buf).unwrap();
1823
1824 // 0-length fdAT should result in an error.
1825 let err = reader.next_frame(&mut buf).unwrap_err();
1826 assert!(matches!(&err, DecodingError::Format(_)));
1827 let msg = format!("{err}");
1828 assert_eq!("fdAT chunk shorter than 4 bytes", msg);
1829 }
1830
1831 #[test]
1832 fn test_fdat_chunk_payload_length_3() {
1833 let mut png = Vec::new();
1834 write_fdat_prefix(&mut png, 2, 8);
1835 write_chunk(&mut png, b"fdAT", &[1, 0, 0]);
1836
1837 let decoder = Decoder::new(png.as_slice());
1838 let mut reader = decoder.read_info().unwrap();
1839 let mut buf = vec![0; reader.output_buffer_size()];
1840 reader.next_frame(&mut buf).unwrap();
1841
1842 // 3-bytes-long fdAT should result in an error.
1843 let err = reader.next_frame(&mut buf).unwrap_err();
1844 assert!(matches!(&err, DecodingError::Format(_)));
1845 let msg = format!("{err}");
1846 assert_eq!("fdAT chunk shorter than 4 bytes", msg);
1847 }
1848
1849 #[test]
1850 fn test_frame_split_across_two_fdat_chunks() {
1851 // Generate test data where the 2nd animation frame is split across 2 fdAT chunks.
1852 //
1853 // This is similar to the example given in
1854 // https://wiki.mozilla.org/APNG_Specification#Chunk_Sequence_Numbers:
1855 //
1856 // ```
1857 // Sequence number Chunk
1858 // (none) `acTL`
1859 // 0 `fcTL` first frame
1860 // (none) `IDAT` first frame / default image
1861 // 1 `fcTL` second frame
1862 // 2 first `fdAT` for second frame
1863 // 3 second `fdAT` for second frame
1864 // ```
1865 let png = {
1866 let mut png = Vec::new();
1867 write_fdat_prefix(&mut png, 2, 8);
1868 let image_data = generate_rgba8_with_width_and_height(8, 8);
1869 write_fdat(&mut png, 2, &image_data[..30]);
1870 write_fdat(&mut png, 3, &image_data[30..]);
1871 write_iend(&mut png);
1872 png
1873 };
1874
1875 // Start decoding.
1876 let decoder = Decoder::new(png.as_slice());
1877 let mut reader = decoder.read_info().unwrap();
1878 let mut buf = vec![0; reader.output_buffer_size()];
1879 let Some(animation_control) = reader.info().animation_control else {
1880 panic!("No acTL");
1881 };
1882 assert_eq!(animation_control.num_frames, 2);
1883
1884 // Process the 1st animation frame.
1885 let first_frame: Vec<u8>;
1886 {
1887 reader.next_frame(&mut buf).unwrap();
1888 first_frame = buf.clone();
1889
1890 // Note that the doc comment of `Reader::next_frame` says that "[...]
1891 // can be checked afterwards by calling `info` **after** a successful call and
1892 // inspecting the `frame_control` data.". (Note the **emphasis** on "after".)
1893 let Some(frame_control) = reader.info().frame_control else {
1894 panic!("No fcTL (1st frame)");
1895 };
1896 // The sequence number is taken from the `fcTL` chunk that comes before the `IDAT`
1897 // chunk.
1898 assert_eq!(frame_control.sequence_number, 0);
1899 }
1900
1901 // Process the 2nd animation frame.
1902 let second_frame: Vec<u8>;
1903 {
1904 reader.next_frame(&mut buf).unwrap();
1905 second_frame = buf.clone();
1906
1907 // Same as above - updated `frame_control` is available *after* the `next_frame` call.
1908 let Some(frame_control) = reader.info().frame_control else {
1909 panic!("No fcTL (2nd frame)");
1910 };
1911 // The sequence number is taken from the `fcTL` chunk that comes before the two `fdAT`
1912 // chunks. Note that sequence numbers inside `fdAT` chunks are not publically exposed
1913 // (but they are still checked when decoding to verify that they are sequential).
1914 assert_eq!(frame_control.sequence_number, 1);
1915 }
1916
1917 assert_eq!(first_frame, second_frame);
1918 }
1919
1920 #[test]
1921 fn test_idat_bigger_than_image_size_from_ihdr() {
1922 let png = {
1923 let mut png = Vec::new();
1924 write_png_sig(&mut png);
1925 write_rgba8_ihdr_with_width(&mut png, 8);
1926
1927 // Here we want to test an invalid image where the `IDAT` chunk contains more data
1928 // (data for 8x256 image) than declared in the `IHDR` chunk (which only describes an
1929 // 8x8 image).
1930 write_chunk(
1931 &mut png,
1932 b"IDAT",
1933 &generate_rgba8_with_width_and_height(8, 256),
1934 );
1935
1936 write_iend(&mut png);
1937 png
1938 };
1939 let decoder = Decoder::new(png.as_slice());
1940 let mut reader = decoder.read_info().unwrap();
1941 let mut buf = vec![0; reader.output_buffer_size()];
1942
1943 // TODO: Should this return an error instead? For now let's just have test assertions for
1944 // the current behavior.
1945 reader.next_frame(&mut buf).unwrap();
1946 assert_eq!(3093270825, crc32fast::hash(&buf));
1947 }
1948}
1949