gowebp

package module
v0.1.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 14, 2026 License: MIT Imports: 13 Imported by: 0

README

Tests Coverage Status Go Reference

gowebp

A pure-Go WebP codec. No libwebp, no cgo, no runtime dependencies beyond golang.org/x/image (which is used for decoding only).

Encodes to either WebP format:

  • VP8L (lossless) — default. Every pixel preserved exactly.
  • VP8 (lossy) — opt-in via Options.Lossy. Full intra-only VP8 keyframe encoder with I16 + B_PRED + UV8 intra prediction, DCT + Walsh-Hadamard transforms, boolean arithmetic coding, loop filter, and reconstruction-based RD mode selection.

Also encodes WebP animations (ANIM/ANMF) with either codec, and preserves alpha channels via ALPH chunks (raw or VP8L-compressed, static or in-animation).

Installation

go get github.com/KarpelesLab/gowebp

Usage

Lossless
import "github.com/KarpelesLab/gowebp"

f, _ := os.Create(name)
defer f.Close()
if err := gowebp.Encode(f, img, nil); err != nil {
    log.Fatal(err)
}
Lossy
err := gowebp.Encode(f, img, &gowebp.Options{
    Lossy:   true,
    Quality: 75, // 0 (smallest) to 100 (best), default 75
    Method:  4,  // 0 (fastest) to 6 (highest quality), default 0
})
Animation
ani := gowebp.Animation{
    Images:          []image.Image{frame1, frame2, frame3},
    Durations:       []uint{100, 100, 100}, // ms per frame
    Disposals:       []uint{0, 0, 0},       // 0=keep, 1=clear
    LoopCount:       0,                     // 0 = infinite
    BackgroundColor: 0xffffffff,            // BGRA
}
// Pass Options{Lossy: true} for VP8 frames; nil for VP8L.
gowebp.EncodeAll(f, &ani, nil)
Decoding
img, err := gowebp.Decode(r)

Wraps golang.org/x/image/webp. Also provides DecodeIgnoreAlphaFlag for VP8X files where a misset alpha flag trips up the standard decoder.

Lossy method tiers

The Method field picks between different speed/quality/size tradeoffs. Higher values spend more CPU per macroblock to produce smaller or higher-quality output.

Method Strategy Notes
0 I16 with DC_PRED only fastest baseline
1 I16 with 4-mode SSE search +1–3 dB over M0
2 B_PRED with 10 I4 modes per sub-block best on textured content
3 Per-MB I16/B_PRED arbitration (prediction SSE) fast, good default
4 Per-MB arbitration with reconstruction-aware RDO recommended
5 Dual-path (measure both I16 and B_PRED) principled reference
6 Adds trailing-coefficient trellis trim helps on high-freq content

Encoding is goroutine-safe: each Encode/EncodeAll call is self-contained and has no shared mutable state.

Benchmarks

Measured on Intel i9-14900K (Linux, Go 1.25) against the 5 natural photos linked from Google's WebP Gallery. Baseline is Go's image/png with png.BestCompression.

Reproduce:

NATIVEWEBP_FETCH=1 go test -run TestGalleryPSNR -v    # populate testdata/
go test -bench=. -benchtime=5x
File sizes (bytes per frame)
Image PNG Lossless Lossy Q=50 M=4 Lossy Q=75 M=3 Lossy Q=75 M=4 Lossy Q=90 M=4
1.png (400×301) 120 188 97 720 16 766 17 648 24 906 43 518
2.png (386×395) 45 659 37 018 15 050 13 808 21 888 33 632
3.png (800×600) 236 018 194 714 80 992 80 552 95 330 130 828
4.png (421×163) 52 681 41 554 27 304 24 838 35 736 47 510
5.png (300×300) 138 919 123 932 74 202 78 300 97 588 133 548
  • Lossless (VP8L) averages 13–23 % smaller than PNG's best compression.
  • Lossy Q=75 M=3 is 1.8×–3.3× smaller than lossless on the same images.
  • Lossy Q=50 M=4 can reach 7× smaller than PNG while still producing visually acceptable output on photos.
Encode time (ns/op)
Image PNG Lossless Lossy Q=75 M=3 Lossy Q=75 M=4
1.png (400×301) 38 034 25 745 30 476 30 629
2.png (386×395) 82 888 29 999 34 298 49 051
3.png (800×600) 152 804 100 415 110 645 117 259
4.png (421×163) 23 707 24 294 17 631 19 426
5.png (300×300) 54 435 20 294 27 094 27 931

All timings in microseconds for brevity: lossless WebP is ≈ 1.5–2.8× faster than PNG across the gallery, while producing smaller files. Lossy encoding is typically within a small constant factor of lossless speed.

Quality (spec-correct Y-PSNR, lossy Q=75 M=3)

Y-PSNR measured against VP8 limited-range BT.601 (what a real decoder shows). Higher is better; >40 dB is visually lossless on photographic content.

Image Y-PSNR
1.png 39.2 dB
2.png 42.2 dB
3.png 43.5 dB
4.png 38.7 dB
5.png 36.7 dB

Threshold floors on these numbers are asserted by TestGalleryPSNR so future encoder changes can't quietly regress quality.

BT.601 color handling

VP8 stores pixels in limited-range BT.601 YCbCr (luma 16–235, chroma 16–240) per RFC 6386. Go's stdlib image/color.YCbCrToRGB uses the JFIF full-range inverse, which shifts the resulting RGB by 2–5 units per channel when applied to VP8 samples.

Decode handles this automatically: for VP8 sources the returned image is a *gowebp.BT601YCbCr (or *gowebp.BT601NYCbCrA when the source has an ALPH chunk) whose At() method uses the spec-correct limited-range BT.601 inverse. Calling img.At(x, y).RGBA() gives the same RGB values a libwebp or browser decoder would produce.

Users who want the raw *image.YCbCr for zero-copy plane access can type-assert to *gowebp.BT601YCbCr and read the embedded field:

img, _ := gowebp.Decode(r)
if y, ok := img.(*gowebp.BT601YCbCr); ok {
    rawYCbCr := y.YCbCr     // *image.YCbCr with limited-range samples
    _ = rawYCbCr
}

VP8L (lossless) sources decode to *image.NRGBA with exact pixel fidelity and don't go through YCbCr, so no wrapping is applied.

Implementation notes

The lossy VP8 encoder lives in internal/vp8enc/ and is a pure-Go implementation of RFC 6386 (The VP8 Data Format and Decoding Guide). The following open-source implementations were consulted as references for bit-exact compatibility; no code was copied, but table values (which are specification constants) were transcribed:

  • golang.org/x/image/vp8 — pure-Go VP8 decoder, used as the round-trip test oracle (BSD-3-Clause).
  • libwebp — the reference C encoder/decoder from Google (BSD-3-Clause).
  • libvpx — VP8/VP9 reference codec (BSD-3-Clause).

Every file in internal/vp8enc/ references the relevant RFC 6386 section and the matching path in the x/image/vp8 decoder, which is the oracle used by the test suite's round-trip integration tests.

Remaining work (tracked in the source, not here): full rate-distortion optimization with calibrated λ; proper Viterbi trellis quantization with context-aware token-tree rate estimation.

License

MIT, same as the upstream project this was forked from (HugoSmits86/nativewebp).

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	BT601YCbCrColorModel   = color.ModelFunc(bt601YCbCrModel)
	BT601NYCbCrAColorModel = color.ModelFunc(bt601NYCbCrAModel)
)

BT601YCbCrColorModel and BT601NYCbCrAColorModel convert an arbitrary color into the limited-range BT.601 representation.

Functions

func Decode

func Decode(r io.Reader) (image.Image, error)

Decode reads a WebP image from the provided io.Reader and returns it as an image.Image.

For VP8L (lossless) sources the returned image is an *image.NRGBA (or compatible) from the underlying x/image/webp decoder. For VP8 (lossy) sources it is wrapped as a *BT601YCbCr (or *BT601NYCbCrA when the source has an ALPH chunk) so that img.At(x, y).RGBA() produces the colors a spec-compliant VP8 decoder would show; Go's stdlib color.YCbCrToRGB uses JFIF full-range math which shifts VP8 pixels by several units per channel.

Callers that specifically need the raw *image.YCbCr / *image.NYCbCrA (e.g. for zero-copy plane access) can type-assert to *BT601YCbCr or *BT601NYCbCrA and read the embedded field.

func DecodeConfig

func DecodeConfig(r io.Reader) (image.Config, error)

DecodeConfig reads the image configuration from the provided io.Reader without fully decoding the image.

This function is a wrapper around the underlying WebP decode package (golang.org/x/image/webp) and provides access to the image's metadata, such as its dimensions and color model. It is useful for obtaining image information before performing a full decode.

Parameters:

r - The source io.Reader containing the WebP encoded image.

Returns:

An image.Config containing the image's dimensions and color model, or an error if the configuration cannot be retrieved

func DecodeIgnoreAlphaFlag

func DecodeIgnoreAlphaFlag(r io.Reader) (image.Image, error)

DecodeIgnoreAlphaFlag reads a WebP image from the provided io.Reader and returns it as an image.Image.

This function fixes x/image/webp rejecting VP8L images with the VP8X alpha flag, expecting an ALPHA chunk. VP8L handles transparency internally, and the WebP spec requires the flag for transparency.

This function is a wrapper around the underlying WebP decode package (golang.org/x/image/webp). It supports both lossy and lossless WebP formats, decoding the image accordingly.

Parameters:

r - The source io.Reader containing the WebP encoded image.

Returns:

The decoded image as image.Image or an error if the decoding fails.

func Encode

func Encode(w io.Writer, img image.Image, o *Options) error

Encode writes the provided image.Image to the specified io.Writer in WebP format.

This function always encodes the image using VP8L (lossless WebP). If `UseExtendedFormat` is enabled, it wraps the VP8L frame inside a VP8X container, allowing the use of metadata such as EXIF, ICC color profiles, or XMP metadata.

Note: VP8L already supports transparency, so VP8X is **not required** for alpha support.

Parameters:

w   - The destination writer where the encoded WebP image will be written.
img - The input image to be encoded.
o   - Pointer to Options containing encoding settings:
      - UseExtendedFormat: If true, wraps the image in a VP8X container to enable
        extended WebP features like metadata.

Returns:

An error if encoding fails or writing to the io.Writer encounters an issue.

func EncodeAll

func EncodeAll(w io.Writer, ani *Animation, o *Options) error

EncodeAll writes the provided animation sequence to the specified io.Writer in WebP format.

This function encodes a list of frames as a WebP animation using the VP8X container, which supports features like looping, frame timing, disposal methods, and background color settings. Each frame is individually compressed using the VP8L (lossless) format.

Note: Even if `UseExtendedFormat` is not explicitly set, animations always use the VP8X container because it is required for WebP animation support.

Parameters:

w   - The destination writer where the encoded WebP animation will be written.
ani - Pointer to Animation containing the frames and animation settings:
      - Images: List of frames to encode.
      - Durations: Display times for each frame in milliseconds.
      - Disposals: Disposal methods after frame display (keep or clear).
      - LoopCount: Number of times the animation should loop (0 = infinite).
      - BackgroundColor: Background color for the canvas, used when clearing.
o   - Pointer to Options containing additional encoding settings:
      - UseExtendedFormat: Currently unused for animations, but accepted for consistency.

Returns:

An error if encoding fails or writing to the io.Writer encounters an issue.

Types

type Animation

type Animation struct {
	Images          []image.Image
	Durations       []uint
	Disposals       []uint
	LoopCount       uint16
	BackgroundColor uint32
}

Animation holds configuration settings for WebP animations.

It allows encoding a sequence of frames with individual timing and disposal options, supporting features like looping and background color settings.

Fields:

  • Images: A list of frames to be displayed in sequence.
  • Durations: Timing for each frame in milliseconds, matching the Images slice.
  • Disposals: Disposal methods for frames after display; 0 = keep, 1 = clear to background.
  • LoopCount: Number of times the animation should repeat; 0 means infinite looping.
  • BackgroundColor: Canvas background color in BGRA order, used for clear operations.

type BT601NYCbCrA added in v0.1.1

type BT601NYCbCrA struct {
	*image.NYCbCrA
}

BT601NYCbCrA wraps an *image.NYCbCrA for sources with an ALPH chunk.

func (*BT601NYCbCrA) At added in v0.1.1

func (b *BT601NYCbCrA) At(x, y int) color.Color

func (*BT601NYCbCrA) BT601NYCbCrAAt added in v0.1.1

func (b *BT601NYCbCrA) BT601NYCbCrAAt(x, y int) BT601NYCbCrAColor

func (*BT601NYCbCrA) ColorModel added in v0.1.1

func (b *BT601NYCbCrA) ColorModel() color.Model

type BT601NYCbCrAColor added in v0.1.1

type BT601NYCbCrAColor struct {
	BT601YCbCrColor
	A uint8
}

BT601NYCbCrAColor is BT601YCbCrColor with a non-premultiplied alpha channel. Used by the WebP decoder output when the source has an ALPH chunk.

func (BT601NYCbCrAColor) RGBA added in v0.1.1

func (c BT601NYCbCrAColor) RGBA() (r, g, b, a uint32)

RGBA returns premultiplied 16-bit components.

type BT601YCbCr added in v0.1.1

type BT601YCbCr struct {
	*image.YCbCr
}

BT601YCbCr wraps an *image.YCbCr whose samples are in VP8's limited-range BT.601. It forwards everything to the embedded image except At(), which returns BT601YCbCrColor for spec-correct RGB conversion.

Callers who specifically want the raw stdlib YCbCr (e.g. for fast plane-level access) can dereference the embedded *image.YCbCr field directly.

func (*BT601YCbCr) At added in v0.1.1

func (b *BT601YCbCr) At(x, y int) color.Color

func (*BT601YCbCr) BT601YCbCrAt added in v0.1.1

func (b *BT601YCbCr) BT601YCbCrAt(x, y int) BT601YCbCrColor

BT601YCbCrAt is a typed helper that avoids the color.Color interface allocation when callers know they want BT601YCbCrColor.

func (*BT601YCbCr) ColorModel added in v0.1.1

func (b *BT601YCbCr) ColorModel() color.Model

type BT601YCbCrColor added in v0.1.1

type BT601YCbCrColor struct {
	Y, Cb, Cr uint8
}

BT601YCbCrColor is a single pixel in VP8 limited-range BT.601 color space (luma 16–235, chroma 16–240), the color space WebP files actually store per RFC 6386.

It exists because Go's stdlib `image/color.YCbCrToRGB` applies the JFIF (full-range) inverse even to values that were produced under limited-range encoding, which shifts the resulting RGB by 2–5 units per channel. BT601YCbCrColor.RGBA() uses the spec-correct inverse:

R = clip((298·(Y-16) + 409·(Cr-128) + 128) >> 8)
G = clip((298·(Y-16) -  100·(Cb-128) - 208·(Cr-128) + 128) >> 8)
B = clip((298·(Y-16) + 516·(Cb-128) + 128) >> 8)

Decode returns images whose pixels are BT601YCbCrColor (wrapped in BT601YCbCr or BT601NYCbCrA) for VP8 sources, so calling img.At(x, y).RGBA() produces the colors a real VP8 decoder (libwebp, browsers) would show.

func (BT601YCbCrColor) RGBA added in v0.1.1

func (c BT601YCbCrColor) RGBA() (r, g, b, a uint32)

RGBA implements color.Color using the VP8 spec's limited-range BT.601 inverse. Returns premultiplied 16-bit components (α is always 0xffff for this type).

type Options

type Options struct {
	UseExtendedFormat bool
	Lossy             bool
	Quality           float32
	Method            int
}

Options holds configuration settings for WebP encoding.

Fields:

  • UseExtendedFormat: If true, wraps the VP8L frame inside a VP8X container to enable metadata support. This does not affect image compression or encoding itself, as VP8L remains the encoding format.
  • Lossy: If true, encode as VP8 (lossy). Otherwise (default) encode as VP8L (lossless), preserving existing behavior byte-for-byte.
  • Quality: Lossy quality in [0, 100]. Higher values preserve more detail. Default (when zero) is 75. Ignored when Lossy is false.
  • Method: Lossy speed/quality tradeoff in [0, 6]. Higher values spend more time searching for better compression. Default 4. Ignored when Lossy is false.

Directories

Path Synopsis
internal
vp8enc
Package vp8enc implements a VP8 (lossy) keyframe encoder for WebP.
Package vp8enc implements a VP8 (lossy) keyframe encoder for WebP.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL