Skip to main content

decode_animation_webp

Function decode_animation_webp 

Source
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
Hide additional 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}