Coverage Report

Created: 2025-12-05 07:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/image/src/codecs/webp/decoder.rs
Line
Count
Source
1
use std::io::{BufRead, Read, Seek};
2
3
use crate::buffer::ConvertBuffer;
4
use crate::error::{DecodingError, ImageError, ImageResult};
5
use crate::metadata::Orientation;
6
use 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.
14
pub struct WebPDecoder<R> {
15
    inner: image_webp::WebPDecoder<R>,
16
    orientation: Option<Orientation>,
17
}
18
19
impl<R: BufRead + Seek> WebPDecoder<R> {
20
    /// Create a new `WebPDecoder` from the Reader `r`.
21
4.24k
    pub fn new(r: R) -> ImageResult<Self> {
22
        Ok(Self {
23
4.24k
            inner: image_webp::WebPDecoder::new(r).map_err(ImageError::from_webp_decode)?,
24
4.12k
            orientation: None,
25
        })
26
4.24k
    }
27
28
    /// Returns true if the image as described by the bitstream is animated.
29
0
    pub fn has_animation(&self) -> bool {
30
0
        self.inner.is_animated()
31
0
    }
32
33
    /// Sets the background color if the image is an extended and animated webp.
34
0
    pub fn set_background_color(&mut self, color: Rgba<u8>) -> ImageResult<()> {
35
0
        self.inner
36
0
            .set_background_color(color.0)
37
0
            .map_err(ImageError::from_webp_decode)
38
0
    }
39
}
40
41
impl<R: BufRead + Seek> ImageDecoder for WebPDecoder<R> {
42
20.6k
    fn dimensions(&self) -> (u32, u32) {
43
20.6k
        self.inner.dimensions()
44
20.6k
    }
45
46
16.4k
    fn color_type(&self) -> ColorType {
47
16.4k
        if self.inner.has_alpha() {
48
11.3k
            ColorType::Rgba8
49
        } else {
50
5.09k
            ColorType::Rgb8
51
        }
52
16.4k
    }
53
54
4.12k
    fn read_image(mut self, buf: &mut [u8]) -> ImageResult<()> {
55
4.12k
        assert_eq!(u64::try_from(buf.len()), Ok(self.total_bytes()));
56
57
4.12k
        self.inner
58
4.12k
            .read_image(buf)
59
4.12k
            .map_err(ImageError::from_webp_decode)
60
4.12k
    }
61
62
4.12k
    fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()> {
63
4.12k
        (*self).read_image(buf)
64
4.12k
    }
65
66
0
    fn icc_profile(&mut self) -> ImageResult<Option<Vec<u8>>> {
67
0
        self.inner
68
0
            .icc_profile()
69
0
            .map_err(ImageError::from_webp_decode)
70
0
    }
71
72
0
    fn exif_metadata(&mut self) -> ImageResult<Option<Vec<u8>>> {
73
0
        let exif = self
74
0
            .inner
75
0
            .exif_metadata()
76
0
            .map_err(ImageError::from_webp_decode)?;
77
78
        self.orientation = Some(
79
0
            exif.as_ref()
80
0
                .and_then(|exif| Orientation::from_exif_chunk(exif))
81
0
                .unwrap_or(Orientation::NoTransforms),
82
        );
83
84
0
        Ok(exif)
85
0
    }
86
87
0
    fn xmp_metadata(&mut self) -> ImageResult<Option<Vec<u8>>> {
88
0
        self.inner
89
0
            .xmp_metadata()
90
0
            .map_err(ImageError::from_webp_decode)
91
0
    }
92
93
0
    fn orientation(&mut self) -> ImageResult<Orientation> {
94
        // `exif_metadata` caches the orientation, so call it if `orientation` hasn't been set yet.
95
0
        if self.orientation.is_none() {
96
0
            let _ = self.exif_metadata()?;
97
0
        }
98
0
        Ok(self.orientation.unwrap())
99
0
    }
100
}
101
102
impl<'a, R: 'a + BufRead + Seek> AnimationDecoder<'a> for WebPDecoder<R> {
103
0
    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
0
            fn next(&mut self) -> Option<Self::Item> {
112
0
                if self.current == self.decoder.inner.num_frames() {
113
0
                    return None;
114
0
                }
115
0
                self.current += 1;
116
0
                let (width, height) = self.decoder.inner.dimensions();
117
118
0
                let (img, delay) = if self.decoder.inner.has_alpha() {
119
0
                    let mut img = RgbaImage::new(width, height);
120
0
                    match self.decoder.inner.read_frame(&mut img) {
121
0
                        Ok(delay) => (img, delay),
122
0
                        Err(image_webp::DecodingError::NoMoreFrames) => return None,
123
0
                        Err(e) => return Some(Err(ImageError::from_webp_decode(e))),
124
                    }
125
                } else {
126
0
                    let mut img = RgbImage::new(width, height);
127
0
                    match self.decoder.inner.read_frame(&mut img) {
128
0
                        Ok(delay) => (img.convert(), delay),
129
0
                        Err(image_webp::DecodingError::NoMoreFrames) => return None,
130
0
                        Err(e) => return Some(Err(ImageError::from_webp_decode(e))),
131
                    }
132
                };
133
134
0
                Some(Ok(Frame::from_parts(
135
0
                    img,
136
0
                    0,
137
0
                    0,
138
0
                    Delay::from_numer_denom_ms(delay, 1),
139
0
                )))
140
0
            }
141
        }
142
143
0
        Frames::new(Box::new(FramesInner {
144
0
            decoder: self,
145
0
            current: 0,
146
0
        }))
147
0
    }
148
}
149
150
impl ImageError {
151
1.92k
    fn from_webp_decode(e: image_webp::DecodingError) -> Self {
152
1.92k
        match e {
153
111
            image_webp::DecodingError::IoError(e) => ImageError::IoError(e),
154
1.81k
            _ => ImageError::Decoding(DecodingError::new(ImageFormat::WebP.into(), e)),
155
        }
156
1.92k
    }
157
}
158
159
#[cfg(test)]
160
mod 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
}