Skip to content

WebP support - #49

Merged
matmen merged 2 commits into
masterfrom
webp
Oct 15, 2026
Merged

matmen merged 2 commits into
masterfrom
webp

Conversation

@matmen

@matmen matmen commented Oct 15, 2026 •

Copy link
Copy Markdown
Collaborator

Adds WebP decoding (lossy, lossless, alpha, animated) and lossless WebP encoding, all in plain TypeScript.

Depends on the Animation PR (it uses Animation for animated WebPs) and is based on its branch. Merge that one first, then retarget this PR to master.

Decoding

  • Formats:
    • lossless (VP8L, RFC 9649)
    • lossy (VP8, RFC 6386)
    • alpha (ALPH chunk: raw or VP8L compressed, all filter methods)
    • animations (ANIM/ANMF)
  • Still WebPs: Image.decode(), including the Exif orientation like the other formats.
  • Animated WebPs: Animation.decode() composites the frames the way libwebp's animation decoder does. Image.decode() refuses them with a pointer to Animation.decode(), and decode() returns them as Animation.
  • Detection: ImageType.isWebP(), and ImageType.getType() returns 'webp'.
  • Speed (12 MP photo): lossy decodes in ~0.8 s, lossless in ~0.8 s.

Encoding (lossless)

  • Image.encodeWebP(): files are about 25% smaller than our PNGs at compression level 6.
    • up to 256 colors: palette, with several pixels packed per byte for up to 16 colors
    • otherwise: subtract green and a predictor picked per 16×16 block
    • entropy coding: LZ77 (including the short 2D distance codes), color cache, length-limited Huffman codes
  • Animation.encodeWebP(): writes animated WebPs.
    • WebP has no "previous" disposal and stores frame offsets halved. Animations that rely on either are written as full composited frames, so they always look as intended.
  • Not included: lossy encoding.

Verification

  • libwebp test vectors: all 96 files of the official libwebp-test-data that have reference checksums decode bit-exactly to dwebp's output. That covers lossy, lossless, alpha, segmentation, both loop filters and all transforms.
  • Pillow (libwebp 1.6) on the WebPs from the Pillow, image-rs and libwebp test suites: 97 of 98 files are pixel-identical, animations included. The remaining file (bad_palette_index.webp) matches libwebp's reference checksum, Pillow handles the invalid index differently.
  • Encoder: 172 images (PngSuite, photos, screenshots, logos) were encoded and then decoded by libwebp. All of them are lossless and identical to our own decoder's output.
  • Robustness: 3000 mutated files (bit flips, truncation, random bytes) only raise errors: no crashes, no hangs, slowest 158 ms.
  • New tests: tests/webp.js, with fixtures written by libwebp and checksums of libwebp's decoded pixels.

Credits

The VP8 constant tables are extracted from RFC 6386. The YUV→RGB conversion, the "fancy" chroma upsampling and the blending of animation frames reproduce libwebp's integer arithmetic, so the output matches libwebp exactly. Both are BSD-licensed and credited in THIRD_PARTY_NOTICES.md and the source headers.
The WebP container and the lossless codec follow RFC 9649, whose short distance code table is included under the Revised BSD License.

Package size grows from ~112 kB to ~140 kB (compressed).

Closes #19
Closes #20

@matmen matmen added this to the v3.1.0 milestone Oct 15, 2026
@matmen matmen self-assigned this Oct 15, 2026
@matmen matmen added the enhancement New feature or request label Oct 15, 2026
matmen and others added 2 commits October 15, 2026 08:27
Lossless (VP8L) and lossy (VP8) decoding with alpha and animation,
following RFC 9649 and RFC 6386. The output is bit-exact with libwebp: all
96 test vectors of libwebp-test-data match their reference checksums (the
color conversion and chroma upsampling reproduce libwebp's).

A lossless encoder (palette or subtract green + predictor, LZ77 with the
short 2D distance codes, color cache) writes files about 25% smaller than
our PNGs, and libwebp decodes them identically. Animation gains
encodeWebP() and Animation.decode() reads animated WebPs; Image.decode()
reads still ones, Image.encodeWebP() writes them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ljnxm1Qjrocq1izGajLx8J
The short distance code table is taken from the RFC, whose code components
come under the Revised BSD License: the notice is in the vp8l.ts header and
THIRD_PARTY_NOTICES.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ljnxm1Qjrocq1izGajLx8J
@matmen
matmen merged commit b855aea into master Oct 15, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FR] Webp Support for Deno Decoding animated Webp images to manipulate them [FR]

1 participant