summaryrefslogtreecommitdiffstats
path: root/third_party/rust/png/src
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /third_party/rust/png/src
parentInitial commit. (diff)
downloadfirefox-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.rs59
-rw-r--r--third_party/rust/png/src/common.rs404
-rw-r--r--third_party/rust/png/src/decoder/mod.rs543
-rw-r--r--third_party/rust/png/src/decoder/stream.rs665
-rw-r--r--third_party/rust/png/src/encoder.rs477
-rw-r--r--third_party/rust/png/src/filter.rs158
-rw-r--r--third_party/rust/png/src/lib.rs61
-rw-r--r--third_party/rust/png/src/traits.rs72
-rw-r--r--third_party/rust/png/src/utils.rs376
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(&current)?;
+ 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);
+}