Coverage Report

Created: 2026-01-10 07:01

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