image/codecs/webp/
decoder.rs

1use std::io::{BufRead, Read, Seek};
2
3use crate::buffer::ConvertBuffer;
4use crate::error::{DecodingError, ImageError, ImageResult};
5use crate::image::{ImageDecoder, ImageFormat};
6use crate::metadata::Orientation;
7use crate::{AnimationDecoder, ColorType, Delay, Frame, Frames, RgbImage, Rgba, RgbaImage};
8
9/// WebP Image format decoder.
10///
11/// Supports both lossless and lossy WebP images.
12pub struct WebPDecoder<R> {
13    inner: image_webp::WebPDecoder<R>,
14    orientation: Option<Orientation>,
15}
16
17impl<R: BufRead + Seek> WebPDecoder<R> {
18    /// Create a new `WebPDecoder` from the Reader `r`.
19    pub fn new(r: R) -> ImageResult<Self> {
20        Ok(Self {
21            inner: image_webp::WebPDecoder::new(r).map_err(ImageError::from_webp_decode)?,
22            orientation: None,
23        })
24    }
25
26    /// Returns true if the image as described by the bitstream is animated.
27    pub fn has_animation(&self) -> bool {
28        self.inner.is_animated()
29    }
30
31    /// Sets the background color if the image is an extended and animated webp.
32    pub fn set_background_color(&mut self, color: Rgba<u8>) -> ImageResult<()> {
33        self.inner
34            .set_background_color(color.0)
35            .map_err(ImageError::from_webp_decode)
36    }
37}
38
39impl<R: BufRead + Seek> ImageDecoder for WebPDecoder<R> {
40    fn dimensions(&self) -> (u32, u32) {
41        self.inner.dimensions()
42    }
43
44    fn color_type(&self) -> ColorType {
45        if self.inner.has_alpha() {
46            ColorType::Rgba8
47        } else {
48            ColorType::Rgb8
49        }
50    }
51
52    fn read_image(mut self, buf: &mut [u8]) -> ImageResult<()> {
53        assert_eq!(u64::try_from(buf.len()), Ok(self.total_bytes()));
54
55        self.inner
56            .read_image(buf)
57            .map_err(ImageError::from_webp_decode)
58    }
59
60    fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()> {
61        (*self).read_image(buf)
62    }
63
64    fn icc_profile(&mut self) -> ImageResult<Option<Vec<u8>>> {
65        self.inner
66            .icc_profile()
67            .map_err(ImageError::from_webp_decode)
68    }
69
70    fn exif_metadata(&mut self) -> ImageResult<Option<Vec<u8>>> {
71        let exif = self
72            .inner
73            .exif_metadata()
74            .map_err(ImageError::from_webp_decode)?;
75
76        self.orientation = Some(
77            exif.as_ref()
78                .and_then(|exif| Orientation::from_exif_chunk(exif))
79                .unwrap_or(Orientation::NoTransforms),
80        );
81
82        Ok(exif)
83    }
84
85    fn orientation(&mut self) -> ImageResult<Orientation> {
86        // `exif_metadata` caches the orientation, so call it if `orientation` hasn't been set yet.
87        if self.orientation.is_none() {
88            let _ = self.exif_metadata()?;
89        }
90        Ok(self.orientation.unwrap())
91    }
92}
93
94impl<'a, R: 'a + BufRead + Seek> AnimationDecoder<'a> for WebPDecoder<R> {
95    fn into_frames(self) -> Frames<'a> {
96        struct FramesInner<R: Read + Seek> {
97            decoder: WebPDecoder<R>,
98            current: u32,
99        }
100        impl<R: BufRead + Seek> Iterator for FramesInner<R> {
101            type Item = ImageResult<Frame>;
102
103            fn next(&mut self) -> Option<Self::Item> {
104                if self.current == self.decoder.inner.num_frames() {
105                    return None;
106                }
107                self.current += 1;
108                let (width, height) = self.decoder.inner.dimensions();
109
110                let (img, delay) = if self.decoder.inner.has_alpha() {
111                    let mut img = RgbaImage::new(width, height);
112                    match self.decoder.inner.read_frame(&mut img) {
113                        Ok(delay) => (img, delay),
114                        Err(image_webp::DecodingError::NoMoreFrames) => return None,
115                        Err(e) => return Some(Err(ImageError::from_webp_decode(e))),
116                    }
117                } else {
118                    let mut img = RgbImage::new(width, height);
119                    match self.decoder.inner.read_frame(&mut img) {
120                        Ok(delay) => (img.convert(), delay),
121                        Err(image_webp::DecodingError::NoMoreFrames) => return None,
122                        Err(e) => return Some(Err(ImageError::from_webp_decode(e))),
123                    }
124                };
125
126                Some(Ok(Frame::from_parts(
127                    img,
128                    0,
129                    0,
130                    Delay::from_numer_denom_ms(delay, 1),
131                )))
132            }
133        }
134
135        Frames::new(Box::new(FramesInner {
136            decoder: self,
137            current: 0,
138        }))
139    }
140}
141
142impl ImageError {
143    fn from_webp_decode(e: image_webp::DecodingError) -> Self {
144        match e {
145            image_webp::DecodingError::IoError(e) => ImageError::IoError(e),
146            _ => ImageError::Decoding(DecodingError::new(ImageFormat::WebP.into(), e)),
147        }
148    }
149}
150
151#[cfg(test)]
152mod tests {
153    use super::*;
154
155    #[test]
156    fn add_with_overflow_size() {
157        let bytes = vec![
158            0x52, 0x49, 0x46, 0x46, 0xaf, 0x37, 0x80, 0x47, 0x57, 0x45, 0x42, 0x50, 0x6c, 0x64,
159            0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfb, 0x7e, 0x73, 0x00, 0x06, 0x00, 0x00, 0x00,
160            0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65,
161            0x40, 0xfb, 0xff, 0xff, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65,
162            0x00, 0x00, 0x00, 0x00, 0x62, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49,
163            0x49, 0x54, 0x55, 0x50, 0x4c, 0x54, 0x59, 0x50, 0x45, 0x33, 0x37, 0x44, 0x4d, 0x46,
164        ];
165
166        let data = std::io::Cursor::new(bytes);
167
168        let _ = WebPDecoder::new(data);
169    }
170}