Vendor things
This commit is contained in:
parent
5deceec006
commit
977e3c17e5
19434 changed files with 10682014 additions and 0 deletions
811
third-party/vendor/png/src/decoder/mod.rs
vendored
Normal file
811
third-party/vendor/png/src/decoder/mod.rs
vendored
Normal file
|
|
@ -0,0 +1,811 @@
|
|||
mod stream;
|
||||
pub(crate) mod transform;
|
||||
mod zlib;
|
||||
|
||||
pub use self::stream::{DecodeOptions, Decoded, DecodingError, StreamingDecoder};
|
||||
use self::stream::{FormatErrorInner, CHUNCK_BUFFER_SIZE};
|
||||
use self::transform::{create_transform_fn, TransformFn};
|
||||
|
||||
use std::io::{BufRead, BufReader, Read};
|
||||
use std::mem;
|
||||
use std::ops::Range;
|
||||
|
||||
use crate::adam7;
|
||||
use crate::chunk;
|
||||
use crate::common::{
|
||||
BitDepth, BytesPerPixel, ColorType, Info, ParameterErrorKind, Transformations,
|
||||
};
|
||||
use crate::filter::{unfilter, FilterType};
|
||||
|
||||
/*
|
||||
pub enum InterlaceHandling {
|
||||
/// Outputs the raw rows
|
||||
RawRows,
|
||||
/// Fill missing the pixels from the existing ones
|
||||
Rectangle,
|
||||
/// Only fill the needed pixels
|
||||
Sparkle
|
||||
}
|
||||
*/
|
||||
|
||||
/// Output info.
|
||||
///
|
||||
/// This describes one particular frame of the image that was written into the output buffer.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct OutputInfo {
|
||||
/// The pixel width of this frame.
|
||||
pub width: u32,
|
||||
/// The pixel height of this frame.
|
||||
pub height: u32,
|
||||
/// The chosen output color type.
|
||||
pub color_type: ColorType,
|
||||
/// The chosen output bit depth.
|
||||
pub bit_depth: BitDepth,
|
||||
/// The byte count of each scan line in the image.
|
||||
pub line_size: usize,
|
||||
}
|
||||
|
||||
impl OutputInfo {
|
||||
/// Returns the size needed to hold a decoded frame
|
||||
/// If the output buffer was larger then bytes after this count should be ignored. They may
|
||||
/// still have been changed.
|
||||
pub fn buffer_size(&self) -> usize {
|
||||
self.line_size * self.height as usize
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
/// Limits on the resources the `Decoder` is allowed too use
|
||||
pub struct Limits {
|
||||
/// maximum number of bytes the decoder is allowed to allocate, default is 64Mib
|
||||
pub bytes: usize,
|
||||
}
|
||||
|
||||
impl Limits {
|
||||
pub(crate) fn reserve_bytes(&mut self, bytes: usize) -> Result<(), DecodingError> {
|
||||
if self.bytes >= bytes {
|
||||
self.bytes -= bytes;
|
||||
Ok(())
|
||||
} else {
|
||||
Err(DecodingError::LimitsExceeded)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Limits {
|
||||
fn default() -> Limits {
|
||||
Limits {
|
||||
bytes: 1024 * 1024 * 64,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// PNG Decoder
|
||||
pub struct Decoder<R: Read> {
|
||||
read_decoder: ReadDecoder<R>,
|
||||
/// Output transformations
|
||||
transform: Transformations,
|
||||
}
|
||||
|
||||
/// A row of data with interlace information attached.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct InterlacedRow<'data> {
|
||||
data: &'data [u8],
|
||||
interlace: InterlaceInfo,
|
||||
}
|
||||
|
||||
impl<'data> InterlacedRow<'data> {
|
||||
pub fn data(&self) -> &'data [u8] {
|
||||
self.data
|
||||
}
|
||||
|
||||
pub fn interlace(&self) -> InterlaceInfo {
|
||||
self.interlace
|
||||
}
|
||||
}
|
||||
|
||||
/// PNG (2003) specifies two interlace modes, but reserves future extensions.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum InterlaceInfo {
|
||||
/// the null method means no interlacing
|
||||
Null,
|
||||
/// Adam7 derives its name from doing 7 passes over the image, only decoding a subset of all pixels in each pass.
|
||||
/// The following table shows pictorially what parts of each 8x8 area of the image is found in each pass:
|
||||
///
|
||||
/// 1 6 4 6 2 6 4 6
|
||||
/// 7 7 7 7 7 7 7 7
|
||||
/// 5 6 5 6 5 6 5 6
|
||||
/// 7 7 7 7 7 7 7 7
|
||||
/// 3 6 4 6 3 6 4 6
|
||||
/// 7 7 7 7 7 7 7 7
|
||||
/// 5 6 5 6 5 6 5 6
|
||||
/// 7 7 7 7 7 7 7 7
|
||||
Adam7 { pass: u8, line: u32, width: u32 },
|
||||
}
|
||||
|
||||
/// A row of data without interlace information.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Row<'data> {
|
||||
data: &'data [u8],
|
||||
}
|
||||
|
||||
impl<'data> Row<'data> {
|
||||
pub fn data(&self) -> &'data [u8] {
|
||||
self.data
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Read> Decoder<R> {
|
||||
/// Create a new decoder configuration with default limits.
|
||||
pub fn new(r: R) -> Decoder<R> {
|
||||
Decoder::new_with_limits(r, Limits::default())
|
||||
}
|
||||
|
||||
/// Create a new decoder configuration with custom limits.
|
||||
pub fn new_with_limits(r: R, limits: Limits) -> Decoder<R> {
|
||||
let mut decoder = StreamingDecoder::new();
|
||||
decoder.limits = limits;
|
||||
|
||||
Decoder {
|
||||
read_decoder: ReadDecoder {
|
||||
reader: BufReader::with_capacity(CHUNCK_BUFFER_SIZE, r),
|
||||
decoder,
|
||||
at_eof: false,
|
||||
},
|
||||
transform: Transformations::IDENTITY,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new decoder configuration with custom `DecodeOptions`.
|
||||
pub fn new_with_options(r: R, decode_options: DecodeOptions) -> Decoder<R> {
|
||||
let mut decoder = StreamingDecoder::new_with_options(decode_options);
|
||||
decoder.limits = Limits::default();
|
||||
|
||||
Decoder {
|
||||
read_decoder: ReadDecoder {
|
||||
reader: BufReader::with_capacity(CHUNCK_BUFFER_SIZE, r),
|
||||
decoder,
|
||||
at_eof: false,
|
||||
},
|
||||
transform: Transformations::IDENTITY,
|
||||
}
|
||||
}
|
||||
|
||||
/// Limit resource usage.
|
||||
///
|
||||
/// Note that your allocations, e.g. when reading into a pre-allocated buffer, are __NOT__
|
||||
/// considered part of the limits. Nevertheless, required intermediate buffers such as for
|
||||
/// singular lines is checked against the limit.
|
||||
///
|
||||
/// Note that this is a best-effort basis.
|
||||
///
|
||||
/// ```
|
||||
/// use std::fs::File;
|
||||
/// use png::{Decoder, Limits};
|
||||
/// // This image is 32×32, 1bit per pixel. The reader buffers one row which requires 4 bytes.
|
||||
/// let mut limits = Limits::default();
|
||||
/// limits.bytes = 3;
|
||||
/// let mut decoder = Decoder::new_with_limits(File::open("tests/pngsuite/basi0g01.png").unwrap(), limits);
|
||||
/// assert!(decoder.read_info().is_err());
|
||||
///
|
||||
/// // This image is 32x32 pixels, so the decoder will allocate less than 10Kib
|
||||
/// let mut limits = Limits::default();
|
||||
/// limits.bytes = 10*1024;
|
||||
/// let mut decoder = Decoder::new_with_limits(File::open("tests/pngsuite/basi0g01.png").unwrap(), limits);
|
||||
/// assert!(decoder.read_info().is_ok());
|
||||
/// ```
|
||||
pub fn set_limits(&mut self, limits: Limits) {
|
||||
self.read_decoder.decoder.limits = limits;
|
||||
}
|
||||
|
||||
/// Read the PNG header and return the information contained within.
|
||||
///
|
||||
/// Most image metadata will not be read until `read_info` is called, so those fields will be
|
||||
/// None or empty.
|
||||
pub fn read_header_info(&mut self) -> Result<&Info<'static>, DecodingError> {
|
||||
let mut buf = Vec::new();
|
||||
while self.read_decoder.info().is_none() {
|
||||
buf.clear();
|
||||
if self.read_decoder.decode_next(&mut buf)?.is_none() {
|
||||
return Err(DecodingError::Format(
|
||||
FormatErrorInner::UnexpectedEof.into(),
|
||||
));
|
||||
}
|
||||
}
|
||||
Ok(self.read_decoder.info().unwrap())
|
||||
}
|
||||
|
||||
/// Reads all meta data until the first IDAT chunk
|
||||
pub fn read_info(mut self) -> Result<Reader<R>, DecodingError> {
|
||||
self.read_header_info()?;
|
||||
|
||||
let mut reader = Reader {
|
||||
decoder: self.read_decoder,
|
||||
bpp: BytesPerPixel::One,
|
||||
subframe: SubframeInfo::not_yet_init(),
|
||||
fctl_read: 0,
|
||||
next_frame: SubframeIdx::Initial,
|
||||
data_stream: Vec::new(),
|
||||
prev_start: 0,
|
||||
current_start: 0,
|
||||
transform: self.transform,
|
||||
transform_fn: None,
|
||||
scratch_buffer: Vec::new(),
|
||||
};
|
||||
|
||||
// Check if the decoding buffer of a single raw line has a valid size.
|
||||
if reader.info().checked_raw_row_length().is_none() {
|
||||
return Err(DecodingError::LimitsExceeded);
|
||||
}
|
||||
|
||||
// Check if the output buffer has a valid size.
|
||||
let (width, height) = reader.info().size();
|
||||
let (color, depth) = reader.output_color_type();
|
||||
let rowlen = color
|
||||
.checked_raw_row_length(depth, width)
|
||||
.ok_or(DecodingError::LimitsExceeded)?
|
||||
- 1;
|
||||
let height: usize =
|
||||
std::convert::TryFrom::try_from(height).map_err(|_| DecodingError::LimitsExceeded)?;
|
||||
if rowlen.checked_mul(height).is_none() {
|
||||
return Err(DecodingError::LimitsExceeded);
|
||||
}
|
||||
|
||||
reader.read_until_image_data()?;
|
||||
Ok(reader)
|
||||
}
|
||||
|
||||
/// Set the allowed and performed transformations.
|
||||
///
|
||||
/// A transformation is a pre-processing on the raw image data modifying content or encoding.
|
||||
/// Many options have an impact on memory or CPU usage during decoding.
|
||||
pub fn set_transformations(&mut self, transform: Transformations) {
|
||||
self.transform = transform;
|
||||
}
|
||||
|
||||
/// Set the decoder to ignore all text chunks while parsing.
|
||||
///
|
||||
/// eg.
|
||||
/// ```
|
||||
/// use std::fs::File;
|
||||
/// use png::Decoder;
|
||||
/// let mut decoder = Decoder::new(File::open("tests/pngsuite/basi0g01.png").unwrap());
|
||||
/// decoder.set_ignore_text_chunk(true);
|
||||
/// assert!(decoder.read_info().is_ok());
|
||||
/// ```
|
||||
pub fn set_ignore_text_chunk(&mut self, ignore_text_chunk: bool) {
|
||||
self.read_decoder
|
||||
.decoder
|
||||
.set_ignore_text_chunk(ignore_text_chunk);
|
||||
}
|
||||
|
||||
/// Set the decoder to ignore and not verify the Adler-32 checksum
|
||||
/// and CRC code.
|
||||
pub fn ignore_checksums(&mut self, ignore_checksums: bool) {
|
||||
self.read_decoder
|
||||
.decoder
|
||||
.set_ignore_adler32(ignore_checksums);
|
||||
self.read_decoder.decoder.set_ignore_crc(ignore_checksums);
|
||||
}
|
||||
}
|
||||
|
||||
struct ReadDecoder<R: Read> {
|
||||
reader: BufReader<R>,
|
||||
decoder: StreamingDecoder,
|
||||
at_eof: bool,
|
||||
}
|
||||
|
||||
impl<R: Read> ReadDecoder<R> {
|
||||
/// Returns the next decoded chunk. If the chunk is an ImageData chunk, its contents are written
|
||||
/// into image_data.
|
||||
fn decode_next(&mut self, image_data: &mut Vec<u8>) -> Result<Option<Decoded>, DecodingError> {
|
||||
while !self.at_eof {
|
||||
let (consumed, result) = {
|
||||
let buf = self.reader.fill_buf()?;
|
||||
if buf.is_empty() {
|
||||
return Err(DecodingError::Format(
|
||||
FormatErrorInner::UnexpectedEof.into(),
|
||||
));
|
||||
}
|
||||
self.decoder.update(buf, image_data)?
|
||||
};
|
||||
self.reader.consume(consumed);
|
||||
match result {
|
||||
Decoded::Nothing => (),
|
||||
Decoded::ImageEnd => self.at_eof = true,
|
||||
result => return Ok(Some(result)),
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn finish_decoding(&mut self) -> Result<(), DecodingError> {
|
||||
while !self.at_eof {
|
||||
let buf = self.reader.fill_buf()?;
|
||||
if buf.is_empty() {
|
||||
return Err(DecodingError::Format(
|
||||
FormatErrorInner::UnexpectedEof.into(),
|
||||
));
|
||||
}
|
||||
let (consumed, event) = self.decoder.update(buf, &mut vec![])?;
|
||||
self.reader.consume(consumed);
|
||||
match event {
|
||||
Decoded::Nothing => (),
|
||||
Decoded::ImageEnd => self.at_eof = true,
|
||||
// ignore more data
|
||||
Decoded::ChunkComplete(_, _) | Decoded::ChunkBegin(_, _) | Decoded::ImageData => {}
|
||||
Decoded::ImageDataFlushed => return Ok(()),
|
||||
Decoded::PartialChunk(_) => {}
|
||||
new => unreachable!("{:?}", new),
|
||||
}
|
||||
}
|
||||
|
||||
Err(DecodingError::Format(
|
||||
FormatErrorInner::UnexpectedEof.into(),
|
||||
))
|
||||
}
|
||||
|
||||
fn info(&self) -> Option<&Info<'static>> {
|
||||
self.decoder.info.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
/// PNG reader (mostly high-level interface)
|
||||
///
|
||||
/// Provides a high level that iterates over lines or whole images.
|
||||
pub struct Reader<R: Read> {
|
||||
decoder: ReadDecoder<R>,
|
||||
bpp: BytesPerPixel,
|
||||
subframe: SubframeInfo,
|
||||
/// Number of frame control chunks read.
|
||||
/// By the APNG specification the total number must equal the count specified in the animation
|
||||
/// control chunk. The IDAT image _may_ have such a chunk applying to it.
|
||||
fctl_read: u32,
|
||||
next_frame: SubframeIdx,
|
||||
/// Vec containing the uncompressed image data currently being processed.
|
||||
data_stream: Vec<u8>,
|
||||
/// Index in `data_stream` where the previous row starts.
|
||||
prev_start: usize,
|
||||
/// Index in `data_stream` where the current row starts.
|
||||
current_start: usize,
|
||||
/// Output transformations
|
||||
transform: Transformations,
|
||||
/// Function that can transform decompressed, unfiltered rows into final output.
|
||||
/// See the `transform.rs` module for more details.
|
||||
transform_fn: Option<TransformFn>,
|
||||
/// This buffer is only used so that `next_row` and `next_interlaced_row` can return reference
|
||||
/// to a byte slice. In a future version of this library, this buffer will be removed and
|
||||
/// `next_row` and `next_interlaced_row` will write directly into a user provided output buffer.
|
||||
scratch_buffer: Vec<u8>,
|
||||
}
|
||||
|
||||
/// The subframe specific information.
|
||||
///
|
||||
/// In APNG the frames are constructed by combining previous frame and a new subframe (through a
|
||||
/// combination of `dispose_op` and `overlay_op`). These sub frames specify individual dimension
|
||||
/// information and reuse the global interlace options. This struct encapsulates the state of where
|
||||
/// in a particular IDAT-frame or subframe we are.
|
||||
struct SubframeInfo {
|
||||
width: u32,
|
||||
height: u32,
|
||||
rowlen: usize,
|
||||
interlace: InterlaceIter,
|
||||
consumed_and_flushed: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
enum InterlaceIter {
|
||||
None(Range<u32>),
|
||||
Adam7(adam7::Adam7Iterator),
|
||||
}
|
||||
|
||||
/// Denote a frame as given by sequence numbers.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
enum SubframeIdx {
|
||||
/// The initial frame in an IDAT chunk without fcTL chunk applying to it.
|
||||
/// Note that this variant precedes `Some` as IDAT frames precede fdAT frames and all fdAT
|
||||
/// frames must have a fcTL applying to it.
|
||||
Initial,
|
||||
/// An IDAT frame with fcTL or an fdAT frame.
|
||||
Some(u32),
|
||||
/// The past-the-end index.
|
||||
End,
|
||||
}
|
||||
|
||||
impl<R: Read> Reader<R> {
|
||||
/// Reads all meta data until the next frame data starts.
|
||||
/// Requires IHDR before the IDAT and fcTL before fdAT.
|
||||
fn read_until_image_data(&mut self) -> Result<(), DecodingError> {
|
||||
loop {
|
||||
// This is somewhat ugly. The API requires us to pass a buffer to decode_next but we
|
||||
// know that we will stop before reading any image data from the stream. Thus pass an
|
||||
// empty buffer and assert that remains empty.
|
||||
let mut buf = Vec::new();
|
||||
let state = self.decoder.decode_next(&mut buf)?;
|
||||
assert!(buf.is_empty());
|
||||
|
||||
match state {
|
||||
Some(Decoded::ChunkBegin(_, chunk::IDAT))
|
||||
| Some(Decoded::ChunkBegin(_, chunk::fdAT)) => break,
|
||||
Some(Decoded::FrameControl(_)) => {
|
||||
self.subframe = SubframeInfo::new(self.info());
|
||||
// The next frame is the one to which this chunk applies.
|
||||
self.next_frame = SubframeIdx::Some(self.fctl_read);
|
||||
// TODO: what about overflow here? That would imply there are more fctl chunks
|
||||
// than can be specified in the animation control but also that we have read
|
||||
// several gigabytes of data.
|
||||
self.fctl_read += 1;
|
||||
}
|
||||
None => {
|
||||
return Err(DecodingError::Format(
|
||||
FormatErrorInner::MissingImageData.into(),
|
||||
))
|
||||
}
|
||||
// Ignore all other chunk events. Any other chunk may be between IDAT chunks, fdAT
|
||||
// chunks and their control chunks.
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
let info = self
|
||||
.decoder
|
||||
.info()
|
||||
.ok_or(DecodingError::Format(FormatErrorInner::MissingIhdr.into()))?;
|
||||
self.bpp = info.bpp_in_prediction();
|
||||
self.subframe = SubframeInfo::new(info);
|
||||
|
||||
// Allocate output buffer.
|
||||
let buflen = self.output_line_size(self.subframe.width);
|
||||
self.decoder.decoder.limits.reserve_bytes(buflen)?;
|
||||
|
||||
self.prev_start = self.current_start;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get information on the image.
|
||||
///
|
||||
/// The structure will change as new frames of an animated image are decoded.
|
||||
pub fn info(&self) -> &Info<'static> {
|
||||
self.decoder.info().unwrap()
|
||||
}
|
||||
|
||||
/// Decodes the next frame into `buf`.
|
||||
///
|
||||
/// Note that this decodes raw subframes that need to be mixed according to blend-op and
|
||||
/// dispose-op by the caller.
|
||||
///
|
||||
/// The caller must always provide a buffer large enough to hold a complete frame (the APNG
|
||||
/// specification restricts subframes to the dimensions given in the image header). The region
|
||||
/// that has been written be checked afterwards by calling `info` after a successful call and
|
||||
/// inspecting the `frame_control` data. This requirement may be lifted in a later version of
|
||||
/// `png`.
|
||||
///
|
||||
/// Output lines will be written in row-major, packed matrix with width and height of the read
|
||||
/// frame (or subframe), all samples are in big endian byte order where this matters.
|
||||
pub fn next_frame(&mut self, buf: &mut [u8]) -> Result<OutputInfo, DecodingError> {
|
||||
let subframe_idx = match self.decoder.info().unwrap().frame_control() {
|
||||
None => SubframeIdx::Initial,
|
||||
Some(_) => SubframeIdx::Some(self.fctl_read - 1),
|
||||
};
|
||||
|
||||
if self.next_frame == SubframeIdx::End {
|
||||
return Err(DecodingError::Parameter(
|
||||
ParameterErrorKind::PolledAfterEndOfImage.into(),
|
||||
));
|
||||
} else if self.next_frame != subframe_idx {
|
||||
// Advance until we've read the info / fcTL for this frame.
|
||||
self.read_until_image_data()?;
|
||||
}
|
||||
|
||||
if buf.len() < self.output_buffer_size() {
|
||||
return Err(DecodingError::Parameter(
|
||||
ParameterErrorKind::ImageBufferSize {
|
||||
expected: buf.len(),
|
||||
actual: self.output_buffer_size(),
|
||||
}
|
||||
.into(),
|
||||
));
|
||||
}
|
||||
|
||||
let (color_type, bit_depth) = self.output_color_type();
|
||||
let output_info = OutputInfo {
|
||||
width: self.subframe.width,
|
||||
height: self.subframe.height,
|
||||
color_type,
|
||||
bit_depth,
|
||||
line_size: self.output_line_size(self.subframe.width),
|
||||
};
|
||||
|
||||
self.data_stream.clear();
|
||||
self.current_start = 0;
|
||||
self.prev_start = 0;
|
||||
let width = self.info().width;
|
||||
if self.info().interlaced {
|
||||
while let Some(InterlacedRow {
|
||||
data: row,
|
||||
interlace,
|
||||
..
|
||||
}) = self.next_interlaced_row()?
|
||||
{
|
||||
let (line, pass) = match interlace {
|
||||
InterlaceInfo::Adam7 { line, pass, .. } => (line, pass),
|
||||
InterlaceInfo::Null => unreachable!("expected interlace information"),
|
||||
};
|
||||
let samples = color_type.samples() as u8;
|
||||
adam7::expand_pass(buf, width, row, pass, line, samples * (bit_depth as u8));
|
||||
}
|
||||
} else {
|
||||
for row in buf
|
||||
.chunks_exact_mut(output_info.line_size)
|
||||
.take(self.subframe.height as usize)
|
||||
{
|
||||
self.next_interlaced_row_impl(self.subframe.rowlen, row)?;
|
||||
}
|
||||
}
|
||||
|
||||
// Advance over the rest of data for this (sub-)frame.
|
||||
if !self.subframe.consumed_and_flushed {
|
||||
self.decoder.finish_decoding()?;
|
||||
}
|
||||
|
||||
// Advance our state to expect the next frame.
|
||||
let past_end_subframe = self
|
||||
.info()
|
||||
.animation_control()
|
||||
.map(|ac| ac.num_frames)
|
||||
.unwrap_or(0);
|
||||
self.next_frame = match self.next_frame {
|
||||
SubframeIdx::End => unreachable!("Next frame called when already at image end"),
|
||||
// Reached the end of non-animated image.
|
||||
SubframeIdx::Initial if past_end_subframe == 0 => SubframeIdx::End,
|
||||
// An animated image, expecting first subframe.
|
||||
SubframeIdx::Initial => SubframeIdx::Some(0),
|
||||
// This was the last subframe, slightly fuzzy condition in case of programmer error.
|
||||
SubframeIdx::Some(idx) if past_end_subframe <= idx + 1 => SubframeIdx::End,
|
||||
// Expecting next subframe.
|
||||
SubframeIdx::Some(idx) => SubframeIdx::Some(idx + 1),
|
||||
};
|
||||
|
||||
Ok(output_info)
|
||||
}
|
||||
|
||||
/// Returns the next processed row of the image
|
||||
pub fn next_row(&mut self) -> Result<Option<Row>, DecodingError> {
|
||||
self.next_interlaced_row()
|
||||
.map(|v| v.map(|v| Row { data: v.data }))
|
||||
}
|
||||
|
||||
/// Returns the next processed row of the image
|
||||
pub fn next_interlaced_row(&mut self) -> Result<Option<InterlacedRow>, DecodingError> {
|
||||
let (rowlen, interlace) = match self.next_pass() {
|
||||
Some((rowlen, interlace)) => (rowlen, interlace),
|
||||
None => return Ok(None),
|
||||
};
|
||||
|
||||
let width = if let InterlaceInfo::Adam7 { width, .. } = interlace {
|
||||
width
|
||||
} else {
|
||||
self.subframe.width
|
||||
};
|
||||
let output_line_size = self.output_line_size(width);
|
||||
|
||||
// TODO: change the interface of `next_interlaced_row` to take an output buffer instead of
|
||||
// making us return a reference to a buffer that we own.
|
||||
let mut output_buffer = mem::take(&mut self.scratch_buffer);
|
||||
output_buffer.resize(output_line_size, 0u8);
|
||||
let ret = self.next_interlaced_row_impl(rowlen, &mut output_buffer);
|
||||
self.scratch_buffer = output_buffer;
|
||||
ret?;
|
||||
|
||||
Ok(Some(InterlacedRow {
|
||||
data: &self.scratch_buffer[..output_line_size],
|
||||
interlace,
|
||||
}))
|
||||
}
|
||||
|
||||
/// Read the rest of the image and chunks and finish up, including text chunks or others
|
||||
/// This will discard the rest of the image if the image is not read already with [`Reader::next_frame`], [`Reader::next_row`] or [`Reader::next_interlaced_row`]
|
||||
pub fn finish(&mut self) -> Result<(), DecodingError> {
|
||||
self.next_frame = SubframeIdx::End;
|
||||
self.data_stream.clear();
|
||||
self.current_start = 0;
|
||||
self.prev_start = 0;
|
||||
loop {
|
||||
let mut buf = Vec::new();
|
||||
let state = self.decoder.decode_next(&mut buf)?;
|
||||
|
||||
if state.is_none() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Fetch the next interlaced row and filter it according to our own transformations.
|
||||
fn next_interlaced_row_impl(
|
||||
&mut self,
|
||||
rowlen: usize,
|
||||
output_buffer: &mut [u8],
|
||||
) -> Result<(), DecodingError> {
|
||||
self.next_raw_interlaced_row(rowlen)?;
|
||||
assert_eq!(self.current_start - self.prev_start, rowlen - 1);
|
||||
let row = &self.data_stream[self.prev_start..self.current_start];
|
||||
|
||||
// Apply transformations and write resulting data to buffer.
|
||||
let transform_fn = {
|
||||
if self.transform_fn.is_none() {
|
||||
self.transform_fn = Some(create_transform_fn(self.info(), self.transform)?);
|
||||
}
|
||||
self.transform_fn.as_deref().unwrap()
|
||||
};
|
||||
transform_fn(row, output_buffer, self.info());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns the color type and the number of bits per sample
|
||||
/// of the data returned by `Reader::next_row` and Reader::frames`.
|
||||
pub fn output_color_type(&self) -> (ColorType, BitDepth) {
|
||||
use crate::common::ColorType::*;
|
||||
let t = self.transform;
|
||||
let info = self.info();
|
||||
if t == Transformations::IDENTITY {
|
||||
(info.color_type, info.bit_depth)
|
||||
} else {
|
||||
let bits = match info.bit_depth as u8 {
|
||||
16 if t.intersects(Transformations::STRIP_16) => 8,
|
||||
n if n < 8
|
||||
&& (t.contains(Transformations::EXPAND)
|
||||
|| t.contains(Transformations::ALPHA)) =>
|
||||
{
|
||||
8
|
||||
}
|
||||
n => n,
|
||||
};
|
||||
let color_type =
|
||||
if t.contains(Transformations::EXPAND) || t.contains(Transformations::ALPHA) {
|
||||
let has_trns = info.trns.is_some() || t.contains(Transformations::ALPHA);
|
||||
match info.color_type {
|
||||
Grayscale if has_trns => GrayscaleAlpha,
|
||||
Rgb if has_trns => Rgba,
|
||||
Indexed if has_trns => Rgba,
|
||||
Indexed => Rgb,
|
||||
ct => ct,
|
||||
}
|
||||
} else {
|
||||
info.color_type
|
||||
};
|
||||
(color_type, BitDepth::from_u8(bits).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the number of bytes required to hold a deinterlaced image frame
|
||||
/// that is decoded using the given input transformations.
|
||||
pub fn output_buffer_size(&self) -> usize {
|
||||
let (width, height) = self.info().size();
|
||||
let size = self.output_line_size(width);
|
||||
size * height as usize
|
||||
}
|
||||
|
||||
/// Returns the number of bytes required to hold a deinterlaced row.
|
||||
pub fn output_line_size(&self, width: u32) -> usize {
|
||||
let (color, depth) = self.output_color_type();
|
||||
color.raw_row_length_from_width(depth, width) - 1
|
||||
}
|
||||
|
||||
fn next_pass(&mut self) -> Option<(usize, InterlaceInfo)> {
|
||||
match self.subframe.interlace {
|
||||
InterlaceIter::Adam7(ref mut adam7) => {
|
||||
let last_pass = adam7.current_pass();
|
||||
let (pass, line, width) = adam7.next()?;
|
||||
let rowlen = self.info().raw_row_length_from_width(width);
|
||||
if last_pass != pass {
|
||||
self.prev_start = self.current_start;
|
||||
}
|
||||
Some((rowlen, InterlaceInfo::Adam7 { pass, line, width }))
|
||||
}
|
||||
InterlaceIter::None(ref mut height) => {
|
||||
let _ = height.next()?;
|
||||
Some((self.subframe.rowlen, InterlaceInfo::Null))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Write the next raw interlaced row into `self.prev`.
|
||||
///
|
||||
/// The scanline is filtered against the previous scanline according to the specification.
|
||||
fn next_raw_interlaced_row(&mut self, rowlen: usize) -> Result<(), DecodingError> {
|
||||
// Read image data until we have at least one full row (but possibly more than one).
|
||||
while self.data_stream.len() - self.current_start < rowlen {
|
||||
if self.subframe.consumed_and_flushed {
|
||||
return Err(DecodingError::Format(
|
||||
FormatErrorInner::NoMoreImageData.into(),
|
||||
));
|
||||
}
|
||||
|
||||
// Clear the current buffer before appending more data.
|
||||
if self.prev_start > 0 {
|
||||
self.data_stream.copy_within(self.prev_start.., 0);
|
||||
self.data_stream
|
||||
.truncate(self.data_stream.len() - self.prev_start);
|
||||
self.current_start -= self.prev_start;
|
||||
self.prev_start = 0;
|
||||
}
|
||||
|
||||
match self.decoder.decode_next(&mut self.data_stream)? {
|
||||
Some(Decoded::ImageData) => {}
|
||||
Some(Decoded::ImageDataFlushed) => {
|
||||
self.subframe.consumed_and_flushed = true;
|
||||
}
|
||||
None => {
|
||||
return Err(DecodingError::Format(
|
||||
if self.data_stream.is_empty() {
|
||||
FormatErrorInner::NoMoreImageData
|
||||
} else {
|
||||
FormatErrorInner::UnexpectedEndOfChunk
|
||||
}
|
||||
.into(),
|
||||
));
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
// Get a reference to the current row and point scan_start to the next one.
|
||||
let (prev, row) = self.data_stream.split_at_mut(self.current_start);
|
||||
|
||||
// Unfilter the row.
|
||||
let filter = FilterType::from_u8(row[0]).ok_or(DecodingError::Format(
|
||||
FormatErrorInner::UnknownFilterMethod(row[0]).into(),
|
||||
))?;
|
||||
unfilter(
|
||||
filter,
|
||||
self.bpp,
|
||||
&prev[self.prev_start..],
|
||||
&mut row[1..rowlen],
|
||||
);
|
||||
|
||||
self.prev_start = self.current_start + 1;
|
||||
self.current_start += rowlen;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl SubframeInfo {
|
||||
fn not_yet_init() -> Self {
|
||||
SubframeInfo {
|
||||
width: 0,
|
||||
height: 0,
|
||||
rowlen: 0,
|
||||
interlace: InterlaceIter::None(0..0),
|
||||
consumed_and_flushed: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn new(info: &Info) -> Self {
|
||||
// The apng fctnl overrides width and height.
|
||||
// All other data is set by the main info struct.
|
||||
let (width, height) = if let Some(fc) = info.frame_control {
|
||||
(fc.width, fc.height)
|
||||
} else {
|
||||
(info.width, info.height)
|
||||
};
|
||||
|
||||
let interlace = if info.interlaced {
|
||||
InterlaceIter::Adam7(adam7::Adam7Iterator::new(width, height))
|
||||
} else {
|
||||
InterlaceIter::None(0..height)
|
||||
};
|
||||
|
||||
SubframeInfo {
|
||||
width,
|
||||
height,
|
||||
rowlen: info.raw_row_length_from_width(width),
|
||||
interlace,
|
||||
consumed_and_flushed: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
1948
third-party/vendor/png/src/decoder/stream.rs
vendored
Normal file
1948
third-party/vendor/png/src/decoder/stream.rs
vendored
Normal file
File diff suppressed because it is too large
Load diff
203
third-party/vendor/png/src/decoder/transform.rs
vendored
Normal file
203
third-party/vendor/png/src/decoder/transform.rs
vendored
Normal file
|
|
@ -0,0 +1,203 @@
|
|||
//! Transforming a decompressed, unfiltered row into the final output.
|
||||
|
||||
mod palette;
|
||||
|
||||
use crate::{BitDepth, ColorType, DecodingError, Info, Transformations};
|
||||
|
||||
use super::stream::FormatErrorInner;
|
||||
|
||||
/// Type of a function that can transform a decompressed, unfiltered row (the
|
||||
/// 1st argument) into the final pixels (the 2nd argument), optionally using
|
||||
/// image metadata (e.g. PLTE data can be accessed using the 3rd argument).
|
||||
///
|
||||
/// TODO: If some precomputed state is needed (e.g. to make `expand_paletted...`
|
||||
/// faster) then consider changing this into `Box<dyn Fn(...)>`.
|
||||
pub type TransformFn = Box<dyn Fn(&[u8], &mut [u8], &Info) + Send + Sync>;
|
||||
|
||||
/// Returns a transformation function that should be applied to image rows based
|
||||
/// on 1) decoded image metadata (`info`) and 2) the transformations requested
|
||||
/// by the crate client (`transform`).
|
||||
pub fn create_transform_fn(
|
||||
info: &Info,
|
||||
transform: Transformations,
|
||||
) -> Result<TransformFn, DecodingError> {
|
||||
let color_type = info.color_type;
|
||||
let bit_depth = info.bit_depth as u8;
|
||||
let trns = info.trns.is_some() || transform.contains(Transformations::ALPHA);
|
||||
let expand =
|
||||
transform.contains(Transformations::EXPAND) || transform.contains(Transformations::ALPHA);
|
||||
let strip16 = bit_depth == 16 && transform.contains(Transformations::STRIP_16);
|
||||
match color_type {
|
||||
ColorType::Indexed if expand => {
|
||||
if info.palette.is_none() {
|
||||
return Err(DecodingError::Format(
|
||||
FormatErrorInner::PaletteRequired.into(),
|
||||
));
|
||||
} else if let BitDepth::Sixteen = info.bit_depth {
|
||||
// This should have been caught earlier but let's check again. Can't hurt.
|
||||
return Err(DecodingError::Format(
|
||||
FormatErrorInner::InvalidColorBitDepth {
|
||||
color_type: ColorType::Indexed,
|
||||
bit_depth: BitDepth::Sixteen,
|
||||
}
|
||||
.into(),
|
||||
));
|
||||
} else {
|
||||
Ok(if trns {
|
||||
palette::create_expansion_into_rgba8(info)
|
||||
} else {
|
||||
palette::create_expansion_into_rgb8(info)
|
||||
})
|
||||
}
|
||||
}
|
||||
ColorType::Grayscale | ColorType::GrayscaleAlpha if bit_depth < 8 && expand => {
|
||||
Ok(Box::new(if trns {
|
||||
expand_gray_u8_with_trns
|
||||
} else {
|
||||
expand_gray_u8
|
||||
}))
|
||||
}
|
||||
ColorType::Grayscale | ColorType::Rgb if expand && trns => {
|
||||
Ok(Box::new(if bit_depth == 8 {
|
||||
expand_trns_line
|
||||
} else if strip16 {
|
||||
expand_trns_and_strip_line16
|
||||
} else {
|
||||
assert_eq!(bit_depth, 16);
|
||||
expand_trns_line16
|
||||
}))
|
||||
}
|
||||
ColorType::Grayscale | ColorType::GrayscaleAlpha | ColorType::Rgb | ColorType::Rgba
|
||||
if strip16 =>
|
||||
{
|
||||
Ok(Box::new(transform_row_strip16))
|
||||
}
|
||||
_ => Ok(Box::new(copy_row)),
|
||||
}
|
||||
}
|
||||
|
||||
fn copy_row(row: &[u8], output_buffer: &mut [u8], _: &Info) {
|
||||
output_buffer.copy_from_slice(row);
|
||||
}
|
||||
|
||||
fn transform_row_strip16(row: &[u8], output_buffer: &mut [u8], _: &Info) {
|
||||
for i in 0..row.len() / 2 {
|
||||
output_buffer[i] = row[2 * i];
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn unpack_bits<F>(input: &[u8], output: &mut [u8], channels: usize, bit_depth: u8, func: F)
|
||||
where
|
||||
F: Fn(u8, &mut [u8]),
|
||||
{
|
||||
// Only [1, 2, 4, 8] are valid bit depths
|
||||
assert!(matches!(bit_depth, 1 | 2 | 4 | 8));
|
||||
// Check that `input` is capable of producing a buffer as long as `output`:
|
||||
// number of shift lookups per bit depth * channels * input length
|
||||
assert!((8 / bit_depth as usize * channels).saturating_mul(input.len()) >= output.len());
|
||||
|
||||
let mut buf_chunks = output.chunks_exact_mut(channels);
|
||||
let mut iter = input.iter();
|
||||
|
||||
// `shift` iterates through the corresponding bit depth sequence:
|
||||
// 1 => &[7, 6, 5, 4, 3, 2, 1, 0],
|
||||
// 2 => &[6, 4, 2, 0],
|
||||
// 4 => &[4, 0],
|
||||
// 8 => &[0],
|
||||
//
|
||||
// `(0..8).step_by(bit_depth.into()).rev()` doesn't always optimize well so
|
||||
// shifts are calculated instead. (2023-08, Rust 1.71)
|
||||
|
||||
if bit_depth == 8 {
|
||||
for (&curr, chunk) in iter.zip(&mut buf_chunks) {
|
||||
func(curr, chunk);
|
||||
}
|
||||
} else {
|
||||
let mask = ((1u16 << bit_depth) - 1) as u8;
|
||||
|
||||
// These variables are initialized in the loop
|
||||
let mut shift = -1;
|
||||
let mut curr = 0;
|
||||
|
||||
for chunk in buf_chunks {
|
||||
if shift < 0 {
|
||||
shift = 8 - bit_depth as i32;
|
||||
curr = *iter.next().expect("input for unpack bits is not empty");
|
||||
}
|
||||
|
||||
let pixel = (curr >> shift) & mask;
|
||||
func(pixel, chunk);
|
||||
|
||||
shift -= bit_depth as i32;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn expand_trns_line(input: &[u8], output: &mut [u8], info: &Info) {
|
||||
let channels = info.color_type.samples();
|
||||
let trns = info.trns.as_deref();
|
||||
for (input, output) in input
|
||||
.chunks_exact(channels)
|
||||
.zip(output.chunks_exact_mut(channels + 1))
|
||||
{
|
||||
output[..channels].copy_from_slice(input);
|
||||
output[channels] = if Some(input) == trns { 0 } else { 0xFF };
|
||||
}
|
||||
}
|
||||
|
||||
fn expand_trns_line16(input: &[u8], output: &mut [u8], info: &Info) {
|
||||
let channels = info.color_type.samples();
|
||||
let trns = info.trns.as_deref();
|
||||
for (input, output) in input
|
||||
.chunks_exact(channels * 2)
|
||||
.zip(output.chunks_exact_mut(channels * 2 + 2))
|
||||
{
|
||||
output[..channels * 2].copy_from_slice(input);
|
||||
if Some(input) == trns {
|
||||
output[channels * 2] = 0;
|
||||
output[channels * 2 + 1] = 0
|
||||
} else {
|
||||
output[channels * 2] = 0xFF;
|
||||
output[channels * 2 + 1] = 0xFF
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn expand_trns_and_strip_line16(input: &[u8], output: &mut [u8], info: &Info) {
|
||||
let channels = info.color_type.samples();
|
||||
let trns = info.trns.as_deref();
|
||||
for (input, output) in input
|
||||
.chunks_exact(channels * 2)
|
||||
.zip(output.chunks_exact_mut(channels + 1))
|
||||
{
|
||||
for i in 0..channels {
|
||||
output[i] = input[i * 2];
|
||||
}
|
||||
output[channels] = if Some(input) == trns { 0 } else { 0xFF };
|
||||
}
|
||||
}
|
||||
|
||||
fn expand_gray_u8(row: &[u8], buffer: &mut [u8], info: &Info) {
|
||||
let scaling_factor = (255) / ((1u16 << info.bit_depth as u8) - 1) as u8;
|
||||
unpack_bits(row, buffer, 1, info.bit_depth as u8, |val, chunk| {
|
||||
chunk[0] = val * scaling_factor
|
||||
});
|
||||
}
|
||||
|
||||
fn expand_gray_u8_with_trns(row: &[u8], buffer: &mut [u8], info: &Info) {
|
||||
let scaling_factor = (255) / ((1u16 << info.bit_depth as u8) - 1) as u8;
|
||||
let trns = info.trns.as_deref();
|
||||
unpack_bits(row, buffer, 2, info.bit_depth as u8, |pixel, chunk| {
|
||||
chunk[1] = if let Some(trns) = trns {
|
||||
if pixel == trns[0] {
|
||||
0
|
||||
} else {
|
||||
0xFF
|
||||
}
|
||||
} else {
|
||||
0xFF
|
||||
};
|
||||
chunk[0] = pixel * scaling_factor
|
||||
});
|
||||
}
|
||||
361
third-party/vendor/png/src/decoder/transform/palette.rs
vendored
Normal file
361
third-party/vendor/png/src/decoder/transform/palette.rs
vendored
Normal file
|
|
@ -0,0 +1,361 @@
|
|||
//! Helpers for taking a slice of indeces (indices into `PLTE` and/or `trNS`
|
||||
//! entries) and transforming this into RGB or RGBA output.
|
||||
//!
|
||||
//! # Memoization
|
||||
//!
|
||||
//! To achieve higher throughput, `create_rgba_palette` combines entries from
|
||||
//! `PLTE` and `trNS` chunks into a single lookup table. This is based on the
|
||||
//! ideas explored in https://crbug.com/706134.
|
||||
//!
|
||||
//! Memoization is a trade-off:
|
||||
//! * On one hand, memoization requires spending X ns before starting to call
|
||||
//! `expand_paletted_...` functions.
|
||||
//! * On the other hand, memoization improves the throughput of the
|
||||
//! `expand_paletted_...` functions - they take Y ns less to process each byte
|
||||
//!
|
||||
//! Based on X and Y, we can try to calculate the breakeven point. It seems
|
||||
//! that memoization is a net benefit for images bigger than around 13x13 pixels.
|
||||
|
||||
use super::{unpack_bits, TransformFn};
|
||||
use crate::{BitDepth, Info};
|
||||
|
||||
pub fn create_expansion_into_rgb8(info: &Info) -> TransformFn {
|
||||
let rgba_palette = create_rgba_palette(info);
|
||||
|
||||
if info.bit_depth == BitDepth::Eight {
|
||||
Box::new(move |input, output, _info| expand_8bit_into_rgb8(input, output, &rgba_palette))
|
||||
} else {
|
||||
Box::new(move |input, output, info| expand_into_rgb8(input, output, info, &rgba_palette))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_expansion_into_rgba8(info: &Info) -> TransformFn {
|
||||
let rgba_palette = create_rgba_palette(info);
|
||||
Box::new(move |input, output, info| {
|
||||
expand_paletted_into_rgba8(input, output, info, &rgba_palette)
|
||||
})
|
||||
}
|
||||
|
||||
fn create_rgba_palette(info: &Info) -> [[u8; 4]; 256] {
|
||||
let palette = info.palette.as_deref().expect("Caller should verify");
|
||||
let trns = info.trns.as_deref().unwrap_or(&[]);
|
||||
|
||||
// > The tRNS chunk shall not contain more alpha values than there are palette
|
||||
// entries, but a tRNS chunk may contain fewer values than there are palette
|
||||
// entries. In this case, the alpha value for all remaining palette entries is
|
||||
// assumed to be 255.
|
||||
//
|
||||
// It seems, accepted reading is to fully *ignore* an invalid tRNS as if it were
|
||||
// completely empty / all pixels are non-transparent.
|
||||
let trns = if trns.len() <= palette.len() / 3 {
|
||||
trns
|
||||
} else {
|
||||
&[]
|
||||
};
|
||||
|
||||
// Default to black, opaque entries.
|
||||
let mut rgba_palette = [[0, 0, 0, 0xFF]; 256];
|
||||
|
||||
// Copy `palette` (RGB) entries into `rgba_palette`. This may clobber alpha
|
||||
// values in `rgba_palette` - we need to fix this later.
|
||||
{
|
||||
let mut palette_iter = palette;
|
||||
let mut rgba_iter = &mut rgba_palette[..];
|
||||
while palette_iter.len() >= 4 {
|
||||
// Copying 4 bytes at a time is more efficient than copying 3.
|
||||
// OTOH, this clobbers the alpha value in `rgba_iter[0][3]` - we
|
||||
// need to fix this later.
|
||||
rgba_iter[0].copy_from_slice(&palette_iter[0..4]);
|
||||
|
||||
palette_iter = &palette_iter[3..];
|
||||
rgba_iter = &mut rgba_iter[1..];
|
||||
}
|
||||
if palette_iter.len() > 0 {
|
||||
rgba_iter[0][0..3].copy_from_slice(&palette_iter[0..3]);
|
||||
}
|
||||
}
|
||||
|
||||
// Copy `trns` (alpha) entries into `rgba_palette`. `trns.len()` may be
|
||||
// smaller than `palette.len()` and therefore this is not sufficient to fix
|
||||
// all the clobbered alpha values.
|
||||
for (alpha, rgba) in trns.iter().copied().zip(rgba_palette.iter_mut()) {
|
||||
rgba[3] = alpha;
|
||||
}
|
||||
|
||||
// Unclobber the remaining alpha values.
|
||||
for rgba in rgba_palette[trns.len()..(palette.len() / 3)].iter_mut() {
|
||||
rgba[3] = 0xFF;
|
||||
}
|
||||
|
||||
rgba_palette
|
||||
}
|
||||
|
||||
fn expand_8bit_into_rgb8(mut input: &[u8], mut output: &mut [u8], rgba_palette: &[[u8; 4]; 256]) {
|
||||
while output.len() >= 4 {
|
||||
// Copying 4 bytes at a time is more efficient than 3.
|
||||
let rgba = &rgba_palette[input[0] as usize];
|
||||
output[0..4].copy_from_slice(rgba);
|
||||
|
||||
input = &input[1..];
|
||||
output = &mut output[3..];
|
||||
}
|
||||
if output.len() > 0 {
|
||||
let rgba = &rgba_palette[input[0] as usize];
|
||||
output[0..3].copy_from_slice(&rgba[0..3]);
|
||||
}
|
||||
}
|
||||
|
||||
fn expand_into_rgb8(row: &[u8], buffer: &mut [u8], info: &Info, rgba_palette: &[[u8; 4]; 256]) {
|
||||
unpack_bits(row, buffer, 3, info.bit_depth as u8, |i, chunk| {
|
||||
let rgba = &rgba_palette[i as usize];
|
||||
chunk[0] = rgba[0];
|
||||
chunk[1] = rgba[1];
|
||||
chunk[2] = rgba[2];
|
||||
})
|
||||
}
|
||||
|
||||
fn expand_paletted_into_rgba8(
|
||||
row: &[u8],
|
||||
buffer: &mut [u8],
|
||||
info: &Info,
|
||||
rgba_palette: &[[u8; 4]; 256],
|
||||
) {
|
||||
unpack_bits(row, buffer, 4, info.bit_depth as u8, |i, chunk| {
|
||||
chunk.copy_from_slice(&rgba_palette[i as usize]);
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::{BitDepth, ColorType, Info, Transformations};
|
||||
|
||||
/// Old, non-memoized version of the code is used as a test oracle.
|
||||
fn oracle_expand_paletted_into_rgb8(row: &[u8], buffer: &mut [u8], info: &Info) {
|
||||
let palette = info.palette.as_deref().expect("Caller should verify");
|
||||
let black = [0, 0, 0];
|
||||
|
||||
super::unpack_bits(row, 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];
|
||||
})
|
||||
}
|
||||
|
||||
/// Old, non-memoized version of the code is used as a test oracle.
|
||||
fn oracle_expand_paletted_into_rgba8(row: &[u8], buffer: &mut [u8], info: &Info) {
|
||||
let palette = info.palette.as_deref().expect("Caller should verify");
|
||||
let trns = info.trns.as_deref().unwrap_or(&[]);
|
||||
let black = [0, 0, 0];
|
||||
|
||||
// > The tRNS chunk shall not contain more alpha values than there are palette
|
||||
// entries, but a tRNS chunk may contain fewer values than there are palette
|
||||
// entries. In this case, the alpha value for all remaining palette entries is
|
||||
// assumed to be 255.
|
||||
//
|
||||
// It seems, accepted reading is to fully *ignore* an invalid tRNS as if it were
|
||||
// completely empty / all pixels are non-transparent.
|
||||
let trns = if trns.len() <= palette.len() / 3 {
|
||||
trns
|
||||
} else {
|
||||
&[]
|
||||
};
|
||||
|
||||
super::unpack_bits(row, 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;
|
||||
});
|
||||
}
|
||||
|
||||
fn create_info<'a>(src_bit_depth: u8, palette: &'a [u8], trns: Option<&'a [u8]>) -> Info<'a> {
|
||||
Info {
|
||||
color_type: ColorType::Indexed,
|
||||
bit_depth: BitDepth::from_u8(src_bit_depth).unwrap(),
|
||||
palette: Some(palette.into()),
|
||||
trns: trns.map(Into::into),
|
||||
..Info::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn expand_paletted(
|
||||
src: &[u8],
|
||||
src_bit_depth: u8,
|
||||
palette: &[u8],
|
||||
trns: Option<&[u8]>,
|
||||
) -> Vec<u8> {
|
||||
let info = create_info(src_bit_depth, palette, trns);
|
||||
let output_bytes_per_input_sample = match trns {
|
||||
None => 3,
|
||||
Some(_) => 4,
|
||||
};
|
||||
let samples_count_per_byte = (8 / src_bit_depth) as usize;
|
||||
let samples_count = src.len() * samples_count_per_byte;
|
||||
|
||||
let mut dst = vec![0; samples_count * output_bytes_per_input_sample];
|
||||
let transform_fn =
|
||||
super::super::create_transform_fn(&info, Transformations::EXPAND).unwrap();
|
||||
transform_fn(src, dst.as_mut_slice(), &info);
|
||||
|
||||
{
|
||||
// Compare the memoization-based calculations with the old, non-memoized code.
|
||||
let mut simple_dst = vec![0; samples_count * output_bytes_per_input_sample];
|
||||
if trns.is_none() {
|
||||
oracle_expand_paletted_into_rgb8(src, &mut simple_dst, &info)
|
||||
} else {
|
||||
oracle_expand_paletted_into_rgba8(src, &mut simple_dst, &info)
|
||||
}
|
||||
assert_eq!(&dst, &simple_dst);
|
||||
}
|
||||
|
||||
dst
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_expand_paletted_rgba_8bit() {
|
||||
let actual = expand_paletted(
|
||||
&[0, 1, 2, 3], // src
|
||||
8, // src_bit_depth
|
||||
&[
|
||||
// palette
|
||||
0, 1, 2, // entry #0
|
||||
4, 5, 6, // entry #1
|
||||
8, 9, 10, // entry #2
|
||||
12, 13, 14, // entry #3
|
||||
],
|
||||
Some(&[3, 7, 11, 15]), // trns
|
||||
);
|
||||
assert_eq!(actual, (0..16).collect::<Vec<u8>>());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_expand_paletted_rgb_8bit() {
|
||||
let actual = expand_paletted(
|
||||
&[0, 1, 2, 3], // src
|
||||
8, // src_bit_depth
|
||||
&[
|
||||
// palette
|
||||
0, 1, 2, // entry #0
|
||||
3, 4, 5, // entry #1
|
||||
6, 7, 8, // entry #2
|
||||
9, 10, 11, // entry #3
|
||||
],
|
||||
None, // trns
|
||||
);
|
||||
assert_eq!(actual, (0..12).collect::<Vec<u8>>());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_expand_paletted_rgba_4bit() {
|
||||
let actual = expand_paletted(
|
||||
&[0x01, 0x23], // src
|
||||
4, // src_bit_depth
|
||||
&[
|
||||
// palette
|
||||
0, 1, 2, // entry #0
|
||||
4, 5, 6, // entry #1
|
||||
8, 9, 10, // entry #2
|
||||
12, 13, 14, // entry #3
|
||||
],
|
||||
Some(&[3, 7, 11, 15]), // trns
|
||||
);
|
||||
assert_eq!(actual, (0..16).collect::<Vec<u8>>());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_expand_paletted_rgb_4bit() {
|
||||
let actual = expand_paletted(
|
||||
&[0x01, 0x23], // src
|
||||
4, // src_bit_depth
|
||||
&[
|
||||
// palette
|
||||
0, 1, 2, // entry #0
|
||||
3, 4, 5, // entry #1
|
||||
6, 7, 8, // entry #2
|
||||
9, 10, 11, // entry #3
|
||||
],
|
||||
None, // trns
|
||||
);
|
||||
assert_eq!(actual, (0..12).collect::<Vec<u8>>());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_expand_paletted_rgba_8bit_more_trns_entries_than_palette_entries() {
|
||||
let actual = expand_paletted(
|
||||
&[0, 1, 2, 3], // src
|
||||
8, // src_bit_depth
|
||||
&[
|
||||
// palette
|
||||
0, 1, 2, // entry #0
|
||||
4, 5, 6, // entry #1
|
||||
8, 9, 10, // entry #2
|
||||
12, 13, 14, // entry #3
|
||||
],
|
||||
Some(&[123; 5]), // trns
|
||||
);
|
||||
|
||||
// Invalid (too-long) `trns` means that we'll use 0xFF / opaque alpha everywhere.
|
||||
assert_eq!(
|
||||
actual,
|
||||
vec![0, 1, 2, 0xFF, 4, 5, 6, 0xFF, 8, 9, 10, 0xFF, 12, 13, 14, 0xFF],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_expand_paletted_rgba_8bit_less_trns_entries_than_palette_entries() {
|
||||
let actual = expand_paletted(
|
||||
&[0, 1, 2, 3], // src
|
||||
8, // src_bit_depth
|
||||
&[
|
||||
// palette
|
||||
0, 1, 2, // entry #0
|
||||
4, 5, 6, // entry #1
|
||||
8, 9, 10, // entry #2
|
||||
12, 13, 14, // entry #3
|
||||
],
|
||||
Some(&[3, 7]), // trns
|
||||
);
|
||||
|
||||
// Too-short `trns` is treated differently from too-long - only missing entries are
|
||||
// replaced with 0XFF / opaque.
|
||||
assert_eq!(
|
||||
actual,
|
||||
vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0xFF, 12, 13, 14, 0xFF],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_create_rgba_palette() {
|
||||
fn create_expected_rgba_palette(plte: &[u8], trns: &[u8]) -> [[u8; 4]; 256] {
|
||||
let mut rgba = [[1, 2, 3, 4]; 256];
|
||||
for (i, rgba) in rgba.iter_mut().enumerate() {
|
||||
rgba[0] = plte.get(i * 3 + 0).map(|&r| r).unwrap_or(0);
|
||||
rgba[1] = plte.get(i * 3 + 1).map(|&g| g).unwrap_or(0);
|
||||
rgba[2] = plte.get(i * 3 + 2).map(|&b| b).unwrap_or(0);
|
||||
rgba[3] = trns.get(i * 1 + 0).map(|&a| a).unwrap_or(0xFF);
|
||||
}
|
||||
rgba
|
||||
}
|
||||
|
||||
for plte_len in 1..=32 {
|
||||
for trns_len in 0..=plte_len {
|
||||
let plte: Vec<u8> = (0..plte_len * 3).collect();
|
||||
let trns: Vec<u8> = (0..trns_len).map(|alpha| alpha + 200).collect();
|
||||
|
||||
let info = create_info(8, &plte, Some(&trns));
|
||||
let expected = create_expected_rgba_palette(&plte, &trns);
|
||||
let actual = super::create_rgba_palette(&info);
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
230
third-party/vendor/png/src/decoder/zlib.rs
vendored
Normal file
230
third-party/vendor/png/src/decoder/zlib.rs
vendored
Normal file
|
|
@ -0,0 +1,230 @@
|
|||
use super::{stream::FormatErrorInner, DecodingError, CHUNCK_BUFFER_SIZE};
|
||||
|
||||
use fdeflate::Decompressor;
|
||||
|
||||
/// Ergonomics wrapper around `miniz_oxide::inflate::stream` for zlib compressed data.
|
||||
pub(super) struct ZlibStream {
|
||||
/// Current decoding state.
|
||||
state: Box<fdeflate::Decompressor>,
|
||||
/// If there has been a call to decompress already.
|
||||
started: bool,
|
||||
/// Remaining buffered decoded bytes.
|
||||
/// The decoder sometimes wants inspect some already finished bytes for further decoding. So we
|
||||
/// keep a total of 32KB of decoded data available as long as more data may be appended.
|
||||
out_buffer: Vec<u8>,
|
||||
/// The first index of `out_buffer` where new data can be written.
|
||||
out_pos: usize,
|
||||
/// The first index of `out_buffer` that hasn't yet been passed to our client
|
||||
/// (i.e. not yet appended to the `image_data` parameter of `fn decompress` or `fn
|
||||
/// finish_compressed_chunks`).
|
||||
read_pos: usize,
|
||||
/// Limit on how many bytes can be decompressed in total. This field is mostly used for
|
||||
/// performance optimizations (e.g. to avoid allocating and zeroing out large buffers when only
|
||||
/// a small image is being decoded).
|
||||
max_total_output: usize,
|
||||
/// Ignore and do not calculate the Adler-32 checksum. Defaults to `true`.
|
||||
///
|
||||
/// This flag overrides `TINFL_FLAG_COMPUTE_ADLER32`.
|
||||
///
|
||||
/// This flag should not be modified after decompression has started.
|
||||
ignore_adler32: bool,
|
||||
}
|
||||
|
||||
impl ZlibStream {
|
||||
pub(crate) fn new() -> Self {
|
||||
ZlibStream {
|
||||
state: Box::new(Decompressor::new()),
|
||||
started: false,
|
||||
out_buffer: Vec::new(),
|
||||
out_pos: 0,
|
||||
read_pos: 0,
|
||||
max_total_output: usize::MAX,
|
||||
ignore_adler32: true,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn reset(&mut self) {
|
||||
self.started = false;
|
||||
self.out_buffer.clear();
|
||||
self.out_pos = 0;
|
||||
self.read_pos = 0;
|
||||
self.max_total_output = usize::MAX;
|
||||
*self.state = Decompressor::new();
|
||||
}
|
||||
|
||||
pub(crate) fn set_max_total_output(&mut self, n: usize) {
|
||||
self.max_total_output = n;
|
||||
}
|
||||
|
||||
/// Set the `ignore_adler32` flag and return `true` if the flag was
|
||||
/// successfully set.
|
||||
///
|
||||
/// The default is `true`.
|
||||
///
|
||||
/// This flag cannot be modified after decompression has started until the
|
||||
/// [ZlibStream] is reset.
|
||||
pub(crate) fn set_ignore_adler32(&mut self, flag: bool) -> bool {
|
||||
if !self.started {
|
||||
self.ignore_adler32 = flag;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the `ignore_adler32` flag.
|
||||
pub(crate) fn ignore_adler32(&self) -> bool {
|
||||
self.ignore_adler32
|
||||
}
|
||||
|
||||
/// Fill the decoded buffer as far as possible from `data`.
|
||||
/// On success returns the number of consumed input bytes.
|
||||
pub(crate) fn decompress(
|
||||
&mut self,
|
||||
data: &[u8],
|
||||
image_data: &mut Vec<u8>,
|
||||
) -> Result<usize, DecodingError> {
|
||||
// There may be more data past the adler32 checksum at the end of the deflate stream. We
|
||||
// match libpng's default behavior and ignore any trailing data. In the future we may want
|
||||
// to add a flag to control this behavior.
|
||||
if self.state.is_done() {
|
||||
return Ok(data.len());
|
||||
}
|
||||
|
||||
self.prepare_vec_for_appending();
|
||||
|
||||
if !self.started && self.ignore_adler32 {
|
||||
self.state.ignore_adler32();
|
||||
}
|
||||
|
||||
let (in_consumed, out_consumed) = self
|
||||
.state
|
||||
.read(data, self.out_buffer.as_mut_slice(), self.out_pos, false)
|
||||
.map_err(|err| {
|
||||
DecodingError::Format(FormatErrorInner::CorruptFlateStream { err }.into())
|
||||
})?;
|
||||
|
||||
self.started = true;
|
||||
self.out_pos += out_consumed;
|
||||
self.transfer_finished_data(image_data);
|
||||
self.compact_out_buffer_if_needed();
|
||||
|
||||
Ok(in_consumed)
|
||||
}
|
||||
|
||||
/// Called after all consecutive IDAT chunks were handled.
|
||||
///
|
||||
/// The compressed stream can be split on arbitrary byte boundaries. This enables some cleanup
|
||||
/// within the decompressor and flushing additional data which may have been kept back in case
|
||||
/// more data were passed to it.
|
||||
pub(crate) fn finish_compressed_chunks(
|
||||
&mut self,
|
||||
image_data: &mut Vec<u8>,
|
||||
) -> Result<(), DecodingError> {
|
||||
if !self.started {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
while !self.state.is_done() {
|
||||
self.prepare_vec_for_appending();
|
||||
let (_in_consumed, out_consumed) = self
|
||||
.state
|
||||
.read(&[], self.out_buffer.as_mut_slice(), self.out_pos, true)
|
||||
.map_err(|err| {
|
||||
DecodingError::Format(FormatErrorInner::CorruptFlateStream { err }.into())
|
||||
})?;
|
||||
|
||||
self.out_pos += out_consumed;
|
||||
|
||||
if !self.state.is_done() {
|
||||
let transferred = self.transfer_finished_data(image_data);
|
||||
assert!(
|
||||
transferred > 0 || out_consumed > 0,
|
||||
"No more forward progress made in stream decoding."
|
||||
);
|
||||
self.compact_out_buffer_if_needed();
|
||||
}
|
||||
}
|
||||
|
||||
self.transfer_finished_data(image_data);
|
||||
self.out_buffer.clear();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Resize the vector to allow allocation of more data.
|
||||
fn prepare_vec_for_appending(&mut self) {
|
||||
// The `debug_assert` below explains why we can use `>=` instead of `>` in the condition
|
||||
// that compares `self.out_post >= self.max_total_output` in the next `if` statement.
|
||||
debug_assert!(!self.state.is_done());
|
||||
if self.out_pos >= self.max_total_output {
|
||||
// This can happen when the `max_total_output` was miscalculated (e.g.
|
||||
// because the `IHDR` chunk was malformed and didn't match the `IDAT` chunk). In
|
||||
// this case, let's reset `self.max_total_output` before further calculations.
|
||||
self.max_total_output = usize::MAX;
|
||||
}
|
||||
|
||||
let current_len = self.out_buffer.len();
|
||||
let desired_len = self
|
||||
.out_pos
|
||||
.saturating_add(CHUNCK_BUFFER_SIZE)
|
||||
.min(self.max_total_output);
|
||||
if current_len >= desired_len {
|
||||
return;
|
||||
}
|
||||
|
||||
let buffered_len = self.decoding_size(self.out_buffer.len());
|
||||
debug_assert!(self.out_buffer.len() <= buffered_len);
|
||||
self.out_buffer.resize(buffered_len, 0u8);
|
||||
}
|
||||
|
||||
fn decoding_size(&self, len: usize) -> usize {
|
||||
// Allocate one more chunk size than currently or double the length while ensuring that the
|
||||
// allocation is valid and that any cursor within it will be valid.
|
||||
len
|
||||
// This keeps the buffer size a power-of-two, required by miniz_oxide.
|
||||
.saturating_add(CHUNCK_BUFFER_SIZE.max(len))
|
||||
// Ensure all buffer indices are valid cursor positions.
|
||||
// Note: both cut off and zero extension give correct results.
|
||||
.min(u64::max_value() as usize)
|
||||
// Ensure the allocation request is valid.
|
||||
// TODO: maximum allocation limits?
|
||||
.min(isize::max_value() as usize)
|
||||
// Don't unnecessarily allocate more than `max_total_output`.
|
||||
.min(self.max_total_output)
|
||||
}
|
||||
|
||||
fn transfer_finished_data(&mut self, image_data: &mut Vec<u8>) -> usize {
|
||||
let transferred = &self.out_buffer[self.read_pos..self.out_pos];
|
||||
image_data.extend_from_slice(transferred);
|
||||
self.read_pos = self.out_pos;
|
||||
transferred.len()
|
||||
}
|
||||
|
||||
fn compact_out_buffer_if_needed(&mut self) {
|
||||
// [PNG spec](https://www.w3.org/TR/2003/REC-PNG-20031110/#10Compression) says that
|
||||
// "deflate/inflate compression with a sliding window (which is an upper bound on the
|
||||
// distances appearing in the deflate stream) of at most 32768 bytes".
|
||||
//
|
||||
// `fdeflate` requires that we keep this many most recently decompressed bytes in the
|
||||
// `out_buffer` - this allows referring back to them when handling "length and distance
|
||||
// codes" in the deflate stream).
|
||||
const LOOKBACK_SIZE: usize = 32768;
|
||||
|
||||
// Compact `self.out_buffer` when "needed". Doing this conditionally helps to put an upper
|
||||
// bound on the amortized cost of copying the data within `self.out_buffer`.
|
||||
//
|
||||
// TODO: The factor of 4 is an ad-hoc heuristic. Consider measuring and using a different
|
||||
// factor. (Early experiments seem to indicate that factor of 4 is faster than a factor of
|
||||
// 2 and 4 * `LOOKBACK_SIZE` seems like an acceptable memory trade-off. Higher factors
|
||||
// result in higher memory usage, but the compaction cost is lower - factor of 4 means
|
||||
// that 1 byte gets copied during compaction for 3 decompressed bytes.)
|
||||
if self.out_pos > LOOKBACK_SIZE * 4 {
|
||||
// Only preserve the `lookback_buffer` and "throw away" the earlier prefix.
|
||||
let lookback_buffer = self.out_pos.saturating_sub(LOOKBACK_SIZE)..self.out_pos;
|
||||
let preserved_len = lookback_buffer.len();
|
||||
self.out_buffer.copy_within(lookback_buffer, 0);
|
||||
self.read_pos = preserved_len;
|
||||
self.out_pos = preserved_len;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue