1use crate::os_lib::webp::io::{LittleEndian, ReadBytesExt};
2use alloc::borrow::ToOwned;
3use alloc::string::String;
4use alloc::vec;
5use alloc::vec::Vec;
6
7use crate::os_lib::webp::io::{self, BufRead, Cursor, Read, Seek};
8use alloc::collections::BTreeMap as HashMap;
9use core::num::NonZeroU16;
10use core::ops::Range;
11
12use crate::os_lib::webp::extended::{
13 self, get_alpha_predictor, read_alpha_chunk, WebPExtendedInfo,
14};
15
16use super::lossless::LosslessDecoder;
17use super::vp8::Vp8Decoder;
18
19#[derive(Debug)]
21#[non_exhaustive]
22pub enum DecodingError {
23 IoError(io::Error),
25 RiffSignatureInvalid([u8; 4]),
27 WebpSignatureInvalid([u8; 4]),
29 ChunkMissing,
31 ChunkHeaderInvalid([u8; 4]),
33 ReservedBitSet,
35 InvalidAlphaPreprocessing,
37 InvalidCompressionMethod,
39 AlphaChunkSizeMismatch,
41 ImageTooLarge,
43 FrameOutsideImage,
45 LosslessSignatureInvalid(u8),
47 VersionNumberInvalid(u8),
49 InvalidColorCacheBits(u8),
51 HuffmanError,
53 BitStreamError,
55 TransformError,
57 Vp8MagicInvalid([u8; 3]),
59 NotEnoughInitData,
61 ColorSpaceInvalid(u8),
63 LumaPredictionModeInvalid(i8),
65 IntraPredictionModeInvalid(i8),
67 ChromaPredictionModeInvalid(i8),
69 InconsistentImageSizes,
71 UnsupportedFeature(String),
73 InvalidParameter(String),
75 MemoryLimitExceeded,
77 InvalidChunkSize,
79 NoMoreFrames,
81}
82
83impl From<io::Error> for DecodingError {
84 fn from(err: io::Error) -> Self {
85 DecodingError::IoError(err)
86 }
87}
88
89impl core::fmt::Display for DecodingError {
90 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
91 write!(f, "{:?}", self)
92 }
93}
94
95#[allow(clippy::upper_case_acronyms)]
97#[derive(Debug, Clone, Copy, PartialEq, Hash, Eq, PartialOrd, Ord)]
98pub(crate) enum WebPRiffChunk {
99 RIFF,
100 WEBP,
101 VP8,
102 VP8L,
103 VP8X,
104 ANIM,
105 ANMF,
106 ALPH,
107 ICCP,
108 EXIF,
109 XMP,
110 Unknown([u8; 4]),
111}
112
113impl WebPRiffChunk {
114 pub(crate) const fn from_fourcc(chunk_fourcc: [u8; 4]) -> Self {
115 match &chunk_fourcc {
116 b"RIFF" => Self::RIFF,
117 b"WEBP" => Self::WEBP,
118 b"VP8 " => Self::VP8,
119 b"VP8L" => Self::VP8L,
120 b"VP8X" => Self::VP8X,
121 b"ANIM" => Self::ANIM,
122 b"ANMF" => Self::ANMF,
123 b"ALPH" => Self::ALPH,
124 b"ICCP" => Self::ICCP,
125 b"EXIF" => Self::EXIF,
126 b"XMP " => Self::XMP,
127 _ => Self::Unknown(chunk_fourcc),
128 }
129 }
130
131 pub(crate) const fn to_fourcc(self) -> [u8; 4] {
132 match self {
133 Self::RIFF => *b"RIFF",
134 Self::WEBP => *b"WEBP",
135 Self::VP8 => *b"VP8 ",
136 Self::VP8L => *b"VP8L",
137 Self::VP8X => *b"VP8X",
138 Self::ANIM => *b"ANIM",
139 Self::ANMF => *b"ANMF",
140 Self::ALPH => *b"ALPH",
141 Self::ICCP => *b"ICCP",
142 Self::EXIF => *b"EXIF",
143 Self::XMP => *b"XMP ",
144 Self::Unknown(fourcc) => fourcc,
145 }
146 }
147
148 pub(crate) const fn is_unknown(self) -> bool {
149 matches!(self, Self::Unknown(_))
150 }
151}
152
153enum ImageKind {
160 Lossy,
161 Lossless,
162 Extended(WebPExtendedInfo),
163}
164
165struct AnimationState {
166 next_frame: u32,
167 next_frame_start: u64,
168 dispose_next_frame: bool,
169 previous_frame_width: u32,
170 previous_frame_height: u32,
171 previous_frame_x_offset: u32,
172 previous_frame_y_offset: u32,
173 canvas: Option<Vec<u8>>,
174}
175impl Default for AnimationState {
176 fn default() -> Self {
177 Self {
178 next_frame: 0,
179 next_frame_start: 0,
180 dispose_next_frame: true,
181 previous_frame_width: 0,
182 previous_frame_height: 0,
183 previous_frame_x_offset: 0,
184 previous_frame_y_offset: 0,
185 canvas: None,
186 }
187 }
188}
189
190#[derive(Copy, Clone, Debug, Eq, PartialEq)]
192pub enum LoopCount {
193 Forever,
195 Times(NonZeroU16),
197}
198
199#[derive(Clone)]
201#[non_exhaustive]
202pub struct WebPDecodeOptions {
203 pub lossy_upsampling: UpsamplingMethod,
207}
208
209impl Default for WebPDecodeOptions {
210 fn default() -> Self {
211 Self {
212 lossy_upsampling: UpsamplingMethod::Bilinear,
213 }
214 }
215}
216
217#[derive(Clone, Copy, Default)]
222pub enum UpsamplingMethod {
223 #[default]
228 Bilinear,
229 Simple,
234}
235
236pub struct WebPDecoder<R> {
238 r: R,
239 memory_limit: usize,
240
241 width: u32,
242 height: u32,
243
244 kind: ImageKind,
245 animation: AnimationState,
246
247 is_lossy: bool,
248 has_alpha: bool,
249 num_frames: u32,
250 loop_count: LoopCount,
251 loop_duration: u64,
252
253 chunks: HashMap<WebPRiffChunk, Range<u64>>,
254
255 webp_decode_options: WebPDecodeOptions,
256}
257
258impl<R: BufRead + Seek> WebPDecoder<R> {
259 pub fn new(r: R) -> Result<Self, DecodingError> {
262 Self::new_with_options(r, WebPDecodeOptions::default())
263 }
264
265 pub fn new_with_options(
268 r: R,
269 webp_decode_options: WebPDecodeOptions,
270 ) -> Result<Self, DecodingError> {
271 let mut decoder = Self {
272 r,
273 width: 0,
274 height: 0,
275 num_frames: 0,
276 kind: ImageKind::Lossy,
277 chunks: HashMap::new(),
278 animation: Default::default(),
279 memory_limit: usize::MAX,
280 is_lossy: false,
281 has_alpha: false,
282 loop_count: LoopCount::Times(NonZeroU16::MIN), loop_duration: 0,
284 webp_decode_options,
285 };
286 decoder.read_data()?;
287 Ok(decoder)
288 }
289
290 fn read_data(&mut self) -> Result<(), DecodingError> {
291 let (WebPRiffChunk::RIFF, riff_size, _) = read_chunk_header(&mut self.r)? else {
292 return Err(DecodingError::ChunkHeaderInvalid(*b"RIFF"));
293 };
294
295 match &read_fourcc(&mut self.r)? {
296 WebPRiffChunk::WEBP => {}
297 fourcc => return Err(DecodingError::WebpSignatureInvalid(fourcc.to_fourcc())),
298 }
299
300 let (chunk, chunk_size, chunk_size_rounded) = read_chunk_header(&mut self.r)?;
301 let start = self.r.stream_position()?;
302
303 match chunk {
304 WebPRiffChunk::VP8 => {
305 let tag = self.r.read_u24::<LittleEndian>()?;
306
307 let keyframe = tag & 1 == 0;
308 if !keyframe {
309 return Err(DecodingError::UnsupportedFeature(
310 "Non-keyframe frames".to_owned(),
311 ));
312 }
313
314 let mut tag = [0u8; 3];
315 self.r.read_exact(&mut tag)?;
316 if tag != [0x9d, 0x01, 0x2a] {
317 return Err(DecodingError::Vp8MagicInvalid(tag));
318 }
319
320 let w = self.r.read_u16::<LittleEndian>()?;
321 let h = self.r.read_u16::<LittleEndian>()?;
322
323 self.width = u32::from(w & 0x3FFF);
324 self.height = u32::from(h & 0x3FFF);
325 if self.width == 0 || self.height == 0 {
326 return Err(DecodingError::InconsistentImageSizes);
327 }
328
329 self.chunks
330 .insert(WebPRiffChunk::VP8, start..start + chunk_size);
331 self.kind = ImageKind::Lossy;
332 self.is_lossy = true;
333 }
334 WebPRiffChunk::VP8L => {
335 let signature = self.r.read_u8()?;
336 if signature != 0x2f {
337 return Err(DecodingError::LosslessSignatureInvalid(signature));
338 }
339
340 let header = self.r.read_u32::<LittleEndian>()?;
341 let version = header >> 29;
342 if version != 0 {
343 return Err(DecodingError::VersionNumberInvalid(version as u8));
344 }
345
346 self.width = (1 + header) & 0x3FFF;
347 self.height = (1 + (header >> 14)) & 0x3FFF;
348 self.chunks
349 .insert(WebPRiffChunk::VP8L, start..start + chunk_size);
350 self.kind = ImageKind::Lossless;
351 self.has_alpha = (header >> 28) & 1 != 0;
352 }
353 WebPRiffChunk::VP8X => {
354 let mut info = extended::read_extended_header(&mut self.r)?;
355 self.width = info.canvas_width;
356 self.height = info.canvas_height;
357
358 let mut position = start + chunk_size_rounded;
359 let max_position = position + riff_size.saturating_sub(12);
360 self.r.seek(io::SeekFrom::Start(position))?;
361
362 while position < max_position {
363 match read_chunk_header(&mut self.r) {
364 Ok((chunk, chunk_size, chunk_size_rounded)) => {
365 let range = position + 8..position + 8 + chunk_size;
366 position += 8 + chunk_size_rounded;
367
368 if !chunk.is_unknown() {
369 self.chunks.entry(chunk).or_insert(range);
370 }
371
372 if chunk == WebPRiffChunk::ANMF {
373 self.num_frames += 1;
374 if chunk_size < 24 {
375 return Err(DecodingError::InvalidChunkSize);
376 }
377
378 self.r.seek_relative(12)?;
379 let duration = self.r.read_u32::<LittleEndian>()? & 0xffffff;
380 self.loop_duration =
381 self.loop_duration.wrapping_add(u64::from(duration));
382
383 if !self.is_lossy {
389 let (subchunk, ..) = read_chunk_header(&mut self.r)?;
390 if let WebPRiffChunk::VP8 | WebPRiffChunk::ALPH = subchunk {
391 self.is_lossy = true;
392 }
393 self.r.seek_relative(chunk_size_rounded as i64 - 24)?;
394 } else {
395 self.r.seek_relative(chunk_size_rounded as i64 - 16)?;
396 }
397
398 continue;
399 }
400
401 self.r.seek_relative(chunk_size_rounded as i64)?;
402 }
403 Err(DecodingError::IoError(e))
404 if e.kind() == io::ErrorKind::UnexpectedEof =>
405 {
406 break;
407 }
408 Err(e) => return Err(e),
409 }
410 }
411 self.is_lossy = self.is_lossy || self.chunks.contains_key(&WebPRiffChunk::VP8);
412
413 if info.animation
416 && (!self.chunks.contains_key(&WebPRiffChunk::ANIM)
417 || !self.chunks.contains_key(&WebPRiffChunk::ANMF))
418 || info.exif_metadata && !self.chunks.contains_key(&WebPRiffChunk::EXIF)
419 || info.xmp_metadata && !self.chunks.contains_key(&WebPRiffChunk::XMP)
420 || !info.animation
421 && self.chunks.contains_key(&WebPRiffChunk::VP8)
422 == self.chunks.contains_key(&WebPRiffChunk::VP8L)
423 {
424 return Err(DecodingError::ChunkMissing);
425 }
426
427 if info.animation {
429 match self.read_chunk(WebPRiffChunk::ANIM, 6) {
430 Ok(Some(chunk)) => {
431 let mut cursor = Cursor::new(chunk);
432 cursor.read_exact(&mut info.background_color_hint)?;
433 self.loop_count =
434 match NonZeroU16::new(cursor.read_u16::<LittleEndian>()?) {
435 None => LoopCount::Forever,
436 Some(n) => LoopCount::Times(n),
437 };
438 self.animation.next_frame_start =
439 self.chunks.get(&WebPRiffChunk::ANMF).unwrap().start - 8;
440 }
441 Ok(None) => return Err(DecodingError::ChunkMissing),
442 Err(DecodingError::MemoryLimitExceeded) => {
443 return Err(DecodingError::InvalidChunkSize)
444 }
445 Err(e) => return Err(e),
446 }
447 }
448
449 if let Some(range) = self.chunks.get(&WebPRiffChunk::ANMF).cloned() {
453 let mut position = range.start + 16;
454 self.r.seek(io::SeekFrom::Start(position))?;
455 for _ in 0..2 {
456 let (subchunk, subchunk_size, subchunk_size_rounded) =
457 read_chunk_header(&mut self.r)?;
458 let subrange = position + 8..position + 8 + subchunk_size;
459 self.chunks.entry(subchunk).or_insert(subrange.clone());
460
461 position += 8 + subchunk_size_rounded;
462 if position + 8 > range.end {
463 break;
464 }
465 }
466 }
467
468 self.has_alpha = info.alpha;
469 self.kind = ImageKind::Extended(info);
470 }
471 _ => return Err(DecodingError::ChunkHeaderInvalid(chunk.to_fourcc())),
472 };
473
474 Ok(())
475 }
476
477 pub fn set_memory_limit(&mut self, limit: usize) {
481 self.memory_limit = limit;
482 }
483
484 pub fn background_color_hint(&self) -> Option<[u8; 4]> {
486 if let ImageKind::Extended(info) = &self.kind {
487 Some(info.background_color_hint)
488 } else {
489 None
490 }
491 }
492
493 pub fn set_background_color(&mut self, color: [u8; 4]) -> Result<(), DecodingError> {
495 if let ImageKind::Extended(info) = &mut self.kind {
496 info.background_color = Some(color);
497 Ok(())
498 } else {
499 Err(DecodingError::InvalidParameter(
500 "Background color can only be set on animated webp".to_owned(),
501 ))
502 }
503 }
504
505 pub fn dimensions(&self) -> (u32, u32) {
507 (self.width, self.height)
508 }
509
510 pub fn has_alpha(&self) -> bool {
513 self.has_alpha
514 }
515
516 pub fn is_animated(&self) -> bool {
518 match &self.kind {
519 ImageKind::Lossy | ImageKind::Lossless => false,
520 ImageKind::Extended(extended) => extended.animation,
521 }
522 }
523
524 pub fn is_lossy(&mut self) -> bool {
526 self.is_lossy
527 }
528
529 pub fn num_frames(&self) -> u32 {
532 self.num_frames
533 }
534
535 pub fn loop_count(&self) -> LoopCount {
537 self.loop_count
538 }
539
540 pub fn loop_duration(&self) -> u64 {
545 self.loop_duration
546 }
547
548 fn read_chunk(
549 &mut self,
550 chunk: WebPRiffChunk,
551 max_size: usize,
552 ) -> Result<Option<Vec<u8>>, DecodingError> {
553 match self.chunks.get(&chunk) {
554 Some(range) => {
555 if range.end - range.start > max_size as u64 {
556 return Err(DecodingError::MemoryLimitExceeded);
557 }
558
559 self.r.seek(io::SeekFrom::Start(range.start))?;
560 let mut data = vec![0; (range.end - range.start) as usize];
561 self.r.read_exact(&mut data)?;
562 Ok(Some(data))
563 }
564 None => Ok(None),
565 }
566 }
567
568 pub fn icc_profile(&mut self) -> Result<Option<Vec<u8>>, DecodingError> {
570 self.read_chunk(WebPRiffChunk::ICCP, self.memory_limit)
571 }
572
573 pub fn exif_metadata(&mut self) -> Result<Option<Vec<u8>>, DecodingError> {
575 self.read_chunk(WebPRiffChunk::EXIF, self.memory_limit)
576 }
577
578 pub fn xmp_metadata(&mut self) -> Result<Option<Vec<u8>>, DecodingError> {
580 self.read_chunk(WebPRiffChunk::XMP, self.memory_limit)
581 }
582
583 pub fn output_buffer_size(&self) -> Option<usize> {
586 let bytes_per_pixel = if self.has_alpha() { 4 } else { 3 };
587 (self.width as usize)
588 .checked_mul(self.height as usize)?
589 .checked_mul(bytes_per_pixel)
590 }
591
592 pub fn read_image(&mut self, buf: &mut [u8]) -> Result<(), DecodingError> {
596 if Some(buf.len()) != self.output_buffer_size() {
597 return Err(DecodingError::ImageTooLarge);
598 }
599
600 if self.is_animated() {
601 let saved = core::mem::take(&mut self.animation);
602 self.animation.next_frame_start =
603 self.chunks.get(&WebPRiffChunk::ANMF).unwrap().start - 8;
604 let result = self.read_frame(buf);
605 self.animation = saved;
606 result?;
607 } else if let Some(range) = self.chunks.get(&WebPRiffChunk::VP8L) {
608 let mut decoder = LosslessDecoder::new(range_reader(&mut self.r, range.clone())?);
609
610 if self.has_alpha {
611 decoder.decode_frame(self.width, self.height, false, buf)?;
612 } else {
613 let mut data = vec![0; self.width as usize * self.height as usize * 4];
614 decoder.decode_frame(self.width, self.height, false, &mut data)?;
615 for (rgba_val, chunk) in data.chunks_exact(4).zip(buf.chunks_exact_mut(3)) {
616 chunk.copy_from_slice(&rgba_val[..3]);
617 }
618 }
619 } else {
620 let range = self
621 .chunks
622 .get(&WebPRiffChunk::VP8)
623 .ok_or(DecodingError::ChunkMissing)?;
624 let reader = range_reader(&mut self.r, range.start..range.end)?;
625 let frame = Vp8Decoder::decode_frame(reader)?;
626 if u32::from(frame.width) != self.width || u32::from(frame.height) != self.height {
627 return Err(DecodingError::InconsistentImageSizes);
628 }
629
630 if self.has_alpha() {
631 frame.fill_rgba(buf, self.webp_decode_options.lossy_upsampling);
632
633 let range = self
634 .chunks
635 .get(&WebPRiffChunk::ALPH)
636 .ok_or(DecodingError::ChunkMissing)?
637 .clone();
638 let alpha_chunk = read_alpha_chunk(
639 &mut range_reader(&mut self.r, range)?,
640 self.width as u16,
641 self.height as u16,
642 )?;
643
644 for y in 0..frame.height {
645 for x in 0..frame.width {
646 let predictor: u8 = get_alpha_predictor(
647 x.into(),
648 y.into(),
649 frame.width.into(),
650 alpha_chunk.filtering_method,
651 buf,
652 );
653
654 let alpha_index =
655 usize::from(y) * usize::from(frame.width) + usize::from(x);
656 let buffer_index = alpha_index * 4 + 3;
657
658 buf[buffer_index] = predictor.wrapping_add(alpha_chunk.data[alpha_index]);
659 }
660 }
661 } else {
662 frame.fill_rgb(buf, self.webp_decode_options.lossy_upsampling);
663 }
664 }
665
666 Ok(())
667 }
668
669 pub fn read_frame(&mut self, buf: &mut [u8]) -> Result<u32, DecodingError> {
679 assert!(self.is_animated());
680 assert_eq!(Some(buf.len()), self.output_buffer_size());
681
682 if self.animation.next_frame == self.num_frames {
683 return Err(DecodingError::NoMoreFrames);
684 }
685
686 let ImageKind::Extended(info) = &self.kind else {
687 unreachable!()
688 };
689
690 self.r
691 .seek(io::SeekFrom::Start(self.animation.next_frame_start))?;
692
693 let anmf_size = match read_chunk_header(&mut self.r)? {
694 (WebPRiffChunk::ANMF, size, _) if size >= 32 => size,
695 _ => return Err(DecodingError::ChunkHeaderInvalid(*b"ANMF")),
696 };
697
698 let frame_x = extended::read_3_bytes(&mut self.r)? * 2;
700 let frame_y = extended::read_3_bytes(&mut self.r)? * 2;
701 let frame_width = extended::read_3_bytes(&mut self.r)? + 1;
702 let frame_height = extended::read_3_bytes(&mut self.r)? + 1;
703 if frame_width > 16384 || frame_height > 16384 {
704 return Err(DecodingError::ImageTooLarge);
705 }
706 if frame_x + frame_width > self.width || frame_y + frame_height > self.height {
707 return Err(DecodingError::FrameOutsideImage);
708 }
709 let duration = extended::read_3_bytes(&mut self.r)?;
710 let frame_info = self.r.read_u8()?;
711 let use_alpha_blending = frame_info & 0b00000010 == 0;
712 let dispose = frame_info & 0b00000001 != 0;
713
714 let clear_color = if self.animation.dispose_next_frame {
715 info.background_color
716 } else {
717 None
718 };
719
720 let (chunk, chunk_size, chunk_size_rounded) = read_chunk_header(&mut self.r)?;
722 if chunk_size_rounded + 24 > anmf_size {
723 return Err(DecodingError::ChunkHeaderInvalid(chunk.to_fourcc()));
724 }
725
726 let (frame, frame_has_alpha): (Vec<u8>, bool) = match chunk {
727 WebPRiffChunk::VP8 => {
728 let reader = (&mut self.r).take(chunk_size);
729 let raw_frame = Vp8Decoder::decode_frame(reader)?;
730 if u32::from(raw_frame.width) != frame_width
731 || u32::from(raw_frame.height) != frame_height
732 {
733 return Err(DecodingError::InconsistentImageSizes);
734 }
735 let mut rgb_frame = vec![0; frame_width as usize * frame_height as usize * 3];
736 raw_frame.fill_rgb(&mut rgb_frame, self.webp_decode_options.lossy_upsampling);
737 (rgb_frame, false)
738 }
739 WebPRiffChunk::VP8L => {
740 let reader = (&mut self.r).take(chunk_size);
741 let mut lossless_decoder = LosslessDecoder::new(reader);
742 let mut rgba_frame = vec![0; frame_width as usize * frame_height as usize * 4];
743 lossless_decoder.decode_frame(frame_width, frame_height, false, &mut rgba_frame)?;
744 (rgba_frame, true)
745 }
746 WebPRiffChunk::ALPH => {
747 if chunk_size_rounded + 32 > anmf_size {
748 return Err(DecodingError::ChunkHeaderInvalid(chunk.to_fourcc()));
749 }
750
751 let next_chunk_start = self.r.stream_position()? + chunk_size_rounded;
753 let mut reader = (&mut self.r).take(chunk_size);
754 let alpha_chunk =
755 read_alpha_chunk(&mut reader, frame_width as u16, frame_height as u16)?;
756
757 self.r.seek(io::SeekFrom::Start(next_chunk_start))?;
759 let (next_chunk, next_chunk_size, _) = read_chunk_header(&mut self.r)?;
760 if chunk_size + next_chunk_size + 32 > anmf_size {
761 return Err(DecodingError::ChunkHeaderInvalid(next_chunk.to_fourcc()));
762 }
763
764 let frame = Vp8Decoder::decode_frame((&mut self.r).take(next_chunk_size))?;
765
766 let mut rgba_frame = vec![0; frame_width as usize * frame_height as usize * 4];
767 frame.fill_rgba(&mut rgba_frame, self.webp_decode_options.lossy_upsampling);
768
769 for y in 0..frame.height {
770 for x in 0..frame.width {
771 let predictor: u8 = get_alpha_predictor(
772 x.into(),
773 y.into(),
774 frame.width.into(),
775 alpha_chunk.filtering_method,
776 &rgba_frame,
777 );
778
779 let alpha_index =
780 usize::from(y) * usize::from(frame.width) + usize::from(x);
781 let buffer_index = alpha_index * 4 + 3;
782
783 rgba_frame[buffer_index] =
784 predictor.wrapping_add(alpha_chunk.data[alpha_index]);
785 }
786 }
787
788 (rgba_frame, true)
789 }
790 _ => return Err(DecodingError::ChunkHeaderInvalid(chunk.to_fourcc())),
791 };
792
793 if self.animation.canvas.is_none() {
795 self.animation.canvas = {
796 let mut canvas = vec![0; (self.width * self.height * 4) as usize];
797 if let Some(color) = info.background_color.as_ref() {
798 canvas
799 .chunks_exact_mut(4)
800 .for_each(|c| c.copy_from_slice(color))
801 }
802 Some(canvas)
803 }
804 }
805 extended::composite_frame(
806 self.animation.canvas.as_mut().unwrap(),
807 self.width,
808 self.height,
809 clear_color,
810 &frame,
811 frame_x,
812 frame_y,
813 frame_width,
814 frame_height,
815 frame_has_alpha,
816 use_alpha_blending,
817 self.animation.previous_frame_width,
818 self.animation.previous_frame_height,
819 self.animation.previous_frame_x_offset,
820 self.animation.previous_frame_y_offset,
821 );
822
823 self.animation.previous_frame_width = frame_width;
824 self.animation.previous_frame_height = frame_height;
825 self.animation.previous_frame_x_offset = frame_x;
826 self.animation.previous_frame_y_offset = frame_y;
827
828 self.animation.dispose_next_frame = dispose;
829 self.animation.next_frame_start += anmf_size + 8;
830 self.animation.next_frame += 1;
831
832 if self.has_alpha() {
833 buf.copy_from_slice(self.animation.canvas.as_ref().unwrap());
834 } else {
835 for (b, c) in buf
836 .chunks_exact_mut(3)
837 .zip(self.animation.canvas.as_ref().unwrap().chunks_exact(4))
838 {
839 b.copy_from_slice(&c[..3]);
840 }
841 }
842
843 Ok(duration)
844 }
845
846 pub fn reset_animation(&mut self) {
852 assert!(self.is_animated());
853
854 self.animation.next_frame = 0;
855 self.animation.next_frame_start = self.chunks.get(&WebPRiffChunk::ANMF).unwrap().start - 8;
856 self.animation.dispose_next_frame = true;
857 }
858
859 pub fn set_lossy_upsampling(&mut self, upsampling_method: UpsamplingMethod) {
861 self.webp_decode_options.lossy_upsampling = upsampling_method;
862 }
863}
864
865pub(crate) fn range_reader<R: BufRead + Seek>(
866 mut r: R,
867 range: Range<u64>,
868) -> Result<impl BufRead, DecodingError> {
869 r.seek(io::SeekFrom::Start(range.start))?;
870 Ok(r.take(range.end - range.start))
871}
872
873pub(crate) fn read_fourcc<R: BufRead>(mut r: R) -> Result<WebPRiffChunk, DecodingError> {
874 let mut chunk_fourcc = [0; 4];
875 r.read_exact(&mut chunk_fourcc)?;
876 Ok(WebPRiffChunk::from_fourcc(chunk_fourcc))
877}
878
879pub(crate) fn read_chunk_header<R: BufRead>(
880 mut r: R,
881) -> Result<(WebPRiffChunk, u64, u64), DecodingError> {
882 let chunk = read_fourcc(&mut r)?;
883 let chunk_size = r.read_u32::<LittleEndian>()?;
884 let chunk_size_rounded = chunk_size.saturating_add(chunk_size & 1);
885 Ok((chunk, chunk_size.into(), chunk_size_rounded.into()))
886}
887
888