1use std::{error::Error, fmt, time::Duration};
2
3use crate::{
4 AnimationDecoder, AnimationEncoder, AnimationEncoderOptions, AnimationInfo,
5 AnimationMuxOverrides, CanvasSize, DecodeError, DecodeLimits, EncodeError,
6 EncoderConfigOverrides, ResizeError, ResizeOptions, ResizePlan,
7};
8
9#[derive(Clone, Debug)]
15pub struct AnimationTranscodeOptions {
16 pub decode_limits: DecodeLimits,
18 pub resize: ResizeOptions,
20 pub encoder_config: EncoderConfigOverrides,
22 pub animation: AnimationMuxOverrides,
24}
25
26impl AnimationTranscodeOptions {
27 pub fn new(resize: ResizeOptions) -> Self {
30 Self {
31 decode_limits: DecodeLimits::default(),
32 resize,
33 encoder_config: EncoderConfigOverrides::default(),
34 animation: AnimationMuxOverrides::default(),
35 }
36 }
37}
38
39#[derive(Clone, Debug, PartialEq)]
41pub struct TranscodedAnimation {
42 pub bytes: Vec<u8>,
44 pub input: AnimationInfo,
46 pub output_canvas: CanvasSize,
48 pub frame_count: u32,
50 pub total_duration: Duration,
52}
53
54pub fn transcode_animated_webp(
63 input: &[u8],
64 options: AnimationTranscodeOptions,
65) -> Result<TranscodedAnimation, TranscodeError> {
66 let mut decoder =
67 AnimationDecoder::new(input, options.decode_limits).map_err(TranscodeError::Decode)?;
68 let source = *decoder.info();
69 let resize = ResizePlan::new(source.canvas, options.resize).map_err(TranscodeError::Resize)?;
70 let mut workspace = resize.workspace().map_err(TranscodeError::Resize)?;
71
72 let mut encoder_options = AnimationEncoderOptions::from_animation_info(source);
73 encoder_options.config = options.encoder_config;
74 encoder_options.animation = options.animation;
75 let mut encoder = AnimationEncoder::new(resize.destination(), encoder_options)
76 .map_err(TranscodeError::Encode)?;
77
78 let mut frame_count = 0_u32;
79 let mut total_duration = Duration::ZERO;
80 while let Some(frame) = decoder.next_frame().map_err(TranscodeError::Decode)? {
81 total_duration = total_duration
82 .checked_add(frame.duration)
83 .ok_or(TranscodeError::DurationOverflow)?;
84 let mut rgba = frame.rgba;
85 workspace
86 .transform_rgba(&mut rgba)
87 .map_err(TranscodeError::Resize)?;
88 encoder
89 .add_rgba(workspace.pixels(), frame.duration)
90 .map_err(TranscodeError::Encode)?;
91 frame_count = frame_count
92 .checked_add(1)
93 .ok_or(TranscodeError::FrameCountOverflow)?;
94 }
95 if frame_count != source.frame_count {
96 return Err(TranscodeError::FrameCountMismatch {
97 decoded: frame_count,
98 declared: source.frame_count,
99 });
100 }
101
102 let bytes = encoder.finish().map_err(TranscodeError::Encode)?;
103 Ok(TranscodedAnimation {
104 bytes,
105 input: source,
106 output_canvas: resize.destination(),
107 frame_count,
108 total_duration,
109 })
110}
111
112#[derive(Clone, Debug, PartialEq)]
114pub enum TranscodeError {
115 Decode(DecodeError),
117 Resize(ResizeError),
119 Encode(EncodeError),
121 DurationOverflow,
123 FrameCountOverflow,
125 FrameCountMismatch {
127 decoded: u32,
129 declared: u32,
131 },
132}
133
134impl fmt::Display for TranscodeError {
135 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
136 match self {
137 Self::Decode(error) => write!(f, "animated WebP decode failed: {error}"),
138 Self::Resize(error) => write!(f, "animated WebP resize failed: {error}"),
139 Self::Encode(error) => write!(f, "animated WebP encode failed: {error}"),
140 Self::DurationOverflow => f.write_str("animated WebP duration overflows Duration"),
141 Self::FrameCountOverflow => f.write_str("animated WebP frame count overflows u32"),
142 Self::FrameCountMismatch { decoded, declared } => write!(
143 f,
144 "decoder produced {decoded} frames; source declared {declared}"
145 ),
146 }
147 }
148}
149
150impl Error for TranscodeError {
151 fn source(&self) -> Option<&(dyn Error + 'static)> {
152 match self {
153 Self::Decode(error) => Some(error),
154 Self::Resize(error) => Some(error),
155 Self::Encode(error) => Some(error),
156 Self::DurationOverflow | Self::FrameCountOverflow | Self::FrameCountMismatch { .. } => {
157 None
158 }
159 }
160 }
161}