1use byteorder::{LittleEndian, ReadBytesExt};
2use std::io::{self, Cursor, Error, Read};
3use std::marker::PhantomData;
4use std::{error, fmt, mem};
5
6use crate::error::{DecodingError, ImageError, ImageResult, ParameterError, ParameterErrorKind};
7use crate::image::{ImageDecoder, ImageFormat};
8use crate::{color, AnimationDecoder, Frames, Rgba};
9
10use super::lossless::{LosslessDecoder, LosslessFrame};
11use super::vp8::{Frame as VP8Frame, Vp8Decoder};
12
13use 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)]
17pub(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
26impl 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
56impl From<DecoderError> for ImageError {
57 fn from(e: DecoderError) -> ImageError {
58 ImageError::Decoding(DecodingError::new(format:ImageFormat::WebP.into(), err:e))
59 }
60}
61
62impl 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)]
67pub(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
81impl 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
116enum 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.
123pub struct WebPDecoder<R> {
124 r: R,
125 image: WebPImage,
126}
127
128impl<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
221pub(crate) fn read_len_cursor<R>(r: &mut R) -> ImageResult<Cursor<Vec<u8>>>
222where
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
247pub(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
269pub(crate) fn read_chunk<R>(r: &mut R) -> ImageResult<Option<(Cursor<Vec<u8>>, WebPRiffChunk)>>
270where
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>>`
283pub struct WebpReader<R>(Cursor<Vec<u8>>, PhantomData<R>);
284impl<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
298impl<'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
368impl<'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)]
380mod 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