diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 1e17f3e..0d53608 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -29,7 +29,6 @@ jobs: rustfmt: runs-on: ubuntu-latest continue-on-error: false - if: false steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 diff --git a/benches/decode.rs b/benches/decode.rs index 94977d1..8862751 100644 --- a/benches/decode.rs +++ b/benches/decode.rs @@ -1,4 +1,6 @@ -use criterion::{black_box, BenchmarkId, BenchmarkGroup, Criterion, Throughput, measurement::Measurement}; +use criterion::{ + black_box, measurement::Measurement, BenchmarkGroup, BenchmarkId, Criterion, Throughput, +}; use gif::Decoder; fn read_image(image: &[u8]) -> Option> { @@ -42,38 +44,49 @@ fn main() { let mut c = Criterion::default().configure_from_args(); let mut group = c.benchmark_group("gif"); - run_bench_def(&mut group, BenchDef { - data: include_bytes!("note.gif"), - id: "note.gif", - sample_size: 100, - }); + run_bench_def( + &mut group, + BenchDef { + data: include_bytes!("note.gif"), + id: "note.gif", + sample_size: 100, + }, + ); - run_bench_def(&mut group, BenchDef { - data: include_bytes!("photo.gif"), - id: "photo.gif", - sample_size: 20, - }); + run_bench_def( + &mut group, + BenchDef { + data: include_bytes!("photo.gif"), + id: "photo.gif", + sample_size: 20, + }, + ); - run_bench_def(&mut group, BenchDef { - data: include_bytes!("../tests/samples/sample_1.gif"), - id: "sample_1.gif", - sample_size: 100, - }); + run_bench_def( + &mut group, + BenchDef { + data: include_bytes!("../tests/samples/sample_1.gif"), + id: "sample_1.gif", + sample_size: 100, + }, + ); - run_bench_def(&mut group, BenchDef { - data: include_bytes!("../tests/samples/sample_big.gif"), - id: "sample_big.gif", - sample_size: 20, - }); + run_bench_def( + &mut group, + BenchDef { + data: include_bytes!("../tests/samples/sample_big.gif"), + id: "sample_big.gif", + sample_size: 20, + }, + ); - group - .bench_with_input( - "extract-metadata-note", - include_bytes!("note.gif"), - |b, input| { - b.iter(|| read_metadata(input)); - } - ); + group.bench_with_input( + "extract-metadata-note", + include_bytes!("note.gif"), + |b, input| { + b.iter(|| read_metadata(input)); + }, + ); group.finish(); diff --git a/benches/rgb_frame.rs b/benches/rgb_frame.rs index 6e9bc09..e58dc57 100644 --- a/benches/rgb_frame.rs +++ b/benches/rgb_frame.rs @@ -35,20 +35,20 @@ fn main() { group .sample_size(50) .throughput(Throughput::Bytes(size as u64)) - .bench_function(path.file_name().unwrap().to_str().unwrap(), - |b| { - match info.color_type { - png::ColorType::Rgb => b.iter(|| { - Frame::from_rgb_speed(w, h, &mut buf[..size], 30) - }), - png::ColorType::Rgba => b.iter(|| { - Frame::from_rgba_speed(w, h, &mut buf[..size], 30) - }), + .bench_function( + path.file_name().unwrap().to_str().unwrap(), + |b| match info.color_type { + png::ColorType::Rgb => { + b.iter(|| Frame::from_rgb_speed(w, h, &mut buf[..size], 30)) + } + png::ColorType::Rgba => { + b.iter(|| Frame::from_rgba_speed(w, h, &mut buf[..size], 30)) + } c => { println!("Image has wrong color type: {c:?}"); } - } - }); + }, + ); // actually write the image as a singe frame gif... while MSE can be used // for quality check, it might not be as good as visual inspection diff --git a/examples/check.rs b/examples/check.rs index 03dc294..ea288f2 100644 --- a/examples/check.rs +++ b/examples/check.rs @@ -1,10 +1,8 @@ use std::{env, fs, process}; fn main() { - let file = env::args().nth(1) - .unwrap_or_else(|| explain_usage()); - let file = fs::File::open(file) - .expect("failed to open input file"); + let file = env::args().nth(1).unwrap_or_else(|| explain_usage()); + let file = fs::File::open(file).expect("failed to open input file"); let mut reader = { let mut options = gif::DecodeOptions::new(); options.allow_unknown_blocks(true); @@ -28,7 +26,10 @@ fn main() { dispose: {:?}\n \ needs_input: {:?}", frame.delay, - frame.width, frame.height, frame.left, frame.top, + frame.width, + frame.height, + frame.left, + frame.top, frame.dispose, frame.needs_user_input ); diff --git a/examples/parallel.rs b/examples/parallel.rs index 893368c..28475c4 100644 --- a/examples/parallel.rs +++ b/examples/parallel.rs @@ -40,17 +40,21 @@ fn main() -> Result<(), Box> { let (send, recv) = std::sync::mpsc::channel(); - decoder.into_iter().enumerate().par_bridge().try_for_each(move |(frame_number, frame)| { - let mut frame = frame?; - FrameDecoder::new(DecodeOptions::new()) - .decode_lzw_encoded_frame(&mut frame) - .unwrap(); - // frame is now pixels - frame.make_lzw_pre_encoded(); - // frame is now LZW again, re-encoded - send.send((frame_number, frame)).unwrap(); - Ok::<_, gif::DecodingError>(()) - })?; + decoder + .into_iter() + .enumerate() + .par_bridge() + .try_for_each(move |(frame_number, frame)| { + let mut frame = frame?; + FrameDecoder::new(DecodeOptions::new()) + .decode_lzw_encoded_frame(&mut frame) + .unwrap(); + // frame is now pixels + frame.make_lzw_pre_encoded(); + // frame is now LZW again, re-encoded + send.send((frame_number, frame)).unwrap(); + Ok::<_, gif::DecodingError>(()) + })?; // Decoding and encoding can happen in parallel, but writing to the GIF file is sequential let mut next_frame_number = 0; @@ -59,7 +63,10 @@ fn main() -> Result<(), Box> { // frames can arrive in any order, since they're processed in parallel, // so they have to be stored in a queue frames_to_process.push((frame_number, frame)); - while let Some(index) = frames_to_process.iter().position(|&(num, _)| num == next_frame_number) { + while let Some(index) = frames_to_process + .iter() + .position(|&(num, _)| num == next_frame_number) + { let frame = frames_to_process.remove(index).1; encoder.write_lzw_pre_encoded_frame(&frame)?; next_frame_number += 1; @@ -70,6 +77,13 @@ fn main() -> Result<(), Box> { let seconds = start.elapsed().as_millis() as f64 / 1000.; let rate = (input_size / 1024 / 1024) as f64 / seconds; - eprintln!("Finished in {seconds:0.2}s, {rate:0.0}MiB/s {}", if cfg!(debug_assertions) { ". Run with --release for more speed." } else { "" }); + eprintln!( + "Finished in {seconds:0.2}s, {rate:0.0}MiB/s {}", + if cfg!(debug_assertions) { + ". Run with --release for more speed." + } else { + "" + } + ); Ok(()) } diff --git a/src/common.rs b/src/common.rs index 2458d06..9ed5740 100644 --- a/src/common.rs +++ b/src/common.rs @@ -212,7 +212,10 @@ impl Frame<'static> { #[track_caller] pub fn from_rgba_speed(width: u16, height: u16, pixels: &mut [u8], speed: i32) -> Self { assert_eq!(width as usize * height as usize * 4, pixels.len(), "Too much or too little pixel data for the given width and height to create a GIF Frame"); - assert!(speed >= 1 && speed <= 30, "speed needs to be in the range [1, 30]"); + assert!( + speed >= 1 && speed <= 30, + "speed needs to be in the range [1, 30]" + ); let mut transparent = None; for pix in pixels.chunks_exact_mut(4) { if pix[3] != 0 { @@ -233,7 +236,12 @@ impl Frame<'static> { return Frame { width, height, - buffer: Cow::Owned(pixels.chunks_exact(4).map(|pix| nq.index_of(pix) as u8).collect()), + buffer: Cow::Owned( + pixels + .chunks_exact(4) + .map(|pix| nq.index_of(pix) as u8) + .collect(), + ), palette: Some(nq.color_map_rgb()), transparent: transparent.map(|t| nq.index_of(&t) as u8), ..Frame::default() @@ -244,11 +252,19 @@ impl Frame<'static> { // Palette size <= 256 elements, we can build an exact palette. let mut colors_vec: Vec<(u8, u8, u8, u8)> = colors.into_iter().collect(); colors_vec.sort_unstable(); - let palette = colors_vec.iter().flat_map(|&(r, g, b, _a)| [r, g, b]).collect(); - let colors_lookup: HashMap<(u8, u8, u8, u8), u8> = colors_vec.into_iter().zip(0..=255).collect(); + let palette = colors_vec + .iter() + .flat_map(|&(r, g, b, _a)| [r, g, b]) + .collect(); + let colors_lookup: HashMap<(u8, u8, u8, u8), u8> = + colors_vec.into_iter().zip(0..=255).collect(); - let index_of = | pixel: &[u8] | - colors_lookup.get(&(pixel[0], pixel[1], pixel[2], pixel[3])).copied().unwrap_or(0); + let index_of = |pixel: &[u8]| { + colors_lookup + .get(&(pixel[0], pixel[1], pixel[2], pixel[3])) + .copied() + .unwrap_or(0) + }; Frame { width, @@ -266,11 +282,24 @@ impl Frame<'static> { /// * If the length of pixels does not equal `width * height`. /// * If the length of palette > `256 * 3`. #[track_caller] - pub fn from_palette_pixels(width: u16, height: u16, pixels: impl Into>, palette: impl Into>, transparent: Option) -> Self { + pub fn from_palette_pixels( + width: u16, + height: u16, + pixels: impl Into>, + palette: impl Into>, + transparent: Option, + ) -> Self { let pixels = pixels.into(); let palette = palette.into(); - assert_eq!(width as usize * height as usize, pixels.len(), "Too many or too little pixels for the given width and height to create a GIF Frame"); - assert!(palette.len() <= 256*3, "Too many palette values to create a GIF Frame"); + assert_eq!( + width as usize * height as usize, + pixels.len(), + "Too many or too little pixels for the given width and height to create a GIF Frame" + ); + assert!( + palette.len() <= 256 * 3, + "Too many palette values to create a GIF Frame" + ); Frame { width, @@ -287,9 +316,18 @@ impl Frame<'static> { /// # Panics: /// * If the length of pixels does not equal `width * height`. #[track_caller] - pub fn from_indexed_pixels(width: u16, height: u16, pixels: impl Into>, transparent: Option) -> Self { + pub fn from_indexed_pixels( + width: u16, + height: u16, + pixels: impl Into>, + transparent: Option, + ) -> Self { let pixels = pixels.into(); - assert_eq!(width as usize * height as usize, pixels.len(), "Too many or too little pixels for the given width and height to create a GIF Frame"); + assert_eq!( + width as usize * height as usize, + pixels.len(), + "Too many or too little pixels for the given width and height to create a GIF Frame" + ); Frame { width, @@ -338,7 +376,8 @@ impl Frame<'static> { pub fn from_rgb_speed(width: u16, height: u16, pixels: &[u8], speed: i32) -> Self { assert_eq!(width as usize * height as usize * 3, pixels.len(), "Too much or too little pixel data for the given width and height to create a GIF Frame"); let mut vec: Vec = Vec::new(); - vec.try_reserve_exact(pixels.len() + width as usize * height as usize).expect("OOM"); + vec.try_reserve_exact(pixels.len() + width as usize * height as usize) + .expect("OOM"); for v in pixels.chunks_exact(3) { vec.extend_from_slice(&[v[0], v[1], v[2], 0xFF]); } diff --git a/src/encoder.rs b/src/encoder.rs index 1933478..4480beb 100644 --- a/src/encoder.rs +++ b/src/encoder.rs @@ -1,14 +1,14 @@ //! # Minimal gif encoder +use std::borrow::Cow; +use std::error; +use std::fmt; use std::io; use std::io::prelude::*; -use std::fmt; -use std::error; -use std::borrow::Cow; -use weezl::{BitOrder, encode::Encoder as LzwEncoder}; +use weezl::{encode::Encoder as LzwEncoder, BitOrder}; -use crate::traits::WriteBytesExt; use crate::common::{AnyExtension, Block, DisposalMethod, Extension, Frame}; +use crate::traits::WriteBytesExt; /// The image has incorrect properties, making it impossible to encode as a gif. #[derive(Debug)] @@ -28,7 +28,10 @@ impl fmt::Display for EncodingFormatError { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::TooManyColors => write!(fmt, "the image has too many colors"), - Self::MissingColorPalette => write!(fmt, "the GIF format requires a color palette but none was given"), + Self::MissingColorPalette => write!( + fmt, + "the GIF format requires a color palette but none was given" + ), Self::InvalidMinCodeSize => write!(fmt, "LZW data is invalid"), } } @@ -113,13 +116,18 @@ impl ExtensionData { /// /// `delay` is given in units of 10 ms. #[must_use] - pub fn new_control_ext(delay: u16, dispose: DisposalMethod, needs_user_input: bool, trns: Option) -> Self { + pub fn new_control_ext( + delay: u16, + dispose: DisposalMethod, + needs_user_input: bool, + trns: Option, + ) -> Self { let mut flags = 0; let trns = match trns { Some(trns) => { flags |= 1; trns - }, + } None => 0, }; flags |= u8::from(needs_user_input) << 1; @@ -133,13 +141,20 @@ impl Encoder { /// /// `global_palette` gives the global color palette in the format `[r, g, b, ...]`, /// if no global palette shall be used an empty slice may be supplied. - pub fn new(w: W, width: u16, height: u16, global_palette: &[u8]) -> Result { + pub fn new( + w: W, + width: u16, + height: u16, + global_palette: &[u8], + ) -> Result { Self { w: Some(w), global_palette: false, - width, height, + width, + height, buffer: Vec::new(), - }.write_global_palette(global_palette) + } + .write_global_palette(global_palette) } /// Write an extension block that signals a repeat behaviour. @@ -167,10 +182,20 @@ impl Encoder { /// /// Note: This function also writes a control extension if necessary. pub fn write_frame(&mut self, frame: &Frame<'_>) -> Result<(), EncodingError> { - if usize::from(frame.width).checked_mul(usize::from(frame.height)).map_or(true, |size| frame.buffer.len() < size) { - return Err(io::Error::new(io::ErrorKind::InvalidInput, "frame.buffer is too small for its width/height").into()); + if usize::from(frame.width) + .checked_mul(usize::from(frame.height)) + .map_or(true, |size| frame.buffer.len() < size) + { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "frame.buffer is too small for its width/height", + ) + .into()); } - debug_assert!((frame.width > 0 && frame.height > 0) || frame.buffer.is_empty(), "the frame has 0 pixels, but non-empty buffer"); + debug_assert!( + (frame.width > 0 && frame.height > 0) || frame.buffer.is_empty(), + "the frame has 0 pixels, but non-empty buffer" + ); self.write_frame_header(frame)?; self.write_image_block(&frame.buffer) } @@ -192,9 +217,13 @@ impl Encoder { let (palette, padding, table_size) = Self::check_color_table(palette)?; flags |= table_size; Some((palette, padding)) - }, + } None if self.global_palette => None, - _ => return Err(EncodingError::from(EncodingFormatError::MissingColorPalette)), + _ => { + return Err(EncodingError::from( + EncodingFormatError::MissingColorPalette, + )) + } }; let mut tmp = tmp_buf::<10>(); tmp.write_le(Block::Image as u8)?; @@ -213,15 +242,22 @@ impl Encoder { fn write_image_block(&mut self, data: &[u8]) -> Result<(), EncodingError> { self.buffer.clear(); - self.buffer.try_reserve(data.len() / 4) + self.buffer + .try_reserve(data.len() / 4) .map_err(|_| io::Error::from(io::ErrorKind::OutOfMemory))?; lzw_encode(data, &mut self.buffer); - let writer = self.w.as_mut().ok_or(io::Error::from(io::ErrorKind::Unsupported))?; + let writer = self + .w + .as_mut() + .ok_or(io::Error::from(io::ErrorKind::Unsupported))?; Self::write_encoded_image_block(writer, &self.buffer) } - fn write_encoded_image_block(writer: &mut W, data_with_min_code_size: &[u8]) -> Result<(), EncodingError> { + fn write_encoded_image_block( + writer: &mut W, + data_with_min_code_size: &[u8], + ) -> Result<(), EncodingError> { let (&min_code_size, data) = data_with_min_code_size.split_first().unwrap_or((&2, &[])); writer.write_le(min_code_size)?; @@ -240,7 +276,11 @@ impl Encoder { writer.write_le(0u8).map_err(Into::into) } - fn write_color_table(writer: &mut W, table: &[u8], padding: usize) -> Result<(), EncodingError> { + fn write_color_table( + writer: &mut W, + table: &[u8], + padding: usize, + ) -> Result<(), EncodingError> { writer.write_all(table)?; // Waste some space as of gif spec for _ in 0..padding { @@ -325,7 +365,9 @@ impl Encoder { // empty data is allowed if let Some(&min_code_size) = frame.buffer.first() { if min_code_size > 11 || min_code_size < 2 { - return Err(EncodingError::Format(EncodingFormatError::InvalidMinCodeSize)); + return Err(EncodingError::Format( + EncodingFormatError::InvalidMinCodeSize, + )); } } @@ -361,7 +403,9 @@ impl Encoder { /// Finishes writing, and returns the `io::Write` instance used by this encoder pub fn into_inner(mut self) -> io::Result { self.write_trailer()?; - self.w.take().ok_or(io::Error::from(io::ErrorKind::Unsupported)) + self.w + .take() + .ok_or(io::Error::from(io::ErrorKind::Unsupported)) } /// Write the final tailer. @@ -371,7 +415,9 @@ impl Encoder { #[inline] fn writer(&mut self) -> io::Result<&mut W> { - self.w.as_mut().ok_or(io::Error::from(io::ErrorKind::Unsupported)) + self.w + .as_mut() + .ok_or(io::Error::from(io::ErrorKind::Unsupported)) } } @@ -442,6 +488,7 @@ fn flag_size(size: usize) -> u8 { #[test] fn test_flag_size() { + #[rustfmt::skip] fn expected(size: usize) -> u8 { match size { 0 ..=2 => 0, @@ -464,7 +511,10 @@ fn test_flag_size() { 1 => 2, n => n, }; - let actual = (u32::from(i) + 1).max(4).next_power_of_two().trailing_zeros() as u8; + let actual = (u32::from(i) + 1) + .max(4) + .next_power_of_two() + .trailing_zeros() as u8; assert_eq!(actual, expected); } } @@ -479,18 +529,24 @@ impl Write for Buf { fn write(&mut self, buf: &[u8]) -> io::Result { let len = buf.len(); let pos = self.pos; - self.buf.get_mut(pos..pos + len) + self.buf + .get_mut(pos..pos + len) .ok_or(io::ErrorKind::WriteZero)? .copy_from_slice(buf); self.pos += len; Ok(len) } - fn flush(&mut self) -> io::Result<()> { Ok(()) } + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } } fn tmp_buf() -> Buf { - Buf { buf: [0; N], pos: 0 } + Buf { + buf: [0; N], + pos: 0, + } } impl Buf { @@ -503,5 +559,6 @@ impl Buf { #[test] fn error_cast() { - let _: Box = EncodingError::from(EncodingFormatError::MissingColorPalette).into(); + let _: Box = + EncodingError::from(EncodingFormatError::MissingColorPalette).into(); } diff --git a/src/lib.rs b/src/lib.rs index c5de6f4..4dccf05 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -114,18 +114,18 @@ #![allow(clippy::manual_range_contains)] #![allow(clippy::new_without_default)] -mod traits; mod common; -mod reader; mod encoder; +mod reader; +mod traits; -pub use crate::common::{AnyExtension, Extension, DisposalMethod, Frame}; +pub use crate::common::{AnyExtension, DisposalMethod, Extension, Frame}; -pub use crate::reader::{DecodingError, DecodingFormatError}; pub use crate::reader::{ColorOutput, MemoryLimit}; pub use crate::reader::{DecodeOptions, Decoder, Version}; +pub use crate::reader::{DecodingError, DecodingFormatError}; -pub use crate::encoder::{Encoder, ExtensionData, Repeat, EncodingError, EncodingFormatError}; +pub use crate::encoder::{Encoder, EncodingError, EncodingFormatError, ExtensionData, Repeat}; /// Low-level, advanced decoder. Prefer [`Decoder`] instead, which can stream frames too. pub mod streaming_decoder { diff --git a/src/reader/converter.rs b/src/reader/converter.rs index dbc80df..d70cac7 100644 --- a/src/reader/converter.rs +++ b/src/reader/converter.rs @@ -1,9 +1,9 @@ +use crate::common::Frame; +use crate::MemoryLimit; use std::borrow::Cow; use std::io; -use std::mem; use std::iter; -use crate::common::Frame; -use crate::MemoryLimit; +use std::mem; use super::decoder::{DecodingError, OutputBuffer, PLTE_CHANNELS}; @@ -23,7 +23,8 @@ pub enum ColorOutput { Indexed = 1, } -pub(crate) type FillBufferCallback<'a> = &'a mut dyn FnMut(&mut OutputBuffer<'_>) -> Result; +pub(crate) type FillBufferCallback<'a> = + &'a mut dyn FnMut(&mut OutputBuffer<'_>) -> Result; /// Deinterlaces and expands to RGBA if needed pub(crate) struct PixelConverter { @@ -44,26 +45,32 @@ impl PixelConverter { } pub(crate) fn check_buffer_size(&self, frame: &Frame<'_>) -> Result { - let pixel_bytes = self.memory_limit + let pixel_bytes = self + .memory_limit .buffer_size(self.color_output, frame.width, frame.height) .ok_or_else(|| io::Error::new(io::ErrorKind::OutOfMemory, "image is too large"))?; debug_assert_eq!( - pixel_bytes, self.buffer_size(frame).unwrap(), + pixel_bytes, + self.buffer_size(frame).unwrap(), "Checked computation diverges from required buffer size" ); Ok(pixel_bytes) } #[inline] - pub(crate) fn read_frame(&mut self, frame: &mut Frame<'_>, data_callback: FillBufferCallback<'_>) -> Result<(), DecodingError> { + pub(crate) fn read_frame( + &mut self, + frame: &mut Frame<'_>, + data_callback: FillBufferCallback<'_>, + ) -> Result<(), DecodingError> { let pixel_bytes = self.check_buffer_size(frame)?; let mut vec = match mem::replace(&mut frame.buffer, Cow::Borrowed(&[])) { // reuse buffer if possible without reallocating Cow::Owned(mut vec) if vec.capacity() >= pixel_bytes => { vec.resize(pixel_bytes, 0); vec - }, + } // resizing would realloc anyway, and 0-init is faster than a copy _ => vec![0; pixel_bytes], }; @@ -89,7 +96,12 @@ impl PixelConverter { /// Use `read_into_buffer` to deinterlace #[inline(never)] - pub(crate) fn fill_buffer(&mut self, current_frame: &Frame<'_>, mut buf: &mut [u8], data_callback: FillBufferCallback<'_>) -> Result { + pub(crate) fn fill_buffer( + &mut self, + current_frame: &Frame<'_>, + mut buf: &mut [u8], + data_callback: FillBufferCallback<'_>, + ) -> Result { loop { let decode_into = match self.color_output { // When decoding indexed data, LZW can write the pixels directly @@ -113,27 +125,38 @@ impl PixelConverter { match self.color_output { ColorOutput::RGBA => { let transparent = current_frame.transparent; - let palette: &[u8] = current_frame.palette.as_deref() + let palette: &[u8] = current_frame + .palette + .as_deref() .or(self.global_palette.as_deref()) .unwrap_or_default(); // next_frame_info already checked it won't happen let (pixels, rest) = buf.split_at_mut(bytes_decoded * N_CHANNELS); buf = rest; - for (rgba, idx) in pixels.chunks_exact_mut(N_CHANNELS).zip(self.buffer.iter().copied().take(bytes_decoded)) { + for (rgba, idx) in pixels + .chunks_exact_mut(N_CHANNELS) + .zip(self.buffer.iter().copied().take(bytes_decoded)) + { let plte_offset = PLTE_CHANNELS * idx as usize; - if let Some(colors) = palette.get(plte_offset..plte_offset+PLTE_CHANNELS) { + if let Some(colors) = + palette.get(plte_offset..plte_offset + PLTE_CHANNELS) + { rgba[0] = colors[0]; rgba[1] = colors[1]; rgba[2] = colors[2]; rgba[3] = if let Some(t) = transparent { - if t == idx { 0x00 } else { 0xFF } + if t == idx { + 0x00 + } else { + 0xFF + } } else { 0xFF }; } } - }, + } ColorOutput::Indexed => { buf = &mut buf[bytes_decoded..]; } @@ -141,7 +164,7 @@ impl PixelConverter { if buf.is_empty() { return Ok(true); } - }, + } } } } @@ -161,21 +184,34 @@ impl PixelConverter { /// Applies deinterlacing /// /// Set `frame.interlaced = false` afterwards if you're putting the buffer back into the `Frame` - pub(crate) fn read_into_buffer(&mut self, frame: &Frame<'_>, buf: &mut [u8], data_callback: FillBufferCallback<'_>) -> Result<(), DecodingError> { + pub(crate) fn read_into_buffer( + &mut self, + frame: &Frame<'_>, + buf: &mut [u8], + data_callback: FillBufferCallback<'_>, + ) -> Result<(), DecodingError> { if frame.interlaced { let width = self.line_length(frame); - for row in (InterlaceIterator { len: frame.height, next: 0, pass: 0 }) { + for row in (InterlaceIterator { + len: frame.height, + next: 0, + pass: 0, + }) { // this can't overflow 32-bit, because row never equals (maximum) height let start = row * width; // Handle a too-small buffer and 32-bit usize overflow without panicking - let line = buf.get_mut(start..).and_then(|b| b.get_mut(..width)) + let line = buf + .get_mut(start..) + .and_then(|b| b.get_mut(..width)) .ok_or_else(|| DecodingError::format("buffer too small"))?; if !self.fill_buffer(frame, line, data_callback)? { return Err(DecodingError::format("image truncated")); } } } else { - let buf = self.buffer_size(frame).and_then(|buffer_size| buf.get_mut(..buffer_size)) + let buf = self + .buffer_size(frame) + .and_then(|buffer_size| buf.get_mut(..buffer_size)) .ok_or_else(|| DecodingError::format("buffer too small"))?; if !self.fill_buffer(frame, buf, data_callback)? { return Err(DecodingError::format("image truncated")); @@ -216,6 +252,7 @@ impl iter::Iterator for InterlaceIterator { mod test { use super::InterlaceIterator; + #[rustfmt::skip] #[test] fn test_interlace_iterator() { for &(len, expect) in &[ @@ -246,7 +283,11 @@ mod test { #[test] fn interlace_max() { - let iter = InterlaceIterator { len: 0xFFFF, next: 0, pass: 0 }; + let iter = InterlaceIterator { + len: 0xFFFF, + next: 0, + pass: 0, + }; assert_eq!(65533, iter.last().unwrap()); } } diff --git a/src/reader/decoder.rs b/src/reader/decoder.rs index 35994f3..a4cfc0e 100644 --- a/src/reader/decoder.rs +++ b/src/reader/decoder.rs @@ -1,18 +1,18 @@ use std::borrow::Cow; use std::cmp; +use std::default::Default; use std::error; use std::fmt; use std::io; use std::mem; -use std::default::Default; use std::num::NonZeroUsize; -use crate::Repeat; -use crate::MemoryLimit; use crate::common::{AnyExtension, Block, DisposalMethod, Extension, Frame}; use crate::reader::DecodeOptions; +use crate::MemoryLimit; +use crate::Repeat; -use weezl::{BitOrder, decode::Decoder as LzwDecoder, LzwError, LzwStatus}; +use weezl::{decode::Decoder as LzwDecoder, BitOrder, LzwError, LzwStatus}; /// GIF palettes are RGB pub const PLTE_CHANNELS: usize = 3; @@ -219,19 +219,22 @@ impl FrameDecoder { /// Converts into the given buffer. It must be [`buffer_size()`] bytes large. /// /// Pixels are always deinterlaced, so update `frame.interlaced` afterwards if you're putting the buffer back into the frame. - pub fn decode_lzw_encoded_frame_into_buffer(&mut self, frame: &Frame<'_>, buf: &mut [u8]) -> Result<(), DecodingError> { + pub fn decode_lzw_encoded_frame_into_buffer( + &mut self, + frame: &Frame<'_>, + buf: &mut [u8], + ) -> Result<(), DecodingError> { let (&min_code_size, mut data) = frame.buffer.split_first().unwrap_or((&2, &[])); self.lzw_reader.reset(min_code_size)?; let lzw_reader = &mut self.lzw_reader; - self.pixel_converter.read_into_buffer(frame, buf, &mut move |out| { - loop { + self.pixel_converter + .read_into_buffer(frame, buf, &mut move |out| loop { let (bytes_read, bytes_written) = lzw_reader.decode_bytes(data, out)?; data = data.get(bytes_read..).unwrap_or_default(); if bytes_written > 0 || bytes_read == 0 || data.is_empty() { return Ok(bytes_written); } - } - })?; + })?; Ok(()) } @@ -275,7 +278,10 @@ impl LzwReader { self.min_code_size = min_code_size; self.decoder = Some(LzwDecoder::new(BitOrder::Lsb, min_code_size)); } else { - self.decoder.as_mut().ok_or_else(|| DecodingError::format("bad state"))?.reset(); + self.decoder + .as_mut() + .ok_or_else(|| DecodingError::format("bad state"))? + .reset(); } Ok(()) @@ -285,7 +291,11 @@ impl LzwReader { self.decoder.as_ref().map_or(true, |e| e.has_ended()) } - pub fn decode_bytes(&mut self, lzw_data: &[u8], decode_buffer: &mut OutputBuffer<'_>) -> io::Result<(usize, usize)> { + pub fn decode_bytes( + &mut self, + lzw_data: &[u8], + decode_buffer: &mut OutputBuffer<'_>, + ) -> io::Result<(usize, usize)> { let decoder = self.decoder.as_mut().ok_or(io::ErrorKind::Unsupported)?; let decode_buffer = match decode_buffer { @@ -297,12 +307,15 @@ impl LzwReader { let decoded = decoder.decode_bytes(lzw_data, decode_buffer); match decoded.status { - Ok(LzwStatus::Done | LzwStatus::Ok) => {}, + Ok(LzwStatus::Done | LzwStatus::Ok) => {} Ok(LzwStatus::NoProgress) => { if self.check_for_end_code { - return Err(io::Error::new(io::ErrorKind::InvalidData, "no end code in lzw stream")); + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "no end code in lzw stream", + )); } - }, + } Err(err @ LzwError::InvalidCode) => { return Err(io::Error::new(io::ErrorKind::InvalidData, err)); } @@ -362,23 +375,28 @@ pub enum OutputBuffer<'a> { } impl OutputBuffer<'_> { - fn append(&mut self, buf: &[u8], memory_limit: &MemoryLimit) -> Result<(usize, usize), DecodingError> { + fn append( + &mut self, + buf: &[u8], + memory_limit: &MemoryLimit, + ) -> Result<(usize, usize), DecodingError> { let (consumed, copied) = match self { OutputBuffer::Slice(slice) => { let len = cmp::min(buf.len(), slice.len()); slice[..len].copy_from_slice(&buf[..len]); (len, len) - }, + } OutputBuffer::Vec(vec) => { let vec: &mut Vec = vec; let len = buf.len(); memory_limit.check_size(vec.len() + len)?; - vec.try_reserve(len).map_err(|_| io::ErrorKind::OutOfMemory)?; + vec.try_reserve(len) + .map_err(|_| io::ErrorKind::OutOfMemory)?; if vec.capacity() - vec.len() >= len { vec.extend_from_slice(buf); } (len, len) - }, + } // It's valid that bytes are discarded. For example, // when using next_frame_info() with skip_frame_decoding to only get metadata. OutputBuffer::None => (buf.len(), 0), @@ -433,10 +451,10 @@ impl StreamingDecoder { let (bytes, decoded) = self.next_state(buf, write_into)?; buf = buf.get(bytes..).unwrap_or_default(); match decoded { - Decoded::Nothing => {}, + Decoded::Nothing => {} result => { - return Ok((len-buf.len(), result)); - }, + return Ok((len - buf.len(), result)); + } }; } Ok((len - buf.len(), Decoded::Nothing)) @@ -465,7 +483,9 @@ impl StreamingDecoder { /// Current frame info as a mutable ref. #[inline(always)] fn try_current_frame(&mut self) -> Result<&mut Frame<'static>, DecodingError> { - self.current.as_mut().ok_or_else(|| DecodingError::format("bad state")) + self.current + .as_mut() + .ok_or_else(|| DecodingError::format("bad state")) } /// Width of the image @@ -490,7 +510,11 @@ impl StreamingDecoder { } #[inline] - fn next_state(&mut self, buf: &[u8], write_into: &mut OutputBuffer<'_>) -> Result<(usize, Decoded), DecodingError> { + fn next_state( + &mut self, + buf: &[u8], + write_into: &mut OutputBuffer<'_>, + ) -> Result<(usize, Decoded), DecodingError> { macro_rules! goto ( ($n:expr, $state:expr) => ({ self.state = $state; @@ -548,7 +572,7 @@ impl StreamingDecoder { }; goto!(consumed, ScreenDescriptor) - }, + } ScreenDescriptor => { let (consumed, desc) = ensure_min_length_buffer!(7); @@ -560,7 +584,9 @@ impl StreamingDecoder { let global_table = global_flags & 0x80 != 0; let table_size = if global_table { let table_size = PLTE_CHANNELS * (1 << ((global_flags & 0b111) + 1) as usize); - self.global_color_table.try_reserve_exact(table_size).map_err(|_| io::ErrorKind::OutOfMemory)?; + self.global_color_table + .try_reserve_exact(table_size) + .map_err(|_| io::ErrorKind::OutOfMemory)?; table_size } else { 0usize @@ -571,11 +597,14 @@ impl StreamingDecoder { GlobalPalette(table_size), emit Decoded::BackgroundColor(background_color) ) - }, + } ImageBlockStart => { let (consumed, header) = ensure_min_length_buffer!(9); - let frame = self.current.as_mut().ok_or_else(|| DecodingError::format("bad state"))?; + let frame = self + .current + .as_mut() + .ok_or_else(|| DecodingError::format("bad state"))?; frame.left = u16::from_le_bytes(header[..2].try_into().unwrap()); frame.top = u16::from_le_bytes(header[2..4].try_into().unwrap()); frame.width = u16::from_le_bytes(header[4..6].try_into().unwrap()); @@ -597,13 +626,16 @@ impl StreamingDecoder { if local_table { let table_size = flags & 0b0000_0111; let pal_len = PLTE_CHANNELS * (1 << (table_size + 1)); - frame.palette.get_or_insert_with(Vec::new) - .try_reserve_exact(pal_len).map_err(|_| io::ErrorKind::OutOfMemory)?; + frame + .palette + .get_or_insert_with(Vec::new) + .try_reserve_exact(pal_len) + .map_err(|_| io::ErrorKind::OutOfMemory)?; goto!(consumed, LocalPalette(pal_len)) } else { goto!(consumed, LocalPalette(0)) } - }, + } GlobalPalette(left) => { // the global_color_table is guaranteed to have the exact capacity required if left > 0 { @@ -649,7 +681,7 @@ impl StreamingDecoder { } } } - }, + } BlockEnd => { if b == Block::Trailer as u8 { // can't consume yet, because the trailer is not a real block, @@ -667,7 +699,10 @@ impl StreamingDecoder { if left > 0 { let n = cmp::min(left, buf.len()); self.memory_limit.check_size(self.ext.data.len() + n)?; - self.ext.data.try_reserve(n).map_err(|_| io::Error::from(io::ErrorKind::OutOfMemory))?; + self.ext + .data + .try_reserve(n) + .map_err(|_| io::Error::from(io::ErrorKind::OutOfMemory))?; self.ext.data.extend_from_slice(&buf[..n]); goto!(n, ExtensionDataBlock(left - n)) } else if b == 0 { @@ -679,7 +714,7 @@ impl StreamingDecoder { Some(Extension::Control) => { self.read_control_extension()?; goto!(BlockEnd, emit Decoded::BlockFinished(self.ext.id)) - }, + } _ => { goto!(BlockEnd, emit Decoded::BlockFinished(self.ext.id)) } @@ -745,7 +780,8 @@ impl StreamingDecoder { return goto!(n, DecodeSubBlock(left - n), emit Decoded::Nothing); } - let (mut consumed, bytes_len) = self.lzw_reader.decode_bytes(&buf[..n], write_into)?; + let (mut consumed, bytes_len) = + self.lzw_reader.decode_bytes(&buf[..n], write_into)?; // skip if can't make progress (decode would fail if check_for_end_code was set) if consumed == 0 && bytes_len == 0 { @@ -808,5 +844,5 @@ impl StreamingDecoder { #[test] fn error_cast() { - let _ : Box = DecodingError::format("testing").into(); + let _: Box = DecodingError::format("testing").into(); } diff --git a/src/reader/mod.rs b/src/reader/mod.rs index 8911d8e..47ed135 100644 --- a/src/reader/mod.rs +++ b/src/reader/mod.rs @@ -3,23 +3,23 @@ use std::io; use std::iter::FusedIterator; use std::mem; +use std::convert::{TryFrom, TryInto}; use std::io::prelude::*; use std::num::NonZeroU64; -use std::convert::{TryFrom, TryInto}; -use crate::Repeat; use crate::common::{Block, Frame}; +use crate::Repeat; -mod decoder; mod converter; +mod decoder; pub use self::decoder::{ - PLTE_CHANNELS, StreamingDecoder, Decoded, DecodingError, DecodingFormatError, - Version, FrameDataType, OutputBuffer, FrameDecoder + Decoded, DecodingError, DecodingFormatError, FrameDataType, FrameDecoder, OutputBuffer, + StreamingDecoder, Version, PLTE_CHANNELS, }; -use self::converter::PixelConverter; pub use self::converter::ColorOutput; +use self::converter::PixelConverter; #[derive(Clone, Debug)] /// The maximum amount of memory the decoder is allowed to use for each frame @@ -56,7 +56,7 @@ impl MemoryLimit { } else { Err(DecodingError::format("memory limit reached")) } - }, + } } } @@ -82,7 +82,7 @@ impl MemoryLimit { } else { Some(usize_bytes) } - }, + } } } } @@ -202,7 +202,10 @@ struct ReadDecoder { impl ReadDecoder { #[inline(never)] - fn decode_next(&mut self, write_into: &mut OutputBuffer<'_>) -> Result, DecodingError> { + fn decode_next( + &mut self, + write_into: &mut OutputBuffer<'_>, + ) -> Result, DecodingError> { while !self.at_eof { let (consumed, result) = { let buf = self.reader.fill_buf()?; @@ -217,7 +220,7 @@ impl ReadDecoder { Decoded::Nothing => (), Decoded::BlockStart(Block::Trailer) => { self.at_eof = true; - }, + } result => return Ok(Some(result)), } } @@ -248,7 +251,10 @@ pub struct Decoder { current_frame_data_type: FrameDataType, } -impl Decoder where R: Read { +impl Decoder +where + R: Read, +{ /// Create a new decoder with default options. #[inline] pub fn new(reader: R) -> Result { @@ -285,18 +291,20 @@ impl Decoder where R: Read { } Some(Decoded::GlobalPalette(palette)) => { self.pixel_converter.set_global_palette(palette.into()); - }, + } Some(Decoded::Repetitions(repeat)) => { self.repeat = repeat; - }, + } Some(Decoded::HeaderEnd) => break, Some(_) => { // There will be extra events when parsing application extension continue; - }, - None => return Err(DecodingError::format( - "file does not contain any image data" - )) + } + None => { + return Err(DecodingError::format( + "file does not contain any image data", + )) + } } } // If the background color is invalid, ignore it @@ -339,22 +347,31 @@ impl Decoder where R: Read { if self.next_frame_info()?.is_some() { match self.current_frame_data_type { FrameDataType::Pixels => { - self.pixel_converter.read_frame(&mut self.current_frame, &mut |out| self.decoder.decode_next_bytes(out))?; - }, + self.pixel_converter + .read_frame(&mut self.current_frame, &mut |out| { + self.decoder.decode_next_bytes(out) + })?; + } FrameDataType::Lzw { min_code_size } => { let mut vec = if matches!(self.current_frame.buffer, Cow::Owned(_)) { - let mut vec = mem::replace(&mut self.current_frame.buffer, Cow::Borrowed(&[])).into_owned(); + let mut vec = + mem::replace(&mut self.current_frame.buffer, Cow::Borrowed(&[])) + .into_owned(); vec.clear(); vec } else { Vec::new() }; // Guesstimate 2bpp - vec.try_reserve(usize::from(self.current_frame.width) * usize::from(self.current_frame.height) / 4) - .map_err(|_| io::Error::from(io::ErrorKind::OutOfMemory))?; + vec.try_reserve( + usize::from(self.current_frame.width) + * usize::from(self.current_frame.height) + / 4, + ) + .map_err(|_| io::Error::from(io::ErrorKind::OutOfMemory))?; self.copy_lzw_into_buffer(min_code_size, &mut vec)?; self.current_frame.buffer = Cow::Owned(vec); - }, + } } Ok(Some(&self.current_frame)) } else { @@ -376,15 +393,22 @@ impl Decoder where R: Read { /// The length of `buf` must be at least `Self::buffer_size`. /// Deinterlaces the result. pub fn read_into_buffer(&mut self, buf: &mut [u8]) -> Result<(), DecodingError> { - self.pixel_converter.read_into_buffer(&self.current_frame, buf, &mut |out| self.decoder.decode_next_bytes(out)) + self.pixel_converter + .read_into_buffer(&self.current_frame, buf, &mut |out| { + self.decoder.decode_next_bytes(out) + }) } - fn copy_lzw_into_buffer(&mut self, min_code_size: u8, buf: &mut Vec) -> Result<(), DecodingError> { + fn copy_lzw_into_buffer( + &mut self, + min_code_size: u8, + buf: &mut Vec, + ) -> Result<(), DecodingError> { // `write_lzw_pre_encoded_frame` smuggles `min_code_size` in the first byte. buf.push(min_code_size); loop { match self.decoder.decode_next(&mut OutputBuffer::Vec(buf))? { - Some(Decoded::LzwDataCopied(_len)) => {}, + Some(Decoded::LzwDataCopied(_len)) => {} Some(Decoded::DataEnd) => return Ok(()), _ => return Err(DecodingError::format("unexpected data")), } @@ -399,12 +423,17 @@ impl Decoder where R: Read { /// `Self::next_frame_info` needs to be called beforehand. Returns `true` if the supplied /// buffer could be filled completely. Should not be called after `false` had been returned. pub fn fill_buffer(&mut self, buf: &mut [u8]) -> Result { - self.pixel_converter.fill_buffer(&self.current_frame, buf, &mut |out| self.decoder.decode_next_bytes(out)) + self.pixel_converter + .fill_buffer(&self.current_frame, buf, &mut |out| { + self.decoder.decode_next_bytes(out) + }) } /// Output buffer size pub fn buffer_size(&self) -> usize { - self.pixel_converter.buffer_size(&self.current_frame).unwrap() + self.pixel_converter + .buffer_size(&self.current_frame) + .unwrap() } /// Line length of the current frame @@ -500,11 +529,11 @@ impl Iterator for DecoderIter { Ok(None) => { self.ended = true; None - }, + } Err(err) => { self.ended = true; Some(Err(err)) - }, + } } } else { None diff --git a/tests/check_testimages.rs b/tests/check_testimages.rs index 1336cc7..8030b71 100644 --- a/tests/check_testimages.rs +++ b/tests/check_testimages.rs @@ -4,13 +4,15 @@ use std::collections::HashMap; use std::fs::File; use std::path::PathBuf; -use std::io::BufReader; use std::io::prelude::*; +use std::io::BufReader; const BASE_PATH: [&str; 2] = [".", "tests"]; fn process_images(func: F) -where F: Fn(PathBuf) -> Result { +where + F: Fn(PathBuf) -> Result, +{ let base: PathBuf = BASE_PATH.iter().collect(); let test_suites = &["samples"]; let mut results = HashMap::new(); @@ -26,11 +28,11 @@ where F: Fn(PathBuf) -> Result { Ok(crc) => { results.insert(path, format!("{crc}")); println!("{crc}"); - }, + } Err(_) if path.file_name().unwrap().to_str().unwrap().starts_with('x') => { expected_failures += 1; println!("Expected failure"); - }, + } err => panic!("{err:?}"), } } @@ -51,7 +53,9 @@ where F: Fn(PathBuf) -> Result { assert_eq!(expected_failures, failures); for (path, crc) in &results { assert_eq!( - ref_results.get(path).unwrap_or_else(|| panic!("reference for {path:?} is missing")), + ref_results + .get(path) + .unwrap_or_else(|| panic!("reference for {path:?} is missing")), crc ); } @@ -77,7 +81,7 @@ fn render_images() { }); } - +#[rustfmt::skip] const CRC_TABLE: [u32; 256] = [ 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, diff --git a/tests/crashtest.rs b/tests/crashtest.rs index 74a387f..b9726f0 100644 --- a/tests/crashtest.rs +++ b/tests/crashtest.rs @@ -1,7 +1,7 @@ #![cfg(feature = "std")] -use std::{fs, io}; use gif::DecodeOptions; +use std::{fs, io}; #[test] fn try_decode_crash_regression() { @@ -11,9 +11,17 @@ fn try_decode_crash_regression() { for entry in files { let entry = entry.unwrap(); if let Some(ext) = entry.path().extension() { - assert_eq!(ext.to_str(), Some("gif"), "Unexpected file {} in crashtests, should end with .gif", entry.path().display()); + assert_eq!( + ext.to_str(), + Some("gif"), + "Unexpected file {} in crashtests, should end with .gif", + entry.path().display() + ); } else { - panic!("Unexpected file {} in crashtests, should end with .gif", entry.path().display()); + panic!( + "Unexpected file {} in crashtests, should end with .gif", + entry.path().display() + ); } let file_data = fs::read(entry.path()).unwrap(); diff --git a/tests/decode.rs b/tests/decode.rs index 38b9f13..8c9f3ce 100644 --- a/tests/decode.rs +++ b/tests/decode.rs @@ -1,12 +1,13 @@ #![cfg(feature = "std")] -use gif::{Decoder, DecodeOptions, DisposalMethod, Encoder, Frame}; +use gif::{DecodeOptions, Decoder, DisposalMethod, Encoder, Frame}; use std::fs::File; #[test] fn test_simple_indexed() { let mut decoder = Decoder::new(File::open("tests/samples/sample_1.gif").unwrap()).unwrap(); let frame = decoder.read_next_frame().unwrap().unwrap(); + #[rustfmt::skip] assert_eq!(&*frame.buffer, &[ 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, @@ -119,7 +120,13 @@ fn rebuild_without_reencode(image: &[u8]) { options.skip_frame_decoding(true); let mut decoder = options.read_info(image).unwrap(); - let mut encoder = Encoder::new(Vec::new(), decoder.width(), decoder.height(), decoder.global_palette().unwrap_or_default()).unwrap(); + let mut encoder = Encoder::new( + Vec::new(), + decoder.width(), + decoder.height(), + decoder.global_palette().unwrap_or_default(), + ) + .unwrap(); let mut num_frames = 0; while let Some(frame) = decoder.read_next_frame().unwrap() { diff --git a/tests/roundtrip.rs b/tests/roundtrip.rs index 073c965..0ffce88 100644 --- a/tests/roundtrip.rs +++ b/tests/roundtrip.rs @@ -1,13 +1,16 @@ #![cfg(feature = "std")] -use gif::{ColorOutput, Decoder, Encoder, Frame, AnyExtension, DecodeOptions}; +use gif::{AnyExtension, ColorOutput, DecodeOptions, Decoder, Encoder, Frame}; #[test] fn round_trip() { - use std::io::prelude::*; use std::fs::File; + use std::io::prelude::*; let mut data = vec![]; - File::open("tests/samples/sample_1.gif").unwrap().read_to_end(&mut data).unwrap(); + File::open("tests/samples/sample_1.gif") + .unwrap() + .read_to_end(&mut data) + .unwrap(); let mut decoder = Decoder::new(&*data).unwrap(); let palette: Vec = decoder.palette().unwrap().into(); let frame = decoder.read_next_frame().unwrap().unwrap(); @@ -57,18 +60,15 @@ fn round_trip_from_image(original: &[u8]) { width = decoder.width(); height = decoder.height(); repeat = decoder.repeat(); - global_palette = decoder - .global_palette() - .unwrap_or_default() - .to_vec(); - core::iter::from_fn(move || { - decoder.read_next_frame().unwrap().cloned() - }).collect() + global_palette = decoder.global_palette().unwrap_or_default().to_vec(); + core::iter::from_fn(move || decoder.read_next_frame().unwrap().cloned()).collect() }; let mut encoder = Encoder::new(vec![], width, height, &global_palette).unwrap(); encoder.set_repeat(repeat).unwrap(); - encoder.write_raw_extension(AnyExtension(gif::Extension::Comment as _), &[b"hello"]).unwrap(); + encoder + .write_raw_extension(AnyExtension(gif::Extension::Comment as _), &[b"hello"]) + .unwrap(); for frame in &frames { encoder.write_frame(frame).unwrap(); } @@ -80,9 +80,8 @@ fn round_trip_from_image(original: &[u8]) { assert_eq!(decoder.height(), height); assert_eq!(decoder.repeat(), repeat); assert_eq!(global_palette, decoder.global_palette().unwrap_or_default()); - let new_frames: Vec<_> = core::iter::from_fn(|| { - decoder.read_next_frame().unwrap().cloned() - }).collect(); + let new_frames: Vec<_> = + core::iter::from_fn(|| decoder.read_next_frame().unwrap().cloned()).collect(); assert_eq!(new_frames.len(), frames.len(), "Diverging number of frames"); for (new, reference) in new_frames.iter().zip(&frames) { assert_eq!(new.delay, reference.delay); @@ -134,16 +133,17 @@ fn encode_roundtrip_few_colors() { let mut decoder = { let mut builder = Decoder::<&[u8]>::build(); builder.set_color_output(ColorOutput::RGBA); - builder.read_info(&buffer[..]).expect("Invalid info encoded") + builder + .read_info(&buffer[..]) + .expect("Invalid info encoded") }; // Only check key fields, assuming "round_trip_from_image" // covers the rest. We are primarily concerned with quantisation. assert_eq!(decoder.width(), WIDTH); assert_eq!(decoder.height(), HEIGHT); - let new_frames: Vec<_> = core::iter::from_fn(move || { - decoder.read_next_frame().unwrap().cloned() - }).collect(); + let new_frames: Vec<_> = + core::iter::from_fn(move || decoder.read_next_frame().unwrap().cloned()).collect(); assert_eq!(new_frames.len(), 2, "Diverging number of frames"); // NB: reference.buffer can't be used as it contains the palette version. assert_eq!(new_frames[0].buffer, pixels); @@ -153,8 +153,12 @@ fn encode_roundtrip_few_colors() { #[test] fn palette_sizes() { - let global_pal = (0..=255u8).flat_map(|i| [i, i/2, i.wrapping_add(13)]).collect::>(); - let local_pal = (0..=255u8).flat_map(|i| [i^0x55, i, i.wrapping_add(7)]).collect::>(); + let global_pal = (0..=255u8) + .flat_map(|i| [i, i / 2, i.wrapping_add(13)]) + .collect::>(); + let local_pal = (0..=255u8) + .flat_map(|i| [i ^ 0x55, i, i.wrapping_add(7)]) + .collect::>(); for size in 1..=256 { let global = &global_pal[..size * 3]; @@ -183,7 +187,13 @@ fn palette_sizes() { assert!(padding.iter().all(|&b| b == 0)); assert!(d.read_next_frame().unwrap().unwrap().palette.is_none()); - let decoded_local_pal = d.read_next_frame().unwrap().unwrap().palette.as_deref().unwrap(); + let decoded_local_pal = d + .read_next_frame() + .unwrap() + .unwrap() + .palette + .as_deref() + .unwrap(); let (decoded_local_pal, padding) = decoded_local_pal.split_at(local.len()); assert_eq!(local, decoded_local_pal); assert_eq!(padding.len(), 3 * (size.max(2).next_power_of_two() - size)); @@ -200,5 +210,10 @@ fn palette_fail() { f.width = 1; f.height = 1; f.buffer = [1][..].into(); - assert!(matches!(encoder.write_frame(&f), Err(gif::EncodingError::Format(gif::EncodingFormatError::MissingColorPalette)))); + assert!(matches!( + encoder.write_frame(&f), + Err(gif::EncodingError::Format( + gif::EncodingFormatError::MissingColorPalette + )) + )); } diff --git a/tests/stall.rs b/tests/stall.rs index 6cc681e..47b9c7d 100644 --- a/tests/stall.rs +++ b/tests/stall.rs @@ -1,6 +1,6 @@ #![cfg(feature = "std")] -use std::{fs, sync::mpsc, thread, time::Duration, io}; +use std::{fs, io, sync::mpsc, thread, time::Duration}; #[test] fn try_decode_crash_regression() { @@ -9,9 +9,17 @@ fn try_decode_crash_regression() { for entry in files { let entry = entry.unwrap(); if let Some(ext) = entry.path().extension() { - assert_eq!(ext.to_str(), Some("gif"), "Unexpected file {} in crashtests, should end with .gif", entry.path().display()); + assert_eq!( + ext.to_str(), + Some("gif"), + "Unexpected file {} in crashtests, should end with .gif", + entry.path().display() + ); } else { - panic!("Unexpected file {} in crashtests, should end with .gif", entry.path().display()); + panic!( + "Unexpected file {} in crashtests, should end with .gif", + entry.path().display() + ); } let file_data = fs::read(entry.path()).unwrap(); @@ -27,7 +35,8 @@ fn decode_on_timer(data: Vec) { send.send(result).expect("still waiting"); }); - let _ = recv.recv_timeout(Duration::from_secs(1)) + let _ = recv + .recv_timeout(Duration::from_secs(1)) .expect("any result"); } @@ -55,8 +64,11 @@ fn test_truncated_file() { #[track_caller] fn decode_chopped_anim(r: ChoppedReader) { - let frames = gif::DecodeOptions::new().read_info(r).unwrap() - .into_iter().enumerate() + let frames = gif::DecodeOptions::new() + .read_info(r) + .unwrap() + .into_iter() + .enumerate() .map(|(n, f)| f.expect(&n.to_string())) .count(); assert_eq!(frames, 14);