From 4fc7f3496a438d7c42064e583ffbf181b57d9143 Mon Sep 17 00:00:00 2001 From: Kornel Date: Wed, 10 Jan 2024 17:01:45 +0000 Subject: [PATCH] Iterator interface --- src/reader/mod.rs | 46 +++++++++++++++++++++++++++++++++++++++++++++- tests/decode.rs | 44 +++++++++++++++++++++++++++++++++++++++++++- tests/roundtrip.rs | 6 ++++++ 3 files changed, 94 insertions(+), 2 deletions(-) diff --git a/src/reader/mod.rs b/src/reader/mod.rs index 32883e0..ee3dc95 100644 --- a/src/reader/mod.rs +++ b/src/reader/mod.rs @@ -108,6 +108,12 @@ pub struct DecodeOptions { allow_unknown_blocks: bool, } +impl Default for DecodeOptions { + fn default() -> Self { + Self::new() + } +} + impl DecodeOptions { /// Creates a new decoder builder #[must_use] @@ -345,6 +351,8 @@ impl Decoder where R: Read { /// /// Do not call `Self::next_frame_info` beforehand. /// Deinterlaces the result. + /// + /// You can also call `.into_iter()` on the decoder to use it as a regular iterator. pub fn read_next_frame(&mut self) -> Result>, DecodingError> { if let Some(frame) = self.next_frame_info()? { let (width, height) = (frame.width, frame.height); @@ -397,6 +405,14 @@ impl Decoder where R: Read { } } + /// This is private for iterator's use + fn take_current_frame(&mut self) -> Option> { + if self.current_frame.buffer.is_empty() { + return None; + } + Some(self.current_frame.take()) + } + /// Reads the data of the current frame into a pre-allocated buffer. /// /// `Self::next_frame_info` needs to be called beforehand. @@ -516,7 +532,6 @@ impl Decoder where R: Read { /// Returns the color palette relevant for the frame that has been decoded pub fn palette(&self) -> Result<&[u8], DecodingError> { - // TODO prevent planic Ok(match self.current_frame.palette { Some(ref table) => table, None => self.global_palette.as_ref().ok_or(DecodingError::format( @@ -559,6 +574,35 @@ impl Decoder where R: Read { } } +impl IntoIterator for Decoder { + type Item = Result, DecodingError>; + type IntoIter = DecoderIter; + + #[inline] + fn into_iter(self) -> Self::IntoIter { + DecoderIter { + inner: self, + } + } +} + +/// Use `decoder.into_iter()` to iterate over the frames +pub struct DecoderIter { + inner: Decoder, +} + +impl Iterator for DecoderIter { + type Item = Result, DecodingError>; + + fn next(&mut self) -> Option { + match self.inner.read_next_frame() { + Ok(Some(_)) => self.inner.take_current_frame().map(Ok), + Ok(None) => None, + Err(err) => Some(Err(err)), + } + } +} + struct InterlaceIterator { len: usize, next: usize, diff --git a/tests/decode.rs b/tests/decode.rs index 99d5069..4c09760 100644 --- a/tests/decode.rs +++ b/tests/decode.rs @@ -1,4 +1,4 @@ -use gif::{DecodeOptions, DisposalMethod, Encoder, Frame}; +use gif::{Decoder, DecodeOptions, DisposalMethod, Encoder, Frame}; #[test] fn frame_consistency_is_configurable() { @@ -83,6 +83,48 @@ fn check_for_end_code_is_configurable() { } } +#[test] +fn check_rebuild_without_reencode1() { + rebuild_without_reencode(include_bytes!("samples/gifplayer-muybridge.gif")); +} + +#[test] +fn check_rebuild_without_reencode2() { + rebuild_without_reencode(include_bytes!("samples/interlaced.gif")); +} + +fn rebuild_without_reencode(image: &[u8]) { + let mut options = DecodeOptions::new(); + 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 num_frames = 0; + while let Some(frame) = decoder.read_next_frame().unwrap() { + num_frames += 1; + encoder.write_lzw_pre_encoded_frame(&frame).unwrap(); + } + + let gif = encoder.into_inner().unwrap(); + let rebuilt = Decoder::new(&gif[..]).unwrap(); + let orig = Decoder::new(&image[..]).unwrap(); + + for (orig, rebuilt) in orig.into_iter().zip(rebuilt) { + num_frames -= 1; + let orig = orig.unwrap(); + let rebuilt = rebuilt.unwrap(); + assert_eq!(orig.delay, rebuilt.delay); + assert_eq!(orig.transparent, rebuilt.transparent); + assert_eq!(orig.palette, rebuilt.palette); + assert_eq!(orig.width, rebuilt.width); + assert_eq!(orig.height, rebuilt.height); + assert_eq!(orig.interlaced, rebuilt.interlaced); + assert_eq!(orig.buffer, rebuilt.buffer); + } + assert_eq!(num_frames, 0); +} + #[test] fn check_skip_frame_data() { let image: &[u8] = include_bytes!("samples/moon_impact.gif"); diff --git a/tests/roundtrip.rs b/tests/roundtrip.rs index 953cf6d..2d56aa1 100644 --- a/tests/roundtrip.rs +++ b/tests/roundtrip.rs @@ -6,6 +6,12 @@ fn encode_roundtrip() { round_trip_from_image(ORIGINAL); } +#[test] +fn encode_roundtrip2() { + const ORIGINAL: &[u8] = include_bytes!("samples/interlaced.gif"); + round_trip_from_image(ORIGINAL); +} + fn round_trip_from_image(original: &[u8]) { let (width, height, global_palette, repeat); let frames: Vec<_> = {