diff options
Diffstat (limited to 'vendor/png/src/decoder')
-rw-r--r-- | vendor/png/src/decoder/mod.rs | 961 | ||||
-rw-r--r-- | vendor/png/src/decoder/stream.rs | 1576 | ||||
-rw-r--r-- | vendor/png/src/decoder/zlib.rs | 212 |
3 files changed, 0 insertions, 2749 deletions
diff --git a/vendor/png/src/decoder/mod.rs b/vendor/png/src/decoder/mod.rs deleted file mode 100644 index 09772fe..0000000 --- a/vendor/png/src/decoder/mod.rs +++ /dev/null @@ -1,961 +0,0 @@ -mod stream; -mod zlib; - -pub use self::stream::{DecodeOptions, Decoded, DecodingError, StreamingDecoder}; -use self::stream::{FormatErrorInner, CHUNCK_BUFFER_SIZE}; - -use std::io::{BufRead, BufReader, Read}; -use std::mem; -use std::ops::Range; - -use crate::chunk; -use crate::common::{ - BitDepth, BytesPerPixel, ColorType, Info, ParameterErrorKind, Transformations, -}; -use crate::filter::{unfilter, FilterType}; -use crate::utils; - -/* -pub enum InterlaceHandling { - /// Outputs the raw rows - RawRows, - /// Fill missing the pixels from the existing ones - Rectangle, - /// Only fill the needed pixels - Sparkle -} -*/ - -/// Output info. -/// -/// This describes one particular frame of the image that was written into the output buffer. -#[derive(Debug, PartialEq, Eq)] -pub struct OutputInfo { - /// The pixel width of this frame. - pub width: u32, - /// The pixel height of this frame. - pub height: u32, - /// The chosen output color type. - pub color_type: ColorType, - /// The chosen output bit depth. - pub bit_depth: BitDepth, - /// The byte count of each scan line in the image. - pub line_size: usize, -} - -impl OutputInfo { - /// Returns the size needed to hold a decoded frame - /// If the output buffer was larger then bytes after this count should be ignored. They may - /// still have been changed. - pub fn buffer_size(&self) -> usize { - self.line_size * self.height as usize - } -} - -#[derive(Clone, Copy, Debug)] -/// Limits on the resources the `Decoder` is allowed too use -pub struct Limits { - /// maximum number of bytes the decoder is allowed to allocate, default is 64Mib - pub bytes: usize, -} - -impl Default for Limits { - fn default() -> Limits { - Limits { - bytes: 1024 * 1024 * 64, - } - } -} - -/// PNG Decoder -pub struct Decoder<R: Read> { - read_decoder: ReadDecoder<R>, - /// Output transformations - transform: Transformations, - /// Limits on resources the Decoder is allowed to use - limits: Limits, -} - -/// A row of data with interlace information attached. -#[derive(Clone, Copy, Debug)] -pub struct InterlacedRow<'data> { - data: &'data [u8], - interlace: InterlaceInfo, -} - -impl<'data> InterlacedRow<'data> { - pub fn data(&self) -> &'data [u8] { - self.data - } - - pub fn interlace(&self) -> InterlaceInfo { - self.interlace - } -} - -/// PNG (2003) specifies two interlace modes, but reserves future extensions. -#[derive(Clone, Copy, Debug)] -pub enum InterlaceInfo { - /// the null method means no interlacing - Null, - /// Adam7 derives its name from doing 7 passes over the image, only decoding a subset of all pixels in each pass. - /// The following table shows pictorially what parts of each 8x8 area of the image is found in each pass: - /// - /// 1 6 4 6 2 6 4 6 - /// 7 7 7 7 7 7 7 7 - /// 5 6 5 6 5 6 5 6 - /// 7 7 7 7 7 7 7 7 - /// 3 6 4 6 3 6 4 6 - /// 7 7 7 7 7 7 7 7 - /// 5 6 5 6 5 6 5 6 - /// 7 7 7 7 7 7 7 7 - Adam7 { pass: u8, line: u32, width: u32 }, -} - -/// A row of data without interlace information. -#[derive(Clone, Copy, Debug)] -pub struct Row<'data> { - data: &'data [u8], -} - -impl<'data> Row<'data> { - pub fn data(&self) -> &'data [u8] { - self.data - } -} - -impl<R: Read> Decoder<R> { - /// Create a new decoder configuration with default limits. - pub fn new(r: R) -> Decoder<R> { - Decoder::new_with_limits(r, Limits::default()) - } - - /// Create a new decoder configuration with custom limits. - pub fn new_with_limits(r: R, limits: Limits) -> Decoder<R> { - Decoder { - read_decoder: ReadDecoder { - reader: BufReader::with_capacity(CHUNCK_BUFFER_SIZE, r), - decoder: StreamingDecoder::new(), - at_eof: false, - }, - transform: Transformations::IDENTITY, - limits, - } - } - - /// Create a new decoder configuration with custom `DecodeOptions`. - pub fn new_with_options(r: R, decode_options: DecodeOptions) -> Decoder<R> { - Decoder { - read_decoder: ReadDecoder { - reader: BufReader::with_capacity(CHUNCK_BUFFER_SIZE, r), - decoder: StreamingDecoder::new_with_options(decode_options), - at_eof: false, - }, - transform: Transformations::IDENTITY, - limits: Limits::default(), - } - } - - /// Limit resource usage. - /// - /// Note that your allocations, e.g. when reading into a pre-allocated buffer, are __NOT__ - /// considered part of the limits. Nevertheless, required intermediate buffers such as for - /// singular lines is checked against the limit. - /// - /// Note that this is a best-effort basis. - /// - /// ``` - /// use std::fs::File; - /// use png::{Decoder, Limits}; - /// // This image is 32×32, 1bit per pixel. The reader buffers one row which requires 4 bytes. - /// let mut limits = Limits::default(); - /// limits.bytes = 3; - /// let mut decoder = Decoder::new_with_limits(File::open("tests/pngsuite/basi0g01.png").unwrap(), limits); - /// assert!(decoder.read_info().is_err()); - /// - /// // This image is 32x32 pixels, so the decoder will allocate less than 10Kib - /// let mut limits = Limits::default(); - /// limits.bytes = 10*1024; - /// let mut decoder = Decoder::new_with_limits(File::open("tests/pngsuite/basi0g01.png").unwrap(), limits); - /// assert!(decoder.read_info().is_ok()); - /// ``` - pub fn set_limits(&mut self, limits: Limits) { - self.limits = limits; - } - - /// Read the PNG header and return the information contained within. - /// - /// Most image metadata will not be read until `read_info` is called, so those fields will be - /// None or empty. - pub fn read_header_info(&mut self) -> Result<&Info, DecodingError> { - let mut buf = Vec::new(); - while self.read_decoder.info().is_none() { - buf.clear(); - if self.read_decoder.decode_next(&mut buf)?.is_none() { - return Err(DecodingError::Format( - FormatErrorInner::UnexpectedEof.into(), - )); - } - } - Ok(self.read_decoder.info().unwrap()) - } - - /// Reads all meta data until the first IDAT chunk - pub fn read_info(mut self) -> Result<Reader<R>, DecodingError> { - self.read_header_info()?; - - let mut reader = Reader { - decoder: self.read_decoder, - bpp: BytesPerPixel::One, - subframe: SubframeInfo::not_yet_init(), - fctl_read: 0, - next_frame: SubframeIdx::Initial, - prev: Vec::new(), - current: Vec::new(), - scan_start: 0, - transform: self.transform, - scratch_buffer: Vec::new(), - limits: self.limits, - }; - - // Check if the decoding buffer of a single raw line has a valid size. - if reader.info().checked_raw_row_length().is_none() { - return Err(DecodingError::LimitsExceeded); - } - - // Check if the output buffer has a valid size. - let (width, height) = reader.info().size(); - let (color, depth) = reader.output_color_type(); - let rowlen = color - .checked_raw_row_length(depth, width) - .ok_or(DecodingError::LimitsExceeded)? - - 1; - let height: usize = - std::convert::TryFrom::try_from(height).map_err(|_| DecodingError::LimitsExceeded)?; - if rowlen.checked_mul(height).is_none() { - return Err(DecodingError::LimitsExceeded); - } - - reader.read_until_image_data()?; - Ok(reader) - } - - /// Set the allowed and performed transformations. - /// - /// A transformation is a pre-processing on the raw image data modifying content or encoding. - /// Many options have an impact on memory or CPU usage during decoding. - pub fn set_transformations(&mut self, transform: Transformations) { - self.transform = transform; - } - - /// Set the decoder to ignore all text chunks while parsing. - /// - /// eg. - /// ``` - /// use std::fs::File; - /// use png::Decoder; - /// let mut decoder = Decoder::new(File::open("tests/pngsuite/basi0g01.png").unwrap()); - /// decoder.set_ignore_text_chunk(true); - /// assert!(decoder.read_info().is_ok()); - /// ``` - pub fn set_ignore_text_chunk(&mut self, ignore_text_chunk: bool) { - self.read_decoder - .decoder - .set_ignore_text_chunk(ignore_text_chunk); - } - - /// Set the decoder to ignore and not verify the Adler-32 checksum - /// and CRC code. - pub fn ignore_checksums(&mut self, ignore_checksums: bool) { - self.read_decoder - .decoder - .set_ignore_adler32(ignore_checksums); - self.read_decoder.decoder.set_ignore_crc(ignore_checksums); - } -} - -struct ReadDecoder<R: Read> { - reader: BufReader<R>, - decoder: StreamingDecoder, - at_eof: bool, -} - -impl<R: Read> ReadDecoder<R> { - /// Returns the next decoded chunk. If the chunk is an ImageData chunk, its contents are written - /// into image_data. - fn decode_next(&mut self, image_data: &mut Vec<u8>) -> Result<Option<Decoded>, DecodingError> { - while !self.at_eof { - let (consumed, result) = { - let buf = self.reader.fill_buf()?; - if buf.is_empty() { - return Err(DecodingError::Format( - FormatErrorInner::UnexpectedEof.into(), - )); - } - self.decoder.update(buf, image_data)? - }; - self.reader.consume(consumed); - match result { - Decoded::Nothing => (), - Decoded::ImageEnd => self.at_eof = true, - result => return Ok(Some(result)), - } - } - Ok(None) - } - - fn finish_decoding(&mut self) -> Result<(), DecodingError> { - while !self.at_eof { - let buf = self.reader.fill_buf()?; - if buf.is_empty() { - return Err(DecodingError::Format( - FormatErrorInner::UnexpectedEof.into(), - )); - } - let (consumed, event) = self.decoder.update(buf, &mut vec![])?; - self.reader.consume(consumed); - match event { - Decoded::Nothing => (), - Decoded::ImageEnd => self.at_eof = true, - // ignore more data - Decoded::ChunkComplete(_, _) | Decoded::ChunkBegin(_, _) | Decoded::ImageData => {} - Decoded::ImageDataFlushed => return Ok(()), - Decoded::PartialChunk(_) => {} - new => unreachable!("{:?}", new), - } - } - - Err(DecodingError::Format( - FormatErrorInner::UnexpectedEof.into(), - )) - } - - fn info(&self) -> Option<&Info> { - self.decoder.info.as_ref() - } -} - -/// PNG reader (mostly high-level interface) -/// -/// Provides a high level that iterates over lines or whole images. -pub struct Reader<R: Read> { - decoder: ReadDecoder<R>, - bpp: BytesPerPixel, - subframe: SubframeInfo, - /// Number of frame control chunks read. - /// By the APNG specification the total number must equal the count specified in the animation - /// control chunk. The IDAT image _may_ have such a chunk applying to it. - fctl_read: u32, - next_frame: SubframeIdx, - /// Previous raw line - prev: Vec<u8>, - /// Current raw line - current: Vec<u8>, - /// Start index of the current scan line. - scan_start: usize, - /// Output transformations - transform: Transformations, - /// This buffer is only used so that `next_row` and `next_interlaced_row` can return reference - /// to a byte slice. In a future version of this library, this buffer will be removed and - /// `next_row` and `next_interlaced_row` will write directly into a user provided output buffer. - scratch_buffer: Vec<u8>, - /// How resources we can spend (for example, on allocation). - limits: Limits, -} - -/// The subframe specific information. -/// -/// In APNG the frames are constructed by combining previous frame and a new subframe (through a -/// combination of `dispose_op` and `overlay_op`). These sub frames specify individual dimension -/// information and reuse the global interlace options. This struct encapsulates the state of where -/// in a particular IDAT-frame or subframe we are. -struct SubframeInfo { - width: u32, - height: u32, - rowlen: usize, - interlace: InterlaceIter, - consumed_and_flushed: bool, -} - -#[derive(Clone)] -enum InterlaceIter { - None(Range<u32>), - Adam7(utils::Adam7Iterator), -} - -/// Denote a frame as given by sequence numbers. -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -enum SubframeIdx { - /// The initial frame in an IDAT chunk without fcTL chunk applying to it. - /// Note that this variant precedes `Some` as IDAT frames precede fdAT frames and all fdAT - /// frames must have a fcTL applying to it. - Initial, - /// An IDAT frame with fcTL or an fdAT frame. - Some(u32), - /// The past-the-end index. - End, -} - -impl<R: Read> Reader<R> { - /// Reads all meta data until the next frame data starts. - /// Requires IHDR before the IDAT and fcTL before fdAT. - fn read_until_image_data(&mut self) -> Result<(), DecodingError> { - loop { - // This is somewhat ugly. The API requires us to pass a buffer to decode_next but we - // know that we will stop before reading any image data from the stream. Thus pass an - // empty buffer and assert that remains empty. - let mut buf = Vec::new(); - let state = self.decoder.decode_next(&mut buf)?; - assert!(buf.is_empty()); - - match state { - Some(Decoded::ChunkBegin(_, chunk::IDAT)) - | Some(Decoded::ChunkBegin(_, chunk::fdAT)) => break, - Some(Decoded::FrameControl(_)) => { - self.subframe = SubframeInfo::new(self.info()); - // The next frame is the one to which this chunk applies. - self.next_frame = SubframeIdx::Some(self.fctl_read); - // TODO: what about overflow here? That would imply there are more fctl chunks - // than can be specified in the animation control but also that we have read - // several gigabytes of data. - self.fctl_read += 1; - } - None => { - return Err(DecodingError::Format( - FormatErrorInner::MissingImageData.into(), - )) - } - // Ignore all other chunk events. Any other chunk may be between IDAT chunks, fdAT - // chunks and their control chunks. - _ => {} - } - } - - let info = self - .decoder - .info() - .ok_or(DecodingError::Format(FormatErrorInner::MissingIhdr.into()))?; - self.bpp = info.bpp_in_prediction(); - self.subframe = SubframeInfo::new(info); - - // Allocate output buffer. - let buflen = self.output_line_size(self.subframe.width); - if buflen > self.limits.bytes { - return Err(DecodingError::LimitsExceeded); - } - - self.prev.clear(); - self.prev.resize(self.subframe.rowlen, 0); - - Ok(()) - } - - /// Get information on the image. - /// - /// The structure will change as new frames of an animated image are decoded. - pub fn info(&self) -> &Info { - self.decoder.info().unwrap() - } - - /// Decodes the next frame into `buf`. - /// - /// Note that this decodes raw subframes that need to be mixed according to blend-op and - /// dispose-op by the caller. - /// - /// The caller must always provide a buffer large enough to hold a complete frame (the APNG - /// specification restricts subframes to the dimensions given in the image header). The region - /// that has been written be checked afterwards by calling `info` after a successful call and - /// inspecting the `frame_control` data. This requirement may be lifted in a later version of - /// `png`. - /// - /// Output lines will be written in row-major, packed matrix with width and height of the read - /// frame (or subframe), all samples are in big endian byte order where this matters. - pub fn next_frame(&mut self, buf: &mut [u8]) -> Result<OutputInfo, DecodingError> { - let subframe_idx = match self.decoder.info().unwrap().frame_control() { - None => SubframeIdx::Initial, - Some(_) => SubframeIdx::Some(self.fctl_read - 1), - }; - - if self.next_frame == SubframeIdx::End { - return Err(DecodingError::Parameter( - ParameterErrorKind::PolledAfterEndOfImage.into(), - )); - } else if self.next_frame != subframe_idx { - // Advance until we've read the info / fcTL for this frame. - self.read_until_image_data()?; - } - - if buf.len() < self.output_buffer_size() { - return Err(DecodingError::Parameter( - ParameterErrorKind::ImageBufferSize { - expected: buf.len(), - actual: self.output_buffer_size(), - } - .into(), - )); - } - - let (color_type, bit_depth) = self.output_color_type(); - let output_info = OutputInfo { - width: self.subframe.width, - height: self.subframe.height, - color_type, - bit_depth, - line_size: self.output_line_size(self.subframe.width), - }; - - self.current.clear(); - self.scan_start = 0; - let width = self.info().width; - if self.info().interlaced { - while let Some(InterlacedRow { - data: row, - interlace, - .. - }) = self.next_interlaced_row()? - { - let (line, pass) = match interlace { - InterlaceInfo::Adam7 { line, pass, .. } => (line, pass), - InterlaceInfo::Null => unreachable!("expected interlace information"), - }; - let samples = color_type.samples() as u8; - utils::expand_pass(buf, width, row, pass, line, samples * (bit_depth as u8)); - } - } else { - for row in buf - .chunks_exact_mut(output_info.line_size) - .take(self.subframe.height as usize) - { - self.next_interlaced_row_impl(self.subframe.rowlen, row)?; - } - } - - // Advance over the rest of data for this (sub-)frame. - if !self.subframe.consumed_and_flushed { - self.decoder.finish_decoding()?; - } - - // Advance our state to expect the next frame. - let past_end_subframe = self - .info() - .animation_control() - .map(|ac| ac.num_frames) - .unwrap_or(0); - self.next_frame = match self.next_frame { - SubframeIdx::End => unreachable!("Next frame called when already at image end"), - // Reached the end of non-animated image. - SubframeIdx::Initial if past_end_subframe == 0 => SubframeIdx::End, - // An animated image, expecting first subframe. - SubframeIdx::Initial => SubframeIdx::Some(0), - // This was the last subframe, slightly fuzzy condition in case of programmer error. - SubframeIdx::Some(idx) if past_end_subframe <= idx + 1 => SubframeIdx::End, - // Expecting next subframe. - SubframeIdx::Some(idx) => SubframeIdx::Some(idx + 1), - }; - - Ok(output_info) - } - - /// Returns the next processed row of the image - pub fn next_row(&mut self) -> Result<Option<Row>, DecodingError> { - self.next_interlaced_row() - .map(|v| v.map(|v| Row { data: v.data })) - } - - /// Returns the next processed row of the image - pub fn next_interlaced_row(&mut self) -> Result<Option<InterlacedRow>, DecodingError> { - let (rowlen, interlace) = match self.next_pass() { - Some((rowlen, interlace)) => (rowlen, interlace), - None => return Ok(None), - }; - - let width = if let InterlaceInfo::Adam7 { width, .. } = interlace { - width - } else { - self.subframe.width - }; - let output_line_size = self.output_line_size(width); - - // TODO: change the interface of `next_interlaced_row` to take an output buffer instead of - // making us return a reference to a buffer that we own. - let mut output_buffer = mem::take(&mut self.scratch_buffer); - output_buffer.resize(output_line_size, 0u8); - let ret = self.next_interlaced_row_impl(rowlen, &mut output_buffer); - self.scratch_buffer = output_buffer; - ret?; - - Ok(Some(InterlacedRow { - data: &self.scratch_buffer[..output_line_size], - interlace, - })) - } - - /// Fetch the next interlaced row and filter it according to our own transformations. - fn next_interlaced_row_impl( - &mut self, - rowlen: usize, - output_buffer: &mut [u8], - ) -> Result<(), DecodingError> { - self.next_raw_interlaced_row(rowlen)?; - let row = &self.prev[1..rowlen]; - - // Apply transformations and write resulting data to buffer. - let (color_type, bit_depth, trns) = { - let info = self.info(); - ( - info.color_type, - info.bit_depth as u8, - info.trns.is_some() || self.transform.contains(Transformations::ALPHA), - ) - }; - let expand = self.transform.contains(Transformations::EXPAND) - || self.transform.contains(Transformations::ALPHA); - let strip16 = bit_depth == 16 && self.transform.contains(Transformations::STRIP_16); - let info = self.decoder.info().unwrap(); - let trns = if trns { - Some(info.trns.as_deref()) - } else { - None - }; - match (color_type, trns) { - (ColorType::Indexed, _) if expand => { - output_buffer[..row.len()].copy_from_slice(row); - expand_paletted(output_buffer, info, trns)?; - } - (ColorType::Grayscale | ColorType::GrayscaleAlpha, _) if bit_depth < 8 && expand => { - output_buffer[..row.len()].copy_from_slice(row); - expand_gray_u8(output_buffer, info, trns) - } - (ColorType::Grayscale | ColorType::Rgb, Some(trns)) if expand => { - let channels = color_type.samples(); - if bit_depth == 8 { - utils::expand_trns_line(row, output_buffer, trns, channels); - } else if strip16 { - utils::expand_trns_and_strip_line16(row, output_buffer, trns, channels); - } else { - assert_eq!(bit_depth, 16); - utils::expand_trns_line16(row, output_buffer, trns, channels); - } - } - ( - ColorType::Grayscale | ColorType::GrayscaleAlpha | ColorType::Rgb | ColorType::Rgba, - _, - ) if strip16 => { - for i in 0..row.len() / 2 { - output_buffer[i] = row[2 * i]; - } - } - _ => output_buffer.copy_from_slice(row), - } - - Ok(()) - } - - /// Returns the color type and the number of bits per sample - /// of the data returned by `Reader::next_row` and Reader::frames`. - pub fn output_color_type(&self) -> (ColorType, BitDepth) { - use crate::common::ColorType::*; - let t = self.transform; - let info = self.info(); - if t == Transformations::IDENTITY { - (info.color_type, info.bit_depth) - } else { - let bits = match info.bit_depth as u8 { - 16 if t.intersects(Transformations::STRIP_16) => 8, - n if n < 8 - && (t.contains(Transformations::EXPAND) - || t.contains(Transformations::ALPHA)) => - { - 8 - } - n => n, - }; - let color_type = - if t.contains(Transformations::EXPAND) || t.contains(Transformations::ALPHA) { - let has_trns = info.trns.is_some() || t.contains(Transformations::ALPHA); - match info.color_type { - Grayscale if has_trns => GrayscaleAlpha, - Rgb if has_trns => Rgba, - Indexed if has_trns => Rgba, - Indexed => Rgb, - ct => ct, - } - } else { - info.color_type - }; - (color_type, BitDepth::from_u8(bits).unwrap()) - } - } - - /// Returns the number of bytes required to hold a deinterlaced image frame - /// that is decoded using the given input transformations. - pub fn output_buffer_size(&self) -> usize { - let (width, height) = self.info().size(); - let size = self.output_line_size(width); - size * height as usize - } - - /// Returns the number of bytes required to hold a deinterlaced row. - pub fn output_line_size(&self, width: u32) -> usize { - let (color, depth) = self.output_color_type(); - color.raw_row_length_from_width(depth, width) - 1 - } - - fn next_pass(&mut self) -> Option<(usize, InterlaceInfo)> { - match self.subframe.interlace { - InterlaceIter::Adam7(ref mut adam7) => { - let last_pass = adam7.current_pass(); - let (pass, line, width) = adam7.next()?; - let rowlen = self.info().raw_row_length_from_width(width); - if last_pass != pass { - self.prev.clear(); - self.prev.resize(rowlen, 0u8); - } - Some((rowlen, InterlaceInfo::Adam7 { pass, line, width })) - } - InterlaceIter::None(ref mut height) => { - let _ = height.next()?; - Some((self.subframe.rowlen, InterlaceInfo::Null)) - } - } - } - - /// Write the next raw interlaced row into `self.prev`. - /// - /// The scanline is filtered against the previous scanline according to the specification. - fn next_raw_interlaced_row(&mut self, rowlen: usize) -> Result<(), DecodingError> { - // Read image data until we have at least one full row (but possibly more than one). - while self.current.len() - self.scan_start < rowlen { - if self.subframe.consumed_and_flushed { - return Err(DecodingError::Format( - FormatErrorInner::NoMoreImageData.into(), - )); - } - - // Clear the current buffer before appending more data. - if self.scan_start > 0 { - self.current.drain(..self.scan_start).for_each(drop); - self.scan_start = 0; - } - - match self.decoder.decode_next(&mut self.current)? { - Some(Decoded::ImageData) => {} - Some(Decoded::ImageDataFlushed) => { - self.subframe.consumed_and_flushed = true; - } - None => { - return Err(DecodingError::Format( - if self.current.is_empty() { - FormatErrorInner::NoMoreImageData - } else { - FormatErrorInner::UnexpectedEndOfChunk - } - .into(), - )); - } - _ => (), - } - } - - // Get a reference to the current row and point scan_start to the next one. - let row = &mut self.current[self.scan_start..]; - self.scan_start += rowlen; - - // Unfilter the row. - let filter = FilterType::from_u8(row[0]).ok_or(DecodingError::Format( - FormatErrorInner::UnknownFilterMethod(row[0]).into(), - ))?; - unfilter(filter, self.bpp, &self.prev[1..rowlen], &mut row[1..rowlen]); - - // Save the current row for the next pass. - self.prev[..rowlen].copy_from_slice(&row[..rowlen]); - - Ok(()) - } -} - -impl SubframeInfo { - fn not_yet_init() -> Self { - SubframeInfo { - width: 0, - height: 0, - rowlen: 0, - interlace: InterlaceIter::None(0..0), - consumed_and_flushed: false, - } - } - - fn new(info: &Info) -> Self { - // The apng fctnl overrides width and height. - // All other data is set by the main info struct. - let (width, height) = if let Some(fc) = info.frame_control { - (fc.width, fc.height) - } else { - (info.width, info.height) - }; - - let interlace = if info.interlaced { - InterlaceIter::Adam7(utils::Adam7Iterator::new(width, height)) - } else { - InterlaceIter::None(0..height) - }; - - SubframeInfo { - width, - height, - rowlen: info.raw_row_length_from_width(width), - interlace, - consumed_and_flushed: false, - } - } -} - -fn expand_paletted( - buffer: &mut [u8], - info: &Info, - trns: Option<Option<&[u8]>>, -) -> Result<(), DecodingError> { - if let Some(palette) = info.palette.as_ref() { - if let BitDepth::Sixteen = info.bit_depth { - // This should have been caught earlier but let's check again. Can't hurt. - Err(DecodingError::Format( - FormatErrorInner::InvalidColorBitDepth { - color_type: ColorType::Indexed, - bit_depth: BitDepth::Sixteen, - } - .into(), - )) - } else { - let black = [0, 0, 0]; - if let Some(trns) = trns { - let trns = trns.unwrap_or(&[]); - // > The tRNS chunk shall not contain more alpha values than there are palette - // entries, but a tRNS chunk may contain fewer values than there are palette - // entries. In this case, the alpha value for all remaining palette entries is - // assumed to be 255. - // - // It seems, accepted reading is to fully *ignore* an invalid tRNS as if it were - // completely empty / all pixels are non-transparent. - let trns = if trns.len() <= palette.len() / 3 { - trns - } else { - &[] - }; - - utils::unpack_bits(buffer, 4, info.bit_depth as u8, |i, chunk| { - let (rgb, a) = ( - palette - .get(3 * i as usize..3 * i as usize + 3) - .unwrap_or(&black), - *trns.get(i as usize).unwrap_or(&0xFF), - ); - chunk[0] = rgb[0]; - chunk[1] = rgb[1]; - chunk[2] = rgb[2]; - chunk[3] = a; - }); - } else { - utils::unpack_bits(buffer, 3, info.bit_depth as u8, |i, chunk| { - let rgb = palette - .get(3 * i as usize..3 * i as usize + 3) - .unwrap_or(&black); - chunk[0] = rgb[0]; - chunk[1] = rgb[1]; - chunk[2] = rgb[2]; - }) - } - Ok(()) - } - } else { - Err(DecodingError::Format( - FormatErrorInner::PaletteRequired.into(), - )) - } -} - -fn expand_gray_u8(buffer: &mut [u8], info: &Info, trns: Option<Option<&[u8]>>) { - let rescale = true; - let scaling_factor = if rescale { - (255) / ((1u16 << info.bit_depth as u8) - 1) as u8 - } else { - 1 - }; - if let Some(trns) = trns { - utils::unpack_bits(buffer, 2, info.bit_depth as u8, |pixel, chunk| { - chunk[1] = if let Some(trns) = trns { - if pixel == trns[0] { - 0 - } else { - 0xFF - } - } else { - 0xFF - }; - chunk[0] = pixel * scaling_factor - }) - } else { - utils::unpack_bits(buffer, 1, info.bit_depth as u8, |val, chunk| { - chunk[0] = val * scaling_factor - }) - } -} - -#[cfg(test)] -mod tests { - use super::Decoder; - use std::io::{BufRead, Read, Result}; - use std::mem::discriminant; - - /// A reader that reads at most `n` bytes. - struct SmalBuf<R: BufRead> { - inner: R, - cap: usize, - } - - impl<R: BufRead> SmalBuf<R> { - fn new(inner: R, cap: usize) -> Self { - SmalBuf { inner, cap } - } - } - - impl<R: BufRead> Read for SmalBuf<R> { - fn read(&mut self, buf: &mut [u8]) -> Result<usize> { - let len = buf.len().min(self.cap); - self.inner.read(&mut buf[..len]) - } - } - - impl<R: BufRead> BufRead for SmalBuf<R> { - fn fill_buf(&mut self) -> Result<&[u8]> { - let buf = self.inner.fill_buf()?; - let len = buf.len().min(self.cap); - Ok(&buf[..len]) - } - - fn consume(&mut self, amt: usize) { - assert!(amt <= self.cap); - self.inner.consume(amt) - } - } - - #[test] - fn no_data_dup_on_finish() { - const IMG: &[u8] = include_bytes!(concat!( - env!("CARGO_MANIFEST_DIR"), - "/tests/bugfixes/x_issue#214.png" - )); - - let mut normal = Decoder::new(IMG).read_info().unwrap(); - - let mut buffer = vec![0; normal.output_buffer_size()]; - let normal = normal.next_frame(&mut buffer).unwrap_err(); - - let smal = Decoder::new(SmalBuf::new(IMG, 1)) - .read_info() - .unwrap() - .next_frame(&mut buffer) - .unwrap_err(); - - assert_eq!(discriminant(&normal), discriminant(&smal)); - } -} diff --git a/vendor/png/src/decoder/stream.rs b/vendor/png/src/decoder/stream.rs deleted file mode 100644 index f5df6e9..0000000 --- a/vendor/png/src/decoder/stream.rs +++ /dev/null @@ -1,1576 +0,0 @@ -extern crate crc32fast; - -use std::convert::From; -use std::default::Default; -use std::error; -use std::fmt; -use std::io; -use std::{borrow::Cow, cmp::min}; - -use crc32fast::Hasher as Crc32; - -use super::zlib::ZlibStream; -use crate::chunk::{self, ChunkType, IDAT, IEND, IHDR}; -use crate::common::{ - AnimationControl, BitDepth, BlendOp, ColorType, DisposeOp, FrameControl, Info, ParameterError, - PixelDimensions, ScaledFloat, SourceChromaticities, Unit, -}; -use crate::text_metadata::{ITXtChunk, TEXtChunk, TextDecodingError, ZTXtChunk}; -use crate::traits::ReadBytesExt; - -/// TODO check if these size are reasonable -pub const CHUNCK_BUFFER_SIZE: usize = 32 * 1024; - -/// Determines if checksum checks should be disabled globally. -/// -/// This is used only in fuzzing. `afl` automatically adds `--cfg fuzzing` to RUSTFLAGS which can -/// be used to detect that build. -const CHECKSUM_DISABLED: bool = cfg!(fuzzing); - -#[derive(Debug)] -enum U32Value { - // CHUNKS - Length, - Type(u32), - Crc(ChunkType), -} - -#[derive(Debug)] -enum State { - Signature(u8, [u8; 7]), - U32Byte3(U32Value, u32), - U32Byte2(U32Value, u32), - U32Byte1(U32Value, u32), - U32(U32Value), - ReadChunk(ChunkType), - PartialChunk(ChunkType), - DecodeData(ChunkType, usize), -} - -#[derive(Debug)] -/// Result of the decoding process -pub enum Decoded { - /// Nothing decoded yet - Nothing, - Header(u32, u32, BitDepth, ColorType, bool), - ChunkBegin(u32, ChunkType), - ChunkComplete(u32, ChunkType), - PixelDimensions(PixelDimensions), - AnimationControl(AnimationControl), - FrameControl(FrameControl), - /// Decoded raw image data. - ImageData, - /// The last of a consecutive chunk of IDAT was done. - /// This is distinct from ChunkComplete which only marks that some IDAT chunk was completed but - /// not that no additional IDAT chunk follows. - ImageDataFlushed, - PartialChunk(ChunkType), - ImageEnd, -} - -/// Any kind of error during PNG decoding. -/// -/// This enumeration provides a very rough analysis on the origin of the failure. That is, each -/// variant corresponds to one kind of actor causing the error. It should not be understood as a -/// direct blame but can inform the search for a root cause or if such a search is required. -#[derive(Debug)] -pub enum DecodingError { - /// An error in IO of the underlying reader. - IoError(io::Error), - /// The input image was not a valid PNG. - /// - /// There isn't a lot that can be done here, except if the program itself was responsible for - /// creating this image then investigate the generator. This is internally implemented with a - /// large Enum. If You are interested in accessing some of the more exact information on the - /// variant then we can discuss in an issue. - Format(FormatError), - /// An interface was used incorrectly. - /// - /// This is used in cases where it's expected that the programmer might trip up and stability - /// could be affected. For example when: - /// - /// * The decoder is polled for more animation frames despite being done (or not being animated - /// in the first place). - /// * The output buffer does not have the required size. - /// - /// As a rough guideline for introducing new variants parts of the requirements are dynamically - /// derived from the (untrusted) input data while the other half is from the caller. In the - /// above cases the number of frames respectively the size is determined by the file while the - /// number of calls - /// - /// If you're an application you might want to signal that a bug report is appreciated. - Parameter(ParameterError), - /// The image would have required exceeding the limits configured with the decoder. - /// - /// Note that Your allocations, e.g. when reading into a pre-allocated buffer, is __NOT__ - /// considered part of the limits. Nevertheless, required intermediate buffers such as for - /// singular lines is checked against the limit. - /// - /// Note that this is a best-effort basis. - LimitsExceeded, -} - -#[derive(Debug)] -pub struct FormatError { - inner: FormatErrorInner, -} - -#[derive(Debug)] -pub(crate) enum FormatErrorInner { - /// Bad framing. - CrcMismatch { - /// Stored CRC32 value - crc_val: u32, - /// Calculated CRC32 sum - crc_sum: u32, - /// The chunk type that has the CRC mismatch. - chunk: ChunkType, - }, - /// Not a PNG, the magic signature is missing. - InvalidSignature, - /// End of file, within a chunk event. - UnexpectedEof, - /// End of file, while expecting more image data. - UnexpectedEndOfChunk, - // Errors of chunk level ordering, missing etc. - /// Ihdr must occur. - MissingIhdr, - /// Fctl must occur if an animated chunk occurs. - MissingFctl, - /// Image data that was indicated in IHDR or acTL is missing. - MissingImageData, - /// 4.3., Must be first. - ChunkBeforeIhdr { - kind: ChunkType, - }, - /// 4.3., some chunks must be before IDAT. - AfterIdat { - kind: ChunkType, - }, - /// 4.3., some chunks must be before PLTE. - AfterPlte { - kind: ChunkType, - }, - /// 4.3., some chunks must be between PLTE and IDAT. - OutsidePlteIdat { - kind: ChunkType, - }, - /// 4.3., some chunks must be unique. - DuplicateChunk { - kind: ChunkType, - }, - /// Specifically for fdat there is an embedded sequence number for chunks. - ApngOrder { - /// The sequence number in the chunk. - present: u32, - /// The one that should have been present. - expected: u32, - }, - // Errors specific to particular chunk data to be validated. - /// The palette did not even contain a single pixel data. - ShortPalette { - expected: usize, - len: usize, - }, - /// A palletized image did not have a palette. - PaletteRequired, - /// The color-depth combination is not valid according to Table 11.1. - InvalidColorBitDepth { - color_type: ColorType, - bit_depth: BitDepth, - }, - ColorWithBadTrns(ColorType), - InvalidBitDepth(u8), - InvalidColorType(u8), - InvalidDisposeOp(u8), - InvalidBlendOp(u8), - InvalidUnit(u8), - /// The rendering intent of the sRGB chunk is invalid. - InvalidSrgbRenderingIntent(u8), - UnknownCompressionMethod(u8), - UnknownFilterMethod(u8), - UnknownInterlaceMethod(u8), - /// The subframe is not in bounds of the image. - /// TODO: fields with relevant data. - BadSubFrameBounds {}, - // Errors specific to the IDAT/fDAT chunks. - /// The compression of the data stream was faulty. - CorruptFlateStream { - err: fdeflate::DecompressionError, - }, - /// The image data chunk was too short for the expected pixel count. - NoMoreImageData, - /// Bad text encoding - BadTextEncoding(TextDecodingError), -} - -impl error::Error for DecodingError { - fn cause(&self) -> Option<&(dyn error::Error + 'static)> { - match self { - DecodingError::IoError(err) => Some(err), - _ => None, - } - } -} - -impl fmt::Display for DecodingError { - fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { - use self::DecodingError::*; - match self { - IoError(err) => write!(fmt, "{}", err), - Parameter(desc) => write!(fmt, "{}", &desc), - Format(desc) => write!(fmt, "{}", desc), - LimitsExceeded => write!(fmt, "limits are exceeded"), - } - } -} - -impl fmt::Display for FormatError { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - use FormatErrorInner::*; - match &self.inner { - CrcMismatch { - crc_val, - crc_sum, - chunk, - .. - } => write!( - fmt, - "CRC error: expected 0x{:x} have 0x{:x} while decoding {:?} chunk.", - crc_val, crc_sum, chunk - ), - MissingIhdr => write!(fmt, "IHDR chunk missing"), - MissingFctl => write!(fmt, "fcTL chunk missing before fdAT chunk."), - MissingImageData => write!(fmt, "IDAT or fDAT chunk is missing."), - ChunkBeforeIhdr { kind } => write!(fmt, "{:?} chunk appeared before IHDR chunk", kind), - AfterIdat { kind } => write!(fmt, "Chunk {:?} is invalid after IDAT chunk.", kind), - AfterPlte { kind } => write!(fmt, "Chunk {:?} is invalid after PLTE chunk.", kind), - OutsidePlteIdat { kind } => write!( - fmt, - "Chunk {:?} must appear between PLTE and IDAT chunks.", - kind - ), - DuplicateChunk { kind } => write!(fmt, "Chunk {:?} must appear at most once.", kind), - ApngOrder { present, expected } => write!( - fmt, - "Sequence is not in order, expected #{} got #{}.", - expected, present, - ), - ShortPalette { expected, len } => write!( - fmt, - "Not enough palette entries, expect {} got {}.", - expected, len - ), - PaletteRequired => write!(fmt, "Missing palette of indexed image."), - InvalidColorBitDepth { - color_type, - bit_depth, - } => write!( - fmt, - "Invalid color/depth combination in header: {:?}/{:?}", - color_type, bit_depth, - ), - ColorWithBadTrns(color_type) => write!( - fmt, - "Transparency chunk found for color type {:?}.", - color_type - ), - InvalidBitDepth(nr) => write!(fmt, "Invalid dispose operation {}.", nr), - InvalidColorType(nr) => write!(fmt, "Invalid color type {}.", nr), - InvalidDisposeOp(nr) => write!(fmt, "Invalid dispose op {}.", nr), - InvalidBlendOp(nr) => write!(fmt, "Invalid blend op {}.", nr), - InvalidUnit(nr) => write!(fmt, "Invalid physical pixel size unit {}.", nr), - InvalidSrgbRenderingIntent(nr) => write!(fmt, "Invalid sRGB rendering intent {}.", nr), - UnknownCompressionMethod(nr) => write!(fmt, "Unknown compression method {}.", nr), - UnknownFilterMethod(nr) => write!(fmt, "Unknown filter method {}.", nr), - UnknownInterlaceMethod(nr) => write!(fmt, "Unknown interlace method {}.", nr), - BadSubFrameBounds {} => write!(fmt, "Sub frame is out-of-bounds."), - InvalidSignature => write!(fmt, "Invalid PNG signature."), - UnexpectedEof => write!(fmt, "Unexpected end of data before image end."), - UnexpectedEndOfChunk => write!(fmt, "Unexpected end of data within a chunk."), - NoMoreImageData => write!(fmt, "IDAT or fDAT chunk is has not enough data for image."), - CorruptFlateStream { err } => { - write!(fmt, "Corrupt deflate stream. ")?; - write!(fmt, "{:?}", err) - } - // TODO: Wrap more info in the enum variant - BadTextEncoding(tde) => { - match tde { - TextDecodingError::Unrepresentable => { - write!(fmt, "Unrepresentable data in tEXt chunk.") - } - TextDecodingError::InvalidKeywordSize => { - write!(fmt, "Keyword empty or longer than 79 bytes.") - } - TextDecodingError::MissingNullSeparator => { - write!(fmt, "No null separator in tEXt chunk.") - } - TextDecodingError::InflationError => { - write!(fmt, "Invalid compressed text data.") - } - TextDecodingError::OutOfDecompressionSpace => { - write!(fmt, "Out of decompression space. Try with a larger limit.") - } - TextDecodingError::InvalidCompressionMethod => { - write!(fmt, "Using an unrecognized byte as compression method.") - } - TextDecodingError::InvalidCompressionFlag => { - write!(fmt, "Using a flag that is not 0 or 255 as a compression flag for iTXt chunk.") - } - TextDecodingError::MissingCompressionFlag => { - write!(fmt, "No compression flag in the iTXt chunk.") - } - } - } - } - } -} - -impl From<io::Error> for DecodingError { - fn from(err: io::Error) -> DecodingError { - DecodingError::IoError(err) - } -} - -impl From<FormatError> for DecodingError { - fn from(err: FormatError) -> DecodingError { - DecodingError::Format(err) - } -} - -impl From<FormatErrorInner> for FormatError { - fn from(inner: FormatErrorInner) -> Self { - FormatError { inner } - } -} - -impl From<DecodingError> for io::Error { - fn from(err: DecodingError) -> io::Error { - match err { - DecodingError::IoError(err) => err, - err => io::Error::new(io::ErrorKind::Other, err.to_string()), - } - } -} - -impl From<TextDecodingError> for DecodingError { - fn from(tbe: TextDecodingError) -> Self { - DecodingError::Format(FormatError { - inner: FormatErrorInner::BadTextEncoding(tbe), - }) - } -} - -/// Decoder configuration options -#[derive(Clone)] -pub struct DecodeOptions { - ignore_adler32: bool, - ignore_crc: bool, - ignore_text_chunk: bool, -} - -impl Default for DecodeOptions { - fn default() -> Self { - Self { - ignore_adler32: true, - ignore_crc: false, - ignore_text_chunk: false, - } - } -} - -impl DecodeOptions { - /// When set, the decoder will not compute and verify the Adler-32 checksum. - /// - /// Defaults to `true`. - pub fn set_ignore_adler32(&mut self, ignore_adler32: bool) { - self.ignore_adler32 = ignore_adler32; - } - - /// When set, the decoder will not compute and verify the CRC code. - /// - /// Defaults to `false`. - pub fn set_ignore_crc(&mut self, ignore_crc: bool) { - self.ignore_crc = ignore_crc; - } - - /// Flag to ignore computing and verifying the Adler-32 checksum and CRC - /// code. - pub fn set_ignore_checksums(&mut self, ignore_checksums: bool) { - self.ignore_adler32 = ignore_checksums; - self.ignore_crc = ignore_checksums; - } - - /// Ignore text chunks while decoding. - /// - /// Defaults to `false`. - pub fn set_ignore_text_chunk(&mut self, ignore_text_chunk: bool) { - self.ignore_text_chunk = ignore_text_chunk; - } -} - -/// PNG StreamingDecoder (low-level interface) -/// -/// By default, the decoder does not verify Adler-32 checksum computation. To -/// enable checksum verification, set it with [`StreamingDecoder::set_ignore_adler32`] -/// before starting decompression. -pub struct StreamingDecoder { - state: Option<State>, - current_chunk: ChunkState, - /// The inflater state handling consecutive `IDAT` and `fdAT` chunks. - inflater: ZlibStream, - /// The complete image info read from all prior chunks. - pub(crate) info: Option<Info<'static>>, - /// The animation chunk sequence number. - current_seq_no: Option<u32>, - /// Stores where in decoding an `fdAT` chunk we are. - apng_seq_handled: bool, - have_idat: bool, - decode_options: DecodeOptions, -} - -struct ChunkState { - /// The type of the current chunk. - /// Relevant for `IDAT` and `fdAT` which aggregate consecutive chunks of their own type. - type_: ChunkType, - - /// Partial crc until now. - crc: Crc32, - - /// Remaining bytes to be read. - remaining: u32, - - /// Non-decoded bytes in the chunk. - raw_bytes: Vec<u8>, -} - -impl StreamingDecoder { - /// Creates a new StreamingDecoder - /// - /// Allocates the internal buffers. - pub fn new() -> StreamingDecoder { - StreamingDecoder::new_with_options(DecodeOptions::default()) - } - - pub fn new_with_options(decode_options: DecodeOptions) -> StreamingDecoder { - let mut inflater = ZlibStream::new(); - inflater.set_ignore_adler32(decode_options.ignore_adler32); - - StreamingDecoder { - state: Some(State::Signature(0, [0; 7])), - current_chunk: ChunkState::default(), - inflater, - info: None, - current_seq_no: None, - apng_seq_handled: false, - have_idat: false, - decode_options, - } - } - - /// Resets the StreamingDecoder - pub fn reset(&mut self) { - self.state = Some(State::Signature(0, [0; 7])); - self.current_chunk.crc = Crc32::new(); - self.current_chunk.remaining = 0; - self.current_chunk.raw_bytes.clear(); - self.inflater.reset(); - self.info = None; - self.current_seq_no = None; - self.apng_seq_handled = false; - self.have_idat = false; - } - - /// Provides access to the inner `info` field - pub fn info(&self) -> Option<&Info<'static>> { - self.info.as_ref() - } - - pub fn set_ignore_text_chunk(&mut self, ignore_text_chunk: bool) { - self.decode_options.set_ignore_text_chunk(ignore_text_chunk); - } - - /// Return whether the decoder is set to ignore the Adler-32 checksum. - pub fn ignore_adler32(&self) -> bool { - self.inflater.ignore_adler32() - } - - /// Set whether to compute and verify the Adler-32 checksum during - /// decompression. Return `true` if the flag was successfully set. - /// - /// The decoder defaults to `true`. - /// - /// This flag cannot be modified after decompression has started until the - /// [`StreamingDecoder`] is reset. - pub fn set_ignore_adler32(&mut self, ignore_adler32: bool) -> bool { - self.inflater.set_ignore_adler32(ignore_adler32) - } - - /// Set whether to compute and verify the Adler-32 checksum during - /// decompression. - /// - /// The decoder defaults to `false`. - pub fn set_ignore_crc(&mut self, ignore_crc: bool) { - self.decode_options.set_ignore_crc(ignore_crc) - } - - /// Low level StreamingDecoder interface. - /// - /// Allows to stream partial data to the encoder. Returns a tuple containing the bytes that have - /// been consumed from the input buffer and the current decoding result. If the decoded chunk - /// was an image data chunk, it also appends the read data to `image_data`. - pub fn update( - &mut self, - mut buf: &[u8], - image_data: &mut Vec<u8>, - ) -> Result<(usize, Decoded), DecodingError> { - let len = buf.len(); - while !buf.is_empty() && self.state.is_some() { - match self.next_state(buf, image_data) { - Ok((bytes, Decoded::Nothing)) => buf = &buf[bytes..], - Ok((bytes, result)) => { - buf = &buf[bytes..]; - return Ok((len - buf.len(), result)); - } - Err(err) => return Err(err), - } - } - Ok((len - buf.len(), Decoded::Nothing)) - } - - fn next_state<'a>( - &'a mut self, - buf: &[u8], - image_data: &mut Vec<u8>, - ) -> Result<(usize, Decoded), DecodingError> { - use self::State::*; - - let current_byte = buf[0]; - - // Driver should ensure that state is never None - let state = self.state.take().unwrap(); - - match state { - Signature(i, mut signature) if i < 7 => { - signature[i as usize] = current_byte; - self.state = Some(Signature(i + 1, signature)); - Ok((1, Decoded::Nothing)) - } - Signature(_, signature) - if signature == [137, 80, 78, 71, 13, 10, 26] && current_byte == 10 => - { - self.state = Some(U32(U32Value::Length)); - Ok((1, Decoded::Nothing)) - } - Signature(..) => Err(DecodingError::Format( - FormatErrorInner::InvalidSignature.into(), - )), - U32Byte3(type_, mut val) => { - use self::U32Value::*; - val |= u32::from(current_byte); - match type_ { - Length => { - self.state = Some(U32(Type(val))); - Ok((1, Decoded::Nothing)) - } - Type(length) => { - let type_str = ChunkType([ - (val >> 24) as u8, - (val >> 16) as u8, - (val >> 8) as u8, - val as u8, - ]); - if type_str != self.current_chunk.type_ - && (self.current_chunk.type_ == IDAT - || self.current_chunk.type_ == chunk::fdAT) - { - self.current_chunk.type_ = type_str; - self.inflater.finish_compressed_chunks(image_data)?; - self.inflater.reset(); - self.state = Some(U32Byte3(Type(length), val & !0xff)); - return Ok((0, Decoded::ImageDataFlushed)); - } - self.current_chunk.type_ = type_str; - if !self.decode_options.ignore_crc { - self.current_chunk.crc.reset(); - self.current_chunk.crc.update(&type_str.0); - } - self.current_chunk.remaining = length; - self.apng_seq_handled = false; - self.current_chunk.raw_bytes.clear(); - self.state = Some(ReadChunk(type_str)); - Ok((1, Decoded::ChunkBegin(length, type_str))) - } - Crc(type_str) => { - // If ignore_crc is set, do not calculate CRC. We set - // sum=val so that it short-circuits to true in the next - // if-statement block - let sum = if self.decode_options.ignore_crc { - val - } else { - self.current_chunk.crc.clone().finalize() - }; - - if val == sum || CHECKSUM_DISABLED { - self.state = Some(State::U32(U32Value::Length)); - if type_str == IEND { - Ok((1, Decoded::ImageEnd)) - } else { - Ok((1, Decoded::ChunkComplete(val, type_str))) - } - } else { - Err(DecodingError::Format( - FormatErrorInner::CrcMismatch { - crc_val: val, - crc_sum: sum, - chunk: type_str, - } - .into(), - )) - } - } - } - } - U32Byte2(type_, val) => { - self.state = Some(U32Byte3(type_, val | u32::from(current_byte) << 8)); - Ok((1, Decoded::Nothing)) - } - U32Byte1(type_, val) => { - self.state = Some(U32Byte2(type_, val | u32::from(current_byte) << 16)); - Ok((1, Decoded::Nothing)) - } - U32(type_) => { - self.state = Some(U32Byte1(type_, u32::from(current_byte) << 24)); - Ok((1, Decoded::Nothing)) - } - PartialChunk(type_str) => { - match type_str { - IDAT => { - self.have_idat = true; - self.state = Some(DecodeData(type_str, 0)); - Ok((0, Decoded::PartialChunk(type_str))) - } - chunk::fdAT => { - let data_start; - if let Some(seq_no) = self.current_seq_no { - if !self.apng_seq_handled { - data_start = 4; - let mut buf = &self.current_chunk.raw_bytes[..]; - let next_seq_no = buf.read_be()?; - if next_seq_no != seq_no + 1 { - return Err(DecodingError::Format( - FormatErrorInner::ApngOrder { - present: next_seq_no, - expected: seq_no + 1, - } - .into(), - )); - } - self.current_seq_no = Some(next_seq_no); - self.apng_seq_handled = true; - } else { - data_start = 0; - } - } else { - return Err(DecodingError::Format( - FormatErrorInner::MissingFctl.into(), - )); - } - self.state = Some(DecodeData(type_str, data_start)); - Ok((0, Decoded::PartialChunk(type_str))) - } - // Handle other chunks - _ => { - if self.current_chunk.remaining == 0 { - // complete chunk - Ok((0, self.parse_chunk(type_str)?)) - } else { - // Make sure we have room to read more of the chunk. - // We need it fully before parsing. - self.reserve_current_chunk()?; - - self.state = Some(ReadChunk(type_str)); - Ok((0, Decoded::PartialChunk(type_str))) - } - } - } - } - ReadChunk(type_str) => { - // The _previous_ event wanted to return the contents of raw_bytes, and let the - // caller consume it, - if self.current_chunk.remaining == 0 { - self.state = Some(U32(U32Value::Crc(type_str))); - Ok((0, Decoded::Nothing)) - } else { - let ChunkState { - crc, - remaining, - raw_bytes, - type_: _, - } = &mut self.current_chunk; - - let buf_avail = raw_bytes.capacity() - raw_bytes.len(); - let bytes_avail = min(buf.len(), buf_avail); - let n = min(*remaining, bytes_avail as u32); - if buf_avail == 0 { - self.state = Some(PartialChunk(type_str)); - Ok((0, Decoded::Nothing)) - } else { - let buf = &buf[..n as usize]; - if !self.decode_options.ignore_crc { - crc.update(buf); - } - raw_bytes.extend_from_slice(buf); - - *remaining -= n; - if *remaining == 0 { - self.state = Some(PartialChunk(type_str)); - } else { - self.state = Some(ReadChunk(type_str)); - } - Ok((n as usize, Decoded::Nothing)) - } - } - } - DecodeData(type_str, mut n) => { - let chunk_len = self.current_chunk.raw_bytes.len(); - let chunk_data = &self.current_chunk.raw_bytes[n..]; - let c = self.inflater.decompress(chunk_data, image_data)?; - n += c; - if n == chunk_len && c == 0 { - self.current_chunk.raw_bytes.clear(); - self.state = Some(ReadChunk(type_str)); - Ok((0, Decoded::ImageData)) - } else { - self.state = Some(DecodeData(type_str, n)); - Ok((0, Decoded::ImageData)) - } - } - } - } - - fn reserve_current_chunk(&mut self) -> Result<(), DecodingError> { - // FIXME: use limits, also do so in iccp/zlib decompression. - const MAX: usize = 0x10_0000; - let buffer = &mut self.current_chunk.raw_bytes; - - // Double if necessary, but no more than until the limit is reached. - let reserve_size = MAX.saturating_sub(buffer.capacity()).min(buffer.len()); - buffer.reserve_exact(reserve_size); - - if buffer.capacity() == buffer.len() { - Err(DecodingError::LimitsExceeded) - } else { - Ok(()) - } - } - - fn parse_chunk(&mut self, type_str: ChunkType) -> Result<Decoded, DecodingError> { - self.state = Some(State::U32(U32Value::Crc(type_str))); - if self.info.is_none() && type_str != IHDR { - return Err(DecodingError::Format( - FormatErrorInner::ChunkBeforeIhdr { kind: type_str }.into(), - )); - } - match match type_str { - IHDR => self.parse_ihdr(), - chunk::PLTE => self.parse_plte(), - chunk::tRNS => self.parse_trns(), - chunk::pHYs => self.parse_phys(), - chunk::gAMA => self.parse_gama(), - chunk::acTL => self.parse_actl(), - chunk::fcTL => self.parse_fctl(), - chunk::cHRM => self.parse_chrm(), - chunk::sRGB => self.parse_srgb(), - chunk::iCCP => self.parse_iccp(), - chunk::tEXt if !self.decode_options.ignore_text_chunk => self.parse_text(), - chunk::zTXt if !self.decode_options.ignore_text_chunk => self.parse_ztxt(), - chunk::iTXt if !self.decode_options.ignore_text_chunk => self.parse_itxt(), - _ => Ok(Decoded::PartialChunk(type_str)), - } { - Err(err) => { - // Borrow of self ends here, because Decoding error does not borrow self. - self.state = None; - Err(err) - } - ok => ok, - } - } - - fn parse_fctl(&mut self) -> Result<Decoded, DecodingError> { - let mut buf = &self.current_chunk.raw_bytes[..]; - let next_seq_no = buf.read_be()?; - - // Assuming that fcTL is required before *every* fdAT-sequence - self.current_seq_no = Some(if let Some(seq_no) = self.current_seq_no { - if next_seq_no != seq_no + 1 { - return Err(DecodingError::Format( - FormatErrorInner::ApngOrder { - expected: seq_no + 1, - present: next_seq_no, - } - .into(), - )); - } - next_seq_no - } else { - if next_seq_no != 0 { - return Err(DecodingError::Format( - FormatErrorInner::ApngOrder { - expected: 0, - present: next_seq_no, - } - .into(), - )); - } - 0 - }); - self.inflater.reset(); - let fc = FrameControl { - sequence_number: next_seq_no, - width: buf.read_be()?, - height: buf.read_be()?, - x_offset: buf.read_be()?, - y_offset: buf.read_be()?, - delay_num: buf.read_be()?, - delay_den: buf.read_be()?, - dispose_op: { - let dispose_op = buf.read_be()?; - match DisposeOp::from_u8(dispose_op) { - Some(dispose_op) => dispose_op, - None => { - return Err(DecodingError::Format( - FormatErrorInner::InvalidDisposeOp(dispose_op).into(), - )) - } - } - }, - blend_op: { - let blend_op = buf.read_be()?; - match BlendOp::from_u8(blend_op) { - Some(blend_op) => blend_op, - None => { - return Err(DecodingError::Format( - FormatErrorInner::InvalidBlendOp(blend_op).into(), - )) - } - } - }, - }; - self.info.as_ref().unwrap().validate(&fc)?; - self.info.as_mut().unwrap().frame_control = Some(fc); - Ok(Decoded::FrameControl(fc)) - } - - fn parse_actl(&mut self) -> Result<Decoded, DecodingError> { - if self.have_idat { - Err(DecodingError::Format( - FormatErrorInner::AfterIdat { kind: chunk::acTL }.into(), - )) - } else { - let mut buf = &self.current_chunk.raw_bytes[..]; - let actl = AnimationControl { - num_frames: buf.read_be()?, - num_plays: buf.read_be()?, - }; - self.info.as_mut().unwrap().animation_control = Some(actl); - Ok(Decoded::AnimationControl(actl)) - } - } - - fn parse_plte(&mut self) -> Result<Decoded, DecodingError> { - let info = self.info.as_mut().unwrap(); - if info.palette.is_some() { - // Only one palette is allowed - Err(DecodingError::Format( - FormatErrorInner::DuplicateChunk { kind: chunk::PLTE }.into(), - )) - } else { - info.palette = Some(Cow::Owned(self.current_chunk.raw_bytes.clone())); - Ok(Decoded::Nothing) - } - } - - fn parse_trns(&mut self) -> Result<Decoded, DecodingError> { - let info = self.info.as_mut().unwrap(); - if info.trns.is_some() { - return Err(DecodingError::Format( - FormatErrorInner::DuplicateChunk { kind: chunk::PLTE }.into(), - )); - } - let (color_type, bit_depth) = { (info.color_type, info.bit_depth as u8) }; - let mut vec = self.current_chunk.raw_bytes.clone(); - let len = vec.len(); - match color_type { - ColorType::Grayscale => { - if len < 2 { - return Err(DecodingError::Format( - FormatErrorInner::ShortPalette { expected: 2, len }.into(), - )); - } - if bit_depth < 16 { - vec[0] = vec[1]; - vec.truncate(1); - } - info.trns = Some(Cow::Owned(vec)); - Ok(Decoded::Nothing) - } - ColorType::Rgb => { - if len < 6 { - return Err(DecodingError::Format( - FormatErrorInner::ShortPalette { expected: 6, len }.into(), - )); - } - if bit_depth < 16 { - vec[0] = vec[1]; - vec[1] = vec[3]; - vec[2] = vec[5]; - vec.truncate(3); - } - info.trns = Some(Cow::Owned(vec)); - Ok(Decoded::Nothing) - } - ColorType::Indexed => { - // The transparency chunk must be after the palette chunk and - // before the data chunk. - if info.palette.is_none() { - return Err(DecodingError::Format( - FormatErrorInner::AfterPlte { kind: chunk::tRNS }.into(), - )); - } else if self.have_idat { - return Err(DecodingError::Format( - FormatErrorInner::OutsidePlteIdat { kind: chunk::tRNS }.into(), - )); - } - - info.trns = Some(Cow::Owned(vec)); - Ok(Decoded::Nothing) - } - c => Err(DecodingError::Format( - FormatErrorInner::ColorWithBadTrns(c).into(), - )), - } - } - - fn parse_phys(&mut self) -> Result<Decoded, DecodingError> { - let info = self.info.as_mut().unwrap(); - if self.have_idat { - Err(DecodingError::Format( - FormatErrorInner::AfterIdat { kind: chunk::pHYs }.into(), - )) - } else if info.pixel_dims.is_some() { - Err(DecodingError::Format( - FormatErrorInner::DuplicateChunk { kind: chunk::pHYs }.into(), - )) - } else { - let mut buf = &self.current_chunk.raw_bytes[..]; - let xppu = buf.read_be()?; - let yppu = buf.read_be()?; - let unit = buf.read_be()?; - let unit = match Unit::from_u8(unit) { - Some(unit) => unit, - None => { - return Err(DecodingError::Format( - FormatErrorInner::InvalidUnit(unit).into(), - )) - } - }; - let pixel_dims = PixelDimensions { xppu, yppu, unit }; - info.pixel_dims = Some(pixel_dims); - Ok(Decoded::PixelDimensions(pixel_dims)) - } - } - - fn parse_chrm(&mut self) -> Result<Decoded, DecodingError> { - let info = self.info.as_mut().unwrap(); - if self.have_idat { - Err(DecodingError::Format( - FormatErrorInner::AfterIdat { kind: chunk::cHRM }.into(), - )) - } else if info.chrm_chunk.is_some() { - Err(DecodingError::Format( - FormatErrorInner::DuplicateChunk { kind: chunk::cHRM }.into(), - )) - } else { - let mut buf = &self.current_chunk.raw_bytes[..]; - let white_x: u32 = buf.read_be()?; - let white_y: u32 = buf.read_be()?; - let red_x: u32 = buf.read_be()?; - let red_y: u32 = buf.read_be()?; - let green_x: u32 = buf.read_be()?; - let green_y: u32 = buf.read_be()?; - let blue_x: u32 = buf.read_be()?; - let blue_y: u32 = buf.read_be()?; - - let source_chromaticities = SourceChromaticities { - white: ( - ScaledFloat::from_scaled(white_x), - ScaledFloat::from_scaled(white_y), - ), - red: ( - ScaledFloat::from_scaled(red_x), - ScaledFloat::from_scaled(red_y), - ), - green: ( - ScaledFloat::from_scaled(green_x), - ScaledFloat::from_scaled(green_y), - ), - blue: ( - ScaledFloat::from_scaled(blue_x), - ScaledFloat::from_scaled(blue_y), - ), - }; - - info.chrm_chunk = Some(source_chromaticities); - // Ignore chromaticities if sRGB profile is used. - if info.srgb.is_none() { - info.source_chromaticities = Some(source_chromaticities); - } - - Ok(Decoded::Nothing) - } - } - - fn parse_gama(&mut self) -> Result<Decoded, DecodingError> { - let info = self.info.as_mut().unwrap(); - if self.have_idat { - Err(DecodingError::Format( - FormatErrorInner::AfterIdat { kind: chunk::gAMA }.into(), - )) - } else if info.gama_chunk.is_some() { - Err(DecodingError::Format( - FormatErrorInner::DuplicateChunk { kind: chunk::gAMA }.into(), - )) - } else { - let mut buf = &self.current_chunk.raw_bytes[..]; - let source_gamma: u32 = buf.read_be()?; - let source_gamma = ScaledFloat::from_scaled(source_gamma); - - info.gama_chunk = Some(source_gamma); - // Ignore chromaticities if sRGB profile is used. - if info.srgb.is_none() { - info.source_gamma = Some(source_gamma); - } - - Ok(Decoded::Nothing) - } - } - - fn parse_srgb(&mut self) -> Result<Decoded, DecodingError> { - let info = self.info.as_mut().unwrap(); - if self.have_idat { - Err(DecodingError::Format( - FormatErrorInner::AfterIdat { kind: chunk::acTL }.into(), - )) - } else if info.srgb.is_some() { - Err(DecodingError::Format( - FormatErrorInner::DuplicateChunk { kind: chunk::sRGB }.into(), - )) - } else { - let mut buf = &self.current_chunk.raw_bytes[..]; - let raw: u8 = buf.read_be()?; // BE is is nonsense for single bytes, but this way the size is checked. - let rendering_intent = crate::SrgbRenderingIntent::from_raw(raw).ok_or_else(|| { - FormatError::from(FormatErrorInner::InvalidSrgbRenderingIntent(raw)) - })?; - - // Set srgb and override source gamma and chromaticities. - info.srgb = Some(rendering_intent); - info.source_gamma = Some(crate::srgb::substitute_gamma()); - info.source_chromaticities = Some(crate::srgb::substitute_chromaticities()); - Ok(Decoded::Nothing) - } - } - - fn parse_iccp(&mut self) -> Result<Decoded, DecodingError> { - let info = self.info.as_mut().unwrap(); - if self.have_idat { - Err(DecodingError::Format( - FormatErrorInner::AfterIdat { kind: chunk::iCCP }.into(), - )) - } else if info.icc_profile.is_some() { - Err(DecodingError::Format( - FormatErrorInner::DuplicateChunk { kind: chunk::iCCP }.into(), - )) - } else { - let mut buf = &self.current_chunk.raw_bytes[..]; - - // read profile name - let _: u8 = buf.read_be()?; - for _ in 1..80 { - let raw: u8 = buf.read_be()?; - if raw == 0 { - break; - } - } - - match buf.read_be()? { - // compression method - 0u8 => (), - n => { - return Err(DecodingError::Format( - FormatErrorInner::UnknownCompressionMethod(n).into(), - )) - } - } - - let mut profile = Vec::new(); - let mut inflater = ZlibStream::new(); - while !buf.is_empty() { - let consumed_bytes = inflater.decompress(buf, &mut profile)?; - if profile.len() > 8000000 { - // TODO: this should use Limits.bytes - return Err(DecodingError::LimitsExceeded); - } - buf = &buf[consumed_bytes..]; - } - inflater.finish_compressed_chunks(&mut profile)?; - - info.icc_profile = Some(Cow::Owned(profile)); - Ok(Decoded::Nothing) - } - } - - fn parse_ihdr(&mut self) -> Result<Decoded, DecodingError> { - if self.info.is_some() { - return Err(DecodingError::Format( - FormatErrorInner::DuplicateChunk { kind: IHDR }.into(), - )); - } - let mut buf = &self.current_chunk.raw_bytes[..]; - let width = buf.read_be()?; - let height = buf.read_be()?; - let bit_depth = buf.read_be()?; - let bit_depth = match BitDepth::from_u8(bit_depth) { - Some(bits) => bits, - None => { - return Err(DecodingError::Format( - FormatErrorInner::InvalidBitDepth(bit_depth).into(), - )) - } - }; - let color_type = buf.read_be()?; - let color_type = match ColorType::from_u8(color_type) { - Some(color_type) => { - if color_type.is_combination_invalid(bit_depth) { - return Err(DecodingError::Format( - FormatErrorInner::InvalidColorBitDepth { - color_type, - bit_depth, - } - .into(), - )); - } else { - color_type - } - } - None => { - return Err(DecodingError::Format( - FormatErrorInner::InvalidColorType(color_type).into(), - )) - } - }; - match buf.read_be()? { - // compression method - 0u8 => (), - n => { - return Err(DecodingError::Format( - FormatErrorInner::UnknownCompressionMethod(n).into(), - )) - } - } - match buf.read_be()? { - // filter method - 0u8 => (), - n => { - return Err(DecodingError::Format( - FormatErrorInner::UnknownFilterMethod(n).into(), - )) - } - } - let interlaced = match buf.read_be()? { - 0u8 => false, - 1 => true, - n => { - return Err(DecodingError::Format( - FormatErrorInner::UnknownInterlaceMethod(n).into(), - )) - } - }; - - self.info = Some(Info { - width, - height, - bit_depth, - color_type, - interlaced, - ..Default::default() - }); - - Ok(Decoded::Header( - width, height, bit_depth, color_type, interlaced, - )) - } - - fn split_keyword(buf: &[u8]) -> Result<(&[u8], &[u8]), DecodingError> { - let null_byte_index = buf - .iter() - .position(|&b| b == 0) - .ok_or_else(|| DecodingError::from(TextDecodingError::MissingNullSeparator))?; - - if null_byte_index == 0 || null_byte_index > 79 { - return Err(DecodingError::from(TextDecodingError::InvalidKeywordSize)); - } - - Ok((&buf[..null_byte_index], &buf[null_byte_index + 1..])) - } - - fn parse_text(&mut self) -> Result<Decoded, DecodingError> { - let buf = &self.current_chunk.raw_bytes[..]; - - let (keyword_slice, value_slice) = Self::split_keyword(buf)?; - - self.info - .as_mut() - .unwrap() - .uncompressed_latin1_text - .push(TEXtChunk::decode(keyword_slice, value_slice).map_err(DecodingError::from)?); - - Ok(Decoded::Nothing) - } - - fn parse_ztxt(&mut self) -> Result<Decoded, DecodingError> { - let buf = &self.current_chunk.raw_bytes[..]; - - let (keyword_slice, value_slice) = Self::split_keyword(buf)?; - - let compression_method = *value_slice - .first() - .ok_or_else(|| DecodingError::from(TextDecodingError::InvalidCompressionMethod))?; - - let text_slice = &value_slice[1..]; - - self.info.as_mut().unwrap().compressed_latin1_text.push( - ZTXtChunk::decode(keyword_slice, compression_method, text_slice) - .map_err(DecodingError::from)?, - ); - - Ok(Decoded::Nothing) - } - - fn parse_itxt(&mut self) -> Result<Decoded, DecodingError> { - let buf = &self.current_chunk.raw_bytes[..]; - - let (keyword_slice, value_slice) = Self::split_keyword(buf)?; - - let compression_flag = *value_slice - .first() - .ok_or_else(|| DecodingError::from(TextDecodingError::MissingCompressionFlag))?; - - let compression_method = *value_slice - .get(1) - .ok_or_else(|| DecodingError::from(TextDecodingError::InvalidCompressionMethod))?; - - let second_null_byte_index = value_slice[2..] - .iter() - .position(|&b| b == 0) - .ok_or_else(|| DecodingError::from(TextDecodingError::MissingNullSeparator))? - + 2; - - let language_tag_slice = &value_slice[2..second_null_byte_index]; - - let third_null_byte_index = value_slice[second_null_byte_index + 1..] - .iter() - .position(|&b| b == 0) - .ok_or_else(|| DecodingError::from(TextDecodingError::MissingNullSeparator))? - + (second_null_byte_index + 1); - - let translated_keyword_slice = - &value_slice[second_null_byte_index + 1..third_null_byte_index]; - - let text_slice = &value_slice[third_null_byte_index + 1..]; - - self.info.as_mut().unwrap().utf8_text.push( - ITXtChunk::decode( - keyword_slice, - compression_flag, - compression_method, - language_tag_slice, - translated_keyword_slice, - text_slice, - ) - .map_err(DecodingError::from)?, - ); - - Ok(Decoded::Nothing) - } -} - -impl Info<'_> { - fn validate(&self, fc: &FrameControl) -> Result<(), DecodingError> { - // Validate mathematically: fc.width + fc.x_offset <= self.width - let in_x_bounds = Some(fc.width) <= self.width.checked_sub(fc.x_offset); - // Validate mathematically: fc.height + fc.y_offset <= self.height - let in_y_bounds = Some(fc.height) <= self.height.checked_sub(fc.y_offset); - - if !in_x_bounds || !in_y_bounds { - return Err(DecodingError::Format( - // TODO: do we want to display the bad bounds? - FormatErrorInner::BadSubFrameBounds {}.into(), - )); - } - - Ok(()) - } -} - -impl Default for StreamingDecoder { - fn default() -> Self { - Self::new() - } -} - -impl Default for ChunkState { - fn default() -> Self { - ChunkState { - type_: ChunkType([0; 4]), - crc: Crc32::new(), - remaining: 0, - raw_bytes: Vec::with_capacity(CHUNCK_BUFFER_SIZE), - } - } -} - -#[cfg(test)] -mod tests { - use super::ScaledFloat; - use super::SourceChromaticities; - use std::fs::File; - - #[test] - fn image_gamma() -> Result<(), ()> { - fn trial(path: &str, expected: Option<ScaledFloat>) { - let decoder = crate::Decoder::new(File::open(path).unwrap()); - let reader = decoder.read_info().unwrap(); - let actual: Option<ScaledFloat> = reader.info().source_gamma; - assert!(actual == expected); - } - trial("tests/pngsuite/f00n0g08.png", None); - trial("tests/pngsuite/f00n2c08.png", None); - trial("tests/pngsuite/f01n0g08.png", None); - trial("tests/pngsuite/f01n2c08.png", None); - trial("tests/pngsuite/f02n0g08.png", None); - trial("tests/pngsuite/f02n2c08.png", None); - trial("tests/pngsuite/f03n0g08.png", None); - trial("tests/pngsuite/f03n2c08.png", None); - trial("tests/pngsuite/f04n0g08.png", None); - trial("tests/pngsuite/f04n2c08.png", None); - trial("tests/pngsuite/f99n0g04.png", None); - trial("tests/pngsuite/tm3n3p02.png", None); - trial("tests/pngsuite/g03n0g16.png", Some(ScaledFloat::new(0.35))); - trial("tests/pngsuite/g03n2c08.png", Some(ScaledFloat::new(0.35))); - trial("tests/pngsuite/g03n3p04.png", Some(ScaledFloat::new(0.35))); - trial("tests/pngsuite/g04n0g16.png", Some(ScaledFloat::new(0.45))); - trial("tests/pngsuite/g04n2c08.png", Some(ScaledFloat::new(0.45))); - trial("tests/pngsuite/g04n3p04.png", Some(ScaledFloat::new(0.45))); - trial("tests/pngsuite/g05n0g16.png", Some(ScaledFloat::new(0.55))); - trial("tests/pngsuite/g05n2c08.png", Some(ScaledFloat::new(0.55))); - trial("tests/pngsuite/g05n3p04.png", Some(ScaledFloat::new(0.55))); - trial("tests/pngsuite/g07n0g16.png", Some(ScaledFloat::new(0.7))); - trial("tests/pngsuite/g07n2c08.png", Some(ScaledFloat::new(0.7))); - trial("tests/pngsuite/g07n3p04.png", Some(ScaledFloat::new(0.7))); - trial("tests/pngsuite/g10n0g16.png", Some(ScaledFloat::new(1.0))); - trial("tests/pngsuite/g10n2c08.png", Some(ScaledFloat::new(1.0))); - trial("tests/pngsuite/g10n3p04.png", Some(ScaledFloat::new(1.0))); - trial("tests/pngsuite/g25n0g16.png", Some(ScaledFloat::new(2.5))); - trial("tests/pngsuite/g25n2c08.png", Some(ScaledFloat::new(2.5))); - trial("tests/pngsuite/g25n3p04.png", Some(ScaledFloat::new(2.5))); - Ok(()) - } - - #[test] - fn image_source_chromaticities() -> Result<(), ()> { - fn trial(path: &str, expected: Option<SourceChromaticities>) { - let decoder = crate::Decoder::new(File::open(path).unwrap()); - let reader = decoder.read_info().unwrap(); - let actual: Option<SourceChromaticities> = reader.info().source_chromaticities; - assert!(actual == expected); - } - trial( - "tests/pngsuite/ccwn2c08.png", - Some(SourceChromaticities::new( - (0.3127, 0.3290), - (0.64, 0.33), - (0.30, 0.60), - (0.15, 0.06), - )), - ); - trial( - "tests/pngsuite/ccwn3p08.png", - Some(SourceChromaticities::new( - (0.3127, 0.3290), - (0.64, 0.33), - (0.30, 0.60), - (0.15, 0.06), - )), - ); - trial("tests/pngsuite/basi0g01.png", None); - trial("tests/pngsuite/basi0g02.png", None); - trial("tests/pngsuite/basi0g04.png", None); - trial("tests/pngsuite/basi0g08.png", None); - trial("tests/pngsuite/basi0g16.png", None); - trial("tests/pngsuite/basi2c08.png", None); - trial("tests/pngsuite/basi2c16.png", None); - trial("tests/pngsuite/basi3p01.png", None); - trial("tests/pngsuite/basi3p02.png", None); - trial("tests/pngsuite/basi3p04.png", None); - trial("tests/pngsuite/basi3p08.png", None); - trial("tests/pngsuite/basi4a08.png", None); - trial("tests/pngsuite/basi4a16.png", None); - trial("tests/pngsuite/basi6a08.png", None); - trial("tests/pngsuite/basi6a16.png", None); - trial("tests/pngsuite/basn0g01.png", None); - trial("tests/pngsuite/basn0g02.png", None); - trial("tests/pngsuite/basn0g04.png", None); - trial("tests/pngsuite/basn0g08.png", None); - trial("tests/pngsuite/basn0g16.png", None); - trial("tests/pngsuite/basn2c08.png", None); - trial("tests/pngsuite/basn2c16.png", None); - trial("tests/pngsuite/basn3p01.png", None); - trial("tests/pngsuite/basn3p02.png", None); - trial("tests/pngsuite/basn3p04.png", None); - trial("tests/pngsuite/basn3p08.png", None); - trial("tests/pngsuite/basn4a08.png", None); - trial("tests/pngsuite/basn4a16.png", None); - trial("tests/pngsuite/basn6a08.png", None); - trial("tests/pngsuite/basn6a16.png", None); - trial("tests/pngsuite/bgai4a08.png", None); - trial("tests/pngsuite/bgai4a16.png", None); - trial("tests/pngsuite/bgan6a08.png", None); - trial("tests/pngsuite/bgan6a16.png", None); - trial("tests/pngsuite/bgbn4a08.png", None); - trial("tests/pngsuite/bggn4a16.png", None); - trial("tests/pngsuite/bgwn6a08.png", None); - trial("tests/pngsuite/bgyn6a16.png", None); - trial("tests/pngsuite/cdfn2c08.png", None); - trial("tests/pngsuite/cdhn2c08.png", None); - trial("tests/pngsuite/cdsn2c08.png", None); - trial("tests/pngsuite/cdun2c08.png", None); - trial("tests/pngsuite/ch1n3p04.png", None); - trial("tests/pngsuite/ch2n3p08.png", None); - trial("tests/pngsuite/cm0n0g04.png", None); - trial("tests/pngsuite/cm7n0g04.png", None); - trial("tests/pngsuite/cm9n0g04.png", None); - trial("tests/pngsuite/cs3n2c16.png", None); - trial("tests/pngsuite/cs3n3p08.png", None); - trial("tests/pngsuite/cs5n2c08.png", None); - trial("tests/pngsuite/cs5n3p08.png", None); - trial("tests/pngsuite/cs8n2c08.png", None); - trial("tests/pngsuite/cs8n3p08.png", None); - trial("tests/pngsuite/ct0n0g04.png", None); - trial("tests/pngsuite/ct1n0g04.png", None); - trial("tests/pngsuite/cten0g04.png", None); - trial("tests/pngsuite/ctfn0g04.png", None); - trial("tests/pngsuite/ctgn0g04.png", None); - trial("tests/pngsuite/cthn0g04.png", None); - trial("tests/pngsuite/ctjn0g04.png", None); - trial("tests/pngsuite/ctzn0g04.png", None); - trial("tests/pngsuite/f00n0g08.png", None); - trial("tests/pngsuite/f00n2c08.png", None); - trial("tests/pngsuite/f01n0g08.png", None); - trial("tests/pngsuite/f01n2c08.png", None); - trial("tests/pngsuite/f02n0g08.png", None); - trial("tests/pngsuite/f02n2c08.png", None); - trial("tests/pngsuite/f03n0g08.png", None); - trial("tests/pngsuite/f03n2c08.png", None); - trial("tests/pngsuite/f04n0g08.png", None); - trial("tests/pngsuite/f04n2c08.png", None); - trial("tests/pngsuite/f99n0g04.png", None); - trial("tests/pngsuite/g03n0g16.png", None); - trial("tests/pngsuite/g03n2c08.png", None); - trial("tests/pngsuite/g03n3p04.png", None); - trial("tests/pngsuite/g04n0g16.png", None); - trial("tests/pngsuite/g04n2c08.png", None); - trial("tests/pngsuite/g04n3p04.png", None); - trial("tests/pngsuite/g05n0g16.png", None); - trial("tests/pngsuite/g05n2c08.png", None); - trial("tests/pngsuite/g05n3p04.png", None); - trial("tests/pngsuite/g07n0g16.png", None); - trial("tests/pngsuite/g07n2c08.png", None); - trial("tests/pngsuite/g07n3p04.png", None); - trial("tests/pngsuite/g10n0g16.png", None); - trial("tests/pngsuite/g10n2c08.png", None); - trial("tests/pngsuite/g10n3p04.png", None); - trial("tests/pngsuite/g25n0g16.png", None); - trial("tests/pngsuite/g25n2c08.png", None); - trial("tests/pngsuite/g25n3p04.png", None); - trial("tests/pngsuite/oi1n0g16.png", None); - trial("tests/pngsuite/oi1n2c16.png", None); - trial("tests/pngsuite/oi2n0g16.png", None); - trial("tests/pngsuite/oi2n2c16.png", None); - trial("tests/pngsuite/oi4n0g16.png", None); - trial("tests/pngsuite/oi4n2c16.png", None); - trial("tests/pngsuite/oi9n0g16.png", None); - trial("tests/pngsuite/oi9n2c16.png", None); - trial("tests/pngsuite/PngSuite.png", None); - trial("tests/pngsuite/pp0n2c16.png", None); - trial("tests/pngsuite/pp0n6a08.png", None); - trial("tests/pngsuite/ps1n0g08.png", None); - trial("tests/pngsuite/ps1n2c16.png", None); - trial("tests/pngsuite/ps2n0g08.png", None); - trial("tests/pngsuite/ps2n2c16.png", None); - trial("tests/pngsuite/s01i3p01.png", None); - trial("tests/pngsuite/s01n3p01.png", None); - trial("tests/pngsuite/s02i3p01.png", None); - trial("tests/pngsuite/s02n3p01.png", None); - trial("tests/pngsuite/s03i3p01.png", None); - trial("tests/pngsuite/s03n3p01.png", None); - trial("tests/pngsuite/s04i3p01.png", None); - trial("tests/pngsuite/s04n3p01.png", None); - trial("tests/pngsuite/s05i3p02.png", None); - trial("tests/pngsuite/s05n3p02.png", None); - trial("tests/pngsuite/s06i3p02.png", None); - trial("tests/pngsuite/s06n3p02.png", None); - trial("tests/pngsuite/s07i3p02.png", None); - trial("tests/pngsuite/s07n3p02.png", None); - trial("tests/pngsuite/s08i3p02.png", None); - trial("tests/pngsuite/s08n3p02.png", None); - trial("tests/pngsuite/s09i3p02.png", None); - trial("tests/pngsuite/s09n3p02.png", None); - trial("tests/pngsuite/s32i3p04.png", None); - trial("tests/pngsuite/s32n3p04.png", None); - trial("tests/pngsuite/s33i3p04.png", None); - trial("tests/pngsuite/s33n3p04.png", None); - trial("tests/pngsuite/s34i3p04.png", None); - trial("tests/pngsuite/s34n3p04.png", None); - trial("tests/pngsuite/s35i3p04.png", None); - trial("tests/pngsuite/s35n3p04.png", None); - trial("tests/pngsuite/s36i3p04.png", None); - trial("tests/pngsuite/s36n3p04.png", None); - trial("tests/pngsuite/s37i3p04.png", None); - trial("tests/pngsuite/s37n3p04.png", None); - trial("tests/pngsuite/s38i3p04.png", None); - trial("tests/pngsuite/s38n3p04.png", None); - trial("tests/pngsuite/s39i3p04.png", None); - trial("tests/pngsuite/s39n3p04.png", None); - trial("tests/pngsuite/s40i3p04.png", None); - trial("tests/pngsuite/s40n3p04.png", None); - trial("tests/pngsuite/tbbn0g04.png", None); - trial("tests/pngsuite/tbbn2c16.png", None); - trial("tests/pngsuite/tbbn3p08.png", None); - trial("tests/pngsuite/tbgn2c16.png", None); - trial("tests/pngsuite/tbgn3p08.png", None); - trial("tests/pngsuite/tbrn2c08.png", None); - trial("tests/pngsuite/tbwn0g16.png", None); - trial("tests/pngsuite/tbwn3p08.png", None); - trial("tests/pngsuite/tbyn3p08.png", None); - trial("tests/pngsuite/tm3n3p02.png", None); - trial("tests/pngsuite/tp0n0g08.png", None); - trial("tests/pngsuite/tp0n2c08.png", None); - trial("tests/pngsuite/tp0n3p08.png", None); - trial("tests/pngsuite/tp1n3p08.png", None); - trial("tests/pngsuite/z00n2c08.png", None); - trial("tests/pngsuite/z03n2c08.png", None); - trial("tests/pngsuite/z06n2c08.png", None); - Ok(()) - } -} diff --git a/vendor/png/src/decoder/zlib.rs b/vendor/png/src/decoder/zlib.rs deleted file mode 100644 index 2953c95..0000000 --- a/vendor/png/src/decoder/zlib.rs +++ /dev/null @@ -1,212 +0,0 @@ -use super::{stream::FormatErrorInner, DecodingError, CHUNCK_BUFFER_SIZE}; - -use fdeflate::Decompressor; - -/// Ergonomics wrapper around `miniz_oxide::inflate::stream` for zlib compressed data. -pub(super) struct ZlibStream { - /// Current decoding state. - state: Box<fdeflate::Decompressor>, - /// If there has been a call to decompress already. - started: bool, - /// A buffer of compressed data. - /// We use this for a progress guarantee. The data in the input stream is chunked as given by - /// the underlying stream buffer. We will not read any more data until the current buffer has - /// been fully consumed. The zlib decompression can not fully consume all the data when it is - /// in the middle of the stream, it will treat full symbols and maybe the last bytes need to be - /// treated in a special way. The exact reason isn't as important but the interface does not - /// promise us this. Now, the complication is that the _current_ chunking information of PNG - /// alone is not enough to determine this as indeed the compressed stream is the concatenation - /// of all consecutive `IDAT`/`fdAT` chunks. We would need to inspect the next chunk header. - /// - /// Thus, there needs to be a buffer that allows fully clearing a chunk so that the next chunk - /// type can be inspected. - in_buffer: Vec<u8>, - /// The logical start of the `in_buffer`. - in_pos: usize, - /// Remaining buffered decoded bytes. - /// The decoder sometimes wants inspect some already finished bytes for further decoding. So we - /// keep a total of 32KB of decoded data available as long as more data may be appended. - out_buffer: Vec<u8>, - /// The cursor position in the output stream as a buffer index. - out_pos: usize, - /// Ignore and do not calculate the Adler-32 checksum. Defaults to `true`. - /// - /// This flag overrides `TINFL_FLAG_COMPUTE_ADLER32`. - /// - /// This flag should not be modified after decompression has started. - ignore_adler32: bool, -} - -impl ZlibStream { - pub(crate) fn new() -> Self { - ZlibStream { - state: Box::new(Decompressor::new()), - started: false, - in_buffer: Vec::with_capacity(CHUNCK_BUFFER_SIZE), - in_pos: 0, - out_buffer: vec![0; 2 * CHUNCK_BUFFER_SIZE], - out_pos: 0, - ignore_adler32: true, - } - } - - pub(crate) fn reset(&mut self) { - self.started = false; - self.in_buffer.clear(); - self.in_pos = 0; - self.out_buffer.clear(); - self.out_pos = 0; - *self.state = Decompressor::new(); - } - - /// Set the `ignore_adler32` flag and return `true` if the flag was - /// successfully set. - /// - /// The default is `true`. - /// - /// This flag cannot be modified after decompression has started until the - /// [ZlibStream] is reset. - pub(crate) fn set_ignore_adler32(&mut self, flag: bool) -> bool { - if !self.started { - self.ignore_adler32 = flag; - true - } else { - false - } - } - - /// Return the `ignore_adler32` flag. - pub(crate) fn ignore_adler32(&self) -> bool { - self.ignore_adler32 - } - - /// Fill the decoded buffer as far as possible from `data`. - /// On success returns the number of consumed input bytes. - pub(crate) fn decompress( - &mut self, - data: &[u8], - image_data: &mut Vec<u8>, - ) -> Result<usize, DecodingError> { - self.prepare_vec_for_appending(); - - if !self.started && self.ignore_adler32 { - self.state.ignore_adler32(); - } - - let in_data = if self.in_buffer.is_empty() { - data - } else { - &self.in_buffer[self.in_pos..] - }; - - let (mut in_consumed, out_consumed) = self - .state - .read(in_data, self.out_buffer.as_mut_slice(), self.out_pos, false) - .map_err(|err| { - DecodingError::Format(FormatErrorInner::CorruptFlateStream { err }.into()) - })?; - - if !self.in_buffer.is_empty() { - self.in_pos += in_consumed; - in_consumed = 0; - } - - if self.in_buffer.len() == self.in_pos { - self.in_buffer.clear(); - self.in_pos = 0; - } - - if in_consumed == 0 { - self.in_buffer.extend_from_slice(data); - in_consumed = data.len(); - } - - self.started = true; - self.out_pos += out_consumed; - self.transfer_finished_data(image_data); - - Ok(in_consumed) - } - - /// Called after all consecutive IDAT chunks were handled. - /// - /// The compressed stream can be split on arbitrary byte boundaries. This enables some cleanup - /// within the decompressor and flushing additional data which may have been kept back in case - /// more data were passed to it. - pub(crate) fn finish_compressed_chunks( - &mut self, - image_data: &mut Vec<u8>, - ) -> Result<(), DecodingError> { - if !self.started { - return Ok(()); - } - - let tail = self.in_buffer.split_off(0); - let tail = &tail[self.in_pos..]; - - let mut start = 0; - loop { - self.prepare_vec_for_appending(); - - let (in_consumed, out_consumed) = self - .state - .read( - &tail[start..], - self.out_buffer.as_mut_slice(), - self.out_pos, - true, - ) - .map_err(|err| { - DecodingError::Format(FormatErrorInner::CorruptFlateStream { err }.into()) - })?; - - start += in_consumed; - self.out_pos += out_consumed; - - if self.state.is_done() { - self.out_buffer.truncate(self.out_pos); - image_data.append(&mut self.out_buffer); - return Ok(()); - } else { - let transferred = self.transfer_finished_data(image_data); - assert!( - transferred > 0 || in_consumed > 0 || out_consumed > 0, - "No more forward progress made in stream decoding." - ); - } - } - } - - /// Resize the vector to allow allocation of more data. - fn prepare_vec_for_appending(&mut self) { - if self.out_buffer.len().saturating_sub(self.out_pos) >= CHUNCK_BUFFER_SIZE { - return; - } - - let buffered_len = self.decoding_size(self.out_buffer.len()); - debug_assert!(self.out_buffer.len() <= buffered_len); - self.out_buffer.resize(buffered_len, 0u8); - } - - fn decoding_size(&self, len: usize) -> usize { - // Allocate one more chunk size than currently or double the length while ensuring that the - // allocation is valid and that any cursor within it will be valid. - len - // This keeps the buffer size a power-of-two, required by miniz_oxide. - .saturating_add(CHUNCK_BUFFER_SIZE.max(len)) - // Ensure all buffer indices are valid cursor positions. - // Note: both cut off and zero extension give correct results. - .min(u64::max_value() as usize) - // Ensure the allocation request is valid. - // TODO: maximum allocation limits? - .min(isize::max_value() as usize) - } - - fn transfer_finished_data(&mut self, image_data: &mut Vec<u8>) -> usize { - let safe = self.out_pos.saturating_sub(CHUNCK_BUFFER_SIZE); - // TODO: allocation limits. - image_data.extend(self.out_buffer.drain(..safe)); - self.out_pos -= safe; - safe - } -} |