diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /third_party/rust/png/src | |
parent | Initial commit. (diff) | |
download | firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/png/src')
-rw-r--r-- | third_party/rust/png/src/chunk.rs | 59 | ||||
-rw-r--r-- | third_party/rust/png/src/common.rs | 404 | ||||
-rw-r--r-- | third_party/rust/png/src/decoder/mod.rs | 543 | ||||
-rw-r--r-- | third_party/rust/png/src/decoder/stream.rs | 665 | ||||
-rw-r--r-- | third_party/rust/png/src/encoder.rs | 477 | ||||
-rw-r--r-- | third_party/rust/png/src/filter.rs | 158 | ||||
-rw-r--r-- | third_party/rust/png/src/lib.rs | 61 | ||||
-rw-r--r-- | third_party/rust/png/src/traits.rs | 72 | ||||
-rw-r--r-- | third_party/rust/png/src/utils.rs | 376 |
9 files changed, 2815 insertions, 0 deletions
diff --git a/third_party/rust/png/src/chunk.rs b/third_party/rust/png/src/chunk.rs new file mode 100644 index 0000000000..9e24e6e9a0 --- /dev/null +++ b/third_party/rust/png/src/chunk.rs @@ -0,0 +1,59 @@ +//! Chunk types and functions +#![allow(dead_code)] +#![allow(non_upper_case_globals)] + +pub type ChunkType = [u8; 4]; + +// -- Critical chunks -- + +/// Image header +pub const IHDR: ChunkType = [b'I', b'H', b'D', b'R']; +/// Palette +pub const PLTE: ChunkType = [b'P', b'L', b'T', b'E']; +/// Image data +pub const IDAT: ChunkType = [b'I', b'D', b'A', b'T']; +/// Image trailer +pub const IEND: ChunkType = [b'I', b'E', b'N', b'D']; + +// -- Ancillary chunks -- + +/// Transparency +pub const tRNS: ChunkType = [b't', b'R', b'N', b'S']; +/// Background colour +pub const bKGD: ChunkType = [b'b', b'K', b'G', b'D']; +/// Image last-modification time +pub const tIME: ChunkType = [b't', b'I', b'M', b'E']; +/// Physical pixel dimensions +pub const pHYs: ChunkType = [b'p', b'H', b'Y', b's']; + +// -- Extension chunks -- + +/// Animation control +pub const acTL: ChunkType = [b'a', b'c', b'T', b'L']; +/// Frame control +pub const fcTL: ChunkType = [b'f', b'c', b'T', b'L']; +/// Frame data +pub const fdAT: ChunkType = [b'f', b'd', b'A', b'T']; + +// -- Chunk type determination -- + +/// Returns true if the chunk is critical. +pub fn is_critical(type_: ChunkType) -> bool { + type_[0] & 32 == 0 +} + +/// Returns true if the chunk is private. +pub fn is_private(type_: ChunkType) -> bool { + type_[1] & 32 != 0 +} + +/// Checks whether the reserved bit of the chunk name is set. +/// If it is set the chunk name is invalid. +pub fn reserved_set(type_: ChunkType) -> bool { + type_[2] & 32 != 0 +} + +/// Returns true if the chunk is safe to copy if unknown. +pub fn safe_to_copy(type_: ChunkType) -> bool { + type_[3] & 32 != 0 +}
\ No newline at end of file diff --git a/third_party/rust/png/src/common.rs b/third_party/rust/png/src/common.rs new file mode 100644 index 0000000000..014efb751e --- /dev/null +++ b/third_party/rust/png/src/common.rs @@ -0,0 +1,404 @@ +//! Common types shared between the encoder and decoder +use crate::filter; + +use std::fmt; + +/// Describes the layout of samples in a pixel +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u8)] +pub enum ColorType { + Grayscale = 0, + RGB = 2, + Indexed = 3, + GrayscaleAlpha = 4, + RGBA = 6 +} + +impl ColorType { + /// Returns the number of samples used per pixel of `ColorType` + pub fn samples(&self) -> usize { + use self::ColorType::*; + match *self { + Grayscale | Indexed => 1, + RGB => 3, + GrayscaleAlpha => 2, + RGBA => 4 + } + } + + /// u8 -> Self. Temporary solution until Rust provides a canonical one. + pub fn from_u8(n: u8) -> Option<ColorType> { + match n { + 0 => Some(ColorType::Grayscale), + 2 => Some(ColorType::RGB), + 3 => Some(ColorType::Indexed), + 4 => Some(ColorType::GrayscaleAlpha), + 6 => Some(ColorType::RGBA), + _ => None + } + } +} + +/// Bit depth of the png file +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u8)] +pub enum BitDepth { + One = 1, + Two = 2, + Four = 4, + Eight = 8, + Sixteen = 16, +} + +impl BitDepth { + /// u8 -> Self. Temporary solution until Rust provides a canonical one. + pub fn from_u8(n: u8) -> Option<BitDepth> { + match n { + 1 => Some(BitDepth::One), + 2 => Some(BitDepth::Two), + 4 => Some(BitDepth::Four), + 8 => Some(BitDepth::Eight), + 16 => Some(BitDepth::Sixteen), + _ => None + } + } +} + +/// Pixel dimensions information +#[derive(Clone, Copy, Debug)] +pub struct PixelDimensions { + /// Pixels per unit, X axis + pub xppu: u32, + /// Pixels per unit, Y axis + pub yppu: u32, + /// Either *Meter* or *Unspecified* + pub unit: Unit, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u8)] +/// Physical unit of the pixel dimensions +pub enum Unit { + Unspecified = 0, + Meter = 1, +} + +impl Unit { + /// u8 -> Self. Temporary solution until Rust provides a canonical one. + pub fn from_u8(n: u8) -> Option<Unit> { + match n { + 0 => Some(Unit::Unspecified), + 1 => Some(Unit::Meter), + _ => None + } + } +} + +/// How to reset buffer of an animated png (APNG) at the end of a frame. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u8)] +pub enum DisposeOp { + /// Leave the buffer unchanged. + None = 0, + /// Clear buffer with the background color. + Background = 1, + /// Reset the buffer to the state before the current frame. + Previous = 2, +} + +impl DisposeOp { + /// u8 -> Self. Using enum_primitive or transmute is probably the right thing but this will do for now. + pub fn from_u8(n: u8) -> Option<DisposeOp> { + match n { + 0 => Some(DisposeOp::None), + 1 => Some(DisposeOp::Background), + 2 => Some(DisposeOp::Previous), + _ => None + } + } +} + +impl fmt::Display for DisposeOp { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let name = match *self { + DisposeOp::None => "DISPOSE_OP_NONE", + DisposeOp::Background => "DISPOSE_OP_BACKGROUND", + DisposeOp::Previous => "DISPOSE_OP_PREVIOUS", + }; + write!(f, "{}", name) + } +} + +/// How pixels are written into the buffer. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u8)] +pub enum BlendOp { + /// Pixels overwrite the value at their position. + Source = 0, + /// The new pixels are blended into the current state based on alpha. + Over = 1, +} + +impl BlendOp { + /// u8 -> Self. Using enum_primitive or transmute is probably the right thing but this will do for now. + pub fn from_u8(n: u8) -> Option<BlendOp> { + match n { + 0 => Some(BlendOp::Source), + 1 => Some(BlendOp::Over), + _ => None + } + } +} + +impl fmt::Display for BlendOp { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let name = match *self { + BlendOp::Source => "BLEND_OP_SOURCE", + BlendOp::Over => "BLEND_OP_OVER", + }; + write!(f, "{}", name) + } +} + +/// Frame control information +#[derive(Clone, Copy, Debug)] +pub struct FrameControl { + /// Sequence number of the animation chunk, starting from 0 + pub sequence_number: u32, + /// Width of the following frame + pub width: u32, + /// Height of the following frame + pub height: u32, + /// X position at which to render the following frame + pub x_offset: u32, + /// Y position at which to render the following frame + pub y_offset: u32, + /// Frame delay fraction numerator + pub delay_num: u16, + /// Frame delay fraction denominator + pub delay_den: u16, + /// Type of frame area disposal to be done after rendering this frame + pub dispose_op: DisposeOp, + /// Type of frame area rendering for this frame + pub blend_op: BlendOp, +} + +impl Default for FrameControl { + fn default() -> FrameControl { + FrameControl { + sequence_number: 0, + width: 0, + height: 0, + x_offset: 0, + y_offset: 0, + delay_num: 1, + delay_den: 30, + dispose_op: DisposeOp::None, + blend_op: BlendOp::Source, + } + } +} + +impl FrameControl { + pub fn set_seq_num(&mut self, s: u32) { + self.sequence_number = s; + } + + pub fn inc_seq_num(&mut self, i: u32) { + self.sequence_number += i; + } + +} + +/// Animation control information +#[derive(Clone, Copy, Debug)] +pub struct AnimationControl { + /// Number of frames + pub num_frames: u32, + /// Number of times to loop this APNG. 0 indicates infinite looping. + pub num_plays: u32, +} + +/// The type and strength of applied compression. +#[derive(Debug, Clone)] +pub enum Compression { + /// Default level + Default, + /// Fast minimal compression + Fast, + /// Higher compression level + /// + /// Best in this context isn't actually the highest possible level + /// the encoder can do, but is meant to emulate the `Best` setting in the `Flate2` + /// library. + Best, + Huffman, + Rle, +} + +/// PNG info struct +#[derive(Debug)] +pub struct Info { + pub width: u32, + pub height: u32, + pub bit_depth: BitDepth, + pub color_type: ColorType, + pub interlaced: bool, + pub trns: Option<Vec<u8>>, + pub pixel_dims: Option<PixelDimensions>, + pub palette: Option<Vec<u8>>, + pub frame_control: Option<FrameControl>, + pub animation_control: Option<AnimationControl>, + pub compression: Compression, + pub filter: filter::FilterType, +} + +impl Default for Info { + fn default() -> Info { + Info { + width: 0, + height: 0, + bit_depth: BitDepth::Eight, + color_type: ColorType::Grayscale, + interlaced: false, + palette: None, + trns: None, + pixel_dims: None, + frame_control: None, + animation_control: None, + // Default to `deflate::Compresion::Fast` and `filter::FilterType::Sub` + // to maintain backward compatible output. + compression: Compression::Fast, + filter: filter::FilterType::Sub, + } + } +} + +impl Info { + /// Size of the image + pub fn size(&self) -> (u32, u32) { + (self.width, self.height) + } + + /// Returns true if the image is an APNG image. + pub fn is_animated(&self) -> bool { + self.frame_control.is_some() && self.animation_control.is_some() + } + + /// Returns the frame control information of the image + pub fn animation_control(&self) -> Option<&AnimationControl> { + self.animation_control.as_ref() + } + + /// Returns the frame control information of the current frame + pub fn frame_control(&self) -> Option<&FrameControl> { + self.frame_control.as_ref() + } + + /// Returns the bits per pixel + pub fn bits_per_pixel(&self) -> usize { + self.color_type.samples() * self.bit_depth as usize + } + + /// Returns the bytes per pixel + pub fn bytes_per_pixel(&self) -> usize { + self.color_type.samples() * ((self.bit_depth as usize + 7) >> 3) + } + + /// Returns the number of bytes needed for one deinterlaced image + pub fn raw_bytes(&self) -> usize { + self.height as usize * self.raw_row_length() + } + + /// Returns the number of bytes needed for one deinterlaced row + pub fn raw_row_length(&self) -> usize { + let bits = self.width as usize * self.color_type.samples() * self.bit_depth as usize; + let extra = bits % 8; + bits/8 + + match extra { 0 => 0, _ => 1 } + + 1 // filter method + } + + /// Returns the number of bytes needed for one deinterlaced row of width `width` + pub fn raw_row_length_from_width(&self, width: u32) -> usize { + let bits = width as usize * self.color_type.samples() * self.bit_depth as usize; + let extra = bits % 8; + bits/8 + + match extra { 0 => 0, _ => 1 } + + 1 // filter method + } +} + + +bitflags! { + /// # Output transformations + /// + /// Only `IDENTITY` and `TRANSFORM_EXPAND | TRANSFORM_STRIP_ALPHA` can be used at the moment. + pub struct Transformations: u32 { + /// No transformation + const IDENTITY = 0x0000; // read and write */ + /// Strip 16-bit samples to 8 bits + const STRIP_16 = 0x0001; // read only */ + /// Discard the alpha channel + const STRIP_ALPHA = 0x0002; // read only */ + /// Expand 1; 2 and 4-bit samples to bytes + const PACKING = 0x0004; // read and write */ + /// Change order of packed pixels to LSB first + const PACKSWAP = 0x0008; // read and write */ + /// Expand paletted images to RGB; expand grayscale images of + /// less than 8-bit depth to 8-bit depth; and expand tRNS chunks + /// to alpha channels. + const EXPAND = 0x0010; // read only */ + /// Invert monochrome images + const INVERT_MONO = 0x0020; // read and write */ + /// Normalize pixels to the sBIT depth + const SHIFT = 0x0040; // read and write */ + /// Flip RGB to BGR; RGBA to BGRA + const BGR = 0x0080; // read and write */ + /// Flip RGBA to ARGB or GA to AG + const SWAP_ALPHA = 0x0100; // read and write */ + /// Byte-swap 16-bit samples + const SWAP_ENDIAN = 0x0200; // read and write */ + /// Change alpha from opacity to transparency + const INVERT_ALPHA = 0x0400; // read and write */ + const STRIP_FILLER = 0x0800; // write only */ + const STRIP_FILLER_BEFORE = 0x0800; // write only + const STRIP_FILLER_AFTER = 0x1000; // write only */ + const GRAY_TO_RGB = 0x2000; // read only */ + const EXPAND_16 = 0x4000; // read only */ + const SCALE_16 = 0x8000; // read only */ + } +} + +/// Mod to encapsulate the converters depending on the `deflate` crate. +/// +/// Since this only contains trait impls, there is no need to make this public, they are simply +/// available when the mod is compiled as well. +#[cfg(feature = "png-encoding")] +mod deflate_convert { + extern crate deflate; + use super::Compression; + + impl From<deflate::Compression> for Compression { + fn from(c: deflate::Compression) -> Self { + match c { + deflate::Compression::Default => Compression::Default, + deflate::Compression::Fast => Compression::Fast, + deflate::Compression::Best => Compression::Best, + } + } + } + + impl From<Compression> for deflate::CompressionOptions { + fn from(c: Compression) -> Self { + match c { + Compression::Default => deflate::CompressionOptions::default(), + Compression::Fast => deflate::CompressionOptions::fast(), + Compression::Best => deflate::CompressionOptions::high(), + Compression::Huffman => deflate::CompressionOptions::huffman_only(), + Compression::Rle => deflate::CompressionOptions::rle(), + } + } + } +} + diff --git a/third_party/rust/png/src/decoder/mod.rs b/third_party/rust/png/src/decoder/mod.rs new file mode 100644 index 0000000000..7665983206 --- /dev/null +++ b/third_party/rust/png/src/decoder/mod.rs @@ -0,0 +1,543 @@ +mod stream; + +pub use self::stream::{StreamingDecoder, Decoded, DecodingError}; +use self::stream::{CHUNCK_BUFFER_SIZE, get_info}; + +use std::mem; +use std::borrow; +use std::io::{Read, Write, BufReader, BufRead}; + +use crate::common::{ColorType, BitDepth, Info, Transformations}; +use crate::filter::{unfilter, FilterType}; +use crate::chunk::IDAT; +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 +#[derive(Debug)] +pub struct OutputInfo { + pub width: u32, + pub height: u32, + pub color_type: ColorType, + pub bit_depth: BitDepth, + pub line_size: usize, +} + +impl OutputInfo { + /// Returns the size needed to hold a decoded frame + 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> { + /// Reader + r: R, + /// Output transformations + transform: Transformations, + /// Limits on resources the Decoder is allowed to use + limits: Limits, +} + +impl<R: Read> Decoder<R> { + pub fn new(r: R) -> Decoder<R> { + Decoder::new_with_limits(r, Limits::default()) + } + + pub fn new_with_limits(r: R, limits: Limits) -> Decoder<R> { + Decoder { + r, + transform: crate::Transformations::EXPAND | crate::Transformations::SCALE_16 | crate::Transformations::STRIP_16, + limits, + } + } + + /// Limit resource usage + /// + /// ``` + /// use std::fs::File; + /// use png::{Decoder, Limits}; + /// // This image is 32x32 pixels, so the deocder will allocate more than four bytes + /// let mut limits = Limits::default(); + /// limits.bytes = 4; + /// 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; + } + + /// Reads all meta data until the first IDAT chunk + pub fn read_info(self) -> Result<(OutputInfo, Reader<R>), DecodingError> { + let mut r = Reader::new(self.r, StreamingDecoder::new(), self.transform, self.limits); + r.init()?; + let (ct, bits) = r.output_color_type(); + let info = { + let info = r.info(); + OutputInfo { + width: info.width, + height: info.height, + color_type: ct, + bit_depth: bits, + line_size: r.output_line_size(info.width), + } + }; + Ok((info, r)) + } + + /// 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; + } +} + +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( + "unexpected EOF".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 info(&self) -> Option<&Info> { + get_info(&self.decoder) + } +} + +/// 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: usize, + rowlen: usize, + adam7: Option<utils::Adam7Iterator>, + /// Previous raw line + prev: Vec<u8>, + /// Current raw line + current: Vec<u8>, + /// Output transformations + transform: Transformations, + /// Processed line + processed: Vec<u8>, + limits: Limits, +} + +macro_rules! get_info( + ($this:expr) => { + $this.decoder.info().unwrap() + } +); + +impl<R: Read> Reader<R> { + /// Creates a new PNG reader + fn new(r: R, d: StreamingDecoder, t: Transformations, limits: Limits) -> Reader<R> { + Reader { + decoder: ReadDecoder { + reader: BufReader::with_capacity(CHUNCK_BUFFER_SIZE, r), + decoder: d, + at_eof: false + }, + bpp: 0, + rowlen: 0, + adam7: None, + prev: Vec::new(), + current: Vec::new(), + transform: t, + processed: Vec::new(), + limits, + } + } + + /// Reads all meta data until the first IDAT chunk + fn init(&mut self) -> Result<(), DecodingError> { + use crate::Decoded::*; + if self.decoder.info().is_some() { + Ok(()) + } else { + loop { + match self.decoder.decode_next(&mut Vec::new())? { + Some(ChunkBegin(_, IDAT)) => break, + None => return Err(DecodingError::Format( + "IDAT chunk missing".into() + )), + _ => (), + } + } + { + let info = match self.decoder.info() { + Some(info) => info, + None => return Err(DecodingError::Format( + "IHDR chunk missing".into() + )) + }; + self.bpp = info.bytes_per_pixel(); + self.rowlen = info.raw_row_length(); + if info.interlaced { + self.adam7 = Some(utils::Adam7Iterator::new(info.width, info.height)) + } + } + self.allocate_out_buf()?; + self.prev = vec![0; self.rowlen]; + Ok(()) + } + } + + pub fn info(&self) -> &Info { + get_info!(self) + } + + /// Decodes the next frame into `buf` + pub fn next_frame(&mut self, buf: &mut [u8]) -> Result<(), DecodingError> { + // TODO 16 bit + let (color_type, bit_depth) = self.output_color_type(); + let width = get_info!(self).width; + if buf.len() < self.output_buffer_size() { + return Err(DecodingError::Other( + "supplied buffer is too small to hold the image".into() + )) + } + if get_info!(self).interlaced { + while let Some((row, adam7)) = self.next_interlaced_row()? { + let (pass, line, _) = adam7.unwrap(); + let samples = color_type.samples() as u8; + utils::expand_pass(buf, width, row, pass, line, samples * (bit_depth as u8)); + } + } else { + let mut len = 0; + while let Some(row) = self.next_row()? { + len += (&mut buf[len..]).write(row)?; + } + } + Ok(()) + } + + /// Returns the next processed row of the image + pub fn next_row(&mut self) -> Result<Option<&[u8]>, DecodingError> { + self.next_interlaced_row().map(|v| v.map(|v| v.0)) + } + + /// Returns the next processed row of the image + pub fn next_interlaced_row(&mut self) -> Result<Option<(&[u8], Option<(u8, u32, u32)>)>, DecodingError> { + use crate::common::ColorType::*; + let transform = self.transform; + if transform == crate::Transformations::IDENTITY { + self.next_raw_interlaced_row() + } else { + // swap buffer to circumvent borrow issues + let mut buffer = mem::replace(&mut self.processed, Vec::new()); + let (got_next, adam7) = if let Some((row, adam7)) = self.next_raw_interlaced_row()? { + (&mut buffer[..]).write_all(row)?; + (true, adam7) + } else { + (false, None) + }; + // swap back + let _ = mem::replace(&mut self.processed, buffer); + if got_next { + let (color_type, bit_depth, trns) = { + let info = get_info!(self); + (info.color_type, info.bit_depth as u8, info.trns.is_some()) + }; + let output_buffer = if let Some((_, _, width)) = adam7 { + let width = self.line_size(width); + &mut self.processed[..width] + } else { + &mut *self.processed + }; + let mut len = output_buffer.len(); + if transform.contains(crate::Transformations::EXPAND) { + match color_type { + Indexed => { + expand_paletted(output_buffer, get_info!(self))? + } + Grayscale | GrayscaleAlpha if bit_depth < 8 => expand_gray_u8( + output_buffer, get_info!(self) + ), + Grayscale | RGB if trns => { + let channels = color_type.samples(); + let trns = get_info!(self).trns.as_ref().unwrap(); + if bit_depth == 8 { + utils::expand_trns_line(output_buffer, &*trns, channels); + } else { + utils::expand_trns_line16(output_buffer, &*trns, channels); + } + }, + _ => () + } + } + if bit_depth == 16 && transform.intersects(crate::Transformations::SCALE_16 | crate::Transformations::STRIP_16) { + len /= 2; + for i in 0..len { + output_buffer[i] = output_buffer[2 * i]; + } + } + Ok(Some(( + &output_buffer[..len], + adam7 + ))) + } else { + Ok(None) + } + } + } + + /// 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(&mut self) -> (ColorType, BitDepth) { + use crate::common::ColorType::*; + let t = self.transform; + let info = get_info!(self); + if t == crate::Transformations::IDENTITY { + (info.color_type, info.bit_depth) + } else { + let bits = match info.bit_depth as u8 { + 16 if t.intersects( + crate::Transformations::SCALE_16 | crate::Transformations::STRIP_16 + ) => 8, + n if n < 8 && t.contains(crate::Transformations::EXPAND) => 8, + n => n + }; + let color_type = if t.contains(crate::Transformations::EXPAND) { + let has_trns = info.trns.is_some(); + 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) = get_info!(self).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 size = self.line_size(width); + if get_info!(self).bit_depth as u8 == 16 && self.transform.intersects( + crate::Transformations::SCALE_16 | crate::Transformations::STRIP_16 + ) { + size / 2 + } else { + size + } + } + + /// Returns the number of bytes required to decode a deinterlaced row. + fn line_size(&self, width: u32) -> usize { + use crate::common::ColorType::*; + let t = self.transform; + let info = get_info!(self); + let trns = info.trns.is_some(); + // TODO 16 bit + let bits = match info.color_type { + Indexed if trns && t.contains(crate::Transformations::EXPAND) => 4 * 8, + Indexed if t.contains(crate::Transformations::EXPAND) => 3 * 8, + RGB if trns && t.contains(crate::Transformations::EXPAND) => 4 * 8, + Grayscale if trns && t.contains(crate::Transformations::EXPAND) => 2 * 8, + Grayscale if t.contains(crate::Transformations::EXPAND) => 1 * 8, + GrayscaleAlpha if t.contains(crate::Transformations::EXPAND) => 2 * 8, + // divide by 2 as it will get mutiplied by two later + _ if info.bit_depth as u8 == 16 => info.bits_per_pixel() / 2, + _ => info.bits_per_pixel() + } + * width as usize + * if info.bit_depth as u8 == 16 { 2 } else { 1 }; + let len = bits / 8; + let extra = bits % 8; + len + match extra { 0 => 0, _ => 1 } + } + + fn allocate_out_buf(&mut self) -> Result<(), DecodingError> { + let width = get_info!(self).width; + let bytes = self.limits.bytes; + if bytes < self.line_size(width) { + return Err(DecodingError::LimitsExceeded); + } + self.processed = vec![0; self.line_size(width)]; + Ok(()) + } + + /// Returns the next raw row of the image + fn next_raw_interlaced_row(&mut self) -> Result<Option<(&[u8], Option<(u8, u32, u32)>)>, DecodingError> { + let _ = get_info!(self); + let bpp = self.bpp; + let (rowlen, passdata) = if let Some(ref mut adam7) = self.adam7 { + let last_pass = adam7.current_pass(); + if let Some((pass, line, len)) = adam7.next() { + let rowlen = get_info!(self).raw_row_length_from_width(len); + if last_pass != pass { + self.prev.clear(); + for _ in 0..rowlen { + self.prev.push(0); + } + } + (rowlen, Some((pass, line, len))) + } else { + return Ok(None) + } + } else { + (self.rowlen, None) + }; + loop { + if self.current.len() >= rowlen { + if let Some(filter) = FilterType::from_u8(self.current[0]) { + if let Err(message) = unfilter(filter, bpp, &self.prev[1..rowlen], &mut self.current[1..rowlen]) { + return Err(DecodingError::Format( + borrow::Cow::Borrowed(message) + )) + } + self.prev[..rowlen].copy_from_slice(&self.current[..rowlen]); + self.current.drain(0..rowlen); + return Ok( + Some(( + &self.prev[1..rowlen], + passdata + )) + ) + } else { + return Err(DecodingError::Format( + format!("invalid filter method ({})", self.current[0]).into() + )) + } + } else { + let val = self.decoder.decode_next(&mut self.current)?; + match val { + Some(Decoded::ImageData) => {} + None => { + if !self.current.is_empty() { + return Err(DecodingError::Format( + "file truncated".into() + )) + } else { + return Ok(None) + } + } + _ => () + } + } + } + } +} + +fn expand_paletted(buffer: &mut [u8], info: &Info) -> Result<(), DecodingError> { + if let Some(palette) = info.palette.as_ref() { + if let BitDepth::Sixteen = info.bit_depth { + Err(DecodingError::Format("Bit depth '16' is not valid for paletted images".into())) + } else { + let black = [0, 0, 0]; + if let Some(ref trns) = info.trns { + 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("missing palette".into())) + } +} + +fn expand_gray_u8(buffer: &mut [u8], info: &Info) { + let rescale = true; + let scaling_factor = if rescale { + (255)/((1u16 << info.bit_depth as u8) - 1) as u8 + } else { + 1 + }; + if let Some(ref trns) = info.trns { + utils::unpack_bits(buffer, 2, info.bit_depth as u8, |pixel, chunk| { + if pixel == trns[0] { + chunk[1] = 0 + } else { + chunk[1] = 0xFF + } + chunk[0] = pixel * scaling_factor + }) + } else { + utils::unpack_bits(buffer, 1, info.bit_depth as u8, |val, chunk| { + chunk[0] = val * scaling_factor + }) + } +} diff --git a/third_party/rust/png/src/decoder/stream.rs b/third_party/rust/png/src/decoder/stream.rs new file mode 100644 index 0000000000..648a0275e5 --- /dev/null +++ b/third_party/rust/png/src/decoder/stream.rs @@ -0,0 +1,665 @@ +extern crate crc32fast; +extern crate inflate; + +use std::borrow::Cow; +use std::default::Default; +use std::error; +use std::fmt; +use std::io; +use std::cmp::min; +use std::convert::From; + +use crc32fast::Hasher as Crc32; + +use self::inflate::InflateStream; +use crate::traits::ReadBytesExt; +use crate::common::{BitDepth, BlendOp, ColorType, DisposeOp, Info, Unit, PixelDimensions, AnimationControl, FrameControl}; +use crate::chunk::{self, ChunkType, IHDR, IDAT, IEND}; + +/// 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); + +fn zlib_stream() -> InflateStream { + if CHECKSUM_DISABLED { + InflateStream::from_zlib_no_checksum() + } else { + InflateStream::from_zlib() + } +} + +#[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, bool), + 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, + PartialChunk(ChunkType), + ImageEnd, +} + +#[derive(Debug)] +pub enum DecodingError { + IoError(io::Error), + Format(Cow<'static, str>), + InvalidSignature, + CrcMismatch { + /// bytes to skip to try to recover from this error + recover: usize, + /// Stored CRC32 value + crc_val: u32, + /// Calculated CRC32 sum + crc_sum: u32, + chunk: ChunkType + }, + Other(Cow<'static, str>), + CorruptFlateStream, + LimitsExceeded, +} + +impl error::Error for DecodingError { + fn description(&self) -> &str { + use self::DecodingError::*; + match *self { + IoError(ref err) => err.description(), + Format(ref desc) | Other(ref desc) => &desc, + InvalidSignature => "invalid signature", + CrcMismatch { .. } => "CRC error", + CorruptFlateStream => "compressed data stream corrupted", + LimitsExceeded => "limits are exceeded" + } + } +} + +impl fmt::Display for DecodingError { + fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { + write!(fmt, "{}", (self as &dyn error::Error).description()) + } +} + +impl From<io::Error> for DecodingError { + fn from(err: io::Error) -> DecodingError { + DecodingError::IoError(err) + } +} + +impl From<String> for DecodingError { + fn from(err: String) -> DecodingError { + DecodingError::Other(err.into()) + } +} + +impl From<DecodingError> for io::Error { + fn from(err: DecodingError) -> io::Error { + use std::error::Error; + match err { + DecodingError::IoError(err) => err, + err => io::Error::new( + io::ErrorKind::Other, + err.description() + ) + } + } +} + +/// PNG StreamingDecoder (low-level interface) +pub struct StreamingDecoder { + state: Option<State>, + current_chunk: ChunkState, + inflater: InflateStream, + info: Option<Info>, + current_seq_no: Option<u32>, + have_idat: bool, +} + +struct ChunkState { + /// Partial crc until now. + crc: Crc32, + + /// Remanining 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 { + state: Some(State::Signature(0, [0; 7])), + current_chunk: ChunkState::default(), + inflater: zlib_stream(), + info: None, + current_seq_no: None, + have_idat: false + } + } + + /// 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 = zlib_stream(); + self.info = None; + self.current_seq_no = None; + self.have_idat = false; + } + + /// 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::*; + + macro_rules! goto ( + ($n:expr, $state:expr) => ({ + self.state = Some($state); + Ok(($n, Decoded::Nothing)) + }); + ($state:expr) => ({ + self.state = Some($state); + Ok((1, Decoded::Nothing)) + }); + ($n:expr, $state:expr, emit $res:expr) => ({ + self.state = Some($state); + Ok(($n, $res)) + }); + ($state:expr, emit $res:expr) => ({ + self.state = Some($state); + Ok((1, $res)) + }) + ); + + let current_byte = buf[0]; + + // Driver should ensure that state is never None + let state = self.state.take().unwrap(); + //println!("state: {:?}", state); + + match state { + Signature(i, mut signature) if i < 7 => { + signature[i as usize] = current_byte; + goto!(Signature(i+1, signature)) + } + Signature(_, signature) if signature == [137, 80, 78, 71, 13, 10, 26] && current_byte == 10 => { + goto!(U32(U32Value::Length)) + } + Signature(..) => Err(DecodingError::InvalidSignature), + U32Byte3(type_, mut val) => { + use self::U32Value::*; + val |= u32::from(current_byte); + match type_ { + Length => goto!(U32(Type(val))), + Type(length) => { + let type_str = [ + (val >> 24) as u8, + (val >> 16) as u8, + (val >> 8) as u8, + val as u8 + ]; + self.current_chunk.crc.reset(); + self.current_chunk.crc.update(&type_str); + self.current_chunk.remaining = length; + goto!( + ReadChunk(type_str, true), + emit Decoded::ChunkBegin(length, type_str) + ) + }, + Crc(type_str) => { + let sum = self.current_chunk.crc.clone().finalize(); + if CHECKSUM_DISABLED || val == sum { + goto!( + State::U32(U32Value::Length), + emit if type_str == IEND { + Decoded::ImageEnd + } else { + Decoded::ChunkComplete(val, type_str) + } + ) + } else { + Err(DecodingError::CrcMismatch { + recover: 1, + crc_val: val, + crc_sum: sum, + chunk: type_str + }) + } + }, + } + }, + U32Byte2(type_, val) => { + goto!(U32Byte3(type_, val | u32::from(current_byte) << 8)) + }, + U32Byte1(type_, val) => { + goto!(U32Byte2(type_, val | u32::from(current_byte) << 16)) + }, + U32(type_) => { + goto!(U32Byte1(type_, u32::from(current_byte) << 24)) + }, + PartialChunk(type_str) => { + match type_str { + IDAT => { + self.have_idat = true; + goto!( + 0, + DecodeData(type_str, 0), + emit Decoded::PartialChunk(type_str) + ) + }, + chunk::fdAT => { + if let Some(seq_no) = self.current_seq_no { + 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(format!( + "Sequence is not in order, expected #{} got #{}.", + seq_no + 1, + next_seq_no + ).into())) + } + self.current_seq_no = Some(next_seq_no); + } else { + return Err(DecodingError::Format("fcTL chunk missing before fdAT chunk.".into())) + } + goto!( + 0, + DecodeData(type_str, 4), + emit Decoded::PartialChunk(type_str) + ) + }, + // Handle other chunks + _ => { + if self.current_chunk.remaining == 0 { // complete chunk + Ok((0, self.parse_chunk(type_str)?)) + } else { + goto!( + 0, ReadChunk(type_str, true), + emit Decoded::PartialChunk(type_str) + ) + } + } + } + + }, + ReadChunk(type_str, clear) => { + if clear { + self.current_chunk.raw_bytes.clear(); + } + if self.current_chunk.remaining > 0 { + let ChunkState { crc, remaining, raw_bytes } = &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 { + goto!(0, PartialChunk(type_str)) + } else { + let buf = &buf[..n as usize]; + crc.update(buf); + raw_bytes.extend_from_slice(buf); + *remaining -= n; + if *remaining == 0 { + goto!(n as usize, PartialChunk(type_str + )) + } else { + goto!(n as usize, ReadChunk(type_str, false)) + } + + } + } else { + goto!(0, U32(U32Value::Crc(type_str))) + } + } + 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, data) = self.inflater.update(chunk_data)?; + image_data.extend_from_slice(data); + n += c; + if n == chunk_len && data.is_empty() && c == 0 { + goto!( + 0, + ReadChunk(type_str, true), + emit Decoded::ImageData + ) + } else { + goto!( + 0, + DecodeData(type_str, n), + emit Decoded::ImageData + ) + } + } + } + } + + fn parse_chunk(&mut self, type_str: [u8; 4]) + -> Result<Decoded, DecodingError> { + self.state = Some(State::U32(U32Value::Crc(type_str))); + if self.info.is_none() && type_str != IHDR { + return Err(DecodingError::Format(format!( + "{} chunk appeared before IHDR chunk", String::from_utf8_lossy(&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::acTL => { + self.parse_actl() + } + chunk::fcTL => { + self.parse_fctl() + } + _ => 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 get_info_or_err(&self) -> Result<&Info, DecodingError> { + self.info.as_ref().ok_or_else(|| DecodingError::Format( + "IHDR chunk missing".into() + )) + } + + fn parse_fctl(&mut self) + -> Result<Decoded, DecodingError> { + let mut buf = &self.current_chunk.raw_bytes[..]; + let next_seq_no = buf.read_be()?; + + // Asuming 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(format!( + "Sequence is not in order, expected #{} got #{}.", + seq_no + 1, + next_seq_no + ).into())) + } + next_seq_no + } else { + if next_seq_no != 0 { + return Err(DecodingError::Format(format!( + "Sequence is not in order, expected #{} got #{}.", + 0, + next_seq_no + ).into())) + } + 0 + }); + self.inflater = zlib_stream(); + 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: match DisposeOp::from_u8(buf.read_be()?) { + Some(dispose_op) => dispose_op, + None => return Err(DecodingError::Format("invalid dispose operation".into())) + }, + blend_op : match BlendOp::from_u8(buf.read_be()?) { + Some(blend_op) => blend_op, + None => return Err(DecodingError::Format("invalid blend operation".into())) + }, + }; + 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( + "acTL chunk appeared after first IDAT chunk".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> { + if let Some(info) = self.info.as_mut() { + info.palette = Some(self.current_chunk.raw_bytes.clone()) + } + Ok(Decoded::Nothing) + } + + fn parse_trns(&mut self) + -> Result<Decoded, DecodingError> { + use crate::common::ColorType::*; + let (color_type, bit_depth) = { + let info = self.get_info_or_err()?; + (info.color_type, info.bit_depth as u8) + }; + let vec = self.current_chunk.raw_bytes.clone(); + let len = vec.len(); + let info = match self.info { + Some(ref mut info) => info, + None => return Err(DecodingError::Format( + "tRNS chunk occured before IHDR chunk".into() + )) + }; + info.trns = Some(vec); + let vec = info.trns.as_mut().unwrap(); + match color_type { + Grayscale => { + if len < 2 { + return Err(DecodingError::Format( + "not enough palette entries".into() + )) + } + if bit_depth < 16 { + vec[0] = vec[1]; + vec.truncate(1); + } + Ok(Decoded::Nothing) + }, + RGB => { + if len < 6 { + return Err(DecodingError::Format( + "not enough palette entries".into() + )) + } + if bit_depth < 16 { + vec[0] = vec[1]; + vec[1] = vec[3]; + vec[2] = vec[5]; + vec.truncate(3); + } + Ok(Decoded::Nothing) + }, + Indexed => { + let _ = info.palette.as_ref().ok_or_else(|| DecodingError::Format( + "tRNS chunk occured before PLTE chunk".into() + )); + Ok(Decoded::Nothing) + }, + c => Err(DecodingError::Format( + format!("tRNS chunk found for color type ({})", c as u8).into() + )) + } + + } + + fn parse_phys(&mut self) + -> Result<Decoded, DecodingError> { + if self.have_idat { + Err(DecodingError::Format( + "pHYs chunk appeared after first IDAT chunk".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( + format!("invalid unit ({})", unit).into() + )) + }; + let pixel_dims = PixelDimensions { + xppu, + yppu, + unit, + }; + self.info.as_mut().unwrap().pixel_dims = Some(pixel_dims); + Ok(Decoded::PixelDimensions(pixel_dims)) + } + } + + fn parse_ihdr(&mut self) + -> Result<Decoded, DecodingError> { + // TODO: check if color/bit depths combination is valid + 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( + format!("invalid bit depth ({})", bit_depth).into() + )) + }; + let color_type = buf.read_be()?; + let color_type = match ColorType::from_u8(color_type) { + Some(color_type) => color_type, + None => return Err(DecodingError::Format( + format!("invalid color type ({})", color_type).into() + )) + }; + match buf.read_be()? { // compression method + 0u8 => (), + n => return Err(DecodingError::Format( + format!("unknown compression method ({})", n).into() + )) + } + match buf.read_be()? { // filter method + 0u8 => (), + n => return Err(DecodingError::Format( + format!("unknown filter method ({})", n).into() + )) + } + let interlaced = match buf.read_be()? { + 0u8 => false, + 1 => { + true + }, + n => return Err(DecodingError::Format( + format!("unknown interlace method ({})", n).into() + )) + }; + let mut info = Info::default(); + + info.width = width; + info.height = height; + info.bit_depth = bit_depth; + info.color_type = color_type; + info.interlaced = interlaced; + self.info = Some(info); + Ok(Decoded::Header( + width, + height, + bit_depth, + color_type, + interlaced + )) + } +} + +impl Default for ChunkState { + fn default() -> Self { + ChunkState { + crc:Crc32::new(), + remaining: 0, + raw_bytes: Vec::with_capacity(CHUNCK_BUFFER_SIZE), + } + } +} + +#[inline(always)] +pub fn get_info(d: &StreamingDecoder) -> Option<&Info> { + d.info.as_ref() +} diff --git a/third_party/rust/png/src/encoder.rs b/third_party/rust/png/src/encoder.rs new file mode 100644 index 0000000000..ffa6629ec5 --- /dev/null +++ b/third_party/rust/png/src/encoder.rs @@ -0,0 +1,477 @@ +extern crate crc32fast; +extern crate deflate; + +use std::borrow::Cow; +use std::error; +use std::fmt; +use std::io::{self, Read, Write}; +use std::mem; +use std::result; + +use crc32fast::Hasher as Crc32; + +use crate::chunk; +use crate::common::{Info, ColorType, BitDepth, Compression}; +use crate::filter::{FilterType, filter}; +use crate::traits::WriteBytesExt; + +pub type Result<T> = result::Result<T, EncodingError>; + +#[derive(Debug)] +pub enum EncodingError { + IoError(io::Error), + Format(Cow<'static, str>), +} + +impl error::Error for EncodingError { + fn description(&self) -> &str { + use self::EncodingError::*; + match *self { + IoError(ref err) => err.description(), + Format(ref desc) => &desc, + } + } +} + +impl fmt::Display for EncodingError { + fn fmt(&self, fmt: &mut fmt::Formatter) -> result::Result<(), fmt::Error> { + write!(fmt, "{}", (self as &dyn error::Error).description()) + } +} + +impl From<io::Error> for EncodingError { + fn from(err: io::Error) -> EncodingError { + EncodingError::IoError(err) + } +} +impl From<EncodingError> for io::Error { + fn from(err: EncodingError) -> io::Error { + io::Error::new(io::ErrorKind::Other, (&err as &dyn error::Error).description()) + } +} + +/// PNG Encoder +pub struct Encoder<W: Write> { + w: W, + info: Info, +} + +impl<W: Write> Encoder<W> { + pub fn new(w: W, width: u32, height: u32) -> Encoder<W> { + let mut info = Info::default(); + info.width = width; + info.height = height; + Encoder { w, info } + } + + pub fn write_header(self) -> Result<Writer<W>> { + Writer::new(self.w, self.info).init() + } + + /// Set the color of the encoded image. + /// + /// These correspond to the color types in the png IHDR data that will be written. The length + /// of the image data that is later supplied must match the color type, otherwise an error will + /// be emitted. + pub fn set_color(&mut self, color: ColorType) { + self.info.color_type = color; + } + + /// Set the indicated depth of the image data. + pub fn set_depth(&mut self, depth: BitDepth) { + self.info.bit_depth = depth; + } + + /// Set compression parameters. + /// + /// Accepts a `Compression` or any type that can transform into a `Compression`. Notably `deflate::Compression` and + /// `deflate::CompressionOptions` which "just work". + pub fn set_compression<C: Into<Compression>>(&mut self, compression: C) { + self.info.compression = compression.into(); + } + + /// Set the used filter type. + /// + /// The default filter is [`FilterType::Sub`] which provides a basic prediction algorithm for + /// sample values based on the previous. For a potentially better compression ratio, at the + /// cost of more complex processing, try out [`FilterType::Paeth`]. + /// + /// [`FilterType::Sub`]: enum.FilterType.html#variant.Sub + /// [`FilterType::Paeth`]: enum.FilterType.html#variant.Paeth + pub fn set_filter(&mut self, filter: FilterType) { + self.info.filter = filter; + } +} + +/// PNG writer +pub struct Writer<W: Write> { + w: W, + info: Info, +} + +impl<W: Write> Writer<W> { + fn new(w: W, info: Info) -> Writer<W> { + Writer { w, info } + } + + fn init(mut self) -> Result<Self> { + if self.info.width == 0 { + return Err(EncodingError::Format("Zero width not allowed".into())); + } + + if self.info.height == 0 { + return Err(EncodingError::Format("Zero height not allowed".into())); + } + + self.w.write_all(&[137, 80, 78, 71, 13, 10, 26, 10])?; + let mut data = [0; 13]; + (&mut data[..]).write_be(self.info.width)?; + (&mut data[4..]).write_be(self.info.height)?; + data[8] = self.info.bit_depth as u8; + data[9] = self.info.color_type as u8; + data[12] = if self.info.interlaced { 1 } else { 0 }; + self.write_chunk(chunk::IHDR, &data)?; + Ok(self) + } + + pub fn write_chunk(&mut self, name: [u8; 4], data: &[u8]) -> Result<()> { + self.w.write_be(data.len() as u32)?; + self.w.write_all(&name)?; + self.w.write_all(data)?; + let mut crc = Crc32::new(); + crc.update(&name); + crc.update(data); + self.w.write_be(crc.finalize())?; + Ok(()) + } + + /// Writes the image data. + pub fn write_image_data(&mut self, data: &[u8]) -> Result<()> { + const MAX_CHUNK_LEN: u32 = (1u32 << 31) - 1; + let bpp = self.info.bytes_per_pixel(); + let in_len = self.info.raw_row_length() - 1; + let mut prev = vec![0; in_len]; + let mut current = vec![0; in_len]; + let data_size = in_len * self.info.height as usize; + if data_size != data.len() { + let message = format!("wrong data size, expected {} got {}", data_size, data.len()); + return Err(EncodingError::Format(message.into())); + } + let mut zlib = deflate::write::ZlibEncoder::new(Vec::new(), self.info.compression.clone()); + let filter_method = self.info.filter; + for line in data.chunks(in_len) { + current.copy_from_slice(&line); + zlib.write_all(&[filter_method as u8])?; + filter(filter_method, bpp, &prev, &mut current); + zlib.write_all(¤t)?; + mem::swap(&mut prev, &mut current); + } + let zlib_encoded = zlib.finish()?; + for chunk in zlib_encoded.chunks(MAX_CHUNK_LEN as usize) { + self.write_chunk(chunk::IDAT, &chunk)?; + } + Ok(()) + } + + /// Create an stream writer. + /// + /// This allows you create images that do not fit + /// in memory. The default chunk size is 4K, use + /// `stream_writer_with_size` to set another chuck + /// size. + pub fn stream_writer(&mut self) -> StreamWriter<W> { + self.stream_writer_with_size(4 * 1024) + } + + /// Create a stream writer with custom buffer size. + /// + /// See `stream_writer` + pub fn stream_writer_with_size(&mut self, size: usize) -> StreamWriter<W> { + StreamWriter::new(self, size) + } +} + +impl<W: Write> Drop for Writer<W> { + fn drop(&mut self) { + let _ = self.write_chunk(chunk::IEND, &[]); + } +} + +struct ChunkWriter<'a, W: Write> { + writer: &'a mut Writer<W>, + buffer: Vec<u8>, + index: usize, +} + +impl<'a, W: Write> ChunkWriter<'a, W> { + fn new(writer: &'a mut Writer<W>, buf_len: usize) -> ChunkWriter<'a, W> { + ChunkWriter { + writer, + buffer: vec![0; buf_len], + index: 0, + } + } +} + +impl<'a, W: Write> Write for ChunkWriter<'a, W> { + fn write(&mut self, mut buf: &[u8]) -> io::Result<usize> { + let written = buf.read(&mut self.buffer[self.index..])?; + self.index += written; + + if self.index + 1 >= self.buffer.len() { + self.writer.write_chunk(chunk::IDAT, &self.buffer)?; + self.index = 0; + } + + Ok(written) + } + + fn flush(&mut self) -> io::Result<()> { + if self.index > 0 { + self.writer.write_chunk(chunk::IDAT, &self.buffer[..=self.index])?; + } + self.index = 0; + Ok(()) + } +} + +impl<'a, W: Write> Drop for ChunkWriter<'a, W> { + fn drop(&mut self) { + let _ = self.flush(); + } +} + + +/// Streaming png writer +/// +/// This may silently fail in the destructor, so it is a good idea to call +/// [`finish`](#method.finish) or [`flush`](https://doc.rust-lang.org/stable/std/io/trait.Write.html#tymethod.flush) before dropping. +pub struct StreamWriter<'a, W: Write> { + writer: deflate::write::ZlibEncoder<ChunkWriter<'a, W>>, + prev_buf: Vec<u8>, + curr_buf: Vec<u8>, + index: usize, + bpp: usize, + filter: FilterType, +} + +impl<'a, W: Write> StreamWriter<'a, W> { + fn new(writer: &'a mut Writer<W>, buf_len: usize) -> StreamWriter<'a, W> { + let bpp = writer.info.bytes_per_pixel(); + let in_len = writer.info.raw_row_length() - 1; + let filter = writer.info.filter; + let prev_buf = vec![0; in_len]; + let curr_buf = vec![0; in_len]; + + let compression = writer.info.compression.clone(); + let chunk_writer = ChunkWriter::new(writer, buf_len); + let zlib = deflate::write::ZlibEncoder::new(chunk_writer, compression); + + StreamWriter { + writer: zlib, + index: 0, + prev_buf, + curr_buf, + bpp, + filter, + } + } + + pub fn finish(mut self) -> Result<()> { + // TODO: call `writer.finish` somehow? + self.flush()?; + Ok(()) + } +} + +impl<'a, W: Write> Write for StreamWriter<'a, W> { + fn write(&mut self, mut buf: &[u8]) -> io::Result<usize> { + let written = buf.read(&mut self.curr_buf[self.index..])?; + self.index += written; + + if self.index >= self.curr_buf.len() { + self.writer.write_all(&[self.filter as u8])?; + filter(self.filter, self.bpp, &self.prev_buf, &mut self.curr_buf); + self.writer.write_all(&self.curr_buf)?; + mem::swap(&mut self.prev_buf, &mut self.curr_buf); + self.index = 0; + } + + Ok(written) + } + + fn flush(&mut self) -> io::Result<()> { + self.writer.flush()?; + if self.index > 0 { + let message = format!("wrong data size, got {} bytes too many", self.index); + return Err(EncodingError::Format(message.into()).into()); + } + Ok(()) + } +} + +impl<'a, W: Write> Drop for StreamWriter<'a, W> { + fn drop(&mut self) { + let _ = self.flush(); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + extern crate glob; + + use rand::{thread_rng, Rng}; + use std::{io, cmp}; + use std::io::Write; + use std::fs::File; + + #[test] + fn roundtrip() { + // More loops = more random testing, but also more test wait time + for _ in 0..10 { + for path in glob::glob("tests/pngsuite/*.png").unwrap().map(|r| r.unwrap()) { + if path.file_name().unwrap().to_str().unwrap().starts_with("x") { + // x* files are expected to fail to decode + continue; + } + // Decode image + let decoder = crate::Decoder::new(File::open(path).unwrap()); + let (info, mut reader) = decoder.read_info().unwrap(); + if info.line_size != 32 { + // TODO encoding only works with line size 32? + continue; + } + let mut buf = vec![0; info.buffer_size()]; + reader.next_frame(&mut buf).unwrap(); + // Encode decoded image + let mut out = Vec::new(); + { + let mut wrapper = RandomChunkWriter { + rng: thread_rng(), + w: &mut out + }; + + let mut encoder = Encoder::new(&mut wrapper, info.width, info.height).write_header().unwrap(); + encoder.write_image_data(&buf).unwrap(); + } + // Decode encoded decoded image + let decoder = crate::Decoder::new(&*out); + let (info, mut reader) = decoder.read_info().unwrap(); + let mut buf2 = vec![0; info.buffer_size()]; + reader.next_frame(&mut buf2).unwrap(); + // check if the encoded image is ok: + assert_eq!(buf, buf2); + } + } + } + + #[test] + fn roundtrip_stream() { + // More loops = more random testing, but also more test wait time + for _ in 0..10 { + for path in glob::glob("tests/pngsuite/*.png").unwrap().map(|r| r.unwrap()) { + if path.file_name().unwrap().to_str().unwrap().starts_with("x") { + // x* files are expected to fail to decode + continue; + } + // Decode image + let decoder = crate::Decoder::new(File::open(path).unwrap()); + let (info, mut reader) = decoder.read_info().unwrap(); + if info.line_size != 32 { + // TODO encoding only works with line size 32? + continue; + } + let mut buf = vec![0; info.buffer_size()]; + reader.next_frame(&mut buf).unwrap(); + // Encode decoded image + let mut out = Vec::new(); + { + let mut wrapper = RandomChunkWriter { + rng: thread_rng(), + w: &mut out + }; + + let mut encoder = Encoder::new(&mut wrapper, info.width, info.height).write_header().unwrap(); + let mut stream_writer = encoder.stream_writer(); + + let mut outer_wrapper = RandomChunkWriter { + rng: thread_rng(), + w: &mut stream_writer + }; + + outer_wrapper.write_all(&buf).unwrap(); + } + // Decode encoded decoded image + let decoder = crate::Decoder::new(&*out); + let (info, mut reader) = decoder.read_info().unwrap(); + let mut buf2 = vec![0; info.buffer_size()]; + reader.next_frame(&mut buf2).unwrap(); + // check if the encoded image is ok: + assert_eq!(buf, buf2); + } + } + } + + #[test] + fn expect_error_on_wrong_image_len() -> Result<()> { + use std::io::Cursor; + + let width = 10; + let height = 10; + + let output = vec![0u8; 1024]; + let writer = Cursor::new(output); + let mut encoder = Encoder::new(writer, width as u32, height as u32); + encoder.set_depth(BitDepth::Eight); + encoder.set_color(ColorType::RGB); + let mut png_writer = encoder.write_header()?; + + let correct_image_size = width * height * 3; + let image = vec![0u8; correct_image_size + 1]; + let result = png_writer.write_image_data(image.as_ref()); + assert!(result.is_err()); + + Ok(()) + } + + #[test] + fn expect_error_on_empty_image() -> Result<()> { + use std::io::Cursor; + + let output = vec![0u8; 1024]; + let mut writer = Cursor::new(output); + + let encoder = Encoder::new(&mut writer, 0, 0); + assert!(encoder.write_header().is_err()); + + let encoder = Encoder::new(&mut writer, 100, 0); + assert!(encoder.write_header().is_err()); + + let encoder = Encoder::new(&mut writer, 0, 100); + assert!(encoder.write_header().is_err()); + + Ok(()) + } + + /// A Writer that only writes a few bytes at a time + struct RandomChunkWriter<'a, R: Rng, W: Write + 'a> { + rng: R, + w: &'a mut W + } + + impl<'a, R: Rng, W: Write + 'a> Write for RandomChunkWriter<'a, R, W> { + fn write(&mut self, buf: &[u8]) -> io::Result<usize> { + // choose a random length to write + let len = cmp::min(self.rng.gen_range(1, 50), buf.len()); + + self.w.write(&buf[0..len]) + } + + fn flush(&mut self) -> io::Result<()> { + self.w.flush() + } + } + +} diff --git a/third_party/rust/png/src/filter.rs b/third_party/rust/png/src/filter.rs new file mode 100644 index 0000000000..4f81d8e9f3 --- /dev/null +++ b/third_party/rust/png/src/filter.rs @@ -0,0 +1,158 @@ +use std; + +/// The byte level filter applied to scanlines to prepare them for compression. +/// +/// Compression in general benefits from repetitive data. The filter is a content-aware method of +/// compressing the range of occurring byte values to help the compression algorithm. Note that +/// this does not operate on pixels but on raw bytes of a scanline. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u8)] +pub enum FilterType { + NoFilter = 0, + Sub = 1, + Up = 2, + Avg = 3, + Paeth = 4 +} + + impl FilterType { + /// u8 -> Self. Temporary solution until Rust provides a canonical one. + pub fn from_u8(n: u8) -> Option<FilterType> { + match n { + 0 => Some(FilterType::NoFilter), + 1 => Some(FilterType::Sub), + 2 => Some(FilterType::Up), + 3 => Some(FilterType::Avg), + 4 => Some(FilterType::Paeth), + _ => None + } + } +} + +fn filter_paeth(a: u8, b: u8, c: u8) -> u8 { + let ia = i16::from(a); + let ib = i16::from(b); + let ic = i16::from(c); + + let p = ia + ib - ic; + + let pa = (p - ia).abs(); + let pb = (p - ib).abs(); + let pc = (p - ic).abs(); + + if pa <= pb && pa <= pc { + a + } else if pb <= pc { + b + } else { + c + } +} + +pub fn unfilter(filter: FilterType, bpp: usize, previous: &[u8], current: &mut [u8]) -> std::result::Result<(), &'static str> { + use self::FilterType::*; + assert!(bpp > 0); + let len = current.len(); + + match filter { + NoFilter => Ok(()), + Sub => { + for i in bpp..len { + current[i] = current[i].wrapping_add( + current[i - bpp] + ); + } + Ok(()) + } + Up => { + if previous.len() < len { + Err("Filtering failed: not enough data in previous row") + } else { + for i in 0..len { + current[i] = current[i].wrapping_add( + previous[i] + ); + } + Ok(()) + } + } + Avg => { + if previous.len() < len { + Err("Filtering failed: not enough data in previous row") + } else if bpp > len { + Err("Filtering failed: bytes per pixel is greater than length of row") + } else { + for i in 0..bpp { + current[i] = current[i].wrapping_add( + previous[i] / 2 + ); + } + + for i in bpp..len { + current[i] = current[i].wrapping_add( + ((i16::from(current[i - bpp]) + i16::from(previous[i])) / 2) as u8 + ); + } + Ok(()) + } + } + Paeth => { + if previous.len() < len { + Err("Filtering failed: not enough data in previous row") + } else if bpp > len { + Err("Filtering failed: bytes per pixel is greater than length of row") + } else { + for i in 0..bpp { + current[i] = current[i].wrapping_add( + filter_paeth(0, previous[i], 0) + ); + } + + for i in bpp..len { + current[i] = current[i].wrapping_add( + filter_paeth(current[i - bpp], previous[i], previous[i - bpp]) + ); + } + Ok(()) + } + } + } +} + +pub fn filter(method: FilterType, bpp: usize, previous: &[u8], current: &mut [u8]) { + use self::FilterType::*; + assert!(bpp > 0); + let len = current.len(); + + match method { + NoFilter => (), + Sub => { + for i in (bpp..len).rev() { + current[i] = current[i].wrapping_sub(current[i - bpp]); + } + } + Up => { + for i in 0..len { + current[i] = current[i].wrapping_sub(previous[i]); + } + } + Avg => { + for i in (bpp..len).rev() { + current[i] = current[i].wrapping_sub(current[i - bpp].wrapping_add(previous[i]) / 2); + } + + for i in 0..bpp { + current[i] = current[i].wrapping_sub(previous[i] / 2); + } + } + Paeth => { + for i in (bpp..len).rev() { + current[i] = current[i].wrapping_sub(filter_paeth(current[i - bpp], previous[i], previous[i - bpp])); + } + + for i in 0..bpp { + current[i] = current[i].wrapping_sub(filter_paeth(0, previous[i], 0)); + } + } + } +} diff --git a/third_party/rust/png/src/lib.rs b/third_party/rust/png/src/lib.rs new file mode 100644 index 0000000000..15e9ec3b15 --- /dev/null +++ b/third_party/rust/png/src/lib.rs @@ -0,0 +1,61 @@ +//! # PNG encoder and decoder +//! This crate contains a PNG encoder and decoder. It supports reading of single lines or whole frames. +//! ## The decoder +//! The most important types for decoding purposes are [`Decoder`](struct.Decoder.html) and +//! [`Reader`](struct.Reader.html). They both wrap a `std::io::Read`. +//! `Decoder` serves as a builder for `Reader`. Calling `Decoder::read_info` reads from the `Read` until the +//! image data is reached. +//! ### Using the decoder +//! use std::fs::File; +//! +//! // The decoder is a build for reader and can be used to set various decoding options +//! // via `Transformations`. The default output transformation is `Transformations::EXPAND +//! // | Transformations::STRIP_ALPHA`. +//! let decoder = png::Decoder::new(File::open("tests/pngsuite/basi0g01.png").unwrap()); +//! let (info, mut reader) = decoder.read_info().unwrap(); +//! // Allocate the output buffer. +//! let mut buf = vec![0; info.buffer_size()]; +//! // Read the next frame. Currently this function should only called once. +//! // The default options +//! reader.next_frame(&mut buf).unwrap(); +//! ## Encoder +//! ### Using the encoder +//! ```no_run +//! # #[cfg(feature = "png-encoding")] { +//! // For reading and opening files +//! use std::path::Path; +//! use std::fs::File; +//! use std::io::BufWriter; +//! +//! let path = Path::new(r"/path/to/image.png"); +//! let file = File::create(path).unwrap(); +//! let ref mut w = BufWriter::new(file); +//! +//! let mut encoder = png::Encoder::new(w, 2, 1); // Width is 2 pixels and height is 1. +//! encoder.set_color(png::ColorType::RGBA); +//! encoder.set_depth(png::BitDepth::Eight); +//! let mut writer = encoder.write_header().unwrap(); +//! +//! let data = [255, 0, 0, 255, 0, 0, 0, 255]; // An array containing a RGBA sequence. First pixel is red and second pixel is black. +//! writer.write_image_data(&data).unwrap(); // Save +//! # } +//! ``` +//! +//#![cfg_attr(test, feature(test))] + +#[macro_use] extern crate bitflags; + +pub mod chunk; +mod decoder; +#[cfg(feature = "png-encoding")] +mod encoder; +mod filter; +mod traits; +mod common; +mod utils; + +pub use crate::common::*; +pub use crate::decoder::{Decoder, Reader, OutputInfo, StreamingDecoder, Decoded, DecodingError, Limits}; +#[cfg(feature = "png-encoding")] +pub use crate::encoder::{Encoder, Writer, StreamWriter, EncodingError}; +pub use crate::filter::FilterType; diff --git a/third_party/rust/png/src/traits.rs b/third_party/rust/png/src/traits.rs new file mode 100644 index 0000000000..fac36f3699 --- /dev/null +++ b/third_party/rust/png/src/traits.rs @@ -0,0 +1,72 @@ +use std::io; + +// Will be replaced by stdlib solution +fn read_all<R: io::Read + ?Sized>(this: &mut R, buf: &mut [u8]) -> io::Result<()> { + let mut total = 0; + while total < buf.len() { + match this.read(&mut buf[total..]) { + Ok(0) => return Err(io::Error::new(io::ErrorKind::Other, + "failed to read the whole buffer")), + Ok(n) => total += n, + Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {} + Err(e) => return Err(e), + } + } + Ok(()) +} + +/// Read extension to read big endian data +pub trait ReadBytesExt<T>: io::Read { + /// Read `T` from a bytes stream. Most significant byte first. + fn read_be(&mut self) -> io::Result<T>; + +} + +/// Write extension to write big endian data +pub trait WriteBytesExt<T>: io::Write { + /// Writes `T` to a bytes stream. Most significant byte first. + fn write_be(&mut self, _: T) -> io::Result<()>; + +} + +impl<W: io::Read + ?Sized> ReadBytesExt<u8> for W { + #[inline] + fn read_be(&mut self) -> io::Result<u8> { + let mut byte = [0]; + read_all(self, &mut byte)?; + Ok(byte[0]) + } +} +impl<W: io::Read + ?Sized> ReadBytesExt<u16> for W { + #[inline] + fn read_be(&mut self) -> io::Result<u16> { + let mut bytes = [0, 0]; + read_all(self, &mut bytes)?; + Ok((u16::from(bytes[0])) << 8 | u16::from(bytes[1])) + } +} + +impl<W: io::Read + ?Sized> ReadBytesExt<u32> for W { + #[inline] + fn read_be(&mut self) -> io::Result<u32> { + let mut bytes = [0, 0, 0, 0]; + read_all(self, &mut bytes)?; + Ok( (u32::from(bytes[0])) << 24 + | (u32::from(bytes[1])) << 16 + | (u32::from(bytes[2])) << 8 + | u32::from(bytes[3]) + ) + } +} + +impl<W: io::Write + ?Sized> WriteBytesExt<u32> for W { + #[inline] + fn write_be(&mut self, n: u32) -> io::Result<()> { + self.write_all(&[ + (n >> 24) as u8, + (n >> 16) as u8, + (n >> 8) as u8, + n as u8 + ]) + } +} diff --git a/third_party/rust/png/src/utils.rs b/third_party/rust/png/src/utils.rs new file mode 100644 index 0000000000..1e91629dcf --- /dev/null +++ b/third_party/rust/png/src/utils.rs @@ -0,0 +1,376 @@ +//! Utility functions +use std::iter::{repeat, StepBy}; +use std::ops::Range; + +#[inline(always)] +pub fn unpack_bits<F>(buf: &mut [u8], channels: usize, bit_depth: u8, func: F) +where F: Fn(u8, &mut[u8]) { + // Return early if empty. This enables to subtract `channels` later without overflow. + if buf.len() < channels { + return; + } + + let bits = buf.len()/channels*bit_depth as usize; + let extra_bits = bits % 8; + let entries = bits / 8 + match extra_bits { + 0 => 0, + _ => 1 + }; + let skip = match extra_bits { + 0 => 0, + n => (8-n) / bit_depth as usize + }; + let mask = ((1u16 << bit_depth) - 1) as u8; + let i = + (0..entries) + .rev() // reverse iterator + .flat_map(|idx| + // this has to be reversed too + (0..8).step_by(bit_depth.into()) + .zip(repeat(idx)) + ) + .skip(skip); + let j = (0..=buf.len() - channels).rev().step_by(channels); + for ((shift, i), j) in i.zip(j) { + let pixel = (buf[i] & (mask << shift)) >> shift; + func(pixel, &mut buf[j..(j + channels)]) + } +} + +pub fn expand_trns_line(buf: &mut[u8], trns: &[u8], channels: usize) { + // Return early if empty. This enables to subtract `channels` later without overflow. + if buf.len() < (channels+1) { + return; + } + + let i = (0..=buf.len() / (channels+1) * channels - channels).rev().step_by(channels); + let j = (0..=buf.len() - (channels+1)).rev().step_by(channels+1); + for (i, j) in i.zip(j) { + let i_pixel = i; + let j_chunk = j; + if &buf[i_pixel..i_pixel+channels] == trns { + buf[j_chunk+channels] = 0 + } else { + buf[j_chunk+channels] = 0xFF + } + for k in (0..channels).rev() { + buf[j_chunk+k] = buf[i_pixel+k]; + } + } +} + +pub fn expand_trns_line16(buf: &mut[u8], trns: &[u8], channels: usize) { + let c2 = 2 * channels; + // Return early if empty. This enables to subtract `channels` later without overflow. + if buf.len() < (c2+2) { + return; + } + + let i = (0..=buf.len() / (c2+2) * c2 - c2).rev().step_by(c2); + let j = (0..=buf.len() - (c2+2)).rev().step_by(c2+2); + for (i, j) in i.zip(j) { + let i_pixel = i; + let j_chunk = j; + if &buf[i_pixel..i_pixel+c2] == trns { + buf[j_chunk+c2] = 0; + buf[j_chunk+c2 + 1] = 0 + } else { + buf[j_chunk+c2] = 0xFF; + buf[j_chunk+c2 + 1] = 0xFF + } + for k in (0..c2).rev() { + buf[j_chunk+k] = buf[i_pixel+k]; + } + } +} + + +/// This iterator iterates over the different passes of an image Adam7 encoded +/// PNG image +/// The pattern is: +/// 16462646 +/// 77777777 +/// 56565656 +/// 77777777 +/// 36463646 +/// 77777777 +/// 56565656 +/// 77777777 +/// +#[derive(Clone)] +pub struct Adam7Iterator { + line: u32, + lines: u32, + line_width: u32, + current_pass: u8, + width: u32, + height: u32, +} + +impl Adam7Iterator { + pub fn new(width: u32, height: u32) -> Adam7Iterator { + let mut this = Adam7Iterator { + line: 0, + lines: 0, + line_width: 0, + current_pass: 1, + width, + height, + }; + this.init_pass(); + this + } + + /// Calculates the bounds of the current pass + fn init_pass(&mut self) { + let w = f64::from(self.width); + let h = f64::from(self.height); + let (line_width, lines) = match self.current_pass { + 1 => (w/8.0, h/8.0), + 2 => ((w-4.0)/8.0, h/8.0), + 3 => (w/4.0, (h-4.0)/8.0), + 4 => ((w-2.0)/4.0, h/4.0), + 5 => (w/2.0, (h-2.0)/4.0), + 6 => ((w-1.0)/2.0, h/2.0), + 7 => (w, (h-1.0)/2.0), + _ => unreachable!() + }; + self.line_width = line_width.ceil() as u32; + self.lines = lines.ceil() as u32; + self.line = 0; + } + + /// The current pass#. + pub fn current_pass(&self) -> u8 { + self.current_pass + } +} + +/// Iterates over the (passes, lines, widths) +impl Iterator for Adam7Iterator { + type Item = (u8, u32, u32); + fn next(&mut self) -> Option<(u8, u32, u32)> { + if self.line < self.lines && self.line_width > 0 { + let this_line = self.line; + self.line += 1; + Some((self.current_pass, this_line, self.line_width)) + } else if self.current_pass < 7 { + self.current_pass += 1; + self.init_pass(); + self.next() + } else { + None + } + } +} + +fn subbyte_pixels<'a>(scanline: &'a [u8], bits_pp: usize) -> impl Iterator<Item=u8> + 'a { + (0..scanline.len() * 8).step_by(bits_pp).map(move |bit_idx| { + let byte_idx = bit_idx / 8; + + // sub-byte samples start in the high-order bits + let rem = 8 - bit_idx % 8 - bits_pp; + + match bits_pp { + // evenly divides bytes + 1 => (scanline[byte_idx] >> rem) & 1, + 2 => (scanline[byte_idx] >> rem) & 3, + 4 => (scanline[byte_idx] >> rem) & 15, + _ => unreachable!(), + } + }) +} + +/// Given pass, image width, and line number, produce an iterator of bit positions of pixels to copy +/// from the input scanline to the image buffer. +fn expand_adam7_bits(pass: u8, width: usize, line_no: usize, bits_pp: usize) -> StepBy<Range<usize>> { + let (line_mul, line_off, samp_mul, samp_off) = match pass { + 1 => (8, 0, 8, 0), + 2 => (8, 0, 8, 4), + 3 => (8, 4, 4, 0), + 4 => (4, 0, 4, 2), + 5 => (4, 2, 2, 0), + 6 => (2, 0, 2, 1), + 7 => (2, 1, 1, 0), + _ => panic!("Adam7 pass out of range: {}", pass) + }; + + // the equivalent line number in progressive scan + let prog_line = line_mul * line_no + line_off; + // line width is rounded up to the next byte + let line_width = (width * bits_pp + 7) & !7; + let line_start = prog_line * line_width; + let start = line_start + (samp_off * bits_pp); + let stop = line_start + (width * bits_pp); + + (start .. stop).step_by(bits_pp * samp_mul) +} + +/// Expands an Adam 7 pass +pub fn expand_pass( + img: &mut [u8], width: u32, scanline: &[u8], + pass: u8, line_no: u32, bits_pp: u8) { + + let width = width as usize; + let line_no = line_no as usize; + let bits_pp = bits_pp as usize; + + // pass is out of range but don't blow up + if pass == 0 || pass > 7 { return; } + + let bit_indices = expand_adam7_bits(pass, width, line_no, bits_pp); + + if bits_pp < 8 { + for (pos, px) in bit_indices.zip(subbyte_pixels(scanline, bits_pp)) { + let rem = 8 - pos % 8 - bits_pp; + img[pos / 8] |= px << rem as u8; + } + } else { + let bytes_pp = bits_pp / 8; + + for (bitpos, px) in bit_indices.zip(scanline.chunks(bytes_pp)) { + for (offset, val) in px.iter().enumerate() { + img[bitpos / 8 + offset] = *val; + } + } + } +} + +#[test] +fn test_adam7() { + /* + 1646 + 7777 + 5656 + 7777 + */ + let it = Adam7Iterator::new(4, 4); + let passes: Vec<_> = it.collect(); + assert_eq!(&*passes, &[(1, 0, 1), (4, 0, 1), (5, 0, 2), (6, 0, 2), (6, 1, 2), (7, 0, 4), (7, 1, 4)]); +} + +#[test] +fn test_subbyte_pixels() { + let scanline = &[0b10101010, 0b10101010]; + + + let pixels = subbyte_pixels(scanline, 1).collect::<Vec<_>>(); + assert_eq!(pixels.len(), 16); + assert_eq!(pixels, [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0]); +} + +#[test] +fn test_expand_adam7_bits() { + let width = 32; + let bits_pp = 1; + + let expected = |offset: usize, step: usize, count: usize| (0 .. count).map(move |i| step * i + offset).collect::<Vec<_>>(); + + for line_no in 0..8 { + let start = 8 * line_no * width; + + assert_eq!( + expand_adam7_bits(1, width, line_no, bits_pp).collect::<Vec<_>>(), + expected(start, 8, 4) + ); + + let start = start + 4; + + assert_eq!( + expand_adam7_bits(2, width, line_no, bits_pp).collect::<Vec<_>>(), + expected(start, 8, 4) + ); + + let start = (8 * line_no + 4) as usize * width as usize; + + assert_eq!( + expand_adam7_bits(3, width, line_no, bits_pp).collect::<Vec<_>>(), + expected(start, 4, 8) + ); + } + + for line_no in 0 .. 16 { + let start = 4 * line_no * width + 2; + + assert_eq!( + expand_adam7_bits(4, width, line_no, bits_pp).collect::<Vec<_>>(), + expected(start, 4, 8) + ); + + let start = (4 * line_no + 2) * width; + + assert_eq!( + expand_adam7_bits(5, width, line_no, bits_pp).collect::<Vec<_>>(), + expected(start, 2, 16) + ) + } + + for line_no in 0 .. 32 { + let start = 2 * line_no * width + 1; + + assert_eq!( + expand_adam7_bits(6, width, line_no, bits_pp).collect::<Vec<_>>(), + expected(start, 2, 16), + "line_no: {}", line_no + ); + + let start = (2 * line_no + 1) * width; + + assert_eq!( + expand_adam7_bits(7, width, line_no, bits_pp).collect::<Vec<_>>(), + expected(start, 1, 32) + ); + } +} + +#[test] +fn test_expand_pass_subbyte() { + let mut img = [0u8; 8]; + let width = 8; + let bits_pp = 1; + + expand_pass(&mut img, width, &[0b10000000], 1, 0, bits_pp); + assert_eq!(img, [0b10000000u8, 0, 0, 0, 0, 0, 0, 0]); + + expand_pass(&mut img, width, &[0b10000000], 2, 0, bits_pp); + assert_eq!(img, [0b10001000u8, 0, 0, 0, 0, 0, 0, 0]); + + expand_pass(&mut img, width, &[0b11000000], 3, 0, bits_pp); + assert_eq!(img, [0b10001000u8, 0, 0, 0, 0b10001000, 0, 0, 0]); + + expand_pass(&mut img, width, &[0b11000000], 4, 0, bits_pp); + assert_eq!(img, [0b10101010u8, 0, 0, 0, 0b10001000, 0, 0, 0]); + + expand_pass(&mut img, width, &[0b11000000], 4, 1, bits_pp); + assert_eq!(img, [0b10101010u8, 0, 0, 0, 0b10101010, 0, 0, 0]); + + expand_pass(&mut img, width, &[0b11110000], 5, 0, bits_pp); + assert_eq!(img, [0b10101010u8, 0, 0b10101010, 0, 0b10101010, 0, 0, 0]); + + expand_pass(&mut img, width, &[0b11110000], 5, 1, bits_pp); + assert_eq!(img, [0b10101010u8, 0, 0b10101010, 0, 0b10101010, 0, 0b10101010, 0]); + + expand_pass(&mut img, width, &[0b11110000], 6, 0, bits_pp); + assert_eq!(img, [0b11111111u8, 0, 0b10101010, 0, 0b10101010, 0, 0b10101010, 0]); + + expand_pass(&mut img, width, &[0b11110000], 6, 1, bits_pp); + assert_eq!(img, [0b11111111u8, 0, 0b11111111, 0, 0b10101010, 0, 0b10101010, 0]); + + expand_pass(&mut img, width, &[0b11110000], 6, 2, bits_pp); + assert_eq!(img, [0b11111111u8, 0, 0b11111111, 0, 0b11111111, 0, 0b10101010, 0]); + + expand_pass(&mut img, width, &[0b11110000], 6, 3, bits_pp); + assert_eq!([0b11111111u8, 0, 0b11111111, 0, 0b11111111, 0, 0b11111111, 0], img); + + expand_pass(&mut img, width, &[0b11111111], 7, 0, bits_pp); + assert_eq!([0b11111111u8, 0b11111111, 0b11111111, 0, 0b11111111, 0, 0b11111111, 0], img); + + expand_pass(&mut img, width, &[0b11111111], 7, 1, bits_pp); + assert_eq!([0b11111111u8, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0, 0b11111111, 0], img); + + expand_pass(&mut img, width, &[0b11111111], 7, 2, bits_pp); + assert_eq!([0b11111111u8, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0], img); + + expand_pass(&mut img, width, &[0b11111111], 7, 3, bits_pp); + assert_eq!([0b11111111u8, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111], img); +} |