Skip to content

Commit c7a2be7

Browse files
committed
Remove AnimationDecoder trait
1 parent a2703f4 commit c7a2be7

File tree

8 files changed

+287
-320
lines changed

8 files changed

+287
-320
lines changed

src/codecs/gif.rs

Lines changed: 175 additions & 224 deletions
Large diffs are not rendered by default.

src/codecs/png.rs

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
//!
55
//! # Related Links
66
//! * <http://www.w3.org/TR/PNG/> - The PNG Specification
7+
use core::num::NonZeroU32;
78
use std::borrow::Cow;
89
use std::io::{BufRead, Seek, Write};
910

@@ -16,8 +17,11 @@ use crate::error::{
1617
ParameterErrorKind, UnsupportedError, UnsupportedErrorKind,
1718
};
1819
use crate::io::decoder::DecodedMetadataHint;
19-
use crate::io::{DecodedImageAttributes, DecoderAttributes, SequenceControl};
20+
use crate::io::{
21+
DecodedAnimationAttributes, DecodedImageAttributes, DecoderAttributes, SequenceControl,
22+
};
2023
use crate::math::Rect;
24+
use crate::metadata::LoopCount;
2125
use crate::utils::vec_try_with_capacity;
2226
use crate::{
2327
DynamicImage, GenericImage, GenericImageView, ImageDecoder, ImageEncoder, ImageFormat,
@@ -256,6 +260,11 @@ impl<R: BufRead + Seek> ImageDecoder for PngDecoder<R> {
256260
}
257261
}
258262

263+
/// Only for [`ApngDecoder`].
264+
fn animation_attributes(&mut self) -> Option<DecodedAnimationAttributes> {
265+
None
266+
}
267+
259268
fn icc_profile(&mut self) -> ImageResult<Option<Vec<u8>>> {
260269
let reader = self.ensure_reader_and_header()?;
261270
Ok(reader.info().icc_profile.as_ref().map(|x| x.to_vec()))
@@ -361,11 +370,10 @@ impl<R: BufRead + Seek> ImageDecoder for PngDecoder<R> {
361370
}
362371
}
363372

364-
/// An [`AnimationDecoder`] adapter of [`PngDecoder`].
373+
/// An animated adapter of [`PngDecoder`].
365374
///
366375
/// See [`PngDecoder::apng`] for more information.
367376
///
368-
/// [`AnimationDecoder`]: ../trait.AnimationDecoder.html
369377
/// [`PngDecoder`]: struct.PngDecoder.html
370378
/// [`PngDecoder::apng`]: struct.PngDecoder.html#method.apng
371379
pub struct ApngDecoder<R: BufRead + Seek> {
@@ -602,20 +610,6 @@ impl<R: BufRead + Seek> ApngDecoder<R> {
602610
}
603611
}
604612

605-
/*
606-
impl<'a, R: BufRead + Seek + 'a> AnimationDecoder<'a> for ApngDecoder<R> {
607-
fn loop_count(&self) -> LoopCount {
608-
match self.inner.reader.info().animation_control() {
609-
None => LoopCount::Infinite,
610-
Some(actl) if actl.num_plays == 0 => LoopCount::Infinite,
611-
Some(actl) => LoopCount::Finite(
612-
NonZeroU32::new(actl.num_plays).expect("num_plays should be non-zero"),
613-
),
614-
}
615-
}
616-
}
617-
*/
618-
619613
impl<R: BufRead + Seek> ImageDecoder for ApngDecoder<R> {
620614
fn attributes(&self) -> DecoderAttributes {
621615
DecoderAttributes {
@@ -624,6 +618,24 @@ impl<R: BufRead + Seek> ImageDecoder for ApngDecoder<R> {
624618
}
625619
}
626620

621+
fn animation_attributes(&mut self) -> Option<DecodedAnimationAttributes> {
622+
let count = if let Ok(reader) = self.inner.ensure_reader_and_header() {
623+
reader.info().animation_control()
624+
} else {
625+
return None;
626+
};
627+
628+
let loop_count = match count {
629+
None => LoopCount::Infinite,
630+
Some(actl) if actl.num_plays == 0 => LoopCount::Infinite,
631+
Some(actl) => LoopCount::Finite(
632+
NonZeroU32::new(actl.num_plays).expect("num_plays should be non-zero"),
633+
),
634+
};
635+
636+
Some(DecodedAnimationAttributes { loop_count })
637+
}
638+
627639
fn peek_layout(&mut self) -> ImageResult<ImageLayout> {
628640
self.inner.peek_layout()
629641
}

src/codecs/webp/decoder.rs

Lines changed: 51 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,29 @@
1-
use std::io::{BufRead, Read, Seek};
1+
use std::io::{BufRead, Seek};
22
use std::num::NonZeroU32;
33

44
use image_webp::LoopCount;
55

6-
use crate::buffer::ConvertBuffer;
7-
use crate::error::{DecodingError, ImageError, ImageResult};
8-
use crate::io::decoder::DecodedMetadataHint;
9-
use crate::io::{DecodedImageAttributes, DecoderAttributes};
10-
use crate::{
11-
AnimationDecoder, ColorType, Delay, Frame, Frames, ImageDecoder, ImageFormat, RgbImage, Rgba,
12-
RgbaImage,
6+
use crate::error::{DecodingError, ImageError, ImageResult, ParameterError, ParameterErrorKind};
7+
use crate::io::{
8+
DecodedAnimationAttributes, DecodedImageAttributes, DecodedMetadataHint, DecoderAttributes,
9+
SequenceControl,
1310
};
11+
use crate::{ColorType, Delay, ImageDecoder, ImageFormat, Rgba};
1412

1513
/// WebP Image format decoder.
1614
///
1715
/// Supports both lossless and lossy WebP images.
1816
pub struct WebPDecoder<R> {
1917
inner: image_webp::WebPDecoder<R>,
18+
current: u32,
2019
}
2120

2221
impl<R: BufRead + Seek> WebPDecoder<R> {
2322
/// Create a new `WebPDecoder` from the Reader `r`.
2423
pub fn new(r: R) -> ImageResult<Self> {
2524
Ok(Self {
2625
inner: image_webp::WebPDecoder::new(r).map_err(ImageError::from_webp_decode)?,
26+
current: 0,
2727
})
2828
}
2929

@@ -52,6 +52,17 @@ impl<R: BufRead + Seek> ImageDecoder for WebPDecoder<R> {
5252
}
5353
}
5454

55+
fn animation_attributes(&mut self) -> Option<DecodedAnimationAttributes> {
56+
let loop_count = match self.inner.loop_count() {
57+
LoopCount::Forever => crate::metadata::LoopCount::Infinite,
58+
LoopCount::Times(n) => crate::metadata::LoopCount::Finite(
59+
NonZeroU32::new(n.get().into()).expect("LoopCount::Times should be non-zero"),
60+
),
61+
};
62+
63+
Some(DecodedAnimationAttributes { loop_count })
64+
}
65+
5566
fn peek_layout(&mut self) -> ImageResult<crate::ImageLayout> {
5667
let (width, height) = self.inner.dimensions();
5768

@@ -67,14 +78,35 @@ impl<R: BufRead + Seek> ImageDecoder for WebPDecoder<R> {
6778
}
6879

6980
fn read_image(&mut self, buf: &mut [u8]) -> ImageResult<DecodedImageAttributes> {
81+
let is_animated = self.inner.is_animated();
82+
83+
if is_animated && self.current == self.inner.num_frames() {
84+
return Err(ImageError::Parameter(ParameterError::from_kind(
85+
ParameterErrorKind::NoMoreData,
86+
)));
87+
}
88+
7089
let layout = self.peek_layout()?;
7190
assert_eq!(u64::try_from(buf.len()), Ok(layout.total_bytes()));
7291

73-
self.inner
74-
.read_image(buf)
75-
.map_err(ImageError::from_webp_decode)?;
92+
// `read_frame` panics if the image is not animated.
93+
let delay = if is_animated {
94+
let delay = self
95+
.inner
96+
.read_frame(buf)
97+
.map_err(ImageError::from_webp_decode)?;
98+
Some(Delay::from_numer_denom_ms(delay, 1))
99+
} else {
100+
self.inner
101+
.read_image(buf)
102+
.map_err(ImageError::from_webp_decode)?;
103+
None
104+
};
105+
106+
self.current += 1;
76107

77108
Ok(DecodedImageAttributes {
109+
delay,
78110
..DecodedImageAttributes::default()
79111
})
80112
}
@@ -99,69 +131,23 @@ impl<R: BufRead + Seek> ImageDecoder for WebPDecoder<R> {
99131
.xmp_metadata()
100132
.map_err(ImageError::from_webp_decode)
101133
}
102-
}
103134

104-
impl<'a, R: 'a + BufRead + Seek> AnimationDecoder<'a> for WebPDecoder<R> {
105-
fn loop_count(&self) -> crate::metadata::LoopCount {
106-
match self.inner.loop_count() {
107-
LoopCount::Forever => crate::metadata::LoopCount::Infinite,
108-
LoopCount::Times(n) => crate::metadata::LoopCount::Finite(
109-
NonZeroU32::new(n.get().into()).expect("LoopCount::Times should be non-zero"),
110-
),
135+
fn more_images(&self) -> SequenceControl {
136+
if self.current == self.inner.num_frames() {
137+
SequenceControl::None
138+
} else {
139+
SequenceControl::MaybeMore
111140
}
112141
}
113-
114-
fn into_frames(self) -> Frames<'a> {
115-
struct FramesInner<R: Read + Seek> {
116-
decoder: WebPDecoder<R>,
117-
current: u32,
118-
}
119-
impl<R: BufRead + Seek> Iterator for FramesInner<R> {
120-
type Item = ImageResult<Frame>;
121-
122-
fn next(&mut self) -> Option<Self::Item> {
123-
if self.current == self.decoder.inner.num_frames() {
124-
return None;
125-
}
126-
self.current += 1;
127-
let (width, height) = self.decoder.inner.dimensions();
128-
129-
let (img, delay) = if self.decoder.inner.has_alpha() {
130-
let mut img = RgbaImage::new(width, height);
131-
match self.decoder.inner.read_frame(&mut img) {
132-
Ok(delay) => (img, delay),
133-
Err(image_webp::DecodingError::NoMoreFrames) => return None,
134-
Err(e) => return Some(Err(ImageError::from_webp_decode(e))),
135-
}
136-
} else {
137-
let mut img = RgbImage::new(width, height);
138-
match self.decoder.inner.read_frame(&mut img) {
139-
Ok(delay) => (img.convert(), delay),
140-
Err(image_webp::DecodingError::NoMoreFrames) => return None,
141-
Err(e) => return Some(Err(ImageError::from_webp_decode(e))),
142-
}
143-
};
144-
145-
Some(Ok(Frame::from_parts(
146-
img,
147-
0,
148-
0,
149-
Delay::from_numer_denom_ms(delay, 1),
150-
)))
151-
}
152-
}
153-
154-
Frames::new(Box::new(FramesInner {
155-
decoder: self,
156-
current: 0,
157-
}))
158-
}
159142
}
160143

161144
impl ImageError {
162145
fn from_webp_decode(e: image_webp::DecodingError) -> Self {
163146
match e {
164147
image_webp::DecodingError::IoError(e) => ImageError::IoError(e),
148+
image_webp::DecodingError::NoMoreFrames => {
149+
ImageError::Parameter(ParameterError::from_kind(ParameterErrorKind::NoMoreData))
150+
}
165151
_ => ImageError::Decoding(DecodingError::new(ImageFormat::WebP.into(), e)),
166152
}
167153
}

src/io.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ pub type Limits = limits::Limits;
2121
pub type LimitSupport = limits::LimitSupport;
2222

2323
pub use decoder::{
24-
DecodedImageAttributes, DecodedMetadataHint, DecoderAttributes, SequenceControl,
24+
DecodedAnimationAttributes, DecodedImageAttributes, DecodedMetadataHint, DecoderAttributes,
25+
SequenceControl,
2526
};
2627

2728
/// Adds `read_exact_vec`

src/io/decoder.rs

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
use crate::animation::Frames;
21
use crate::color::ExtendedColorType;
32
use crate::error::ImageResult;
43
use crate::metadata::{LoopCount, Orientation};
@@ -50,6 +49,15 @@ pub trait ImageDecoder {
5049
DecoderAttributes::default()
5150
}
5251

52+
/// Retrieve animation attributes.
53+
///
54+
/// You should check [`DecoderAttributes::is_animated`] before calling this method. It will
55+
/// only be available on animated images. Additionally, most file formats store the metadata in
56+
/// the header which might not be read until after calling [`ImageDecoder::peek_layout`].
57+
fn animation_attributes(&mut self) -> Option<DecodedAnimationAttributes> {
58+
None
59+
}
60+
5361
/// Consume the header of the image, determining the image's layout.
5462
///
5563
/// This must be called before a call to [`Self::read_image`] to ensure that the initial
@@ -158,6 +166,22 @@ pub struct DecoderAttributes {
158166
pub iptc: DecodedMetadataHint,
159167
}
160168

169+
/// Additional attributes of animated image sequences.
170+
#[derive(Clone, Debug)]
171+
#[non_exhaustive]
172+
pub struct DecodedAnimationAttributes {
173+
/// Loop count of the animated image.
174+
pub loop_count: LoopCount,
175+
}
176+
177+
impl Default for DecodedAnimationAttributes {
178+
fn default() -> Self {
179+
Self {
180+
loop_count: LoopCount::Infinite,
181+
}
182+
}
183+
}
184+
161185
/// Additional attributes of an image available after decoding.
162186
///
163187
/// The [`Default`] is implemented and returns a value suitable for very basic images from formats
@@ -262,14 +286,6 @@ impl<T: ?Sized + ImageDecoder> ImageDecoder for Box<T> {
262286
}
263287
}
264288

265-
/// `AnimationDecoder` trait
266-
pub trait AnimationDecoder<'a> {
267-
/// Consume the decoder producing a series of frames.
268-
fn into_frames(self) -> Frames<'a>;
269-
/// Loop count of the animated image.
270-
fn loop_count(&self) -> LoopCount;
271-
}
272-
273289
#[cfg(test)]
274290
mod tests {
275291
use super::{DecodedImageAttributes, ImageDecoder, ImageResult};

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ pub use crate::images::dynimage::{
162162
pub use crate::io::free_functions::{guess_format, load, save_buffer, save_buffer_with_format};
163163

164164
pub use crate::io::{
165-
decoder::{AnimationDecoder, ImageDecoder},
165+
decoder::ImageDecoder,
166166
encoder::ImageEncoder,
167167
format::ImageFormat,
168168
image_reader_type::{ImageReader, ImageReaderOptions},

src/metadata.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ enum ExifEndian {
172172
}
173173

174174
/// The number of times animated image should loop over.
175-
#[derive(Clone, Copy)]
175+
#[derive(Clone, Copy, Debug)]
176176
pub enum LoopCount {
177177
/// Loop the image Infinitely
178178
Infinite,

tests/regression.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::io::{BufReader, Cursor};
33
use std::path::PathBuf;
44

55
#[cfg(feature = "webp")]
6-
use image::{codecs::webp::WebPDecoder, AnimationDecoder};
6+
use image::{codecs::webp::WebPDecoder, ImageReader};
77

88
const BASE_PATH: [&str; 2] = [".", "tests"];
99
const IMAGE_DIR: &str = "images";
@@ -60,10 +60,11 @@ fn check_webp_frames_regressions() {
6060
.unwrap()
6161
.num_frames() as usize;
6262
let decoder = WebPDecoder::new(cursor).unwrap();
63+
let reader = ImageReader::from_decoder(Box::new(decoder));
6364
// The `take` guards against a potentially infinitely running iterator.
6465
// Since we take `frame_count + 1`, we can assume that the last iteration already returns `None`.
6566
// We then check that each frame has been decoded successfully.
66-
let decoded_frames_count = decoder
67+
let decoded_frames_count = reader
6768
.into_frames()
6869
.take(frame_count + 1)
6970
.enumerate()

0 commit comments

Comments
 (0)