diff --git a/src/codecs/webp/decoder.rs b/src/codecs/webp/decoder.rs index 7dd174346a..dd148a820b 100644 --- a/src/codecs/webp/decoder.rs +++ b/src/codecs/webp/decoder.rs @@ -68,11 +68,16 @@ impl<'a, R: 'a + Read + Seek> AnimationDecoder<'a> for WebPDecoder { fn into_frames(self) -> Frames<'a> { struct FramesInner { decoder: WebPDecoder, + current: u32, } impl Iterator for FramesInner { type Item = ImageResult; fn next(&mut self) -> Option { + if self.current == self.decoder.inner.num_frames() { + return None; + } + self.current += 1; let (width, height) = self.decoder.inner.dimensions(); let (img, delay) = if self.decoder.inner.has_alpha() { @@ -98,7 +103,10 @@ impl<'a, R: 'a + Read + Seek> AnimationDecoder<'a> for WebPDecoder { } } - Frames::new(Box::new(FramesInner { decoder: self })) + Frames::new(Box::new(FramesInner { + decoder: self, + current: 0, + })) } } diff --git a/tests/regression.rs b/tests/regression.rs index 32cbdb6e73..a86fa220dc 100644 --- a/tests/regression.rs +++ b/tests/regression.rs @@ -1,4 +1,11 @@ -use std::{fs::File, io::BufReader, path::PathBuf}; +use std::{ + fs::{self, File}, + io::{BufReader, Cursor}, + path::PathBuf, +}; + +#[cfg(feature = "webp")] +use image::{codecs::webp::WebPDecoder, AnimationDecoder}; const BASE_PATH: [&str; 2] = [".", "tests"]; const IMAGE_DIR: &str = "images"; @@ -37,6 +44,30 @@ fn check_regressions() { let _ = image::open(path); }) } +/// Check that the WEBP frames iterator returns the right amount of frames. +#[test] +#[cfg(feature = "webp")] +fn check_webp_frames_regressions() { + let path: PathBuf = BASE_PATH + .iter() + .collect::() + .join(IMAGE_DIR) + .join("webp/extended_images") + .join("*.webp"); + let pattern = &*format!("{}", path.display()); + for path in glob::glob(pattern).unwrap().filter_map(Result::ok) { + let bytes = fs::read(path).unwrap(); + let cursor = Cursor::new(&bytes); + let frame_count = image_webp::WebPDecoder::new(cursor.clone()) + .unwrap() + .num_frames() as usize; + let decoder = WebPDecoder::new(cursor).unwrap(); + // The `take` guards against a potentially infinitely running iterator. + // Since we take `frame_count + 1`, we can assume that the last iteration already returns `None`. + let actual_frame_count = decoder.into_frames().take(frame_count + 1).count(); + assert_eq!(actual_frame_count, frame_count); + } +} /// Check that BMP files with large values could cause OOM issues are rejected. ///