pub fn decode_animation_webp(
data: &[u8],
) -> Result<DecodedAnimation, DecoderError>Expand description
Decodes an animated WebP container to a sequence of composited RGBA frames.
Examples found in repository?
examples/webp2bmp.rs (line 144)
108fn main() -> Result<(), Error> {
109 let mut args = std::env::args_os().skip(1);
110 let input = args
111 .next()
112 .map(PathBuf::from)
113 .unwrap_or_else(|| PathBuf::from("_testdata/sample.webp"));
114 let output = args.next().map(PathBuf::from).unwrap_or_else(|| {
115 if input == Path::new("_testdata/sample.webp") {
116 PathBuf::from("target/sample.bmp")
117 } else if input == Path::new("_testdata/sample_animation.webp") {
118 PathBuf::from("target/sample_animation")
119 } else {
120 default_output_path(&input)
121 }
122 });
123
124 let data = fs::read(&input)?;
125 let features = get_features(&data)?;
126
127 if features.has_animation {
128 let mut prefix = if output.is_dir() {
129 output.join(
130 input
131 .file_stem()
132 .and_then(|stem| stem.to_str())
133 .unwrap_or("frame"),
134 )
135 } else {
136 output
137 };
138 if prefix.extension().is_some() {
139 prefix.set_extension("");
140 } else if prefix.as_os_str().is_empty() {
141 prefix = default_animation_output_prefix(&input);
142 }
143
144 let animation = decode_animation_webp(&data)?;
145 let mut written_paths = Vec::with_capacity(animation.frames.len());
146 for (index, frame) in animation.frames.into_iter().enumerate() {
147 let path = animation_frame_path(&prefix, index);
148 if let Some(parent) = path.parent() {
149 if !parent.as_os_str().is_empty() {
150 fs::create_dir_all(parent)?;
151 }
152 }
153 let bmp = encode_bmp24_from_rgba(animation.width, animation.height, &frame.rgba)?;
154 fs::write(&path, bmp)?;
155 written_paths.push(path);
156 }
157 for path in written_paths {
158 println!("{}", path.display());
159 }
160 return Ok(());
161 }
162
163 let image = decode(&data)?;
164 let bmp = encode_bmp24_from_rgba(image.width, image.height, &image.rgba)?;
165
166 if let Some(parent) = output.parent() {
167 if !parent.as_os_str().is_empty() {
168 fs::create_dir_all(parent)?;
169 }
170 }
171 fs::write(&output, bmp)?;
172
173 println!("{}", output.display());
174 Ok(())
175}More examples
examples/webp_decode_bench.rs (line 178)
152fn main() -> IoResult<()> {
153 let options = parse_options()?;
154 let lossy = include_bytes!("../samples/sample.webp");
155 let lossless = include_bytes!("../samples/sample_lossless.webp");
156 let animation = include_bytes!("../samples/sample_animation.webp");
157
158 let lossy_rgba = decode_lossy_webp_to_rgba(lossy)
159 .map_err(|error| IoError::new(ErrorKind::InvalidData, error))?;
160 let lossy_rgba_hash = hash_bytes(FNV_OFFSET, &lossy_rgba.rgba);
161 let lossy_rgba_width = lossy_rgba.width;
162 let lossy_rgba_height = lossy_rgba.height;
163
164 let lossy_yuv = decode_lossy_webp_to_yuv(lossy)
165 .map_err(|error| IoError::new(ErrorKind::InvalidData, error))?;
166 let mut lossy_yuv_hash = hash_bytes(FNV_OFFSET, &lossy_yuv.y);
167 lossy_yuv_hash = hash_bytes(lossy_yuv_hash, &lossy_yuv.u);
168 lossy_yuv_hash = hash_bytes(lossy_yuv_hash, &lossy_yuv.v);
169 lossy_yuv_hash = hash_usize(lossy_yuv_hash, lossy_yuv.y_stride);
170 lossy_yuv_hash = hash_usize(lossy_yuv_hash, lossy_yuv.uv_stride);
171
172 let lossless_rgba = decode_lossless_webp_to_rgba(lossless)
173 .map_err(|error| IoError::new(ErrorKind::InvalidData, error))?;
174 let lossless_rgba_hash = hash_bytes(FNV_OFFSET, &lossless_rgba.rgba);
175 let lossless_width = lossless_rgba.width;
176 let lossless_height = lossless_rgba.height;
177
178 let decoded_animation = decode_animation_webp(animation)
179 .map_err(|error| IoError::new(ErrorKind::InvalidData, error))?;
180 let mut animation_hash = hash_usize(FNV_OFFSET, decoded_animation.loop_count as usize);
181 for frame in &decoded_animation.frames {
182 animation_hash = hash_usize(animation_hash, frame.duration);
183 animation_hash = hash_bytes(animation_hash, &frame.rgba);
184 }
185 let animation_width = decoded_animation.width;
186 let animation_height = decoded_animation.height;
187 let animation_pixels = animation_width * animation_height * decoded_animation.frames.len();
188
189 let measurements = [
190 measure(
191 &options,
192 CaseMetadata {
193 case_name: "lossy_rgba",
194 format: "VP8",
195 width: lossy_rgba_width,
196 height: lossy_rgba_height,
197 pixels_per_decode: lossy_rgba_width * lossy_rgba_height,
198 output_hash: lossy_rgba_hash,
199 },
200 || {
201 let image = decode_lossy_webp_to_rgba(black_box(lossy))
202 .map_err(|error| IoError::new(ErrorKind::InvalidData, error))?;
203 Ok(image.rgba.len())
204 },
205 )?,
206 measure(
207 &options,
208 CaseMetadata {
209 case_name: "lossy_yuv",
210 format: "VP8",
211 width: lossy_yuv.width,
212 height: lossy_yuv.height,
213 pixels_per_decode: lossy_yuv.width * lossy_yuv.height,
214 output_hash: lossy_yuv_hash,
215 },
216 || {
217 let image = decode_lossy_webp_to_yuv(black_box(lossy))
218 .map_err(|error| IoError::new(ErrorKind::InvalidData, error))?;
219 Ok(image.y.len() + image.u.len() + image.v.len())
220 },
221 )?,
222 measure(
223 &options,
224 CaseMetadata {
225 case_name: "lossless_rgba",
226 format: "VP8L",
227 width: lossless_width,
228 height: lossless_height,
229 pixels_per_decode: lossless_width * lossless_height,
230 output_hash: lossless_rgba_hash,
231 },
232 || {
233 let image = decode_lossless_webp_to_rgba(black_box(lossless))
234 .map_err(|error| IoError::new(ErrorKind::InvalidData, error))?;
235 Ok(image.rgba.len())
236 },
237 )?,
238 measure(
239 &options,
240 CaseMetadata {
241 case_name: "animation",
242 format: "ANIM",
243 width: animation_width,
244 height: animation_height,
245 pixels_per_decode: animation_pixels,
246 output_hash: animation_hash,
247 },
248 || {
249 let image = decode_animation_webp(black_box(animation))
250 .map_err(|error| IoError::new(ErrorKind::InvalidData, error))?;
251 Ok(image.frames.len())
252 },
253 )?,
254 ];
255
256 let mut csv = String::from(
257 "case,format,width,height,pixels_per_decode,output_hash,median_ns,p95_ns,mpixels_per_second\n",
258 );
259 for measurement in measurements {
260 let mpixels_per_second =
261 measurement.pixels_per_decode as f64 * 1_000.0 / measurement.median_ns;
262 csv.push_str(&format!(
263 "{},{},{},{},{},{:016x},{:.0},{:.0},{:.3}\n",
264 measurement.case_name,
265 measurement.format,
266 measurement.width,
267 measurement.height,
268 measurement.pixels_per_decode,
269 measurement.output_hash,
270 measurement.median_ns,
271 measurement.p95_ns,
272 mpixels_per_second,
273 ));
274 }
275 print!("{csv}");
276 if let Some(path) = options.output {
277 if let Some(parent) = path.parent() {
278 fs::create_dir_all(parent)?;
279 }
280 fs::write(path, csv)?;
281 }
282 Ok(())
283}