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