1 | use byteorder::{LittleEndian, ReadBytesExt}; |
2 | use std::io::{self, Cursor, Error, Read}; |
3 | use std::marker::PhantomData; |
4 | use std::{error, fmt, mem}; |
5 | |
6 | use crate::error::{DecodingError, ImageError, ImageResult, ParameterError, ParameterErrorKind}; |
7 | use crate::image::{ImageDecoder, ImageFormat}; |
8 | use crate::{color, AnimationDecoder, Frames, Rgba}; |
9 | |
10 | use super::lossless::{LosslessDecoder, LosslessFrame}; |
11 | use super::vp8::{Frame as VP8Frame, Vp8Decoder}; |
12 | |
13 | use super::extended::{read_extended_header, ExtendedImage}; |
14 | |
15 | /// All errors that can occur when attempting to parse a WEBP container |
16 | #[derive (Debug, Clone, Copy)] |
17 | pub(crate) enum DecoderError { |
18 | /// RIFF's "RIFF" signature not found or invalid |
19 | RiffSignatureInvalid([u8; 4]), |
20 | /// WebP's "WEBP" signature not found or invalid |
21 | WebpSignatureInvalid([u8; 4]), |
22 | /// Chunk Header was incorrect or invalid in its usage |
23 | ChunkHeaderInvalid([u8; 4]), |
24 | } |
25 | |
26 | impl fmt::Display for DecoderError { |
27 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
28 | struct SignatureWriter([u8; 4]); |
29 | impl fmt::Display for SignatureWriter { |
30 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
31 | write!( |
32 | f, |
33 | "[ {:#04X?}, {:#04X?}, {:#04X?}, {:#04X?}]" , |
34 | self.0[0], self.0[1], self.0[2], self.0[3] |
35 | ) |
36 | } |
37 | } |
38 | |
39 | match self { |
40 | DecoderError::RiffSignatureInvalid(riff) => f.write_fmt(format_args!( |
41 | "Invalid RIFF signature: {}" , |
42 | SignatureWriter(*riff) |
43 | )), |
44 | DecoderError::WebpSignatureInvalid(webp) => f.write_fmt(format_args!( |
45 | "Invalid WebP signature: {}" , |
46 | SignatureWriter(*webp) |
47 | )), |
48 | DecoderError::ChunkHeaderInvalid(header) => f.write_fmt(format_args!( |
49 | "Invalid Chunk header: {}" , |
50 | SignatureWriter(*header) |
51 | )), |
52 | } |
53 | } |
54 | } |
55 | |
56 | impl From<DecoderError> for ImageError { |
57 | fn from(e: DecoderError) -> ImageError { |
58 | ImageError::Decoding(DecodingError::new(format:ImageFormat::WebP.into(), err:e)) |
59 | } |
60 | } |
61 | |
62 | impl error::Error for DecoderError {} |
63 | |
64 | /// All possible RIFF chunks in a WebP image file |
65 | #[allow (clippy::upper_case_acronyms)] |
66 | #[derive (Debug, Clone, Copy, PartialEq)] |
67 | pub(crate) enum WebPRiffChunk { |
68 | RIFF, |
69 | WEBP, |
70 | VP8, |
71 | VP8L, |
72 | VP8X, |
73 | ANIM, |
74 | ANMF, |
75 | ALPH, |
76 | ICCP, |
77 | EXIF, |
78 | XMP, |
79 | } |
80 | |
81 | impl WebPRiffChunk { |
82 | pub(crate) fn from_fourcc(chunk_fourcc: [u8; 4]) -> ImageResult<Self> { |
83 | match &chunk_fourcc { |
84 | b"RIFF" => Ok(Self::RIFF), |
85 | b"WEBP" => Ok(Self::WEBP), |
86 | b"VP8 " => Ok(Self::VP8), |
87 | b"VP8L" => Ok(Self::VP8L), |
88 | b"VP8X" => Ok(Self::VP8X), |
89 | b"ANIM" => Ok(Self::ANIM), |
90 | b"ANMF" => Ok(Self::ANMF), |
91 | b"ALPH" => Ok(Self::ALPH), |
92 | b"ICCP" => Ok(Self::ICCP), |
93 | b"EXIF" => Ok(Self::EXIF), |
94 | b"XMP " => Ok(Self::XMP), |
95 | _ => Err(DecoderError::ChunkHeaderInvalid(chunk_fourcc).into()), |
96 | } |
97 | } |
98 | |
99 | pub(crate) fn to_fourcc(&self) -> [u8; 4] { |
100 | match self { |
101 | Self::RIFF => *b"RIFF" , |
102 | Self::WEBP => *b"WEBP" , |
103 | Self::VP8 => *b"VP8 " , |
104 | Self::VP8L => *b"VP8L" , |
105 | Self::VP8X => *b"VP8X" , |
106 | Self::ANIM => *b"ANIM" , |
107 | Self::ANMF => *b"ANMF" , |
108 | Self::ALPH => *b"ALPH" , |
109 | Self::ICCP => *b"ICCP" , |
110 | Self::EXIF => *b"EXIF" , |
111 | Self::XMP => *b"XMP " , |
112 | } |
113 | } |
114 | } |
115 | |
116 | enum WebPImage { |
117 | Lossy(VP8Frame), |
118 | Lossless(LosslessFrame), |
119 | Extended(ExtendedImage), |
120 | } |
121 | |
122 | /// WebP Image format decoder. Currently only supports lossy RGB images or lossless RGBA images. |
123 | pub struct WebPDecoder<R> { |
124 | r: R, |
125 | image: WebPImage, |
126 | } |
127 | |
128 | impl<R: Read> WebPDecoder<R> { |
129 | /// Create a new WebPDecoder from the Reader ```r```. |
130 | /// This function takes ownership of the Reader. |
131 | pub fn new(r: R) -> ImageResult<WebPDecoder<R>> { |
132 | let image = WebPImage::Lossy(Default::default()); |
133 | |
134 | let mut decoder = WebPDecoder { r, image }; |
135 | decoder.read_data()?; |
136 | Ok(decoder) |
137 | } |
138 | |
139 | //reads the 12 bytes of the WebP file header |
140 | fn read_riff_header(&mut self) -> ImageResult<u32> { |
141 | let mut riff = [0; 4]; |
142 | self.r.read_exact(&mut riff)?; |
143 | if &riff != b"RIFF" { |
144 | return Err(DecoderError::RiffSignatureInvalid(riff).into()); |
145 | } |
146 | |
147 | let size = self.r.read_u32::<LittleEndian>()?; |
148 | |
149 | let mut webp = [0; 4]; |
150 | self.r.read_exact(&mut webp)?; |
151 | if &webp != b"WEBP" { |
152 | return Err(DecoderError::WebpSignatureInvalid(webp).into()); |
153 | } |
154 | |
155 | Ok(size) |
156 | } |
157 | |
158 | //reads the chunk header, decodes the frame and returns the inner decoder |
159 | fn read_frame(&mut self) -> ImageResult<WebPImage> { |
160 | let chunk = read_chunk(&mut self.r)?; |
161 | |
162 | match chunk { |
163 | Some((cursor, WebPRiffChunk::VP8)) => { |
164 | let mut vp8_decoder = Vp8Decoder::new(cursor); |
165 | let frame = vp8_decoder.decode_frame()?; |
166 | |
167 | Ok(WebPImage::Lossy(frame.clone())) |
168 | } |
169 | Some((cursor, WebPRiffChunk::VP8L)) => { |
170 | let mut lossless_decoder = LosslessDecoder::new(cursor); |
171 | let frame = lossless_decoder.decode_frame()?; |
172 | |
173 | Ok(WebPImage::Lossless(frame.clone())) |
174 | } |
175 | Some((mut cursor, WebPRiffChunk::VP8X)) => { |
176 | let info = read_extended_header(&mut cursor)?; |
177 | |
178 | let image = ExtendedImage::read_extended_chunks(&mut self.r, info)?; |
179 | |
180 | Ok(WebPImage::Extended(image)) |
181 | } |
182 | None => Err(ImageError::IoError(Error::from( |
183 | io::ErrorKind::UnexpectedEof, |
184 | ))), |
185 | Some((_, chunk)) => Err(DecoderError::ChunkHeaderInvalid(chunk.to_fourcc()).into()), |
186 | } |
187 | } |
188 | |
189 | fn read_data(&mut self) -> ImageResult<()> { |
190 | let _size = self.read_riff_header()?; |
191 | |
192 | let image = self.read_frame()?; |
193 | |
194 | self.image = image; |
195 | |
196 | Ok(()) |
197 | } |
198 | |
199 | /// Returns true if the image as described by the bitstream is animated. |
200 | pub fn has_animation(&self) -> bool { |
201 | match &self.image { |
202 | WebPImage::Lossy(_) => false, |
203 | WebPImage::Lossless(_) => false, |
204 | WebPImage::Extended(extended) => extended.has_animation(), |
205 | } |
206 | } |
207 | |
208 | /// Sets the background color if the image is an extended and animated webp. |
209 | pub fn set_background_color(&mut self, color: Rgba<u8>) -> ImageResult<()> { |
210 | match &mut self.image { |
211 | WebPImage::Extended(image) => image.set_background_color(color), |
212 | _ => Err(ImageError::Parameter(ParameterError::from_kind( |
213 | ParameterErrorKind::Generic( |
214 | "Background color can only be set on animated webp" .to_owned(), |
215 | ), |
216 | ))), |
217 | } |
218 | } |
219 | } |
220 | |
221 | pub(crate) fn read_len_cursor<R>(r: &mut R) -> ImageResult<Cursor<Vec<u8>>> |
222 | where |
223 | R: Read, |
224 | { |
225 | let unpadded_len: u64 = u64::from(r.read_u32::<LittleEndian>()?); |
226 | |
227 | // RIFF chunks containing an uneven number of bytes append |
228 | // an extra 0x00 at the end of the chunk |
229 | // |
230 | // The addition cannot overflow since we have a u64 that was created from a u32 |
231 | let len: u64 = unpadded_len + (unpadded_len % 2); |
232 | |
233 | let mut framedata: Vec = Vec::new(); |
234 | r.by_ref().take(len).read_to_end(&mut framedata)?; |
235 | |
236 | //remove padding byte |
237 | if unpadded_len % 2 == 1 { |
238 | framedata.pop(); |
239 | } |
240 | |
241 | Ok(io::Cursor::new(inner:framedata)) |
242 | } |
243 | |
244 | /// Reads a chunk header FourCC |
245 | /// Returns None if and only if we hit end of file reading the four character code of the chunk |
246 | /// The inner error is `Err` if and only if the chunk header FourCC is present but unknown |
247 | pub(crate) fn read_fourcc<R: Read>(r: &mut R) -> ImageResult<Option<ImageResult<WebPRiffChunk>>> { |
248 | let mut chunk_fourcc: [u8; 4] = [0; 4]; |
249 | let result: Result<(), Error> = r.read_exact(&mut chunk_fourcc); |
250 | |
251 | match result { |
252 | Ok(()) => {} |
253 | Err(err: Error) => { |
254 | if err.kind() == io::ErrorKind::UnexpectedEof { |
255 | return Ok(None); |
256 | } else { |
257 | return Err(err.into()); |
258 | } |
259 | } |
260 | } |
261 | |
262 | let chunk: Result = WebPRiffChunk::from_fourcc(chunk_fourcc); |
263 | Ok(Some(chunk)) |
264 | } |
265 | |
266 | /// Reads a chunk |
267 | /// Returns an error if the chunk header is not a valid webp header or some other reading error |
268 | /// Returns None if and only if we hit end of file reading the four character code of the chunk |
269 | pub(crate) fn read_chunk<R>(r: &mut R) -> ImageResult<Option<(Cursor<Vec<u8>>, WebPRiffChunk)>> |
270 | where |
271 | R: Read, |
272 | { |
273 | if let Some(chunk: Result) = read_fourcc(r)? { |
274 | let chunk: WebPRiffChunk = chunk?; |
275 | let cursor: Cursor> = read_len_cursor(r)?; |
276 | Ok(Some((cursor, chunk))) |
277 | } else { |
278 | Ok(None) |
279 | } |
280 | } |
281 | |
282 | /// Wrapper struct around a `Cursor<Vec<u8>>` |
283 | pub struct WebpReader<R>(Cursor<Vec<u8>>, PhantomData<R>); |
284 | impl<R> Read for WebpReader<R> { |
285 | fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { |
286 | self.0.read(buf) |
287 | } |
288 | fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> { |
289 | if self.0.position() == 0 && buf.is_empty() { |
290 | mem::swap(x:buf, self.0.get_mut()); |
291 | Ok(buf.len()) |
292 | } else { |
293 | self.0.read_to_end(buf) |
294 | } |
295 | } |
296 | } |
297 | |
298 | impl<'a, R: 'a + Read> ImageDecoder<'a> for WebPDecoder<R> { |
299 | type Reader = WebpReader<R>; |
300 | |
301 | fn dimensions(&self) -> (u32, u32) { |
302 | match &self.image { |
303 | WebPImage::Lossy(vp8_frame) => { |
304 | (u32::from(vp8_frame.width), u32::from(vp8_frame.height)) |
305 | } |
306 | WebPImage::Lossless(lossless_frame) => ( |
307 | u32::from(lossless_frame.width), |
308 | u32::from(lossless_frame.height), |
309 | ), |
310 | WebPImage::Extended(extended) => extended.dimensions(), |
311 | } |
312 | } |
313 | |
314 | fn color_type(&self) -> color::ColorType { |
315 | match &self.image { |
316 | WebPImage::Lossy(_) => color::ColorType::Rgb8, |
317 | WebPImage::Lossless(_) => color::ColorType::Rgba8, |
318 | WebPImage::Extended(extended) => extended.color_type(), |
319 | } |
320 | } |
321 | |
322 | fn into_reader(self) -> ImageResult<Self::Reader> { |
323 | match &self.image { |
324 | WebPImage::Lossy(vp8_frame) => { |
325 | let mut data = vec![0; vp8_frame.get_buf_size()]; |
326 | vp8_frame.fill_rgb(data.as_mut_slice()); |
327 | Ok(WebpReader(Cursor::new(data), PhantomData)) |
328 | } |
329 | WebPImage::Lossless(lossless_frame) => { |
330 | let mut data = vec![0; lossless_frame.get_buf_size()]; |
331 | lossless_frame.fill_rgba(data.as_mut_slice()); |
332 | Ok(WebpReader(Cursor::new(data), PhantomData)) |
333 | } |
334 | WebPImage::Extended(extended) => { |
335 | let mut data = vec![0; extended.get_buf_size()]; |
336 | extended.fill_buf(data.as_mut_slice()); |
337 | Ok(WebpReader(Cursor::new(data), PhantomData)) |
338 | } |
339 | } |
340 | } |
341 | |
342 | fn read_image(self, buf: &mut [u8]) -> ImageResult<()> { |
343 | assert_eq!(u64::try_from(buf.len()), Ok(self.total_bytes())); |
344 | |
345 | match &self.image { |
346 | WebPImage::Lossy(vp8_frame) => { |
347 | vp8_frame.fill_rgb(buf); |
348 | } |
349 | WebPImage::Lossless(lossless_frame) => { |
350 | lossless_frame.fill_rgba(buf); |
351 | } |
352 | WebPImage::Extended(extended) => { |
353 | extended.fill_buf(buf); |
354 | } |
355 | } |
356 | Ok(()) |
357 | } |
358 | |
359 | fn icc_profile(&mut self) -> Option<Vec<u8>> { |
360 | if let WebPImage::Extended(extended) = &self.image { |
361 | extended.icc_profile() |
362 | } else { |
363 | None |
364 | } |
365 | } |
366 | } |
367 | |
368 | impl<'a, R: 'a + Read> AnimationDecoder<'a> for WebPDecoder<R> { |
369 | fn into_frames(self) -> Frames<'a> { |
370 | match self.image { |
371 | WebPImage::Lossy(_) | WebPImage::Lossless(_) => { |
372 | Frames::new(iterator:Box::new(std::iter::empty())) |
373 | } |
374 | WebPImage::Extended(extended_image: ExtendedImage) => extended_image.into_frames(), |
375 | } |
376 | } |
377 | } |
378 | |
379 | #[cfg (test)] |
380 | mod tests { |
381 | use super::*; |
382 | |
383 | #[test ] |
384 | fn add_with_overflow_size() { |
385 | let bytes = vec![ |
386 | 0x52, 0x49, 0x46, 0x46, 0xaf, 0x37, 0x80, 0x47, 0x57, 0x45, 0x42, 0x50, 0x6c, 0x64, |
387 | 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfb, 0x7e, 0x73, 0x00, 0x06, 0x00, 0x00, 0x00, |
388 | 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, |
389 | 0x40, 0xfb, 0xff, 0xff, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, |
390 | 0x00, 0x00, 0x00, 0x00, 0x62, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, |
391 | 0x49, 0x54, 0x55, 0x50, 0x4c, 0x54, 0x59, 0x50, 0x45, 0x33, 0x37, 0x44, 0x4d, 0x46, |
392 | ]; |
393 | |
394 | let data = std::io::Cursor::new(bytes); |
395 | |
396 | let _ = WebPDecoder::new(data); |
397 | } |
398 | } |
399 | |