diff --git a/src/decoder/read_decoder.rs b/src/decoder/read_decoder.rs index 316a8522..0c2fe729 100644 --- a/src/decoder/read_decoder.rs +++ b/src/decoder/read_decoder.rs @@ -119,6 +119,7 @@ impl ReadDecoder { _ => {} } } + self.decoder.set_skip_frame_decoding(false); Ok(()) } @@ -148,6 +149,7 @@ impl ReadDecoder { /// /// Prerequisite: Input is currently positioned within `IDAT` / `fdAT` chunk sequence. pub fn finish_decoding_image_data(&mut self) -> Result<(), DecodingError> { + self.decoder.set_skip_frame_decoding(true); loop { let mut to_be_discarded = vec![]; if let ImageDataCompletionStatus::Done = self.decode_image_data(&mut to_be_discarded)? { diff --git a/src/decoder/stream.rs b/src/decoder/stream.rs index 68de12d7..6b22e346 100644 --- a/src/decoder/stream.rs +++ b/src/decoder/stream.rs @@ -428,6 +428,7 @@ pub struct DecodeOptions { ignore_text_chunk: bool, ignore_iccp_chunk: bool, skip_ancillary_crc_failures: bool, + skip_frame_decoding: bool, } impl Default for DecodeOptions { @@ -438,6 +439,7 @@ impl Default for DecodeOptions { ignore_text_chunk: false, ignore_iccp_chunk: false, skip_ancillary_crc_failures: true, + skip_frame_decoding: false, } } } @@ -615,6 +617,10 @@ impl StreamingDecoder { .set_skip_ancillary_crc_failures(skip_ancillary_crc_failures) } + pub(crate) fn set_skip_frame_decoding(&mut self, skip_frame_decoding: bool) { + self.decode_options.skip_frame_decoding = skip_frame_decoding; + } + /// Low level StreamingDecoder interface. /// /// Allows to stream partial data to the encoder. Returns a tuple containing the bytes that have @@ -752,7 +758,17 @@ impl StreamingDecoder { debug_assert!(type_str == IDAT || type_str == chunk::fdAT); let len = std::cmp::min(buf.len(), self.current_chunk.remaining as usize); let buf = &buf[..len]; - let consumed = self.inflater.decompress(buf, image_data)?; + let consumed = if self.decode_options.skip_frame_decoding { + // `inflater.reset()` is not strictly necessary. We do it + // anyway to ensure that if (unexpectedly) + // `skip_frame_decoding` changes before the end of this + // frame, then it will (most likely) lead to decompression + // errors later (when restarting again from the middle). + self.inflater.reset(); + len + } else { + self.inflater.decompress(buf, image_data)? + }; self.current_chunk.crc.update(&buf[..consumed]); self.current_chunk.remaining -= consumed as u32; if self.current_chunk.remaining == 0 { @@ -811,7 +827,9 @@ impl StreamingDecoder { && (self.current_chunk.type_ == IDAT || self.current_chunk.type_ == chunk::fdAT) { self.current_chunk.type_ = type_str; - self.inflater.finish_compressed_chunks(image_data)?; + if !self.decode_options.skip_frame_decoding { + self.inflater.finish_compressed_chunks(image_data)?; + } self.inflater.reset(); self.ready_for_idat_chunks = false; self.ready_for_fdat_chunks = false;