gamut-webp
gamut-webp is a pure-Rust WebP encoder and decoder — a VP8/VP8L still-image bitstream wrapped
in a RIFF container.
Goals
Part of the gamut workspace, this crate provides WebP encoding (and, unusually for the encoder-first workspace, decoding) that is:
- Memory-safe on hostile input.
#![forbid(unsafe_code)], deleting the memory-corruption bug class behind libwebp's CVE record (e.g. the zero-click, wormable CVE-2023-4863). Because every WebP decoder in the Rust ecosystem ultimately wraps libwebp, a clean-slate safe decoder is worth carrying here. - Clean-slate from the spec. Implemented directly from the VP8 / VP8L bitstream specs (see
../../references/) rather than wrapping libwebp. - Layered on shared crates. The container comes from
gamut-riff; the bit-level primitives fromgamut-bitstream; color handling fromgamut-color. - Buildable anywhere
cargois. No C, no nasm — cross-compiles cleanly (wasm32, aarch64, musl). (The differential test suite is the one exception: it builds libwebp vialibwebp-sysas a dev-dependency, so runningcargo testneeds a C toolchain. The shipped library does not.)
WebP is one of gamut's three initial focus formats (alongside AVIF and JPEG).
Usage
The public API follows the same shape as gamut-avif: a WebpEncoder implementing
[gamut_core::EncodeImage] and a WebpDecoder implementing [gamut_core::DecodeImage], both
reachable through the umbrella crate's webp feature. Both codecs are fully implemented, taking
a typed ImageRef and returning a typed ImageBuf, for RGB and RGBA:
- VP8L lossless —
WebpEncoder::losslessemits a conformant bit-exact stream;WebpDecoderdecodes any conformant VP8L stream. - VP8 lossy —
WebpEncoder::lossy(quality)runs the full intra key-frame codec (DC/V/H/TM and per-4×4 B_PRED prediction, the simple and normal loop filters, segmentation, token partitions, and skip);WebpDecoderdecodes any conformant key frame. - Alpha —
EncodeImage<Rgba8>/DecodeImage<Rgba8>. A transparent lossy image uses the extended (VP8X) format with anALPHchunk (raw or lossless); an opaque one stays a simple file.
Status
The intra-frame still-image surface and its milestones (M0 VP8L lossless → M1 VP8L full → M2 VP8
lossy → M3 extended container + alpha) are tracked component-by-component in STATUS.md.
Every component is validated against libwebp as an oracle via libwebp-sys, bit-exact in both
directions (gamut↔libwebp, at the YUV-plane level for lossy), backed by internal forward/inverse
round-trips, the in-crate decoder, and a malformed-input robustness corpus.
Non-core feature paths are decided in STATUS.md:
alpha/transparency (VP8X + ALPH) and color/metadata chunks (ICCP ICC profiles, EXIF, XMP )
are in scope — embedded on encode and preserved on decode. Animation (ANIM/ANMF) is out of
scope under the image-first charter (each frame is an independent keyframe, but multi-frame
sequences don't fit the single-image API); its chunks are tracked only for container completeness.
License
Licensed under either of MIT or Apache-2.0 at your option.