oxideav-webp
Pure-Rust WebP image codec (RIFF + VP8 + VP8L + VP8X + ALPH + ANIM + ANMF). Decoder and encoder both at production status as of 2026-05-27.
- Full decode of every container variant: simple-lossy (VP8),
simple-lossless (VP8L), extended (
VP8X) withALPHalpha plane, ICCP / EXIF / XMP metadata, and animated WebP (ANIM+ANMF). - Encode of complete
.webpfiles in both lossless (VP8L) and lossy (VP8) modes, plus complete animated.webpfiles. - Decoded pixels land in a tightly-packed
Vec<u8>of `width * height- 4
RGBA bytes — drops directly into [image](https://crates.io/crates/image)'sImageBuffer::from_raw` with zero copy.
- 4
- The full crates.io
0.1.2public surface is reachable, both with the defaultregistrybuild and under--no-default-features.tests/api_compat_0_1_2.rsis the 29-test compile-only assertion suite that pins every published symbol in place.
Install
# Standalone — flat RGBA in / flat RGBA out, no framework dep:
[]
= { = "0.1", = false }
# With the OxideAV runtime:
[]
= "0.1"
| Feature | Default | What it does |
|---|---|---|
registry |
✅ on | Pulls oxideav-core plus the framework-trait factories. Cascades into oxideav-vp8/registry so the VP8-lossy encode delegation can reach the sibling crate's factories. With this off, lossless encode/decode + animation + metadata extraction all still work; only the VP8-lossy encode requires registry. |
simd |
off (nightly only) | Opt-in std::simd acceleration of the hottest pixel-repack loop (Vp8lImage::to_rgba). Requires a nightly rustc because it activates #![feature(portable_simd)]. Byte-identical to the scalar path (asserted by vp8l::tests::to_rgba_simd_matches_scalar_byte_for_byte); see BENCHMARKS.md for the round-170 before/after numbers. |
Benchmarks
The crate ships four criterion benches under benches/ (decode +
encode + LZ77 matcher + argb→rgba repack). Numbers, profile
findings, and the optimization log live in BENCHMARKS.md.
Run with:
CARGO_TARGET_DIR=/tmp/oxideav-webp-bench-target \
cargo bench --manifest-path crates/oxideav-webp/Cargo.toml \
--bench <name> -- --quick
Standalone use (no oxideav-core)
Decode any .webp file
use ;
let webp_bytes: & = /* file bytes from disk, HTTP, … */;
let image: WebpImage = decode_webp?;
println!;
for frame in &image.frames
// ICC / EXIF / XMP are on image.metadata.{icc, exif, xmp} (each Option<Vec<u8>>).
Read metadata only (no pixel decode)
use extract_metadata;
let meta = extract_metadata?;
if let Some = meta.icc.as_deref
if let Some = meta.exif.as_deref
if let Some = meta.xmp.as_deref
Encode a lossless .webp from RGBA bytes
The shortest path — flat RGBA in, complete .webp file out:
use encode_webp_lossless;
let rgba: = /* width*height*4 RGBA bytes */;
let webp_bytes: = encode_webp_lossless?;
// Write to disk:
write?;
Encode lossless with metadata (ICC / EXIF / XMP)
use ;
// VP8L works in ARGB, one u32/pixel.
let argb: = /* width*height ARGB pixels */;
let meta = WebpMetadata ;
let webp_bytes = encode_vp8l_argb_with_metadata?;
If has_alpha is true or any metadata field is set, the output
auto-promotes to the extended VP8X layout; otherwise it's the
simple lossless layout.
Bare VP8L bitstream (no RIFF wrap)
For consumers that wrap the bitstream themselves:
use encode_vp8l_argb;
let vp8l: = encode_vp8l_argb?;
Build an animated .webp
use ;
// Each AnimFrame is a tile (width × height RGBA) at (x, y) on the
// canvas, with a duration in milliseconds.
let frames = vec!;
// Defaults: per-frame Auto mode (picks byte-smallest of Lossless / Delta).
let webp = build_animated_webp?;
// Or with options (loop count, background colour, file-level metadata):
let opts = AnimEncoderOptions ;
let webp = build_animated_webp_with_options?;
With the OxideAV runtime (registry feature on)
use RuntimeContext;
use ; // "webp_vp8" / "webp_vp8l"
let mut ctx = new;
register;
// ctx now exposes the "webp" container plus "webp_vp8" + "webp_vp8l" codecs.
This is the only way to reach the VP8-lossy encoder — it delegates
to the oxideav-vp8 sibling crate's framework factory family:
use ;
// Returns Box<dyn oxideav_core::Encoder>; emits RIFF/WEBP-wrapped output.
let enc = make_encoder_with_quality?;
let enc = make_encoder_with_qindex?;
(Lossless encode + decode + animation + metadata extraction all work
without registry; only the VP8 lossy encode path needs it.)
Clean-room sources
Implementation is derived entirely from the public format specs:
- RFC 9649 — WebP Image Format
(
docs/image/webp/rfc9649-webp.txt, alsorfc9649-webp.pdf). - WebP Lossless Bitstream Specification — the LZ77 + prefix-coded literals + color cache + spatial / color / color-indexing transforms (also reproduced in RFC 9649 §3).
- RFC 6386 — VP8 Data Format and Decoding Guide
(
docs/video/vp8/rfc6386-vp8-bitstream.txt) for the VP8 lossy framing routed through theoxideav-vp8sibling.
The 18-fixture corpus at docs/image/webp/fixtures/ is consumed as
opaque byte streams; end-to-end fixture tests validate against the
ARGB pixels of each fixture's committed expected.png. No third-party
codec library source is consulted.
License
MIT. See LICENSE.