summaryrefslogtreecommitdiffstats
path: root/third_party/rust/png/src/decoder
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/decoder
parentInitial commit. (diff)
downloadfirefox-upstream.tar.xz
firefox-upstream.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/decoder')
-rw-r--r--third_party/rust/png/src/decoder/mod.rs543
-rw-r--r--third_party/rust/png/src/decoder/stream.rs665
2 files changed, 1208 insertions, 0 deletions
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()
+}