webp

package module
v1.1.0 Latest Latest
Warning

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

Go to latest
Published: Feb 15, 2026 License: BSD-3-Clause, MIT Imports: 13 Imported by: 0

README

webp

CI Go Reference Go Report Card

Pure Go encoder and decoder for the WebP image format. Zero dependencies, zero CGo.

go get github.com/deepteams/webp

Requires Go 1.24+

Features

  • Lossy encoding & decoding (VP8)
  • Lossless encoding & decoding (VP8L)
  • Alpha channel support (ALPH chunk with VP8L compression)
  • Animation (ANIM/ANMF) with sub-frame optimization, keyframe control, mixed codec mode
  • Extended format (VP8X) with ICC, EXIF, XMP metadata
  • Sharp YUV conversion for high-quality chroma subsampling
  • Presets for photos, pictures, drawings, icons, text
  • Transparent integration with Go's image package (image.Decode just works)
  • CLI tool (gwebp) for encoding, decoding and inspecting WebP files

Quick Start

Decode
package main

import (
    "image"
    "image/png"
    "os"

    _ "github.com/deepteams/webp" // register WebP format
)

func main() {
    // image.Decode auto-detects WebP thanks to init() registration
    f, _ := os.Open("photo.webp")
    defer f.Close()

    img, _, _ := image.Decode(f)

    out, _ := os.Create("photo.png")
    defer out.Close()
    png.Encode(out, img)
}
Encode (lossy)
package main

import (
    "image"
    _ "image/jpeg"
    "os"

    "github.com/deepteams/webp"
)

func main() {
    f, _ := os.Open("photo.jpg")
    defer f.Close()
    img, _, _ := image.Decode(f)

    out, _ := os.Create("photo.webp")
    defer out.Close()

    webp.Encode(out, img, &webp.EncoderOptions{
        Quality: 80,
        Method:  4, // 0=fast, 6=best compression
    })
}
Encode (lossless)
webp.Encode(out, img, &webp.EncoderOptions{
    Lossless: true,
    Quality:  75, // controls compression effort
})
Animation
package main

import (
    "image"
    "image/color"
    "os"
    "time"

    "github.com/deepteams/webp/animation"
)

func main() {
    out, _ := os.Create("anim.webp")
    defer out.Close()

    enc := animation.NewEncoder(out, 256, 256, &animation.EncodeOptions{
        Quality:   80,
        LoopCount: 0, // infinite loop
    })

    for i := 0; i < 10; i++ {
        img := image.NewNRGBA(image.Rect(0, 0, 256, 256))
        // ... draw frame ...
        enc.AddFrame(img, 100*time.Millisecond)
    }

    enc.Close()
}
Inspect
f, _ := os.Open("image.webp")
defer f.Close()
feat, _ := webp.GetFeatures(f)

fmt.Printf("Size:      %dx%d\n", feat.Width, feat.Height)
fmt.Printf("Format:    %s\n", feat.Format)    // "lossy", "lossless", "extended"
fmt.Printf("Alpha:     %v\n", feat.HasAlpha)
fmt.Printf("Animated:  %v\n", feat.HasAnimation)
fmt.Printf("Frames:    %d\n", feat.FrameCount)

CLI Tool

go install github.com/deepteams/webp/cmd/gwebp@latest
Encode
# JPEG/PNG to WebP (lossy, quality 80)
gwebp enc -q 80 photo.jpg -o photo.webp

# Lossless encoding
gwebp enc -lossless input.png -o output.webp

# Sharp YUV for better chroma edges
gwebp enc -q 90 -sharp_yuv photo.jpg

# Content-specific preset
gwebp enc -preset photo -q 85 landscape.jpg

# GIF to animated WebP
gwebp enc -q 75 animation.gif -o animation.webp
Decode
# WebP to PNG
gwebp dec input.webp -o output.png

# Animated WebP to GIF
gwebp dec animation.webp -o animation.gif
Info
gwebp info photo.webp

Encoder Options

Option Type Default Description
Lossless bool false VP8L lossless encoding
Quality float32 75 Compression quality (0-100)
Method int 4 Effort level (0=fast, 6=slowest/best)
Preset Preset Default Content preset (Picture, Photo, Drawing, Icon, Text)
UseSharpYUV bool false Sharp RGB-to-YUV conversion
Exact bool false Preserve RGB under transparent areas
TargetSize int 0 Target output size in bytes
TargetPSNR float32 0 Target PSNR in dB
SNSStrength int 50 Spatial noise shaping (0-100)
FilterStrength int 60 Loop filter strength (0-100)
FilterSharpness int 0 Loop filter sharpness (0-7)
FilterType int 1 Filter type (0=simple, 1=strong)
Segments int 4 Number of segments (1-4)
Pass int 1 Entropy analysis passes (1-10)
AlphaCompression int 1 Alpha compression (0=none, 1=lossless)
AlphaFiltering int 1 Alpha filter (0=none, 1=fast, 2=best)
AlphaQuality int 100 Alpha quality (0-100)

Performance

Benchmarked on Apple M2 Pro (arm64, 10 cores), 1536x1024 RGB image, Go 1.24.2. Median of 3 runs.

Encode (1536x1024, Quality 75)
Library Mode Time B/op Allocs Output
deepteams/webp (Pure Go) Lossy 79 ms 1.5 MB 124 193 KB
gen2brain/webp (WASM) Lossy 82 ms 18 KB 12 253 KB
chai2010/webp (CGo) Lossy 108 ms 234 KB 4 209 KB
gen2brain/webp (WASM) Lossless 275 ms 514 KB 12 2,054 KB
deepteams/webp (Pure Go) Lossless 400 ms 115 MB 1,393 1,783 KB
nativewebp (Pure Go) Lossless 428 ms 89 MB 2,157 2,012 KB
chai2010/webp (CGo) Lossless 1,320 ms 3.5 MB 5 1,751 KB
Decode (1536x1024)
Library Mode Time B/op Allocs
chai2010/webp (CGo) Lossy 13 ms 7.2 MB 24
golang.org/x/image/webp Lossy 25 ms 2.6 MB 13
deepteams/webp (Pure Go) Lossy 27 ms 6.5 MB 7
gen2brain/webp (WASM) Lossy 32 ms 1.2 MB 41
chai2010/webp (CGo) Lossless 32 ms 14.7 MB 33
nativewebp (Pure Go) Lossless 53 ms 6.4 MB 50
gen2brain/webp (WASM) Lossless 55 ms 10.6 MB 50
golang.org/x/image/webp Lossless 57 ms 7.3 MB 1,416
deepteams/webp (Pure Go) Lossless 58 ms 8.5 MB 340

Lossy encoding uses row-pipelined parallelism that scales with available cores. See benchmark/ for full methodology and small-image results.

cd benchmark && go test -bench=. -benchmem -count=3 -run='^$' -timeout=30m

Compatibility

Output files are compatible with all WebP decoders (Chrome, Firefox, Safari, libwebp dwebp, ImageMagick, etc.). The encoder produces bitstream-conformant VP8/VP8L output matching the behavior of Google's C reference implementation (libwebp).

Project Structure

webp.go / encode.go       Public API (Decode, Encode, Options)
animation/                 Animation encoder/decoder (ANIM/ANMF)
cmd/gwebp/                 CLI tool
mux/                       WebP mux/demux (RIFF container)
sharpyuv/                  Sharp YUV color space conversion
internal/
  bitio/                   Bit-level I/O (boolean arithmetic, lossless streams)
  container/               RIFF/WEBP container parsing
  dsp/                     DSP (YUV conversion, filters, prediction, cost)
  lossless/                VP8L encoder/decoder
  lossy/                   VP8 encoder/decoder
  pool/                    Object pool utilities

Contributing

Contributions are welcome! Please:

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/my-change)
  3. Run tests (go test ./...)
  4. Run race detector (go test -race ./...)
  5. Submit a pull request
Guidelines
  • Keep zero external dependencies
  • All codec changes must pass round-trip tests (encode -> decode -> verify)
  • Run go vet ./... and fix any issues before submitting
  • Bitstream code is precision-critical: test thoroughly against reference files

License

MIT License - see LICENSE for details.

Documentation

Overview

Package webp provides a pure Go encoder and decoder for the WebP image format.

WebP is a modern image format developed by Google that provides superior lossless and lossy compression for images on the web. This package implements the full WebP specification without any CGo dependencies, making it fully portable and easy to cross-compile.

The package supports:

  • Lossy decoding (VP8)
  • Lossless decoding (VP8L)
  • Alpha channel
  • Lossy encoding (VP8)
  • Lossless encoding (VP8L)
  • Extended format (VP8X) with ICC, EXIF, XMP metadata
  • Animation (ANIM/ANMF)

Basic usage for decoding:

img, err := webp.Decode(reader)

Basic usage for encoding:

err := webp.Encode(writer, img, &webp.Options{Quality: 80})

Package webp implements a decoder for the WebP image format.

WebP supports lossy (VP8), lossless (VP8L), and extended (VP8X) formats. This package registers itself with the standard library's image package so that image.Decode can transparently read WebP files.

Index

Examples

Constants

View Source
const MaxDimension = 16383

MaxDimension is the maximum allowed width or height for a WebP image, in pixels. This matches libwebp's WEBP_MAX_DIMENSION constant. Images larger than 16383 pixels in either dimension cannot be represented in the WebP bitstream format.

Variables

View Source
var (
	ErrUnsupported = errors.New("webp: unsupported format")
	ErrNoFrames    = errors.New("webp: no image frames found")
)

Errors returned by the decoder.

Functions

func Decode

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

Decode reads a WebP image from r and returns it as an image.Image. For lossless images the returned type is *image.NRGBA. For lossy images the returned type is *image.YCbCr (when available) or *image.NRGBA.

Example
package main

import (
	"fmt"
	"os"

	"github.com/deepteams/webp"
)

func main() {
	f, err := os.Open("testdata/red_4x4_lossy.webp")
	if err != nil {
		fmt.Println(err)
		return
	}
	defer f.Close()

	img, err := webp.Decode(f)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Printf("bounds: %v\n", img.Bounds())
}
Output:

bounds: (0,0)-(4,4)

func DecodeConfig

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

DecodeConfig returns the color model and dimensions of a WebP image without decoding the entire image.

Example
package main

import (
	"fmt"
	"os"

	"github.com/deepteams/webp"
)

func main() {
	f, err := os.Open("testdata/blue_16x16_lossy.webp")
	if err != nil {
		fmt.Println(err)
		return
	}
	defer f.Close()

	cfg, err := webp.DecodeConfig(f)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Printf("%dx%d\n", cfg.Width, cfg.Height)
}
Output:

16x16

func Encode

func Encode(w io.Writer, img image.Image, opts *EncoderOptions) error

Encode writes the image img to w in WebP format. If opts is nil, DefaultOptions() is used. Returns an error if opts contains invalid parameter values.

Example (Lossless)
package main

import (
	"bytes"
	"fmt"
	"image"
	"image/color"

	"github.com/deepteams/webp"
)

func main() {
	img := image.NewNRGBA(image.Rect(0, 0, 8, 8))
	for y := 0; y < 8; y++ {
		for x := 0; x < 8; x++ {
			img.SetNRGBA(x, y, color.NRGBA{R: 255, G: 0, B: 0, A: 255})
		}
	}

	var buf bytes.Buffer
	err := webp.Encode(&buf, img, &webp.EncoderOptions{
		Lossless: true,
		Quality:  75,
	})
	if err != nil {
		fmt.Println(err)
		return
	}
	if buf.Len() > 0 {
		fmt.Println("ok")
	}
}
Output:

ok
Example (Lossy)
package main

import (
	"bytes"
	"fmt"
	"image"
	"image/color"

	"github.com/deepteams/webp"
)

func main() {
	img := image.NewNRGBA(image.Rect(0, 0, 64, 64))
	for y := 0; y < 64; y++ {
		for x := 0; x < 64; x++ {
			img.SetNRGBA(x, y, color.NRGBA{R: uint8(x * 4), G: uint8(y * 4), B: 128, A: 255})
		}
	}

	var buf bytes.Buffer
	err := webp.Encode(&buf, img, &webp.EncoderOptions{
		Quality: 80,
		Method:  4,
	})
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Printf("encoded %d bytes\n", buf.Len())
	if buf.Len() > 0 {
		fmt.Println("ok")
	}
}
Output:

encoded 208 bytes
ok
Example (Roundtrip)
package main

import (
	"bytes"
	"fmt"
	"image"
	"image/color"

	"github.com/deepteams/webp"
)

func main() {
	// Create a simple image.
	original := image.NewNRGBA(image.Rect(0, 0, 16, 16))
	for y := 0; y < 16; y++ {
		for x := 0; x < 16; x++ {
			original.SetNRGBA(x, y, color.NRGBA{R: 100, G: 150, B: 200, A: 255})
		}
	}

	// Encode to WebP lossless.
	var buf bytes.Buffer
	err := webp.Encode(&buf, original, &webp.EncoderOptions{Lossless: true})
	if err != nil {
		fmt.Println(err)
		return
	}

	// Decode back.
	decoded, err := webp.Decode(&buf)
	if err != nil {
		fmt.Println(err)
		return
	}

	// Verify pixel values match (lossless is exact).
	p := decoded.(*image.NRGBA).NRGBAAt(0, 0)
	fmt.Printf("R=%d G=%d B=%d A=%d\n", p.R, p.G, p.B, p.A)
}
Output:

R=100 G=150 B=200 A=255

Types

type EncoderOptions

type EncoderOptions struct {
	// Lossless enables VP8L lossless encoding.
	// When false (default), VP8 lossy encoding is used.
	Lossless bool

	// Quality is the compression quality (0-100, default 75).
	// For lossy: lower means smaller files with more artifacts.
	// For lossless: controls the compression effort.
	Quality float32

	// Method controls encoding effort (0-6, default 4). Higher values
	// produce smaller files at the cost of longer encoding times:
	//   0 = fastest, least compression
	//   4 = good trade-off between speed and quality (default)
	//   6 = slowest, best compression
	Method int

	// Preset selects encoding parameters tuned for specific content types.
	Preset Preset

	// UseSharpYUV enables sharp (and slow) RGB->YUV conversion.
	UseSharpYUV bool

	// Exact preserves the RGB values under transparent areas. In lossless
	// mode, transparent pixels' RGB are kept as-is instead of being zeroed.
	// In lossy mode, it skips the transparent-area cleanup that normally
	// flattens invisible pixels to reduce encoding cost. Note that lossy
	// VP8 quantization will still modify pixel values regardless of this flag.
	Exact bool

	// TargetSize sets a target output size in bytes (0 = use quality instead).
	TargetSize int

	// TargetPSNR sets a target PSNR value (0 = disabled).
	// When set (and TargetSize is 0), the encoder adjusts quality across
	// multiple passes to converge toward this PSNR level.
	// Matches C libwebp's WebPConfig::target_PSNR.
	TargetPSNR float32

	// Preprocessing selects preprocessing applied before/during encoding
	// (lossy encoding only). This is a bitmask matching C libwebp's
	// WebPConfig::preprocessing field:
	//   0 = none
	//   1 = segment smooth (applies a 3x3 majority-vote filter to the
	//       segment map, reducing noise in segment assignment)
	//   2 = pseudo-random dithering on RGB->YUV conversion
	//   3 = both segment smooth and dithering
	// When bit 1 is set, ordered dithering noise is added to the rounding
	// values during the RGB->YUV color space conversion. The amplitude
	// decreases with quality: max dithering at q=0, 0.5 amplitude at q=100.
	// This reduces banding artifacts at lower quality levels.
	Preprocessing int

	// SNSStrength controls spatial noise shaping strength (0-100, default 50).
	// Higher values give more weight to low-frequency content, improving
	// visual quality at the cost of higher distortion in high-frequency areas.
	// Matches C libwebp's WebPConfig::sns_strength.
	// The default value -1 (or any value < 0) is treated as 50.
	SNSStrength int

	// FilterStrength controls the strength of the deblocking loop filter
	// (0-100, default 60). Higher values produce smoother edges but may
	// blur details. Matches C libwebp's WebPConfig::filter_strength.
	// The default value -1 (or any value < 0) is treated as 60.
	FilterStrength int

	// FilterSharpness controls the sharpness of the loop filter (0-7,
	// default 0). Higher values sharpen the filter effect.
	// Matches C libwebp's WebPConfig::filter_sharpness.
	FilterSharpness int

	// FilterType selects the loop filter type (0=simple, 1=strong, default 1).
	// Strong filtering (1) also filters U/V channels.
	// Matches C libwebp's WebPConfig::filter_type.
	// The default value -1 (or any value < 0) is treated as 1 (strong).
	FilterType int

	// Partitions controls the number of token partitions (0-3, default 0).
	// The actual number of partitions is 1 << Partitions (1, 2, 4, or 8).
	// Matches C libwebp's WebPConfig::partitions.
	Partitions int

	// Segments controls the number of segments to use during encoding
	// (1-4, default 4). Fewer segments speed up encoding at the cost of
	// compression efficiency. Matches C libwebp's WebPConfig::segments.
	// The default value -1 (or any value < 0) is treated as 4.
	Segments int

	// Pass controls the number of entropy-analysis passes (1-10, default 1).
	// Higher values improve compression at the cost of encoding speed.
	// Matches C libwebp's WebPConfig::pass.
	// The default value -1 (or any value < 0) is treated as 1.
	Pass int

	// EmulateJpegSize, when true, tries to produce an output of similar
	// size to a JPEG file of equivalent quality. This is a C libwebp
	// compatibility field (WebPConfig::emulate_jpeg_size). The pure Go
	// encoder does not implement this heuristic; the field is accepted
	// for API compatibility but has no effect on output.
	EmulateJpegSize bool

	// QMin sets the minimum quantizer value (0-100, default 0).
	// Must be <= QMax. Matches C libwebp's WebPConfig::qmin.
	QMin int

	// QMax sets the maximum quantizer value (0-100, default 100).
	// Must be >= QMin. Matches C libwebp's WebPConfig::qmax.
	// The default value -1 (or any value < 0) is treated as 100.
	QMax int

	// AlphaCompression selects the compression method for the alpha channel
	// (lossy encoding only, ignored for lossless which handles alpha natively).
	// Matches C libwebp's WebPConfig::alpha_compression.
	//   0 = no compression (raw alpha bytes)
	//   1 = VP8L lossless compression (default)
	// The default value -1 (or any value < 0) is treated as 1 (lossless).
	AlphaCompression int

	// AlphaFiltering selects the predictive filtering method applied to the
	// alpha plane before compression (lossy encoding only).
	// Matches C libwebp's WebPConfig::alpha_filtering.
	//   0 = none
	//   1 = fast (quick heuristic, default)
	//   2 = best (try all filters, pick smallest)
	// The default value -1 (or any value < 0) is treated as 1 (fast).
	AlphaFiltering int

	// AlphaQuality controls the quality of the alpha channel encoding,
	// independently of the main image quality (lossy encoding only).
	// Range: 0-100. Values below 100 enable alpha level quantization
	// (lossy alpha). Matches C libwebp's WebPConfig::alpha_quality.
	// The default value -1 (or any value < 0) is treated as 100.
	AlphaQuality int

	// ICC holds an ICC color profile to embed in the output.
	// When non-nil, the encoder uses VP8X extended format with the ICCP chunk.
	ICC []byte

	// EXIF holds EXIF metadata to embed in the output.
	// When non-nil, the encoder uses VP8X extended format with the EXIF chunk.
	EXIF []byte

	// XMP holds XMP metadata to embed in the output.
	// When non-nil, the encoder uses VP8X extended format with the XMP chunk.
	XMP []byte
}

EncoderOptions controls WebP encoding parameters.

func DefaultOptions

func DefaultOptions() *EncoderOptions

DefaultOptions returns encoding options with quality 75, lossy, method 4. All parameters match C libwebp's WebPConfigInit defaults. Sentinel values (-1) are used for fields where Go's zero value differs from the C default, ensuring that an uninitialized EncoderOptions{} produces sensible output.

Example
package main

import (
	"fmt"

	"github.com/deepteams/webp"
)

func main() {
	opts := webp.DefaultOptions()
	fmt.Printf("quality: %.0f\n", opts.Quality)
	fmt.Printf("lossless: %v\n", opts.Lossless)
	fmt.Printf("method: %d\n", opts.Method)
}
Output:

quality: 75
lossless: false
method: 4

func OptionsForPreset

func OptionsForPreset(preset Preset, quality float32) *EncoderOptions

OptionsForPreset returns encoding options tuned for the given preset and quality, matching C libwebp's WebPConfigPreset (config_enc.c:64-96).

Example
package main

import (
	"fmt"

	"github.com/deepteams/webp"
)

func main() {
	opts := webp.OptionsForPreset(webp.PresetPhoto, 90)
	fmt.Printf("quality: %.0f\n", opts.Quality)
	fmt.Printf("sns: %d\n", opts.SNSStrength)
}
Output:

quality: 90
sns: 80

type Features

type Features struct {
	Width        int    // Image width in pixels.
	Height       int    // Image height in pixels.
	HasAlpha     bool   // True if the image contains an alpha channel.
	HasAnimation bool   // True if the image is animated (ANIM chunk present).
	Format       string // Container format: "lossy" (VP8), "lossless" (VP8L), or "extended" (VP8X).
	LoopCount    int    // Animation loop count (0 = infinite). Only meaningful when HasAnimation is true.
	FrameCount   int    // Number of frames (1 for still images).
}

Features describes a WebP file's properties, as returned by GetFeatures.

func GetFeatures

func GetFeatures(r io.Reader) (*Features, error)

GetFeatures reads WebP features (dimensions, format, alpha, animation) without decoding pixel data. It parses just the RIFF container and chunk headers, making it much cheaper than a full Decode.

Example
package main

import (
	"fmt"
	"os"

	"github.com/deepteams/webp"
)

func main() {
	f, err := os.Open("testdata/red_4x4_lossless.webp")
	if err != nil {
		fmt.Println(err)
		return
	}
	defer f.Close()

	feat, err := webp.GetFeatures(f)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Printf("size: %dx%d\n", feat.Width, feat.Height)
	fmt.Printf("format: %s\n", feat.Format)
	fmt.Printf("alpha: %v\n", feat.HasAlpha)
}
Output:

size: 4x4
format: lossless
alpha: false

type Options

type Options = EncoderOptions

Options is an alias for backward compatibility.

type Preset

type Preset int

Preset selects a set of encoding parameters tuned for specific content types.

const (
	PresetDefault Preset = iota
	PresetPicture
	PresetPhoto
	PresetDrawing
	PresetIcon
	PresetText
)

Directories

Path Synopsis
Package animation provides types and canvas logic for animated WebP images.
Package animation provides types and canvas logic for animated WebP images.
cmd
gwebp command
Command gwebp encodes and decodes WebP images from the command line.
Command gwebp encodes and decodes WebP images from the command line.
internal
bitio
Package bitio provides bit-level I/O primitives for the WebP codec.
Package bitio provides bit-level I/O primitives for the WebP codec.
container
Package container defines constants for the WebP/RIFF container format, including FourCC values, magic bytes, chunk IDs, and VP8/VP8L format constants.
Package container defines constants for the WebP/RIFF container format, including FourCC values, magic bytes, chunk IDs, and VP8/VP8L format constants.
dsp
Package dsp provides low-level DSP routines for the WebP codec.
Package dsp provides low-level DSP routines for the WebP codec.
lossy
Package lossy implements VP8 lossy codec constants, probability tables, and decoder/encoder primitives for WebP lossy compression.
Package lossy implements VP8 lossy codec constants, probability tables, and decoder/encoder primitives for WebP lossy compression.
pool
Package pool provides bucketed sync.Pool instances for reducing allocations in hot paths.
Package pool provides bucketed sync.Pool instances for reducing allocations in hot paths.
Package mux provides muxing and demuxing for the WebP RIFF container format.
Package mux provides muxing and demuxing for the WebP RIFF container format.
Package sharpyuv implements sharp RGB to YUV420 conversion that minimizes chroma subsampling artifacts using iterative error diffusion.
Package sharpyuv implements sharp RGB to YUV420 conversion that minimizes chroma subsampling artifacts using iterative error diffusion.

Jump to

Keyboard shortcuts

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