diff options
Diffstat (limited to 'vendor/png/src/decoder/stream.rs')
-rw-r--r-- | vendor/png/src/decoder/stream.rs | 1576 |
1 files changed, 0 insertions, 1576 deletions
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(()) - } -} |