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