/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 | | } |