From 211833f8530ec6403f78918017ead5b3b0d8c86b Mon Sep 17 00:00:00 2001 From: Jonathan Behrens Date: Sun, 28 Jan 2024 17:44:55 -0800 Subject: [PATCH 01/23] Remove remaining uses of miniz_oxide for decoding --- src/text_metadata.rs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/text_metadata.rs b/src/text_metadata.rs index 42f8df32..5acb4a23 100644 --- a/src/text_metadata.rs +++ b/src/text_metadata.rs @@ -105,7 +105,6 @@ use crate::{chunk, encoder, DecodingError, EncodingError}; use flate2::write::ZlibEncoder; use flate2::Compression; -use miniz_oxide::inflate::{decompress_to_vec_zlib, decompress_to_vec_zlib_with_limit}; use std::{convert::TryFrom, io::Write}; /// Default decompression limit for compressed text chunks. @@ -287,9 +286,9 @@ impl ZTXtChunk { pub fn decompress_text_with_limit(&mut self, limit: usize) -> Result<(), DecodingError> { match &self.text { OptCompressed::Compressed(v) => { - let uncompressed_raw = match decompress_to_vec_zlib_with_limit(&v[..], limit) { + let uncompressed_raw = match fdeflate::decompress_to_vec_bounded(&v[..], limit) { Ok(s) => s, - Err(err) if err.status == miniz_oxide::inflate::TINFLStatus::HasMoreOutput => { + Err(fdeflate::BoundedDecompressionError::OutputTooLarge { .. }) => { return Err(DecodingError::from( TextDecodingError::OutOfDecompressionSpace, )); @@ -310,7 +309,7 @@ impl ZTXtChunk { pub fn get_text(&self) -> Result { match &self.text { OptCompressed::Compressed(v) => { - let uncompressed_raw = decompress_to_vec_zlib(&v[..]) + let uncompressed_raw = fdeflate::decompress_to_vec(v) .map_err(|_| DecodingError::from(TextDecodingError::InflationError))?; Ok(decode_iso_8859_1(&uncompressed_raw)) } @@ -457,9 +456,9 @@ impl ITXtChunk { pub fn decompress_text_with_limit(&mut self, limit: usize) -> Result<(), DecodingError> { match &self.text { OptCompressed::Compressed(v) => { - let uncompressed_raw = match decompress_to_vec_zlib_with_limit(&v[..], limit) { + let uncompressed_raw = match fdeflate::decompress_to_vec_bounded(v, limit) { Ok(s) => s, - Err(err) if err.status == miniz_oxide::inflate::TINFLStatus::HasMoreOutput => { + Err(fdeflate::BoundedDecompressionError::OutputTooLarge { .. }) => { return Err(DecodingError::from( TextDecodingError::OutOfDecompressionSpace, )); @@ -483,7 +482,7 @@ impl ITXtChunk { pub fn get_text(&self) -> Result { match &self.text { OptCompressed::Compressed(v) => { - let uncompressed_raw = decompress_to_vec_zlib(&v[..]) + let uncompressed_raw = fdeflate::decompress_to_vec(v) .map_err(|_| DecodingError::from(TextDecodingError::InflationError))?; String::from_utf8(uncompressed_raw) .map_err(|_| TextDecodingError::Unrepresentable.into()) @@ -571,7 +570,7 @@ impl EncodableTextChunk for ITXtChunk { } else { match &self.text { OptCompressed::Compressed(v) => { - let uncompressed_raw = decompress_to_vec_zlib(&v[..]) + let uncompressed_raw = fdeflate::decompress_to_vec(v) .map_err(|_| EncodingError::from(TextEncodingError::CompressionError))?; data.extend_from_slice(&uncompressed_raw[..]); } From 2bd3dc9742f9cbead117b4dbf96e619effc88c77 Mon Sep 17 00:00:00 2001 From: Jonathan Behrens Date: Sun, 28 Jan 2024 17:56:34 -0800 Subject: [PATCH 02/23] Import BoundedDecompressionError --- src/text_metadata.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/text_metadata.rs b/src/text_metadata.rs index 5acb4a23..18a2abe9 100644 --- a/src/text_metadata.rs +++ b/src/text_metadata.rs @@ -103,6 +103,7 @@ #![warn(missing_docs)] use crate::{chunk, encoder, DecodingError, EncodingError}; +use fdeflate::BoundedDecompressionError; use flate2::write::ZlibEncoder; use flate2::Compression; use std::{convert::TryFrom, io::Write}; @@ -288,7 +289,7 @@ impl ZTXtChunk { OptCompressed::Compressed(v) => { let uncompressed_raw = match fdeflate::decompress_to_vec_bounded(&v[..], limit) { Ok(s) => s, - Err(fdeflate::BoundedDecompressionError::OutputTooLarge { .. }) => { + Err(BoundedDecompressionError::OutputTooLarge { .. }) => { return Err(DecodingError::from( TextDecodingError::OutOfDecompressionSpace, )); @@ -458,7 +459,7 @@ impl ITXtChunk { OptCompressed::Compressed(v) => { let uncompressed_raw = match fdeflate::decompress_to_vec_bounded(v, limit) { Ok(s) => s, - Err(fdeflate::BoundedDecompressionError::OutputTooLarge { .. }) => { + Err(BoundedDecompressionError::OutputTooLarge { .. }) => { return Err(DecodingError::from( TextDecodingError::OutOfDecompressionSpace, )); From bbce63d313d4d1fae6bd4c122631bb7ab78b7c41 Mon Sep 17 00:00:00 2001 From: Lukasz Anforowicz Date: Mon, 29 Jan 2024 19:07:34 +0000 Subject: [PATCH 03/23] Extract a separate `palette.rs` module. This commit moves `expand_paletted_into_rgb8` and `expand_paletted_into_rgba8` (and their unit tests) into a separate `transform/palette.rs` module. This prepares room for encapsulating extra complexity in this module in follow-up commits, where we will start to precompute and memoize some data when creating a `TransformFn`. This commit just moves the code around - it should have no impact on correctness or performance. --- src/decoder/transform.rs | 205 ++----------------------------- src/decoder/transform/palette.rs | 195 +++++++++++++++++++++++++++++ 2 files changed, 204 insertions(+), 196 deletions(-) create mode 100644 src/decoder/transform/palette.rs diff --git a/src/decoder/transform.rs b/src/decoder/transform.rs index 0cbc0740..cc89ac99 100644 --- a/src/decoder/transform.rs +++ b/src/decoder/transform.rs @@ -1,5 +1,7 @@ //! Transforming a decompressed, unfiltered row into the final output. +mod palette; + use crate::{BitDepth, ColorType, DecodingError, Info, Transformations}; use super::stream::FormatErrorInner; @@ -42,9 +44,9 @@ pub fn create_transform_fn( )); } else { if trns { - Ok(expand_paletted_into_rgba8) + Ok(palette::expand_paletted_into_rgba8) } else { - Ok(expand_paletted_into_rgb8) + Ok(palette::expand_paletted_into_rgb8) } } } @@ -132,7 +134,7 @@ where } } -pub fn expand_trns_line(input: &[u8], output: &mut [u8], info: &Info) { +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 @@ -144,7 +146,7 @@ pub fn expand_trns_line(input: &[u8], output: &mut [u8], info: &Info) { } } -pub fn expand_trns_line16(input: &[u8], output: &mut [u8], info: &Info) { +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 @@ -162,7 +164,7 @@ pub fn expand_trns_line16(input: &[u8], output: &mut [u8], info: &Info) { } } -pub fn expand_trns_and_strip_line16(input: &[u8], output: &mut [u8], info: &Info) { +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 @@ -176,60 +178,14 @@ pub fn expand_trns_and_strip_line16(input: &[u8], output: &mut [u8], info: &Info } } -pub fn 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]; - - 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]; - }) -} - -pub fn 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 { - &[] - }; - - 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; - }); -} - -pub fn expand_gray_u8(row: &[u8], buffer: &mut [u8], info: &Info) { +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 }); } -pub fn expand_gray_u8_with_trns(row: &[u8], buffer: &mut [u8], info: &Info) { +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| { @@ -245,146 +201,3 @@ pub fn expand_gray_u8_with_trns(row: &[u8], buffer: &mut [u8], info: &Info) { chunk[0] = pixel * scaling_factor }); } - -#[cfg(test)] -mod test { - use crate::{BitDepth, ColorType, Info, Transformations}; - - fn expand_paletted( - src: &[u8], - src_bit_depth: u8, - palette: &[u8], - trns: Option<&[u8]>, - ) -> Vec { - let info = 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() - }; - 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::create_transform_fn(&info, Transformations::EXPAND).unwrap(); - transform_fn(src, dst.as_mut_slice(), &info); - 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::>()); - } - - #[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::>()); - } - - #[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::>()); - } - - #[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::>()); - } - - #[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], - ); - } -} diff --git a/src/decoder/transform/palette.rs b/src/decoder/transform/palette.rs new file mode 100644 index 00000000..57d5c6a4 --- /dev/null +++ b/src/decoder/transform/palette.rs @@ -0,0 +1,195 @@ +//! Helpers for taking a slice of indeces (indices into `PLTE` and/or `trNS` +//! entries) and transforming this into RGB or RGBA output. + +use super::unpack_bits; +use crate::Info; + +pub fn 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]; + + 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]; + }) +} + +pub fn 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 { + &[] + }; + + 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; + }); +} + +#[cfg(test)] +mod test { + use crate::{BitDepth, ColorType, Info, Transformations}; + + fn expand_paletted( + src: &[u8], + src_bit_depth: u8, + palette: &[u8], + trns: Option<&[u8]>, + ) -> Vec { + let info = 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() + }; + 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); + 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::>()); + } + + #[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::>()); + } + + #[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::>()); + } + + #[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::>()); + } + + #[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], + ); + } +} From d9df1d74a7ae13d24d1c3761f8cf7fb0d1f8004e Mon Sep 17 00:00:00 2001 From: Lukasz Anforowicz Date: Mon, 29 Jan 2024 22:56:55 +0000 Subject: [PATCH 04/23] Fix constants used in palette benchmarks. The `PLTE` chunk's size should be a multiple of 3 (since it contains RGB entries - 3 bytes per entry). Additionally, taking 10000 samples in the `bench_create_fn` benchmarks is a bit excessive after memoization. --- benches/expand_paletted.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/benches/expand_paletted.rs b/benches/expand_paletted.rs index 06294368..caf17239 100644 --- a/benches/expand_paletted.rs +++ b/benches/expand_paletted.rs @@ -114,10 +114,10 @@ fn create_expand_palette_fn(info: &Info) -> TransformFn { fn bench_create_fn(c: &mut Criterion, plte_size: usize, trns_size: usize) { let mut group = c.benchmark_group("expand_paletted(ctor)"); - group.sample_size(10000); + group.sample_size(1000); let mut rng = rand::thread_rng(); - let plte = get_random_bytes(&mut rng, plte_size as usize); + let plte = get_random_bytes(&mut rng, 3 * plte_size as usize); let trns = get_random_bytes(&mut rng, trns_size as usize); let info = create_info_from_plte_trns_bitdepth(&plte, Some(&trns), 8); group.bench_with_input( From b0cc095724d2af7100f26af16e2430efc9c10534 Mon Sep 17 00:00:00 2001 From: Lukasz Anforowicz Date: Mon, 29 Jan 2024 19:11:19 +0000 Subject: [PATCH 05/23] Change `TransformFn` to allow memoization in the future This commit changes the `TransformFn` type alias from `fn(...)` into `Box`. This allows the `TransformFn` to have store some precomputer, memoized state that we plan to add in follow-up commits. In theory this commit may have negative performance impact, but in the grand scheme of things it disappears into the measurement noise. In particular, when there is no state, then `Box` shouldn't allocate. --- src/decoder/mod.rs | 2 +- src/decoder/transform.rs | 32 ++++++++++++++++---------------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/decoder/mod.rs b/src/decoder/mod.rs index 21d51a66..676e04f3 100644 --- a/src/decoder/mod.rs +++ b/src/decoder/mod.rs @@ -638,7 +638,7 @@ impl Reader { if self.transform_fn.is_none() { self.transform_fn = Some(create_transform_fn(self.info(), self.transform)?); } - self.transform_fn.unwrap() + self.transform_fn.as_deref().unwrap() }; transform_fn(row, output_buffer, self.info()); diff --git a/src/decoder/transform.rs b/src/decoder/transform.rs index cc89ac99..43da0dc3 100644 --- a/src/decoder/transform.rs +++ b/src/decoder/transform.rs @@ -12,7 +12,7 @@ use super::stream::FormatErrorInner; /// /// TODO: If some precomputed state is needed (e.g. to make `expand_paletted...` /// faster) then consider changing this into `Box`. -pub type TransformFn = fn(&[u8], &mut [u8], &Info); +pub type TransformFn = Box; /// Returns a transformation function that should be applied to image rows based /// on 1) decoded image metadata (`info`) and 2) the transformations requested @@ -43,36 +43,36 @@ pub fn create_transform_fn( .into(), )); } else { - if trns { - Ok(palette::expand_paletted_into_rgba8) + Ok(Box::new(if trns { + palette::expand_paletted_into_rgba8 } else { - Ok(palette::expand_paletted_into_rgb8) - } + palette::expand_paletted_into_rgb8 + })) } } ColorType::Grayscale | ColorType::GrayscaleAlpha if bit_depth < 8 && expand => { - if trns { - Ok(expand_gray_u8_with_trns) + Ok(Box::new(if trns { + expand_gray_u8_with_trns } else { - Ok(expand_gray_u8) - } + expand_gray_u8 + })) } ColorType::Grayscale | ColorType::Rgb if expand && trns => { - if bit_depth == 8 { - Ok(expand_trns_line) + Ok(Box::new(if bit_depth == 8 { + expand_trns_line } else if strip16 { - Ok(expand_trns_and_strip_line16) + expand_trns_and_strip_line16 } else { assert_eq!(bit_depth, 16); - Ok(expand_trns_line16) - } + expand_trns_line16 + })) } ColorType::Grayscale | ColorType::GrayscaleAlpha | ColorType::Rgb | ColorType::Rgba if strip16 => { - Ok(transform_row_strip16) + Ok(Box::new(transform_row_strip16)) } - _ => Ok(copy_row), + _ => Ok(Box::new(copy_row)), } } From a5858146bd795345853a857e62e75abee434f6eb Mon Sep 17 00:00:00 2001 From: Lukasz Anforowicz Date: Mon, 29 Jan 2024 19:33:38 +0000 Subject: [PATCH 06/23] Memoize combined PLTE+trNS lookup table. Before this commit `expand_paletted_into_rgba8` would: * Perform 2 lookups - `palette.get(i)` and `trns.get(i)` * Check via `unwrap_or` if `i` was within the bounds of `palette`/`trns` This commit introduces `create_rgba_palette` which combines `palette` and `trns` into a fixed-size `[[u8;4]; 256]` look-up table (called `rgba_palette` in the code). After this commit `expand_paletted_into_rgba8` only needs to perform a single look-up and doesn't need to check the bounds. This helps to improve the expansion time by 60+%: - expand_paletted(exec)/trns=yes/src_bits=4/src_size=5461: [-60.208% -60.057% -59.899%] (p = 0.00 < 0.05) - expand_paletted(exec)/trns=yes/src_bits=8/src_size=5461: [-77.520% -77.407% -77.301%] (p = 0.00 < 0.05) `expand_paletted_into_rgb8` performs only a single lookup before and after this commit, but avoiding bounds checks still helps to improve the expansion time by ~12%: - expand_paletted(exec)/trns=no/src_bits=4/src_size=5461: [-12.357% -12.005% -11.664%] (p = 0.00 < 0.05) - expand_paletted(exec)/trns=no/src_bits=8/src_size=5461: [-13.135% -12.584% -12.092%] (p = 0.00 < 0.05) Understandably, this commit regresses the time of `create_transform_fn`. Future commits will reduce this regression 2-4 times: - expand_paletted(ctor)/plte=256/trns=256: [+3757.2% +3763.8% +3770.5%] (p = 0.00 < 0.05) - expand_paletted(ctor)/plte=224/trns=32: [+3807.3% +3816.2% +3824.6%] (p = 0.00 < 0.05) - expand_paletted(ctor)/plte=16/trns=1: [+1672.0% +1675.0% +1678.1%] (p = 0.00 < 0.05) --- src/decoder/transform.rs | 8 +- src/decoder/transform/palette.rs | 192 ++++++++++++++++++++++++++----- 2 files changed, 166 insertions(+), 34 deletions(-) diff --git a/src/decoder/transform.rs b/src/decoder/transform.rs index 43da0dc3..7271bcb9 100644 --- a/src/decoder/transform.rs +++ b/src/decoder/transform.rs @@ -43,11 +43,11 @@ pub fn create_transform_fn( .into(), )); } else { - Ok(Box::new(if trns { - palette::expand_paletted_into_rgba8 + Ok(if trns { + palette::create_expansion_into_rgba8(info) } else { - palette::expand_paletted_into_rgb8 - })) + palette::create_expansion_into_rgb8(info) + }) } } ColorType::Grayscale | ColorType::GrayscaleAlpha if bit_depth < 8 && expand => { diff --git a/src/decoder/transform/palette.rs b/src/decoder/transform/palette.rs index 57d5c6a4..f9bf5f86 100644 --- a/src/decoder/transform/palette.rs +++ b/src/decoder/transform/palette.rs @@ -1,27 +1,41 @@ //! 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; +use super::{unpack_bits, TransformFn}; use crate::Info; -pub fn 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]; +pub fn create_expansion_into_rgb8(info: &Info) -> TransformFn { + let rgba_palette = create_rgba_palette(info); + Box::new(move |input, output, info| { + expand_paletted_into_rgb8(input, output, info, &rgba_palette) + }) +} - 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]; +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) }) } -pub fn expand_paletted_into_rgba8(row: &[u8], buffer: &mut [u8], info: &Info) { +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(&[]); - 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 @@ -36,17 +50,44 @@ pub fn expand_paletted_into_rgba8(row: &[u8], buffer: &mut [u8], info: &Info) { &[] }; + // Default to black, opaque entries. + let mut rgba_palette = [[0, 0, 0, 0xFF]; 256]; + + // Replace missing `trns` entry with 100%-opaque alpha. + let trns = trns.iter().copied().chain(std::iter::repeat(0xFF)); + + // Combine `palette` and `trns` into a single lookup table: `rgba_palette`. + let rgba_and_alpha_iter = palette.chunks_exact(3).zip(trns); + for (rgba, (rgb, alpha)) in rgba_palette.iter_mut().zip(rgba_and_alpha_iter) { + rgba[0..3].copy_from_slice(rgb); + rgba[3] = alpha; + } + + rgba_palette +} + +fn expand_paletted_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| { - 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; + chunk.copy_from_slice(&rgba_palette[i as usize]); }); } @@ -54,29 +95,94 @@ pub fn expand_paletted_into_rgba8(row: &[u8], buffer: &mut [u8], info: &Info) { 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 { - let info = 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() - }; + 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 } @@ -192,4 +298,30 @@ mod test { 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 = (0..plte_len * 3).collect(); + let trns: Vec = (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); + } + } + } } From 72aecc3f4e441cffcc08a629416a6e336da0daca Mon Sep 17 00:00:00 2001 From: Lukasz Anforowicz Date: Mon, 29 Jan 2024 22:00:26 +0000 Subject: [PATCH 07/23] Copy 4 bytes at a time when expanding palette into rgb8. Before this commit `expand_into_rgb8` would copy 3 bytes at a time into the output. After this commit it copies 4 bytes at a time (possibly cloberring pixels that will be populated during the next iteration - this is ok). This improved the performance as follows: expand_paletted(exec)/trns=no/src_bits=8/src_size=5461 time: [-23.852% -23.593% -23.319%] (p = 0.00 < 0.05) --- src/decoder/transform/palette.rs | 33 ++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/src/decoder/transform/palette.rs b/src/decoder/transform/palette.rs index f9bf5f86..2fdc233c 100644 --- a/src/decoder/transform/palette.rs +++ b/src/decoder/transform/palette.rs @@ -17,13 +17,16 @@ //! that memoization is a net benefit for images bigger than around 13x13 pixels. use super::{unpack_bits, TransformFn}; -use crate::Info; +use crate::{BitDepth, Info}; pub fn create_expansion_into_rgb8(info: &Info) -> TransformFn { let rgba_palette = create_rgba_palette(info); - Box::new(move |input, output, info| { - expand_paletted_into_rgb8(input, output, info, &rgba_palette) - }) + + 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 { @@ -66,12 +69,22 @@ fn create_rgba_palette(info: &Info) -> [[u8; 4]; 256] { rgba_palette } -fn expand_paletted_into_rgb8( - row: &[u8], - buffer: &mut [u8], - info: &Info, - rgba_palette: &[[u8; 4]; 256], -) { +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]; From b13388f113d15bf1b57c0eb51ae5b7abc53c3191 Mon Sep 17 00:00:00 2001 From: Lukasz Anforowicz Date: Mon, 29 Jan 2024 22:09:55 +0000 Subject: [PATCH 08/23] Copy 4 bytes at a time in `create_rgba_palette` This improves the performance as follows: - expand_paletted(ctor)/plte=256/trns=256 [-40.581% -40.396% -40.211%] (p = 0.00 < 0.05) - expand_paletted(ctor)/plte=224/trns=32 [-24.070% -23.840% -23.592%] (p = 0.00 < 0.05) Small palettes are mostly unaffected: - expand_paletted(ctor)/plte=16/trns=1 [-0.2525% +0.0338% +0.3239%] (p = 0.81 > 0.05) --- src/decoder/transform/palette.rs | 33 ++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/src/decoder/transform/palette.rs b/src/decoder/transform/palette.rs index 2fdc233c..72dc88f1 100644 --- a/src/decoder/transform/palette.rs +++ b/src/decoder/transform/palette.rs @@ -56,16 +56,37 @@ fn create_rgba_palette(info: &Info) -> [[u8; 4]; 256] { // Default to black, opaque entries. let mut rgba_palette = [[0, 0, 0, 0xFF]; 256]; - // Replace missing `trns` entry with 100%-opaque alpha. - let trns = trns.iter().copied().chain(std::iter::repeat(0xFF)); + // 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]); + } + } - // Combine `palette` and `trns` into a single lookup table: `rgba_palette`. - let rgba_and_alpha_iter = palette.chunks_exact(3).zip(trns); - for (rgba, (rgb, alpha)) in rgba_palette.iter_mut().zip(rgba_and_alpha_iter) { - rgba[0..3].copy_from_slice(rgb); + // 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 } From a6425ca00047f413fa9e093f895bea4357410c17 Mon Sep 17 00:00:00 2001 From: Jonathan Behrens Date: Sun, 4 Feb 2024 15:10:24 -0800 Subject: [PATCH 09/23] Return Info with static lifetime (#465) --- src/decoder/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/decoder/mod.rs b/src/decoder/mod.rs index 676e04f3..ce14edc1 100644 --- a/src/decoder/mod.rs +++ b/src/decoder/mod.rs @@ -202,7 +202,7 @@ impl Decoder { /// /// 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, DecodingError> { + 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(); @@ -345,7 +345,7 @@ impl ReadDecoder { )) } - fn info(&self) -> Option<&Info> { + fn info(&self) -> Option<&Info<'static>> { self.decoder.info.as_ref() } } @@ -466,7 +466,7 @@ impl Reader { /// Get information on the image. /// /// The structure will change as new frames of an animated image are decoded. - pub fn info(&self) -> &Info { + pub fn info(&self) -> &Info<'static> { self.decoder.info().unwrap() } From ec8dbe9e8e155b166d3887c66bd50e5f9e2ff09e Mon Sep 17 00:00:00 2001 From: Jonathan Behrens Date: Sun, 11 Feb 2024 12:45:21 -0800 Subject: [PATCH 10/23] Test minimal versions in CI (#467) --- .github/workflows/rust.yml | 9 +- Cargo.lock.msrv | 2084 ------------------------------------ Cargo.toml | 2 +- 3 files changed, 8 insertions(+), 2087 deletions(-) delete mode 100644 Cargo.lock.msrv diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 4d7e72c5..47d8e38d 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -15,12 +15,17 @@ jobs: runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v2 + + - uses: dtolnay/rust-toolchain@nightly + if: ${{ matrix.rust == '1.57.0' }} + - name: Generate Cargo.lock with minimal-version dependencies + if: ${{ matrix.rust == '1.57.0' }} + run: cargo -Zminimal-versions generate-lockfile + - uses: actions-rs/toolchain@v1 with: toolchain: ${{ matrix.rust }} override: true - - if: ${{ matrix.rust == '1.57.0' }} - run: mv Cargo.lock.msrv Cargo.lock - name: build run: | cargo build --verbose diff --git a/Cargo.lock.msrv b/Cargo.lock.msrv deleted file mode 100644 index 86227bdf..00000000 --- a/Cargo.lock.msrv +++ /dev/null @@ -1,2084 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "addr2line" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - -[[package]] -name = "arrayref" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" - -[[package]] -name = "arrayvec" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" - -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi 0.1.19", - "libc", - "winapi", -] - -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "backtrace" -version = "0.3.68" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12" -dependencies = [ - "addr2line", - "cc", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", -] - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "block" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" - -[[package]] -name = "bumpalo" -version = "3.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" - -[[package]] -name = "bytemuck" -version = "1.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea" - -[[package]] -name = "calloop" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52e0d00eb1ea24371a97d2da6201c6747a633dc6dc1988ef503403b4c59504a8" -dependencies = [ - "bitflags", - "log", - "nix 0.25.1", - "slotmap", - "thiserror", - "vec_map", -] - -[[package]] -name = "cast" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" - -[[package]] -name = "cc" -version = "1.0.82" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "305fe645edc1442a0fa8b6726ba61d422798d37a52e12eaecf4b022ebbb88f01" -dependencies = [ - "libc", -] - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "cgl" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ced0551234e87afee12411d535648dd89d2e7f34c78b753395567aff3d447ff" -dependencies = [ - "libc", -] - -[[package]] -name = "clap" -version = "2.34.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" -dependencies = [ - "bitflags", - "textwrap 0.11.0", - "unicode-width", -] - -[[package]] -name = "clap" -version = "3.2.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" -dependencies = [ - "atty", - "bitflags", - "clap_derive", - "clap_lex", - "indexmap", - "once_cell", - "strsim", - "termcolor", - "textwrap 0.16.0", -] - -[[package]] -name = "clap_derive" -version = "3.2.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae6371b8bdc8b7d3959e9cf7b22d4435ef3e79e138688421ec654acf8c81b008" -dependencies = [ - "heck", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "clap_lex" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" -dependencies = [ - "os_str_bytes", -] - -[[package]] -name = "cmake" -version = "0.1.50" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31c789563b815f77f4250caee12365734369f942439b7defd71e18a48197130" -dependencies = [ - "cc", -] - -[[package]] -name = "cocoa" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f425db7937052c684daec3bd6375c8abe2d146dca4b8b143d6db777c39138f3a" -dependencies = [ - "bitflags", - "block", - "cocoa-foundation", - "core-foundation", - "core-graphics", - "foreign-types 0.3.2", - "libc", - "objc", -] - -[[package]] -name = "cocoa-foundation" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "931d3837c286f56e3c58423ce4eba12d08db2374461a785c86f672b08b5650d6" -dependencies = [ - "bitflags", - "block", - "core-foundation", - "core-graphics-types", - "foreign-types 0.3.2", - "libc", - "objc", -] - -[[package]] -name = "core-foundation" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" - -[[package]] -name = "core-graphics" -version = "0.22.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb" -dependencies = [ - "bitflags", - "core-foundation", - "core-graphics-types", - "foreign-types 0.3.2", - "libc", -] - -[[package]] -name = "core-graphics-types" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bb142d41022986c1d8ff29103a1411c8a3dfad3552f87a4f8dc50d61d4f4e33" -dependencies = [ - "bitflags", - "core-foundation", - "libc", -] - -[[package]] -name = "core-text" -version = "19.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d74ada66e07c1cefa18f8abfba765b486f250de2e4a999e5727fc0dd4b4a25" -dependencies = [ - "core-foundation", - "core-graphics", - "foreign-types 0.3.2", - "libc", -] - -[[package]] -name = "crc32fast" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "criterion" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b01d6de93b2b6c65e17c634a26653a29d107b3c98c607c765bf38d041531cd8f" -dependencies = [ - "atty", - "cast", - "clap 2.34.0", - "criterion-plot", - "csv", - "itertools", - "lazy_static", - "num-traits", - "oorandom", - "plotters", - "rayon", - "regex", - "serde", - "serde_cbor", - "serde_derive", - "serde_json", - "tinytemplate", - "walkdir", -] - -[[package]] -name = "criterion-plot" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2673cc8207403546f45f5fd319a974b1e6983ad1a3ee7e6041650013be041876" -dependencies = [ - "cast", - "itertools", -] - -[[package]] -name = "crossbeam-channel" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" -dependencies = [ - "cfg-if", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" -dependencies = [ - "cfg-if", - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" -dependencies = [ - "autocfg", - "cfg-if", - "crossbeam-utils", - "memoffset 0.9.0", - "scopeguard", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crossfont" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21fd3add36ea31aba1520aa5288714dd63be506106753226d0eb387a93bc9c45" -dependencies = [ - "cocoa", - "core-foundation", - "core-foundation-sys", - "core-graphics", - "core-text", - "dwrote", - "foreign-types 0.5.0", - "freetype-rs", - "libc", - "log", - "objc", - "once_cell", - "pkg-config", - "servo-fontconfig", - "winapi", -] - -[[package]] -name = "csv" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "626ae34994d3d8d668f4269922248239db4ae42d538b14c398b74a52208e8086" -dependencies = [ - "csv-core", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "csv-core" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" -dependencies = [ - "memchr", -] - -[[package]] -name = "cty" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" - -[[package]] -name = "darling" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" -dependencies = [ - "darling_core", - "darling_macro", -] - -[[package]] -name = "darling_core" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim", - "syn 1.0.109", -] - -[[package]] -name = "darling_macro" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" -dependencies = [ - "darling_core", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "dirs-next" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" -dependencies = [ - "cfg-if", - "dirs-sys-next", -] - -[[package]] -name = "dirs-sys-next" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" -dependencies = [ - "libc", - "redox_users", - "winapi", -] - -[[package]] -name = "dispatch" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" - -[[package]] -name = "dlib" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" -dependencies = [ - "libloading 0.8.0", -] - -[[package]] -name = "downcast-rs" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" - -[[package]] -name = "dwrote" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439a1c2ba5611ad3ed731280541d36d2e9c4ac5e7fb818a27b604bdc5a6aa65b" -dependencies = [ - "lazy_static", - "libc", - "serde", - "serde_derive", - "winapi", - "wio", -] - -[[package]] -name = "either" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" - -[[package]] -name = "expat-sys" -version = "2.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "658f19728920138342f68408b7cf7644d90d4784353d8ebc32e7e8663dbe45fa" -dependencies = [ - "cmake", - "pkg-config", -] - -[[package]] -name = "fdeflate" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d329bdeac514ee06249dabc27877490f17f5d371ec693360768b838e19f3ae10" -dependencies = [ - "simd-adler32", -] - -[[package]] -name = "flate2" -version = "1.0.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" -dependencies = [ - "crc32fast", - "miniz_oxide", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared 0.1.1", -] - -[[package]] -name = "foreign-types" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" -dependencies = [ - "foreign-types-macros", - "foreign-types-shared 0.3.1", -] - -[[package]] -name = "foreign-types-macros" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.29", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - -[[package]] -name = "foreign-types-shared" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" - -[[package]] -name = "freetype-rs" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74eadec9d0a5c28c54bb9882e54787275152a4e36ce206b45d7451384e5bf5fb" -dependencies = [ - "bitflags", - "freetype-sys", - "libc", -] - -[[package]] -name = "freetype-sys" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a37d4011c0cc628dfa766fcc195454f4b068d7afdc2adfd28861191d866e731a" -dependencies = [ - "cmake", - "libc", - "pkg-config", -] - -[[package]] -name = "getopts" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" -dependencies = [ - "unicode-width", -] - -[[package]] -name = "getrandom" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - -[[package]] -name = "gimli" -version = "0.27.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" - -[[package]] -name = "gl_generator" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a95dfc23a2b4a9a2f5ab41d194f8bfda3cabec42af4e39f08c339eb2a0c124d" -dependencies = [ - "khronos_api", - "log", - "xml-rs", -] - -[[package]] -name = "glium" -version = "0.32.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2766728ecb86014b91d3d687614b32d65aacbbdc887f424a7b03cba3ab593bf" -dependencies = [ - "backtrace", - "fnv", - "gl_generator", - "glutin", - "lazy_static", - "memoffset 0.6.5", - "smallvec", - "takeable-option", -] - -[[package]] -name = "glob" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" - -[[package]] -name = "glutin" -version = "0.29.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "444c9ad294fdcaf20ccf6726b78f380b5450275540c9b68ab62f49726ad1c713" -dependencies = [ - "cgl", - "cocoa", - "core-foundation", - "glutin_egl_sys", - "glutin_gles2_sys", - "glutin_glx_sys", - "glutin_wgl_sys", - "libloading 0.7.4", - "log", - "objc", - "once_cell", - "osmesa-sys", - "parking_lot", - "raw-window-handle 0.5.2", - "wayland-client", - "wayland-egl", - "winapi", - "winit", -] - -[[package]] -name = "glutin_egl_sys" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68900f84b471f31ea1d1355567eb865a2cf446294f06cef8d653ed7bcf5f013d" -dependencies = [ - "gl_generator", - "winapi", -] - -[[package]] -name = "glutin_gles2_sys" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8094e708b730a7c8a1954f4f8a31880af00eb8a1c5b5bf85d28a0a3c6d69103" -dependencies = [ - "gl_generator", - "objc", -] - -[[package]] -name = "glutin_glx_sys" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d93d0575865098580c5b3a423188cd959419912ea60b1e48e8b3b526f6d02468" -dependencies = [ - "gl_generator", - "x11-dl", -] - -[[package]] -name = "glutin_wgl_sys" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3da5951a1569dbab865c6f2a863efafff193a93caf05538d193e9e3816d21696" -dependencies = [ - "gl_generator", -] - -[[package]] -name = "half" -version = "1.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - -[[package]] -name = "hermit-abi" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" - -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - -[[package]] -name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown", -] - -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "itertools" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" - -[[package]] -name = "jni-sys" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" - -[[package]] -name = "js-sys" -version = "0.3.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "khronos_api" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "libc" -version = "0.2.147" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" - -[[package]] -name = "libloading" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" -dependencies = [ - "cfg-if", - "winapi", -] - -[[package]] -name = "libloading" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d580318f95776505201b28cf98eb1fa5e4be3b689633ba6a3e6cd880ff22d8cb" -dependencies = [ - "cfg-if", - "windows-sys 0.48.0", -] - -[[package]] -name = "lock_api" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" -dependencies = [ - "autocfg", - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" - -[[package]] -name = "malloc_buf" -version = "0.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" -dependencies = [ - "libc", -] - -[[package]] -name = "memchr" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" - -[[package]] -name = "memmap2" -version = "0.5.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" -dependencies = [ - "libc", -] - -[[package]] -name = "memoffset" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" -dependencies = [ - "autocfg", -] - -[[package]] -name = "memoffset" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" -dependencies = [ - "autocfg", -] - -[[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - -[[package]] -name = "miniz_oxide" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" -dependencies = [ - "adler", - "simd-adler32", -] - -[[package]] -name = "mio" -version = "0.8.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" -dependencies = [ - "libc", - "log", - "wasi", - "windows-sys 0.48.0", -] - -[[package]] -name = "ndk" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "451422b7e4718271c8b5b3aadf5adedba43dc76312454b387e98fae0fc951aa0" -dependencies = [ - "bitflags", - "jni-sys", - "ndk-sys", - "num_enum", - "raw-window-handle 0.5.2", - "thiserror", -] - -[[package]] -name = "ndk-context" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" - -[[package]] -name = "ndk-glue" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0434fabdd2c15e0aab768ca31d5b7b333717f03cf02037d5a0a3ff3c278ed67f" -dependencies = [ - "libc", - "log", - "ndk", - "ndk-context", - "ndk-macro", - "ndk-sys", - "once_cell", - "parking_lot", -] - -[[package]] -name = "ndk-macro" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0df7ac00c4672f9d5aece54ee3347520b7e20f158656c7db2e6de01902eb7a6c" -dependencies = [ - "darling", - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "ndk-sys" -version = "0.4.1+23.1.7779620" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cf2aae958bd232cac5069850591667ad422d263686d75b52a065f9badeee5a3" -dependencies = [ - "jni-sys", -] - -[[package]] -name = "nix" -version = "0.24.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" -dependencies = [ - "bitflags", - "cfg-if", - "libc", - "memoffset 0.6.5", -] - -[[package]] -name = "nix" -version = "0.25.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f346ff70e7dbfd675fe90590b92d59ef2de15a8779ae305ebcbfd3f0caf59be4" -dependencies = [ - "autocfg", - "bitflags", - "cfg-if", - "libc", - "memoffset 0.6.5", -] - -[[package]] -name = "nom" -version = "7.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" -dependencies = [ - "memchr", - "minimal-lexical", -] - -[[package]] -name = "num-traits" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" -dependencies = [ - "autocfg", -] - -[[package]] -name = "num_cpus" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" -dependencies = [ - "hermit-abi 0.3.2", - "libc", -] - -[[package]] -name = "num_enum" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" -dependencies = [ - "num_enum_derive", -] - -[[package]] -name = "num_enum_derive" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "objc" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" -dependencies = [ - "malloc_buf", -] - -[[package]] -name = "object" -version = "0.31.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" -dependencies = [ - "memchr", -] - -[[package]] -name = "once_cell" -version = "1.17.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9670a07f94779e00908f3e686eab508878ebb390ba6e604d3a284c00e8d0487b" - -[[package]] -name = "oorandom" -version = "11.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" - -[[package]] -name = "os_str_bytes" -version = "6.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d5d9eb14b174ee9aa2ef96dc2b94637a2d4b6e7cb873c7e171f0c20c6cf3eac" - -[[package]] -name = "osmesa-sys" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88cfece6e95d2e717e0872a7f53a8684712ad13822a7979bc760b9c77ec0013b" -dependencies = [ - "shared_library", -] - -[[package]] -name = "parking_lot" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall 0.3.5", - "smallvec", - "windows-targets", -] - -[[package]] -name = "percent-encoding" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" - -[[package]] -name = "pkg-config" -version = "0.3.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" - -[[package]] -name = "plotters" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2538b639e642295546c50fcd545198c9d64ee2a38620a628724a3b266d5fbf97" -dependencies = [ - "num-traits", - "plotters-backend", - "plotters-svg", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "plotters-backend" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609" - -[[package]] -name = "plotters-svg" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab" -dependencies = [ - "plotters-backend", -] - -[[package]] -name = "png" -version = "0.17.10" -dependencies = [ - "bitflags", - "clap 3.2.25", - "crc32fast", - "criterion", - "fdeflate", - "flate2", - "getopts", - "glium", - "glob", - "miniz_oxide", - "rand", - "term", -] - -[[package]] -name = "png" -version = "0.17.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd75bf2d8dd3702b9707cdbc56a5b9ef42cec752eb8b3bafc01234558442aa64" -dependencies = [ - "bitflags", - "crc32fast", - "fdeflate", - "flate2", - "miniz_oxide", -] - -[[package]] -name = "ppv-lite86" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" - -[[package]] -name = "proc-macro-crate" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eda0fc3b0fb7c975631757e14d9049da17374063edb6ebbcbc54d880d4fe94e9" -dependencies = [ - "once_cell", - "thiserror", - "toml", -] - -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn 1.0.109", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - -[[package]] -name = "proc-macro2" -version = "1.0.66" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom", -] - -[[package]] -name = "raw-window-handle" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b800beb9b6e7d2df1fe337c9e3d04e3af22a124460fb4c30fcc22c9117cefb41" -dependencies = [ - "cty", -] - -[[package]] -name = "raw-window-handle" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" - -[[package]] -name = "rayon" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" -dependencies = [ - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" -dependencies = [ - "crossbeam-channel", - "crossbeam-deque", - "crossbeam-utils", - "num_cpus", -] - -[[package]] -name = "redox_syscall" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" -dependencies = [ - "bitflags", -] - -[[package]] -name = "redox_syscall" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" -dependencies = [ - "bitflags", -] - -[[package]] -name = "redox_users" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" -dependencies = [ - "getrandom", - "redox_syscall 0.2.16", - "thiserror", -] - -[[package]] -name = "regex" -version = "1.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0ab3ca65655bb1e41f2a8c8cd662eb4fb035e67c3f78da1d61dffe89d07300f" -dependencies = [ - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78" - -[[package]] -name = "rustc-demangle" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" - -[[package]] -name = "rustversion" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" - -[[package]] -name = "ryu" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" - -[[package]] -name = "safe_arch" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1ff3d6d9696af502cc3110dacce942840fb06ff4514cad92236ecc455f2ce05" -dependencies = [ - "bytemuck", -] - -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "scoped-tls" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" - -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "sctk-adwaita" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61270629cc6b4d77ec1907db1033d5c2e1a404c412743621981a871dc9c12339" -dependencies = [ - "crossfont", - "log", - "smithay-client-toolkit", - "tiny-skia", -] - -[[package]] -name = "serde" -version = "1.0.183" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32ac8da02677876d532745a130fc9d8e6edfa81a269b107c5b00829b91d8eb3c" - -[[package]] -name = "serde_cbor" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5" -dependencies = [ - "half", - "serde", -] - -[[package]] -name = "serde_derive" -version = "1.0.183" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aafe972d60b0b9bee71a91b92fee2d4fb3c9d7e8f6b179aa99f27203d99a4816" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.29", -] - -[[package]] -name = "serde_json" -version = "1.0.105" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360" -dependencies = [ - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "servo-fontconfig" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7e3e22fe5fd73d04ebf0daa049d3efe3eae55369ce38ab16d07ddd9ac5c217c" -dependencies = [ - "libc", - "servo-fontconfig-sys", -] - -[[package]] -name = "servo-fontconfig-sys" -version = "5.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e36b879db9892dfa40f95da1c38a835d41634b825fbd8c4c418093d53c24b388" -dependencies = [ - "expat-sys", - "freetype-sys", - "pkg-config", -] - -[[package]] -name = "shared_library" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a9e7e0f2bfae24d8a5b5a66c5b257a83c7412304311512a0c054cd5e619da11" -dependencies = [ - "lazy_static", - "libc", -] - -[[package]] -name = "simd-adler32" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" - -[[package]] -name = "slotmap" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1e08e261d0e8f5c43123b7adf3e4ca1690d655377ac93a03b2c9d3e98de1342" -dependencies = [ - "version_check", -] - -[[package]] -name = "smallvec" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" - -[[package]] -name = "smithay-client-toolkit" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f307c47d32d2715eb2e0ece5589057820e0e5e70d07c247d1063e844e107f454" -dependencies = [ - "bitflags", - "calloop", - "dlib", - "lazy_static", - "log", - "memmap2", - "nix 0.24.3", - "pkg-config", - "wayland-client", - "wayland-cursor", - "wayland-protocols", -] - -[[package]] -name = "strsim" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" - -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "takeable-option" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36ae8932fcfea38b7d3883ae2ab357b0d57a02caaa18ebb4f5ece08beaec4aa0" - -[[package]] -name = "term" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" -dependencies = [ - "dirs-next", - "rustversion", - "winapi", -] - -[[package]] -name = "termcolor" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "textwrap" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" -dependencies = [ - "unicode-width", -] - -[[package]] -name = "textwrap" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" - -[[package]] -name = "thiserror" -version = "1.0.47" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97a802ec30afc17eee47b2855fc72e0c4cd62be9b4efe6591edde0ec5bd68d8f" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.47" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bb623b56e39ab7dcd4b1b98bb6c8f8d907ed255b18de254088016b27a8ee19b" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.29", -] - -[[package]] -name = "tiny-skia" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "642680569bb895b16e4b9d181c60be1ed136fa0c9c7f11d004daf053ba89bf82" -dependencies = [ - "arrayref", - "arrayvec", - "bytemuck", - "cfg-if", - "png 0.17.10 (registry+https://github.com/rust-lang/crates.io-index)", - "safe_arch", - "tiny-skia-path", -] - -[[package]] -name = "tiny-skia-path" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c114d32f0c2ee43d585367cb013dfaba967ab9f62b90d9af0d696e955e70fa6c" -dependencies = [ - "arrayref", - "bytemuck", -] - -[[package]] -name = "tinytemplate" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" -dependencies = [ - "serde", - "serde_json", -] - -[[package]] -name = "toml" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" -dependencies = [ - "serde", -] - -[[package]] -name = "unicode-ident" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" - -[[package]] -name = "unicode-width" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" - -[[package]] -name = "vec_map" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" - -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[package]] -name = "walkdir" -version = "2.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" -dependencies = [ - "same-file", - "winapi-util", -] - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "wasm-bindgen" -version = "0.2.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" -dependencies = [ - "cfg-if", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn 2.0.29", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.29", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" - -[[package]] -name = "wayland-client" -version = "0.29.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f3b068c05a039c9f755f881dc50f01732214f5685e379829759088967c46715" -dependencies = [ - "bitflags", - "downcast-rs", - "libc", - "nix 0.24.3", - "scoped-tls", - "wayland-commons", - "wayland-scanner", - "wayland-sys", -] - -[[package]] -name = "wayland-commons" -version = "0.29.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8691f134d584a33a6606d9d717b95c4fa20065605f798a3f350d78dced02a902" -dependencies = [ - "nix 0.24.3", - "once_cell", - "smallvec", - "wayland-sys", -] - -[[package]] -name = "wayland-cursor" -version = "0.29.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6865c6b66f13d6257bef1cd40cbfe8ef2f150fb8ebbdb1e8e873455931377661" -dependencies = [ - "nix 0.24.3", - "wayland-client", - "xcursor", -] - -[[package]] -name = "wayland-egl" -version = "0.29.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "402de949f81a012926d821a2d659f930694257e76dd92b6e0042ceb27be4107d" -dependencies = [ - "wayland-client", - "wayland-sys", -] - -[[package]] -name = "wayland-protocols" -version = "0.29.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b950621f9354b322ee817a23474e479b34be96c2e909c14f7bc0100e9a970bc6" -dependencies = [ - "bitflags", - "wayland-client", - "wayland-commons", - "wayland-scanner", -] - -[[package]] -name = "wayland-scanner" -version = "0.29.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f4303d8fa22ab852f789e75a967f0a2cdc430a607751c0499bada3e451cbd53" -dependencies = [ - "proc-macro2", - "quote", - "xml-rs", -] - -[[package]] -name = "wayland-sys" -version = "0.29.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be12ce1a3c39ec7dba25594b97b42cb3195d54953ddb9d3d95a7c3902bc6e9d4" -dependencies = [ - "dlib", - "lazy_static", - "pkg-config", -] - -[[package]] -name = "web-sys" -version = "0.3.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -dependencies = [ - "winapi", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows-sys" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" -dependencies = [ - "windows_aarch64_msvc 0.36.1", - "windows_i686_gnu 0.36.1", - "windows_i686_msvc 0.36.1", - "windows_x86_64_gnu 0.36.1", - "windows_x86_64_msvc 0.36.1", -] - -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-targets" -version = "0.48.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27f51fb4c64f8b770a823c043c7fad036323e1c48f55287b7bbb7987b2fcdf3b" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc 0.48.3", - "windows_i686_gnu 0.48.3", - "windows_i686_msvc 0.48.3", - "windows_x86_64_gnu 0.48.3", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc 0.48.3", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fde1bb55ae4ce76a597a8566d82c57432bc69c039449d61572a7a353da28f68c" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1513e8d48365a78adad7322fd6b5e4c4e99d92a69db8df2d435b25b1f1f286d4" - -[[package]] -name = "windows_i686_gnu" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60587c0265d2b842298f5858e1a5d79d146f9ee0c37be5782e92a6eb5e1d7a83" - -[[package]] -name = "windows_i686_msvc" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224fe0e0ffff5d2ea6a29f82026c8f43870038a0ffc247aa95a52b47df381ac4" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62fc52a0f50a088de499712cbc012df7ebd94e2d6eb948435449d76a6287e7ad" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2093925509d91ea3d69bcd20238f4c2ecdb1a29d3c281d026a09705d0dd35f3d" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6ade45bc8bf02ae2aa34a9d54ba660a1a58204da34ba793c00d83ca3730b5f1" - -[[package]] -name = "winit" -version = "0.27.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb796d6fbd86b2fd896c9471e6f04d39d750076ebe5680a3958f00f5ab97657c" -dependencies = [ - "bitflags", - "cocoa", - "core-foundation", - "core-graphics", - "dispatch", - "instant", - "libc", - "log", - "mio", - "ndk", - "ndk-glue", - "objc", - "once_cell", - "parking_lot", - "percent-encoding", - "raw-window-handle 0.4.3", - "raw-window-handle 0.5.2", - "sctk-adwaita", - "smithay-client-toolkit", - "wasm-bindgen", - "wayland-client", - "wayland-protocols", - "web-sys", - "windows-sys 0.36.1", - "x11-dl", -] - -[[package]] -name = "wio" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d129932f4644ac2396cb456385cbf9e63b5b30c6e8dc4820bdca4eb082037a5" -dependencies = [ - "winapi", -] - -[[package]] -name = "x11-dl" -version = "2.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f" -dependencies = [ - "libc", - "once_cell", - "pkg-config", -] - -[[package]] -name = "xcursor" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "463705a63313cd4301184381c5e8042f0a7e9b4bb63653f216311d4ae74690b7" -dependencies = [ - "nom", -] - -[[package]] -name = "xml-rs" -version = "0.8.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47430998a7b5d499ccee752b41567bc3afc57e1327dc855b1a2aa44ce29b5fa1" diff --git a/Cargo.toml b/Cargo.toml index 99d0e4bd..505801e0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,7 @@ include = [ bitflags = "1.0" crc32fast = "1.2.0" fdeflate = "0.3.3" -flate2 = "1.0" +flate2 = "1.0.11" miniz_oxide = { version = "0.7.1", features = ["simd"] } [dev-dependencies] From 59e04df13076d82477cf219c0f1d241240cf64d7 Mon Sep 17 00:00:00 2001 From: Jonathan Behrens Date: Thu, 15 Feb 2024 22:36:48 -0800 Subject: [PATCH 11/23] Release 0.17.12 (#468) --- CHANGES.md | 8 ++++++++ Cargo.toml | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index e33335f9..6766cc4a 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,13 @@ ## Unreleased +## 0.17.12 + +* Reject zero-sized frames. +* Optimized decoding of paletted images. +* Removed remaining uses of miniz_oxide for decoding. +* Correct lifetime used for `Info` struct. +* Fix build issue with `-Z minimal-versions`. + ## 0.17.11 * Ignore subsequent iCCP chunks to match libpng behavior. diff --git a/Cargo.toml b/Cargo.toml index 505801e0..8ac8da68 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "png" -version = "0.17.11" +version = "0.17.12" license = "MIT OR Apache-2.0" description = "PNG decoding and encoding library in pure Rust" From c02bd7b28c2a4397249604de7536a86da8c43f95 Mon Sep 17 00:00:00 2001 From: Jonathan Behrens Date: Sun, 18 Feb 2024 15:30:13 -0800 Subject: [PATCH 12/23] Fix Send bound on Reader (#471) --- src/decoder/transform.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/decoder/transform.rs b/src/decoder/transform.rs index 7271bcb9..e7de03eb 100644 --- a/src/decoder/transform.rs +++ b/src/decoder/transform.rs @@ -12,7 +12,7 @@ use super::stream::FormatErrorInner; /// /// TODO: If some precomputed state is needed (e.g. to make `expand_paletted...` /// faster) then consider changing this into `Box`. -pub type TransformFn = Box; +pub type TransformFn = Box; /// Returns a transformation function that should be applied to image rows based /// on 1) decoded image metadata (`info`) and 2) the transformations requested From b5b0d48ee352d638db2810f3083d9323094854f4 Mon Sep 17 00:00:00 2001 From: Jonathan Behrens Date: Sun, 18 Feb 2024 16:19:28 -0800 Subject: [PATCH 13/23] Release 0.17.13 (#472) --- CHANGES.md | 4 ++++ Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 6766cc4a..9a4689a6 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,9 @@ ## Unreleased +## 0.17.13 + +* Fix `Send` bound on `Reader`. + ## 0.17.12 * Reject zero-sized frames. diff --git a/Cargo.toml b/Cargo.toml index 8ac8da68..5849279d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "png" -version = "0.17.12" +version = "0.17.13" license = "MIT OR Apache-2.0" description = "PNG decoding and encoding library in pure Rust" From 9dd2a890ddd6b110bff1a1961ab2e4ac429e4a81 Mon Sep 17 00:00:00 2001 From: Song Date: Mon, 1 Apr 2024 09:11:43 +0800 Subject: [PATCH 14/23] Add an option to ignore iccp chunk (#477) --- src/decoder/mod.rs | 16 ++++++++++++++++ src/decoder/stream.rs | 24 +++++++++++++++++++++++- tests/iccp/broken_iccp.png | Bin 0 -> 3564 bytes 3 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 tests/iccp/broken_iccp.png diff --git a/src/decoder/mod.rs b/src/decoder/mod.rs index ce14edc1..bed13104 100644 --- a/src/decoder/mod.rs +++ b/src/decoder/mod.rs @@ -279,6 +279,22 @@ impl Decoder { .set_ignore_text_chunk(ignore_text_chunk); } + /// Set the decoder to ignore iccp chunks while parsing. + /// + /// eg. + /// ``` + /// use std::fs::File; + /// use png::Decoder; + /// let mut decoder = Decoder::new(File::open("tests/iccp/broken_iccp.png").unwrap()); + /// decoder.set_ignore_iccp_chunk(true); + /// assert!(decoder.read_info().is_ok()); + /// ``` + pub fn set_ignore_iccp_chunk(&mut self, ignore_iccp_chunk: bool) { + self.read_decoder + .decoder + .set_ignore_iccp_chunk(ignore_iccp_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) { diff --git a/src/decoder/stream.rs b/src/decoder/stream.rs index 584bdfaf..c2d6a97e 100644 --- a/src/decoder/stream.rs +++ b/src/decoder/stream.rs @@ -408,6 +408,7 @@ pub struct DecodeOptions { ignore_adler32: bool, ignore_crc: bool, ignore_text_chunk: bool, + ignore_iccp_chunk: bool, skip_ancillary_crc_failures: bool, } @@ -417,6 +418,7 @@ impl Default for DecodeOptions { ignore_adler32: true, ignore_crc: false, ignore_text_chunk: false, + ignore_iccp_chunk: false, skip_ancillary_crc_failures: true, } } @@ -451,6 +453,13 @@ impl DecodeOptions { self.ignore_text_chunk = ignore_text_chunk; } + /// Ignore ICCP chunks while decoding. + /// + /// Defaults to `false`. + pub fn set_ignore_iccp_chunk(&mut self, ignore_iccp_chunk: bool) { + self.ignore_iccp_chunk = ignore_iccp_chunk; + } + /// Ignore ancillary chunks if CRC fails /// /// Defaults to `true` @@ -540,6 +549,10 @@ impl StreamingDecoder { self.decode_options.set_ignore_text_chunk(ignore_text_chunk); } + pub fn set_ignore_iccp_chunk(&mut self, ignore_iccp_chunk: bool) { + self.decode_options.set_ignore_iccp_chunk(ignore_iccp_chunk); + } + /// Return whether the decoder is set to ignore the Adler-32 checksum. pub fn ignore_adler32(&self) -> bool { self.inflater.ignore_adler32() @@ -888,7 +901,7 @@ impl StreamingDecoder { chunk::fcTL => self.parse_fctl(), chunk::cHRM => self.parse_chrm(), chunk::sRGB => self.parse_srgb(), - chunk::iCCP => self.parse_iccp(), + chunk::iCCP if !self.decode_options.ignore_iccp_chunk => self.parse_iccp(), chunk::tEXt if !self.decode_options.ignore_text_chunk => self.parse_text(), chunk::zTXt if !self.decode_options.ignore_text_chunk => self.parse_ztxt(), chunk::iTXt if !self.decode_options.ignore_text_chunk => self.parse_itxt(), @@ -1740,6 +1753,15 @@ mod tests { assert_eq!(4070462061, crc32fast::hash(&icc_profile)); } + #[test] + fn test_png_with_broken_iccp() { + let decoder = crate::Decoder::new(File::open("tests/iccp/broken_iccp.png").unwrap()); + assert!(decoder.read_info().is_err()); + let mut decoder = crate::Decoder::new(File::open("tests/iccp/broken_iccp.png").unwrap()); + decoder.set_ignore_iccp_chunk(true); + assert!(decoder.read_info().is_ok()); + } + /// Writes an acTL chunk. /// See https://wiki.mozilla.org/APNG_Specification#.60acTL.60:_The_Animation_Control_Chunk fn write_actl(w: &mut impl Write, animation: &crate::AnimationControl) { diff --git a/tests/iccp/broken_iccp.png b/tests/iccp/broken_iccp.png new file mode 100644 index 0000000000000000000000000000000000000000..995376f5cdc095355db4b786ebf7448e84a13d64 GIT binary patch literal 3564 zcmcImi8mAq)Mp}Pf6qR)5Yb3vNtThV5-}l$P!dAc$<#1rk~d1$UIvXVvPD9gtTRJd zN*K#nvM-IX%}i!6cAxkC4c~Xqx%YSOJ@?%6yXW3}&P{;WUlrm9@^f)<3E5n;ay-JV z|MbN1Biq^!5;%gBVb|PXTwDTT|0y>YE=QV+i`zHA(h_1701JEQjW`9-<>Gph6PZS^ z{RRx7K#%t#HDjP6i`SYdD6BxoT`FLR;O!s$V#V zg@=h+@%=TKW2904;K@{`^O*8fCw1R22iD-xA`6iLZ35JnGG5uW72zq3zoX-x=Q)-8 zeJU%|}fDZ5n#@E|umKv`k`7&SnR^sDEr}BqfGu!2s zZ+3f&sY$GVnoki~rrkJIii*UUQ(Mknqc3Ii{CHAR}-B;lK_l58i=F4gIuoaX@ zLYD3Hov$tzY%WB!?4H!l0>m@bYrPk~ zDm{)y3ob^_?)eD*kq5xB>?)OBIuMf|3)<&vRXS$C#U*&o#>(9J2{(O`9ywtk*|An( zx@*d4vEvQrQ*ccy0tA7ozW_8OM&I8}=YQy`9l`qrG*bld2R)J0E`z!pp8*-3UOUW< zQ$ZBX_Nh#9zsM9gn%V!ew77dp_q{GU!?2}x>AIpG5J~6{fhf9-lfv$b)g=RJ?IEYV zz5UF$($f(;s0}d!%U!IdTG91`75`@_X@J~x0ANQ-fSXW@c-A0rXK=cK)r%F; znn{g!;W0!)n3_CnvSi?*{vZ9FN)YqprXipias#sal=yj&(-kxqx1W5o_V5_DR~n!G z2Iw{#(mBz{dM>bTe2kkI*<-q4E5I2t26gshWu+F;(AE=wk|Sd2%;K24cgEmBD_SWo zVQfMWd}>$9~;(HQe8kPL|yk~*GE!GT7Bca{*ed4Pz?5Q z-u6w&D4724gQ2QvNM`kctmtXI!$1@POUUvwxl0yHf~m&FmUMtM)fs|r|!jO4l^ zz*wL%o0xcyG0{NKzN^J3B!p8FGYa6o%&x1+Z1-8qpcGD31eLZhF}^C471uNhmp|W? zv&Z+d73=!ZB)GR?KrhhMYs_vU$n&vbY1h-73G2-^>_sC*MM|`**VzdU7)-L9YY5Gz z(T6-MyJHzC==Ym0qWFuf2&rG^jT~Tm#1hm;G4uVIJuR`$GKam8O2!_ONcjg}W&v94 zo5hn}jL9~rQ^rE|NpTkJCOpTjAi(QP%7zImhp^KWdeYh8X(QhzA!uAXs-Jw#JL-L? zLN8vrEtNgh$m-pYMlbH1ew?Rx4$NSrZUoIW93Vo2N3%9d`KUoe1A$B9Dg26d94Z2a zB0)(U)?&)WJ8q|eJ6*2xM) zMc+fx@CCsB-InwT>&RjR1t>L2#DqUUB&QmrS-j0}x>6;W^{kP%TO8@MhfRJPLfFQu z@rz>hf#MHT0gmsw5R zmKu~7!Sc>QP9Q;WdZ z!N{n_uWS5`%Sx-VFlKGZNjSB43040Ctu2?X5K%4>U+lHi_UsiN(zv;K*xB_IoL;9( ze|r^Q_SJAhBh)yLImqW5ssHHZBJ^82g@}wua(y+D#QUT__v&(uJ;Cb!ExOHohFOW6 z=eE91!hI(^LtNZl`91IXOqZiueXdr&Z_1>!!u^%%53ld_4t}4%3d+pKyI7;(X;jFR=KU)z`OP^!G^q&|c9tl5A5WO}Q8(56vZQC1mBva6A6dRs zWgPxtj?p8*3~NS*E>&GNrV231X^1whMdj#(_G;-4$Gn-%`33Ao(G%F`LFuiG7v4Ha z#U`zE|0JP6qqAN<61Q%@k2x^+z5J5J!ZD*!4unnlFtteO}lGuX$-}#v4*)X$QZKJMN%r%Ye3zK6tBBvkNvi<_24E zF89xDXCL(-%CC{J5C$22{$T~7BUD@PXyxUkO_;lw6)be*c7L6kTCREn%bluGYNx^1QbQwal@g zSG$5y4<|<`;If#g`2zVUti-jWo8s?MT=Wrd3bR;}9CnCrJ@DmisINC{WGQm!sUMqb zeYfxPMsOp2mL_e$vd5dMlf$ICdSlj3O759{m*iCWB+xwEN5{)Qv6~`jJ53T+ zvl*v{cn)UH!Q_J&1cz@^@e zceHMT3zAKs@tf~omoK3K-yXb%1{P-qd0iy2J=kjhj6USc{+c#>gUtEYyJjF@LYzWq z>;X3on;0O>L6lKT@M)0cFFjO!MMzVccwqhneoHO;9k51k1Y&?cqq-{R5!k@FbZ`GwVD;d;1wHQR6VhH*UtRuQ_*A}bm4VeYHXa5{1Tyl45 zr{i!xf&QO*z~HFx@YyrH&DBeOq)CI$aoqcmi|}}sx`RF5%hDM*Qp+%2HOSV6cvGjG zbN_V^qV_%NA$3OaOt#435BbA5i7Vdo?H3J@d1&fXote;hDKh#+P2DX&{@g-*)P;1*?F*m8@hvJ+ zcn9g_@rUQniXi*bL!<9F{;|J2?ZO%geDnDo1H`#j4FyndAUtHQ+{{w^0}UFuhQBx- z;D)`t{RMcha*an^@^#zlkiz1~4y_t!Yi;`$`R$&^WwD#rF1$fjc{T)I>iD(`Q(SFd zee14AwM~J_s1-2wRq$g>attirT1{0{KBz)_hWXog^sMEt^irS7g*jYCJlSECU8kPT z8k#EN#$|P=wv`P&<0N*?=s_g(Gav9mpg*$EvU z;gt`P(J+;qir+q=dQ$mtznr)oj(T|l{ z4aTO9aU|J6b4=VE2|ZmzvPjTlpcpy~<^`w5O)$i1UQw?nL|HJ=w`AR&dl;r-f2{VY zNlQ^ice)n{B9m4)`(t+z`qQp-B$j>fj-=Mt6k_xTGp1rzoSEQqe7!iN2R{5a`SMzZ zgpH<-{pmiDk0!d^7Hg6cuU)l;FeSUNSEtbMmnxaSYQO7M+-FpNe*F;6HqUX@d-10D ziwTcmwKtF9PGN@EUjVrn>ti{>WU5k&K!Nsdx4-^=p>RNw4{iuibIQN$Z^P%`f2W(< zh)CSA?%*e43XY$D`gOhkrj{ft> zy`5d4>ltZY)_T!iiTOlgdyP+);rV+l*IVbrANsBaYisg5GP2*V@qhiqNip=N@Oxzc z%syVFmb$8!CSdp#_vLLfX~|fwX(IGgN~Lc_8~MN=$^$^|ro8Ix>@`Z99Iq{Zc=U_W zcXe4?wXZl`HS16xCY}3H@uTAub0=Hu)Y~~ccUJ0vaoT!d`9($P4d>2d+K#r#w<~L#6(VeBuSou0pt!HTcG0Lom7e9R_=kc}}ou+ukt0 zt@Uvk-90aQK8lRi<)Y071yEc4;#&TGfSbHc5(v@Jmw*amaC?az<6;vnfPgJVWgu7% z$`R7c{krL}s}LJ=qmrPoUN{fJC~29Ikp9zTCk|ohs=!GFur68d|LzyR8+{;xqFEcb TDMOY1t7>CyZ}rjQ?z8^^OEtv( literal 0 HcmV?d00001 From f28bc07a7b6a005253e3ec622011f3e1113782da Mon Sep 17 00:00:00 2001 From: nathaniel-daniel <80441888+nathaniel-daniel@users.noreply.github.com> Date: Sun, 31 Mar 2024 18:13:05 -0700 Subject: [PATCH 15/23] Ignore iCCP section if it is invalid (#475) --- src/decoder/stream.rs | 75 ++++++++++++++++++++++++------------------- 1 file changed, 42 insertions(+), 33 deletions(-) diff --git a/src/decoder/stream.rs b/src/decoder/stream.rs index c2d6a97e..61daa5bd 100644 --- a/src/decoder/stream.rs +++ b/src/decoder/stream.rs @@ -485,6 +485,8 @@ pub struct StreamingDecoder { /// Whether we have already seen a start of an IDAT chunk. (Used to validate chunk ordering - /// some chunk types can only appear before or after an IDAT chunk.) have_idat: bool, + /// Whether we have already seen an iCCP chunk. Used to prevent parsing of duplicate iCCP chunks. + have_iccp: bool, decode_options: DecodeOptions, pub(crate) limits: Limits, } @@ -523,6 +525,7 @@ impl StreamingDecoder { info: None, current_seq_no: None, have_idat: false, + have_iccp: false, decode_options, limits: Limits { bytes: usize::MAX }, } @@ -1203,12 +1206,11 @@ impl StreamingDecoder { } fn parse_iccp(&mut self) -> Result { - let info = self.info.as_mut().unwrap(); if self.have_idat { Err(DecodingError::Format( FormatErrorInner::AfterIdat { kind: chunk::iCCP }.into(), )) - } else if info.icc_profile.is_some() { + } else if self.have_iccp { // We have already encountered an iCCP chunk before. // // Section "4.2.2.4. iCCP Embedded ICC profile" of the spec says: @@ -1222,44 +1224,51 @@ impl StreamingDecoder { // (treating them as a benign error). Ok(Decoded::Nothing) } else { - let mut buf = &self.current_chunk.raw_bytes[..]; + self.have_iccp = true; + let _ = self.parse_iccp_raw(); + Ok(Decoded::Nothing) + } + } - // read profile name - let _: u8 = buf.read_be()?; - for _ in 1..80 { - let raw: u8 = buf.read_be()?; - if raw == 0 { - break; - } - } + fn parse_iccp_raw(&mut self) -> Result<(), DecodingError> { + let info = self.info.as_mut().unwrap(); + let mut buf = &self.current_chunk.raw_bytes[..]; - match buf.read_be()? { - // compression method - 0u8 => (), - n => { - return Err(DecodingError::Format( - FormatErrorInner::UnknownCompressionMethod(n).into(), - )) - } + // read profile name + let _: u8 = buf.read_be()?; + for _ in 1..80 { + let raw: u8 = buf.read_be()?; + if raw == 0 { + break; } + } - match fdeflate::decompress_to_vec_bounded(buf, self.limits.bytes) { - Ok(profile) => { - self.limits.reserve_bytes(profile.len())?; - info.icc_profile = Some(Cow::Owned(profile)); - } - Err(fdeflate::BoundedDecompressionError::DecompressionError { inner: err }) => { - return Err(DecodingError::Format( - FormatErrorInner::CorruptFlateStream { err }.into(), - )) - } - Err(fdeflate::BoundedDecompressionError::OutputTooLarge { .. }) => { - return Err(DecodingError::LimitsExceeded); - } + match buf.read_be()? { + // compression method + 0u8 => (), + n => { + return Err(DecodingError::Format( + FormatErrorInner::UnknownCompressionMethod(n).into(), + )) } + } - Ok(Decoded::Nothing) + match fdeflate::decompress_to_vec_bounded(buf, self.limits.bytes) { + Ok(profile) => { + self.limits.reserve_bytes(profile.len())?; + info.icc_profile = Some(Cow::Owned(profile)); + } + Err(fdeflate::BoundedDecompressionError::DecompressionError { inner: err }) => { + return Err(DecodingError::Format( + FormatErrorInner::CorruptFlateStream { err }.into(), + )) + } + Err(fdeflate::BoundedDecompressionError::OutputTooLarge { .. }) => { + return Err(DecodingError::LimitsExceeded); + } } + + Ok(()) } fn parse_ihdr(&mut self) -> Result { From f00ca20187fe33a28b03b76967a4b57dc3cbf764 Mon Sep 17 00:00:00 2001 From: Jonathan Behrens Date: Sun, 31 Mar 2024 18:32:08 -0700 Subject: [PATCH 16/23] Fix test and warnings (#480) --- src/decoder/stream.rs | 5 ++--- src/encoder.rs | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/decoder/stream.rs b/src/decoder/stream.rs index 61daa5bd..ac556bd8 100644 --- a/src/decoder/stream.rs +++ b/src/decoder/stream.rs @@ -1,7 +1,6 @@ extern crate crc32fast; -use std::convert::{From, TryInto}; -use std::default::Default; +use std::convert::TryInto; use std::error; use std::fmt; use std::io; @@ -1765,7 +1764,7 @@ mod tests { #[test] fn test_png_with_broken_iccp() { let decoder = crate::Decoder::new(File::open("tests/iccp/broken_iccp.png").unwrap()); - assert!(decoder.read_info().is_err()); + assert!(decoder.read_info().is_ok()); let mut decoder = crate::Decoder::new(File::open("tests/iccp/broken_iccp.png").unwrap()); decoder.set_ignore_iccp_chunk(true); assert!(decoder.read_info().is_ok()); diff --git a/src/encoder.rs b/src/encoder.rs index c6db9165..7391ec5b 100644 --- a/src/encoder.rs +++ b/src/encoder.rs @@ -1716,9 +1716,9 @@ mod tests { use crate::Decoder; use rand::{thread_rng, Rng}; + use std::cmp; use std::fs::File; - use std::io::{Cursor, Write}; - use std::{cmp, io}; + use std::io::Cursor; #[test] fn roundtrip() { From 186479626efe18fa6b365c77e934f15b53a157da Mon Sep 17 00:00:00 2001 From: Bruce Mitchener Date: Fri, 28 Jun 2024 19:39:16 +0700 Subject: [PATCH 17/23] ci: Update to `actions/checkout@v4` from `v2` --- .github/workflows/rust.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 47d8e38d..09f514d9 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -14,7 +14,7 @@ jobs: features: [""] runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@nightly if: ${{ matrix.rust == '1.57.0' }} @@ -35,7 +35,7 @@ jobs: features: ["", "benchmarks"] runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - uses: actions-rs/toolchain@v1 with: toolchain: nightly @@ -48,7 +48,7 @@ jobs: powerpc_cross: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: add_cross_target run: | rustup target add powerpc-unknown-linux-gnu @@ -56,7 +56,7 @@ jobs: test_all: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - run: rustup default stable - name: test run: > @@ -64,7 +64,7 @@ jobs: rustfmt: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - uses: actions-rs/toolchain@v1 with: toolchain: stable From 3308238f67ff458e99895fd250c2dcba0b813824 Mon Sep 17 00:00:00 2001 From: Bruce Mitchener Date: Sat, 29 Jun 2024 11:36:04 +0700 Subject: [PATCH 18/23] Remove usages of `extern crate` --- examples/pngcheck.rs | 4 ---- fuzz/Cargo.toml | 1 + fuzz/fuzz_targets/buf_independent.rs | 3 +-- fuzz/fuzz_targets/decode.rs | 4 ++-- fuzz/fuzz_targets/roundtrip.rs | 3 +-- png-afl/Cargo.toml | 1 + png-afl/src/main.rs | 6 +----- src/common.rs | 2 +- src/decoder/stream.rs | 2 -- src/lib.rs | 3 --- tests/check_testimages.rs | 4 ---- 11 files changed, 8 insertions(+), 25 deletions(-) diff --git a/examples/pngcheck.rs b/examples/pngcheck.rs index 69e95e3c..0c5ab5a5 100644 --- a/examples/pngcheck.rs +++ b/examples/pngcheck.rs @@ -1,9 +1,5 @@ #![allow(non_upper_case_globals)] -extern crate getopts; -extern crate glob; -extern crate png; - use std::env; use std::fs::File; use std::io; diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index 11b6e96a..1ea29a9a 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -3,6 +3,7 @@ name = "png-fuzz" version = "0.0.1" authors = ["Automatically generated"] +edition = "2018" publish = false [package.metadata] diff --git a/fuzz/fuzz_targets/buf_independent.rs b/fuzz/fuzz_targets/buf_independent.rs index 5c4a4ea7..3bb4e44a 100644 --- a/fuzz/fuzz_targets/buf_independent.rs +++ b/fuzz/fuzz_targets/buf_independent.rs @@ -1,7 +1,6 @@ #![no_main] -extern crate libfuzzer_sys; + use libfuzzer_sys::fuzz_target; -extern crate png; use std::mem::discriminant; use std::io::{BufRead, Read, Result}; diff --git a/fuzz/fuzz_targets/decode.rs b/fuzz/fuzz_targets/decode.rs index 5e52c8e7..ccf50027 100644 --- a/fuzz/fuzz_targets/decode.rs +++ b/fuzz/fuzz_targets/decode.rs @@ -1,6 +1,6 @@ #![no_main] -#[macro_use] extern crate libfuzzer_sys; -extern crate png; + +use libfuzzer_sys::fuzz_target; #[inline(always)] fn png_decode(data: &[u8]) -> Result<(Option, Vec), ()> { diff --git a/fuzz/fuzz_targets/roundtrip.rs b/fuzz/fuzz_targets/roundtrip.rs index 5d0ebae9..e428444d 100644 --- a/fuzz/fuzz_targets/roundtrip.rs +++ b/fuzz/fuzz_targets/roundtrip.rs @@ -1,8 +1,7 @@ #![no_main] +use libfuzzer_sys::fuzz_target; use png::{FilterType, ColorType, BitDepth}; -#[macro_use] extern crate libfuzzer_sys; -extern crate png; fuzz_target!(|data: (u8, u8, u8, u8, u8, Vec, Vec)| { if let Some((raw, encoded)) = encode_png(data.0, data.1, data.2, data.3, data.4, &data.5, &data.6) { diff --git a/png-afl/Cargo.toml b/png-afl/Cargo.toml index 57bdf9fb..42ad5fe3 100644 --- a/png-afl/Cargo.toml +++ b/png-afl/Cargo.toml @@ -2,6 +2,7 @@ name = "png-afl" version = "0.2.0" authors = ["Sergey Davidoff ", "Paul Grandperrin "] +edition = "2018" [dependencies] afl = "0.4.3" diff --git a/png-afl/src/main.rs b/png-afl/src/main.rs index 6168d114..1c0b7ab9 100644 --- a/png-afl/src/main.rs +++ b/png-afl/src/main.rs @@ -1,9 +1,5 @@ #![forbid(unsafe_code)] -#[macro_use] -extern crate afl; -extern crate png; - #[inline(always)] fn png_decode(data: &[u8]) -> Result<(png::OutputInfo, Vec), ()> { let decoder = png::Decoder::new(data); @@ -22,7 +18,7 @@ fn png_decode(data: &[u8]) -> Result<(png::OutputInfo, Vec), ()> { } fn main() { - fuzz!(|data: &[u8]| { + afl::fuzz!(|data: &[u8]| { let _ = png_decode(&data); }); } diff --git a/src/common.rs b/src/common.rs index 400aca11..d946c1f6 100644 --- a/src/common.rs +++ b/src/common.rs @@ -704,7 +704,7 @@ impl BytesPerPixel { } } -bitflags! { +bitflags::bitflags! { /// Output transformations /// /// Many flags from libpng are not yet supported. A PR discussing/adding them would be nice. diff --git a/src/decoder/stream.rs b/src/decoder/stream.rs index ac556bd8..52fa8186 100644 --- a/src/decoder/stream.rs +++ b/src/decoder/stream.rs @@ -1,5 +1,3 @@ -extern crate crc32fast; - use std::convert::TryInto; use std::error; use std::fmt; diff --git a/src/lib.rs b/src/lib.rs index b7e151a2..e514aa51 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -61,9 +61,6 @@ #![cfg_attr(feature = "unstable", feature(portable_simd))] #![forbid(unsafe_code)] -#[macro_use] -extern crate bitflags; - mod adam7; pub mod chunk; mod common; diff --git a/tests/check_testimages.rs b/tests/check_testimages.rs index 31c1ef83..efece006 100644 --- a/tests/check_testimages.rs +++ b/tests/check_testimages.rs @@ -1,7 +1,3 @@ -extern crate crc32fast; -extern crate glob; -extern crate png; - use std::collections::BTreeMap; use std::fs::File; use std::io::prelude::*; From 49f0cad1155d98f16a5c653a2bafae3f623daf2d Mon Sep 17 00:00:00 2001 From: Bruce Mitchener Date: Sat, 29 Jun 2024 11:59:19 +0700 Subject: [PATCH 19/23] Fix `clippy::legacy_numeric_constants` lints --- src/common.rs | 2 +- src/decoder/zlib.rs | 4 ++-- src/encoder.rs | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/common.rs b/src/common.rs index d946c1f6..4475153e 100644 --- a/src/common.rs +++ b/src/common.rs @@ -343,7 +343,7 @@ impl ScaledFloat { /// Gets whether the value is within the clamped range of this type. pub fn in_range(value: f32) -> bool { - value >= 0.0 && (value * Self::SCALING).floor() <= std::u32::MAX as f32 + value >= 0.0 && (value * Self::SCALING).floor() <= u32::MAX as f32 } /// Gets whether the value can be exactly converted in round-trip. diff --git a/src/decoder/zlib.rs b/src/decoder/zlib.rs index 1693154e..58c495bd 100644 --- a/src/decoder/zlib.rs +++ b/src/decoder/zlib.rs @@ -185,10 +185,10 @@ impl ZlibStream { .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) + .min(u64::MAX as usize) // Ensure the allocation request is valid. // TODO: maximum allocation limits? - .min(isize::max_value() as usize) + .min(isize::MAX as usize) // Don't unnecessarily allocate more than `max_total_output`. .min(self.max_total_output) } diff --git a/src/encoder.rs b/src/encoder.rs index 7391ec5b..8326648b 100644 --- a/src/encoder.rs +++ b/src/encoder.rs @@ -652,9 +652,9 @@ impl Writer { } } - const MAX_IDAT_CHUNK_LEN: u32 = std::u32::MAX >> 1; + const MAX_IDAT_CHUNK_LEN: u32 = u32::MAX >> 1; #[allow(non_upper_case_globals)] - const MAX_fdAT_CHUNK_LEN: u32 = (std::u32::MAX >> 1) - 4; + const MAX_fdAT_CHUNK_LEN: u32 = (u32::MAX >> 1) - 4; /// Writes the next image data. pub fn write_image_data(&mut self, data: &[u8]) -> Result<()> { @@ -1119,7 +1119,7 @@ impl<'a, W: Write> ChunkWriter<'a, W> { // // TODO (maybe): find a way to hold two chunks at a time if `usize` // is 64 bits. - const CAP: usize = std::u32::MAX as usize >> 1; + const CAP: usize = u32::MAX as usize >> 1; let curr_chunk = if writer.images_written == 0 { chunk::IDAT } else { From d4390c44b8ba1be19475e3be0f773d787ed570a3 Mon Sep 17 00:00:00 2001 From: Bruce Mitchener Date: Sat, 29 Jun 2024 11:55:21 +0700 Subject: [PATCH 20/23] Fix typos. Fixes #357. --- CHANGES.md | 2 +- examples/pngcheck.rs | 4 ++-- src/decoder/mod.rs | 6 +++--- src/decoder/stream.rs | 13 ++++++++----- src/decoder/transform/palette.rs | 2 +- src/decoder/zlib.rs | 6 +++--- src/encoder.rs | 6 +++--- src/filter.rs | 4 ++-- 8 files changed, 23 insertions(+), 20 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 9a4689a6..65e6c449 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -19,7 +19,7 @@ * Added `new_with_info` constructor for encoder. * Removed hard-coded memory limits. * No longer allow zero sized images. -* Added `Reader::finish` to read all the auxillary chunks that comes after the +* Added `Reader::finish` to read all the auxiliary chunks that comes after the image. ## 0.17.10 diff --git a/examples/pngcheck.rs b/examples/pngcheck.rs index 0c5ab5a5..97f8b0d5 100644 --- a/examples/pngcheck.rs +++ b/examples/pngcheck.rs @@ -73,7 +73,7 @@ fn final_channels(c: png::ColorType, trns: bool) -> u8 { } } fn check_image>(c: Config, fname: P) -> io::Result<()> { - // TODO improve performance by resusing allocations from decoder + // TODO improve performance by reusing allocations from decoder use png::Decoded::*; let mut t = term::stdout() .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "could not open terminal"))?; @@ -248,7 +248,7 @@ fn check_image>(c: Config, fname: P) -> io::Result<()> { } print!( " at offset {:#07x}, length {}", - pos - 4, // substract chunk name length + pos - 4, // subtract chunk name length len ) } diff --git a/src/decoder/mod.rs b/src/decoder/mod.rs index bed13104..2a189649 100644 --- a/src/decoder/mod.rs +++ b/src/decoder/mod.rs @@ -3,7 +3,7 @@ pub(crate) mod transform; mod zlib; pub use self::stream::{DecodeOptions, Decoded, DecodingError, StreamingDecoder}; -use self::stream::{FormatErrorInner, CHUNCK_BUFFER_SIZE}; +use self::stream::{FormatErrorInner, CHUNK_BUFFER_SIZE}; use self::transform::{create_transform_fn, TransformFn}; use std::io::{BufRead, BufReader, Read}; @@ -148,7 +148,7 @@ impl Decoder { Decoder { read_decoder: ReadDecoder { - reader: BufReader::with_capacity(CHUNCK_BUFFER_SIZE, r), + reader: BufReader::with_capacity(CHUNK_BUFFER_SIZE, r), decoder, at_eof: false, }, @@ -163,7 +163,7 @@ impl Decoder { Decoder { read_decoder: ReadDecoder { - reader: BufReader::with_capacity(CHUNCK_BUFFER_SIZE, r), + reader: BufReader::with_capacity(CHUNK_BUFFER_SIZE, r), decoder, at_eof: false, }, diff --git a/src/decoder/stream.rs b/src/decoder/stream.rs index 52fa8186..0f0c58d9 100644 --- a/src/decoder/stream.rs +++ b/src/decoder/stream.rs @@ -17,7 +17,7 @@ use crate::traits::ReadBytesExt; use crate::Limits; /// TODO check if these size are reasonable -pub const CHUNCK_BUFFER_SIZE: usize = 32 * 1024; +pub const CHUNK_BUFFER_SIZE: usize = 32 * 1024; /// Determines if checksum checks should be disabled globally. /// @@ -312,7 +312,7 @@ impl fmt::Display for FormatError { "Transparency chunk found for color type {:?}.", color_type ), - InvalidBitDepth(nr) => write!(fmt, "Invalid dispose operation {}.", nr), + InvalidBitDepth(nr) => write!(fmt, "Invalid bit depth {}.", nr), InvalidColorType(nr) => write!(fmt, "Invalid color type {}.", nr), InvalidDisposeOp(nr) => write!(fmt, "Invalid dispose op {}.", nr), InvalidBlendOp(nr) => write!(fmt, "Invalid blend op {}.", nr), @@ -325,7 +325,10 @@ impl fmt::Display for FormatError { InvalidSignature => write!(fmt, "Invalid PNG signature."), UnexpectedEof => write!(fmt, "Unexpected end of data before image end."), UnexpectedEndOfChunk => write!(fmt, "Unexpected end of data within a chunk."), - NoMoreImageData => write!(fmt, "IDAT or fDAT chunk is has not enough data for image."), + NoMoreImageData => write!( + fmt, + "IDAT or fDAT chunk does not have enough data for image." + ), CorruptFlateStream { err } => { write!(fmt, "Corrupt deflate stream. ")?; write!(fmt, "{:?}", err) @@ -1497,7 +1500,7 @@ impl Default for ChunkState { type_: ChunkType([0; 4]), crc: Crc32::new(), remaining: 0, - raw_bytes: Vec::with_capacity(CHUNCK_BUFFER_SIZE), + raw_bytes: Vec::with_capacity(CHUNK_BUFFER_SIZE), } } } @@ -1937,7 +1940,7 @@ mod tests { panic!("No fcTL (2nd frame)"); }; // The sequence number is taken from the `fcTL` chunk that comes before the two `fdAT` - // chunks. Note that sequence numbers inside `fdAT` chunks are not publically exposed + // chunks. Note that sequence numbers inside `fdAT` chunks are not publicly exposed // (but they are still checked when decoding to verify that they are sequential). assert_eq!(frame_control.sequence_number, 1); } diff --git a/src/decoder/transform/palette.rs b/src/decoder/transform/palette.rs index 72dc88f1..0bf49f25 100644 --- a/src/decoder/transform/palette.rs +++ b/src/decoder/transform/palette.rs @@ -1,4 +1,4 @@ -//! Helpers for taking a slice of indeces (indices into `PLTE` and/or `trNS` +//! Helpers for taking a slice of indices (indices into `PLTE` and/or `trNS` //! entries) and transforming this into RGB or RGBA output. //! //! # Memoization diff --git a/src/decoder/zlib.rs b/src/decoder/zlib.rs index 58c495bd..30cd54f7 100644 --- a/src/decoder/zlib.rs +++ b/src/decoder/zlib.rs @@ -1,4 +1,4 @@ -use super::{stream::FormatErrorInner, DecodingError, CHUNCK_BUFFER_SIZE}; +use super::{stream::FormatErrorInner, DecodingError, CHUNK_BUFFER_SIZE}; use fdeflate::Decompressor; @@ -166,7 +166,7 @@ impl ZlibStream { let current_len = self.out_buffer.len(); let desired_len = self .out_pos - .saturating_add(CHUNCK_BUFFER_SIZE) + .saturating_add(CHUNK_BUFFER_SIZE) .min(self.max_total_output); if current_len >= desired_len { return; @@ -182,7 +182,7 @@ impl ZlibStream { // 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)) + .saturating_add(CHUNK_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 as usize) diff --git a/src/encoder.rs b/src/encoder.rs index 8326648b..15050ec0 100644 --- a/src/encoder.rs +++ b/src/encoder.rs @@ -333,7 +333,7 @@ impl<'a, W: Write> Encoder<'a, W> { /// /// If the denominator is 0, it is to be treated as if it were 100 /// (that is, the numerator then specifies 1/100ths of a second). - /// If the the value of the numerator is 0 the decoder should render the next frame + /// If the value of the numerator is 0 the decoder should render the next frame /// as quickly as possible, though viewers may impose a reasonable lower bound. /// /// The default value is 0 for both the numerator and denominator. @@ -846,7 +846,7 @@ impl Writer { /// /// If the denominator is 0, it is to be treated as if it were 100 /// (that is, the numerator then specifies 1/100ths of a second). - /// If the the value of the numerator is 0 the decoder should render the next frame + /// If the value of the numerator is 0 the decoder should render the next frame /// as quickly as possible, though viewers may impose a reasonable lower bound. /// /// This method will return an error if the image is not animated. @@ -1393,7 +1393,7 @@ impl<'a, W: Write> StreamWriter<'a, W> { /// /// If the denominator is 0, it is to be treated as if it were 100 /// (that is, the numerator then specifies 1/100ths of a second). - /// If the the value of the numerator is 0 the decoder should render the next frame + /// If the value of the numerator is 0 the decoder should render the next frame /// as quickly as possible, though viewers may impose a reasonable lower bound. /// /// This method will return an error if the image is not animated. diff --git a/src/filter.rs b/src/filter.rs index 5afed49e..1a7daf34 100644 --- a/src/filter.rs +++ b/src/filter.rs @@ -15,7 +15,7 @@ mod simd { /// This is an equivalent of the `PaethPredictor` function from /// [the spec](http://www.libpng.org/pub/png/spec/1.2/PNG-Filters.html#Filter-type-4-Paeth) - /// except that it simultaenously calculates the predictor for all SIMD lanes. + /// except that it simultaneously calculates the predictor for all SIMD lanes. /// Mapping between parameter names and pixel positions can be found in /// [a diagram here](https://www.w3.org/TR/png/#filter-byte-positions). /// @@ -777,7 +777,7 @@ fn filter_internal( ) -> FilterType { use self::FilterType::*; - // This value was chosen experimentally based on what acheived the best performance. The + // This value was chosen experimentally based on what achieved the best performance. The // Rust compiler does auto-vectorization, and 32-bytes per loop iteration seems to enable // the fastest code when doing so. const CHUNK_SIZE: usize = 32; From 2cfde02d3a50c531e3160023d1e4b0ae92869f4a Mon Sep 17 00:00:00 2001 From: Bruce Mitchener Date: Sat, 29 Jun 2024 12:16:32 +0700 Subject: [PATCH 21/23] README: Remove broken badges Fixes #273. --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index e6719bc5..2b7bac55 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,7 @@ [![Build Status](https://github.com/image-rs/image-png/workflows/Rust%20CI/badge.svg)](https://github.com/image-rs/image-png/actions) [![Documentation](https://docs.rs/png/badge.svg)](https://docs.rs/png) [![Crates.io](https://img.shields.io/crates/v/png.svg)](https://crates.io/crates/png) -![Lines of Code](https://tokei.rs/b1/github/image-rs/image-png) [![License](https://img.shields.io/crates/l/png.svg)](https://github.com/image-rs/image-png) -[![fuzzit](https://app.fuzzit.dev/badge?org_id=image-rs)](https://app.fuzzit.dev/orgs/image-rs/dashboard) PNG decoder/encoder in pure Rust. From b7d0c0641c5fb379af265a49b33516da3e08a4dc Mon Sep 17 00:00:00 2001 From: Lukasz Anforowicz Date: Wed, 17 Jul 2024 15:57:11 +0000 Subject: [PATCH 22/23] Benchmark of `next_row`-based decoding. --- benches/decoder.rs | 46 +++++++++++++++++++++++++++++++++---------- src/decoder/stream.rs | 1 + 2 files changed, 37 insertions(+), 10 deletions(-) diff --git a/benches/decoder.rs b/benches/decoder.rs index d630ecbd..c47089d7 100644 --- a/benches/decoder.rs +++ b/benches/decoder.rs @@ -41,6 +41,13 @@ fn load_all(c: &mut Criterion) { bench_noncompressed_png(&mut g, 2048, 0x7fffffff); // 16 MB bench_noncompressed_png(&mut g, 12288, 0x7fffffff); // 576 MB g.finish(); + + // Incremental decoding via `next_row` + let mut g = c.benchmark_group("row-by-row"); + let mut data = Vec::new(); + test_utils::write_noncompressed_png(&mut data, 128, 4096); + bench_next_row(&mut g, data, "128x128-4k-idat"); + g.finish(); } criterion_group! {benches, load_all} @@ -52,21 +59,12 @@ fn bench_noncompressed_png(g: &mut BenchmarkGroup, size: u32, idat_byt bench_file(g, data, format!("{size}x{size}.png")); } +/// This benchmarks decoding via a call to `Reader::next_frame`. fn bench_file(g: &mut BenchmarkGroup, data: Vec, name: String) { if data.len() > 1_000_000 { g.sample_size(10); } - fn create_reader(data: &[u8]) -> Reader<&[u8]> { - let mut decoder = Decoder::new(data); - - // Cover default transformations used by the `image` crate when constructing - // `image::codecs::png::PngDecoder`. - decoder.set_transformations(Transformations::EXPAND); - - decoder.read_info().unwrap() - } - let mut reader = create_reader(data.as_slice()); let mut image = vec![0; reader.output_buffer_size()]; let info = reader.next_frame(&mut image).unwrap(); @@ -79,3 +77,31 @@ fn bench_file(g: &mut BenchmarkGroup, data: Vec, name: String) { }) }); } + +/// This benchmarks decoding via a sequence of `Reader::next_row` calls. +fn bench_next_row(g: &mut BenchmarkGroup, data: Vec, name: &str) { + let reader = create_reader(data.as_slice()); + let mut image = vec![0; reader.output_buffer_size()]; + let bytes_per_row = reader.output_line_size(reader.info().width); + g.throughput(Throughput::Bytes(image.len() as u64)); + g.bench_with_input(name, &data, |b, data| { + b.iter(|| { + let mut reader = create_reader(data.as_slice()); + + for output_row in image.chunks_exact_mut(bytes_per_row) { + let decoded_row = reader.next_row().unwrap().unwrap(); + output_row.copy_from_slice(decoded_row.data()); + } + }) + }); +} + +fn create_reader(data: &[u8]) -> Reader<&[u8]> { + let mut decoder = Decoder::new(data); + + // Cover default transformations used by the `image` crate when constructing + // `image::codecs::png::PngDecoder`. + decoder.set_transformations(Transformations::EXPAND); + + decoder.read_info().unwrap() +} diff --git a/src/decoder/stream.rs b/src/decoder/stream.rs index 0f0c58d9..1d1bfcb6 100644 --- a/src/decoder/stream.rs +++ b/src/decoder/stream.rs @@ -23,6 +23,7 @@ pub const CHUNK_BUFFER_SIZE: usize = 32 * 1024; /// /// This is used only in fuzzing. `afl` automatically adds `--cfg fuzzing` to RUSTFLAGS which can /// be used to detect that build. +#[allow(unexpected_cfgs)] const CHECKSUM_DISABLED: bool = cfg!(fuzzing); /// Kind of `u32` value that is being read via `State::U32`. From 29e90cc4ec83ddbbe2286967b12224935c742771 Mon Sep 17 00:00:00 2001 From: Lukasz Anforowicz Date: Tue, 16 Jul 2024 21:05:12 +0000 Subject: [PATCH 23/23] Remove `Reader::scrach_buffer` field + resulting breaking API changes. This commit is desirable, because it avoids copying of image data across intermediate buffers. This commit was motivated by the data gathered in https://github.com/image-rs/image-png/discussions/416#discussioncomment-7436871 The commit results in the followin performance gains seen in the recently introduced `row-by-row/128x128-4k-idat` benchmark: - time: [-18.401% -17.807% -17.192%] (p = 0.00 < 0.05) - time: [-9.4276% -8.8789% -8.2860%] (p = 0.00 < 0.05) - time: [-12.389% -11.780% -11.181%] (p = 0.00 < 0.05) Fixes https://github.com/image-rs/image-png/issues/417 --- CHANGES.md | 7 +++- Cargo.toml | 2 +- benches/decoder.rs | 3 +- src/decoder/mod.rs | 84 ++++++++++++---------------------------------- 4 files changed, 29 insertions(+), 67 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 65e6c449..4b587285 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,4 +1,9 @@ -## Unreleased +## 0.18.0 + +* Breaking API changes (motivated by performance gains in some scenarios): + - Removing the `Row` and `InterlacedRow` structs + - Removing the `Reader::next_interlaced_row` method + - Changing the signature of the `Reader::next_row` method ## 0.17.13 diff --git a/Cargo.toml b/Cargo.toml index 5849279d..0f498b41 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "png" -version = "0.17.13" +version = "0.18.0" license = "MIT OR Apache-2.0" description = "PNG decoding and encoding library in pure Rust" diff --git a/benches/decoder.rs b/benches/decoder.rs index c47089d7..d190b5d7 100644 --- a/benches/decoder.rs +++ b/benches/decoder.rs @@ -89,8 +89,7 @@ fn bench_next_row(g: &mut BenchmarkGroup, data: Vec, name: &str) { let mut reader = create_reader(data.as_slice()); for output_row in image.chunks_exact_mut(bytes_per_row) { - let decoded_row = reader.next_row().unwrap().unwrap(); - output_row.copy_from_slice(decoded_row.data()); + reader.next_row(output_row).unwrap().unwrap(); } }) }); diff --git a/src/decoder/mod.rs b/src/decoder/mod.rs index 2a189649..db3f6c66 100644 --- a/src/decoder/mod.rs +++ b/src/decoder/mod.rs @@ -7,7 +7,6 @@ use self::stream::{FormatErrorInner, CHUNK_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; @@ -87,25 +86,8 @@ pub struct Decoder { 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)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum InterlaceInfo { /// the null method means no interlacing Null, @@ -123,18 +105,6 @@ pub enum InterlaceInfo { 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 Decoder { /// Create a new decoder configuration with default limits. pub fn new(r: R) -> Decoder { @@ -230,7 +200,6 @@ impl Decoder { 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. @@ -389,10 +358,6 @@ pub struct Reader { /// Function that can transform decompressed, unfiltered rows into final output. /// See the `transform.rs` module for more details. transform_fn: Option, - /// 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, } /// The subframe specific information. @@ -538,18 +503,14 @@ impl Reader { 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 mut row = vec![0; self.output_line_size(width)]; + while let Some(interlace) = self.next_row(&mut 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)); + adam7::expand_pass(buf, width, &row, pass, line, samples * (bit_depth as u8)); } } else { for row in buf @@ -586,14 +547,12 @@ impl Reader { Ok(output_info) } - /// Returns the next processed row of the image - pub fn next_row(&mut self) -> Result, 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, DecodingError> { + /// Returns the next processed row of the image. + /// + /// A `ParameterError` will be returned if `row` doesn't have enough space. For initial + /// interlaced rows a smaller buffer may work, but providing a buffer that can hold + /// `self.output_line_size(self.info().width` bytes should always avoid that error. + pub fn next_row(&mut self, row: &mut [u8]) -> Result, DecodingError> { let (rowlen, interlace) = match self.next_pass() { Some((rowlen, interlace)) => (rowlen, interlace), None => return Ok(None), @@ -605,19 +564,18 @@ impl Reader { self.subframe.width }; let output_line_size = self.output_line_size(width); + if row.len() < output_line_size { + return Err(DecodingError::Parameter( + ParameterErrorKind::ImageBufferSize { + expected: output_line_size, + actual: row.len(), + } + .into(), + )); + } - // 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, - })) + self.next_interlaced_row_impl(rowlen, &mut row[..output_line_size])?; + Ok(Some(interlace)) } /// Read the rest of the image and chunks and finish up, including text chunks or others