Skip to content

v1.3.0: WebP encoder

Choose a tag to compare

@sdcb sdcb released this 03 Nov 00:00
· 171 commits to main since this release

Sdcb.SimdCodecs 1.3.0 adds encoding to Sdcb.SimdCodecs.Webp: lossless, lossy (with transparency) and animation. Before this release the package could only decode. The encoders are pure managed, like the decoders.

dotnet add package Sdcb.SimdCodecs.Webp

New: WebP encoding

  • Lossless (VP8L): WebpEncoder.EncodeLossless handles alpha and takes an effort from 0 to 9. It uses a cost-based parse, picks its own transforms and predicts in finer blocks.
  • Lossy (VP8): WebpEncoder.EncodeLossy takes a quality from 0 to 100 (on about libwebp's size scale) and an effort from 0 to 6.
    • Transparency goes into an ALPH plane. It is lossless by default; AlphaQuality below 100 keeps fewer alpha levels.
  • Animation: WebpAnimationEncoder and WebpEncoder.EncodeAnimation write lossy frames by default. They can also write lossless frames, or whichever of the two is smaller for each frame.
    • Given whole canvases, they write only the rectangle that changed. Given ANMF frames, they write them as they are.
  • Metadata: every encoder can embed ICC, Exif and XMP. Any of these makes the output the VP8X extended format.
  • Each encoder has an array overload and a Stream overload.

WebP decoder fixes

  • VP8: the above-right pixels of the right-most macroblocks now come from the reconstructed row, padding included. Before this fix, some lossy files with a width that is not a multiple of 16 decoded differently from libwebp.
  • Animation: frame pixels with alpha from 1 to 254 now blend over the canvas with the same rounding as libwebp 1.6.0's anim_dump. Animations with partly transparent pixels now decode byte-exactly.

Performance

Measured against libwebp on a Ryzen 7 5800X, single thread. A negative BD-rate means fewer bytes than libwebp at the same quality.

Mode Size Speed
Lossless, effort 6 vs libwebp default (14 images) 1.4% smaller 1.14× (2.92 vs 2.56 MP/s)
Lossless, effort 9 1.7% smaller 0.37×
Lossy, effort 4 vs -m 4 (12 opaque images) BD-rate PSNR-Y −6.2%, SSIM-Y −4.8% 1.07× at q75 (18.6 vs 17.4 MP/s)
Lossy + alpha (3 images) BD-rate −4.5% on visible-pixel PSNR-RGB; alpha byte-identical 1.04× at q75 (0.96× the time)
Animation, lossless vs img2webp -lossless (5 sequences) 0.931× the bytes
Animation, lossy vs img2webp -lossy BD-rate −10.1%

Every lossy image beats libwebp on both PSNR and SSIM. The full results are in benchmarks/Sdcb.SimdCodecs.Webp.Benchmarks/results/.

Verification

  • libwebp's dwebp and webpinfo decode and check everything the encoders write.
  • Every SIMD encoder kernel has a scalar twin, and tests prove they give identical output.
  • NativeAOT output matches JIT.

Packaging

  • nuget.org now gets its own copy of the root README. The copy has absolute links and no raw HTML, so the package page renders correctly.

Known gaps

  • The lossy encoder has no trellis quantisation yet.
  • cwebp -z 9 is still 1.1% smaller than our effort 9, although it is about 7× slower.
  • Lossless animation output is 3% larger than img2webp's on the sprite sequence and 9% larger on alpha-sprite.
  • Lossy + alpha: the sticker image is +0.8% BD-rate (its ALPH plane is about 3% larger than libwebp's) and about 10% slower.

Details are in docs/known-gaps.md.