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