Coverage Report

Created: 2026-07-16 07:16

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, Seek};
2
3
use image_webp::LoopCount;
4
5
use crate::error::{DecodingError, ImageError, ImageResult, ParameterError, ParameterErrorKind};
6
use crate::io::{
7
    DecodedAnimationAttributes, DecodedImageAttributes, DecodedMetadataHint, DecoderPreparedImage,
8
    FormatAttributes, SequenceControl,
9
};
10
use crate::{ColorType, Delay, ImageDecoder, ImageFormat, Rgba};
11
12
/// WebP Image format decoder.
13
///
14
/// Supports both lossless and lossy WebP images.
15
pub struct WebPDecoder<R> {
16
    inner: image_webp::WebPDecoder<R>,
17
    current: u32,
18
}
19
20
impl<R: BufRead + Seek> WebPDecoder<R> {
21
    /// Create a new `WebPDecoder` from the Reader `r`.
22
9.44k
    pub fn new(r: R) -> ImageResult<Self> {
23
        Ok(Self {
24
9.44k
            inner: image_webp::WebPDecoder::new(r).map_err(ImageError::from_webp_decode)?,
25
            current: 0,
26
        })
27
9.44k
    }
28
29
    /// Returns true if the image as described by the bitstream is animated.
30
0
    pub fn has_animation(&self) -> bool {
31
0
        self.inner.is_animated()
32
0
    }
33
34
    /// Sets the background color if the image is an extended and animated webp.
35
0
    pub fn set_background_color(&mut self, color: Rgba<u8>) -> ImageResult<()> {
36
0
        self.inner
37
0
            .set_background_color(color.0)
38
0
            .map_err(ImageError::from_webp_decode)
39
0
    }
40
}
41
42
impl<R: BufRead + Seek> ImageDecoder for WebPDecoder<R> {
43
15.9k
    fn format_attributes(&self) -> FormatAttributes {
44
15.9k
        FormatAttributes {
45
15.9k
            // As per extended file format description:
46
15.9k
            // <https://developers.google.com/speed/webp/docs/riff_container#extended_file_format>
47
15.9k
            icc: DecodedMetadataHint::InHeader,
48
15.9k
            exif: DecodedMetadataHint::InHeader,
49
15.9k
            xmp: DecodedMetadataHint::InHeader,
50
15.9k
            ..FormatAttributes::default()
51
15.9k
        }
52
15.9k
    }
53
54
0
    fn animation_attributes(&mut self) -> Option<DecodedAnimationAttributes> {
55
0
        let loop_count = match self.inner.loop_count() {
56
0
            LoopCount::Forever => crate::metadata::LoopCount::Infinite,
57
0
            LoopCount::Times(n) => crate::metadata::LoopCount::Finite(n.into()),
58
        };
59
60
0
        Some(DecodedAnimationAttributes { loop_count })
61
0
    }
62
63
34.7k
    fn prepare_image(&mut self) -> ImageResult<DecoderPreparedImage> {
64
34.7k
        let (width, height) = self.inner.dimensions();
65
34.7k
        let color = if self.inner.has_alpha() {
66
16.2k
            ColorType::Rgba8
67
        } else {
68
18.5k
            ColorType::Rgb8
69
        };
70
71
34.7k
        Ok(DecoderPreparedImage::new(width, height, color))
72
34.7k
    }
73
74
8.66k
    fn read_image(&mut self, buf: &mut [u8]) -> ImageResult<DecodedImageAttributes> {
75
8.66k
        let is_animated = self.inner.is_animated();
76
77
8.66k
        if is_animated && self.current == self.inner.num_frames() {
78
0
            return Err(ImageError::Parameter(ParameterError::from_kind(
79
0
                ParameterErrorKind::NoMoreData,
80
0
            )));
81
8.66k
        }
82
83
8.66k
        let layout = self.prepare_image()?;
84
8.66k
        assert_eq!(u64::try_from(buf.len()), Ok(layout.total_bytes()));
85
86
        // `read_frame` panics if the image is not animated.
87
8.66k
        let delay = if is_animated {
88
1.50k
            let delay = self
89
1.50k
                .inner
90
1.50k
                .read_frame(buf)
91
1.50k
                .map_err(ImageError::from_webp_decode)?;
92
205
            Some(Delay::from_millis(delay))
93
        } else {
94
7.16k
            self.inner
95
7.16k
                .read_image(buf)
96
7.16k
                .map_err(ImageError::from_webp_decode)?;
97
3.39k
            None
98
        };
99
100
3.60k
        self.current += 1;
101
102
3.60k
        Ok(DecodedImageAttributes {
103
3.60k
            delay,
104
3.60k
            ..DecodedImageAttributes::default()
105
3.60k
        })
106
8.66k
    }
107
108
8.71k
    fn icc_profile(&mut self) -> ImageResult<Option<Vec<u8>>> {
109
8.71k
        self.inner
110
8.71k
            .icc_profile()
111
8.71k
            .map_err(ImageError::from_webp_decode)
112
8.71k
    }
113
114
8.71k
    fn exif_metadata(&mut self) -> ImageResult<Option<Vec<u8>>> {
115
8.71k
        let exif = self
116
8.71k
            .inner
117
8.71k
            .exif_metadata()
118
8.71k
            .map_err(ImageError::from_webp_decode)?;
119
120
8.67k
        Ok(exif)
121
8.71k
    }
122
123
8.71k
    fn xmp_metadata(&mut self) -> ImageResult<Option<Vec<u8>>> {
124
8.71k
        self.inner
125
8.71k
            .xmp_metadata()
126
8.71k
            .map_err(ImageError::from_webp_decode)
127
8.71k
    }
128
129
0
    fn more_images(&self) -> SequenceControl {
130
0
        if self.current == self.inner.num_frames() {
131
0
            SequenceControl::None
132
        } else {
133
0
            SequenceControl::MaybeMore
134
        }
135
0
    }
136
}
137
138
impl ImageError {
139
5.85k
    fn from_webp_decode(e: image_webp::DecodingError) -> Self {
140
5.85k
        match e {
141
491
            image_webp::DecodingError::IoError(e) => ImageError::IoError(e),
142
            image_webp::DecodingError::NoMoreFrames => {
143
0
                ImageError::Parameter(ParameterError::from_kind(ParameterErrorKind::NoMoreData))
144
            }
145
5.35k
            _ => ImageError::Decoding(DecodingError::new(ImageFormat::WebP.into(), e)),
146
        }
147
5.85k
    }
148
}
149
150
#[cfg(test)]
151
mod tests {
152
    use super::*;
153
154
    #[test]
155
    fn add_with_overflow_size() {
156
        let bytes = vec![
157
            0x52, 0x49, 0x46, 0x46, 0xaf, 0x37, 0x80, 0x47, 0x57, 0x45, 0x42, 0x50, 0x6c, 0x64,
158
            0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfb, 0x7e, 0x73, 0x00, 0x06, 0x00, 0x00, 0x00,
159
            0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65,
160
            0x40, 0xfb, 0xff, 0xff, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65,
161
            0x00, 0x00, 0x00, 0x00, 0x62, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49,
162
            0x49, 0x54, 0x55, 0x50, 0x4c, 0x54, 0x59, 0x50, 0x45, 0x33, 0x37, 0x44, 0x4d, 0x46,
163
        ];
164
165
        let data = std::io::Cursor::new(bytes);
166
167
        let _ = WebPDecoder::new(data);
168
    }
169
}