image/codecs/webp/
decoder.rs

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