image/codecs/webp/
decoder.rs

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(ImageFormat::WebP.into(), 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::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 = unpadded_len + (unpadded_len % 2);
232
233    let mut framedata = 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(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 = [0; 4];
249    let result = r.read_exact(&mut chunk_fourcc);
250
251    match result {
252        Ok(()) => {}
253        Err(err) => {
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 = 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) = read_fourcc(r)? {
274        let chunk = chunk?;
275        let 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(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(Box::new(std::iter::empty()))
373            }
374            WebPImage::Extended(extended_image) => 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}