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