1use std::{fmt::Debug, mem, pin::Pin};
2
3use libwebp_sys as webp;
4
5use crate::{ColorMode, Error, Frame};
6
7const MAX_CANVAS_SIZE: usize = 3840 * 2160; pub struct DecoderOptions {
13 pub use_threads: bool,
15 pub color_mode: ColorMode,
17}
18
19impl Default for DecoderOptions {
20 fn default() -> Self {
21 Self {
22 use_threads: true,
23 color_mode: ColorMode::Rgba,
24 }
25 }
26}
27
28pub struct Decoder<'a> {
60 buffer: &'a [u8],
61 decoder_wr: DecoderWrapper,
62 info: webp::WebPAnimInfo,
63 options: DecoderOptions,
64}
65
66impl<'a> Decoder<'a> {
67 pub fn new(buffer: &'a [u8]) -> Result<Self, Error> {
78 Decoder::new_with_options(buffer, Default::default())
79 }
80
81 pub fn new_with_options(buffer: &'a [u8], options: DecoderOptions) -> Result<Self, Error> {
95 if buffer.is_empty() {
96 return Err(Error::ZeroSizeBuffer);
97 }
98
99 let mut decoder_options = Box::pin(unsafe {
100 let mut options = mem::zeroed();
101
102 if webp::WebPAnimDecoderOptionsInit(&mut options) != 1 {
103 return Err(Error::OptionsInitFailed);
104 }
105
106 options
107 });
108
109 decoder_options.use_threads = if options.use_threads { 1 } else { 0 };
110 decoder_options.color_mode = match options.color_mode {
111 ColorMode::Rgba => libwebp_sys::MODE_RGBA,
112 ColorMode::Bgra => libwebp_sys::MODE_BGRA,
113 ColorMode::Rgb => libwebp_sys::MODE_RGB,
114 ColorMode::Bgr => libwebp_sys::MODE_BGR,
115 };
116
117 let data = Box::pin(webp::WebPData {
119 bytes: buffer.as_ptr(),
120 size: buffer.len(),
121 });
122
123 let decoder_wr = DecoderWrapper::new(data, decoder_options)?;
124
125 let info = unsafe {
126 let mut info = mem::zeroed();
127 if webp::WebPAnimDecoderGetInfo(decoder_wr.decoder, &mut info) != 1 {
128 return Err(Error::DecoderGetInfoFailed);
129 }
130 info
131 };
132
133 if info.canvas_width * info.canvas_height > MAX_CANVAS_SIZE as u32 {
135 return Err(Error::TooLargeCanvas(
136 info.canvas_width,
137 info.canvas_height,
138 MAX_CANVAS_SIZE,
139 ));
140 }
141
142 log::trace!("Decoder initialized. {:?}", info);
143
144 Ok(Self {
145 buffer,
146 decoder_wr,
147 info,
148 options,
149 })
150 }
151
152 pub fn dimensions(&self) -> (u32, u32) {
162 (self.info.canvas_width, self.info.canvas_height)
163 }
164
165 fn has_more_frames(&self) -> bool {
166 let frames = unsafe { webp::WebPAnimDecoderHasMoreFrames(self.decoder_wr.decoder) };
167 frames > 0
168 }
169}
170
171impl<'a> Debug for Decoder<'a> {
172 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
173 let info = &self.info;
174
175 write!(f, "Decoder {{ buffer: {}b, info: {{ w: {}, h: {}, loop_cnt: {}, bgcolor: 0x{:x}, frame_count: {} }} }}", self.buffer.len(), info.canvas_width, info.canvas_height, info.loop_count, info.bgcolor, info.frame_count)
176 }
177}
178
179struct DecoderWrapper {
180 decoder: *mut webp::WebPAnimDecoder,
181
182 #[allow(dead_code)]
183 data: Pin<Box<webp::WebPData>>,
184 #[allow(dead_code)]
185 options: Pin<Box<webp::WebPAnimDecoderOptions>>,
186}
187
188impl DecoderWrapper {
189 pub fn new(
190 data: Pin<Box<webp::WebPData>>,
191 options: Pin<Box<webp::WebPAnimDecoderOptions>>,
192 ) -> Result<Self, Error> {
193 let decoder = unsafe { webp::WebPAnimDecoderNew(&*data, &*options) };
194 if decoder.is_null() {
195 return Err(Error::DecodeFailed);
196 }
197
198 Ok(Self {
199 decoder,
200 data,
201 options,
202 })
203 }
204}
205
206impl Drop for DecoderWrapper {
207 fn drop(&mut self) {
208 unsafe { webp::WebPAnimDecoderDelete(self.decoder) };
209 }
210}
211
212impl<'a> IntoIterator for Decoder<'a> {
213 type Item = Frame;
214
215 type IntoIter = DecoderIterator<'a>;
216
217 fn into_iter(self) -> Self::IntoIter {
218 DecoderIterator::new(self)
219 }
220}
221
222pub struct DecoderIterator<'a> {
224 animation_decoder: Decoder<'a>,
225}
226
227impl<'a> DecoderIterator<'a> {
228 fn new(animation_decoder: Decoder<'a>) -> Self {
229 Self { animation_decoder }
230 }
231}
232
233impl<'a> Iterator for DecoderIterator<'a> {
234 type Item = Frame;
235
236 fn next(&mut self) -> Option<Self::Item> {
237 if !self.animation_decoder.has_more_frames() {
238 return None;
239 }
240
241 let mut output_buffer = std::ptr::null_mut();
242 let mut timestamp: i32 = 0;
243
244 if unsafe {
245 webp::WebPAnimDecoderGetNext(
246 self.animation_decoder.decoder_wr.decoder,
247 &mut output_buffer,
248 &mut timestamp,
249 )
250 } != 1
251 {
252 log::warn!("webp::WebPAnimDecoderGetNext did not return success - frame parsing failed, parsing/decoding error?");
254 return None;
255 }
256
257 if output_buffer.is_null() {
258 log::error!("webp::WebPAnimDecoderGetNext returned null output ptr, can not decode a frame. This should not happen");
259 return None;
260 }
261
262 let info = &self.animation_decoder.info;
263 let opts = &self.animation_decoder.options;
264 let data = unsafe {
265 std::slice::from_raw_parts(
266 output_buffer,
267 info.canvas_width as usize * info.canvas_height as usize * opts.color_mode.size(),
268 )
269 };
270
271 log::trace!(
272 "Decoded a frame, timestamp {}, {} bytes",
273 timestamp,
274 data.len()
275 );
276
277 Some(Frame::new_from_decoder(
278 timestamp,
279 self.animation_decoder.options.color_mode,
280 data.to_vec(),
281 self.animation_decoder.dimensions(),
282 ))
283 }
284}
285
286#[cfg(test)]
287mod tests {
288 use super::*;
289 use std::fs::File;
290 use std::io::prelude::*;
291
292 #[test]
293 fn test_decoder_failure() {
294 let decoder = Decoder::new(&[]);
295 assert_eq!(decoder.unwrap_err(), Error::ZeroSizeBuffer);
296
297 let decoder = Decoder::new(&[0x00, 0x01]);
298 assert_eq!(decoder.unwrap_err(), Error::DecodeFailed);
299
300 let mut buffer = Vec::new();
301 File::open("./data/animated.webp")
302 .unwrap()
303 .read_to_end(&mut buffer)
304 .unwrap();
305
306 let decoder = Decoder::new(&buffer[..1500]);
307 assert_eq!(decoder.unwrap_err(), Error::DecodeFailed);
308 }
309
310 fn get_animated_buffer() -> Vec<u8> {
311 let mut buffer = Vec::new();
312 File::open("./data/animated.webp")
313 .unwrap()
314 .read_to_end(&mut buffer)
315 .unwrap();
316 buffer
317 }
318
319 #[cfg(feature = "image")]
320 #[test]
321 fn test_decode_to_image() {
322 use std::io::Cursor;
323
324 use image::{codecs::png::PngDecoder, DynamicImage, ImageDecoder as _, ImageOutputFormat};
325
326 let buffer = get_animated_buffer();
327 let decoder = Decoder::new(&buffer).unwrap();
328 let mut iter = decoder.into_iter();
329 let frame = iter.next().unwrap();
330 let image = frame.into_image().unwrap();
331 assert_eq!(image.dimensions(), (400, 400));
332
333 let mut buf = Cursor::new(Vec::new());
334 DynamicImage::ImageRgba8(image)
335 .write_to(&mut buf, ImageOutputFormat::Png)
336 .unwrap();
337
338 let buf = buf.into_inner();
339
340 let png_decoder = PngDecoder::new(&buf[..]).unwrap();
341 assert_eq!(png_decoder.dimensions(), (400, 400));
342 }
343
344 #[test]
345 fn test_decoder_success() {
346 let buffer = get_animated_buffer();
348
349 let decoder = Decoder::new_with_options(
351 &buffer,
352 DecoderOptions {
353 color_mode: ColorMode::Rgba,
354 ..Default::default()
355 },
356 )
357 .unwrap();
358
359 assert_eq!(decoder.dimensions(), (400, 400));
360 let frames: Vec<_> = decoder.into_iter().collect();
361
362 let timestamps: Vec<_> = frames.iter().map(|f| f.timestamp()).collect();
364 assert_eq!(timestamps, [40, 80, 120, 160, 200, 240, 280, 320, 360, 400]);
365 assert_eq!(frames[0].data().len(), 400 * 400 * 4);
366 assert_eq!(
367 frames[2].data()[89394 * 4..89394 * 4 + 4],
368 [167, 166, 167, 255]
369 );
370
371 assert_eq!(
372 frames[2].data().iter().map(|x| *x as usize).sum::<usize>(),
373 41668527
374 )
375 }
376
377 #[test]
378 fn test_fuzz_case_1() {
379 let data = [
381 0x2f, 0xff, 0xff, 0xff, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
382 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
383 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
384 ];
385
386 let decoder = Decoder::new(&data);
387 assert_eq!(
388 decoder.unwrap_err(),
389 Error::TooLargeCanvas(16384, 12288, MAX_CANVAS_SIZE)
390 );
391 }
392}