simple_webp

package module
v0.2.1 Latest Latest
Warning

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

Go to latest
Published: Oct 19, 2026 License: MIT Imports: 11 Imported by: 0

README

simple-webp

[!NOTE] 🚀 Initial release, canary testing in progress. If you hit any problem, please open an issue!

English | 简体中文

Pure-Go WebP parse / decode / encode — no cgo, no third-party runtime dependencies. Container parsing, VP8 lossy decoding, VP8L lossless decoding+encoding, ALPH transparency, animation compositing — all implemented from scratch, returning standard image.Image.

webpDecoder mirrors the architecture of simple-png: chunked, raw bytes preserved, byte-exact write back.

Supported

Capability Status Notes
RIFF/WEBP container ✅ VP8 /VP8L/VP8X/ALPH/ANIM/ANMF/ICCP/EXIF/XMP , incl. padding
VP8L lossless decode ✅ 4 transforms (predictor/color/subtract-green/color-indexing), meta-Huffman, LZ77, color cache
VP8 lossy decode ✅ bool decoder, keyframe header, partitions, coefficient trees, 16x16/8x8/4x4 prediction, WHT+IDCT, loop filter
ALPH transparency ✅ uncompressed (3 row filters) and VP8L-compressed alpha
Animation ✅ ANIM/ANMF, dispose/blend compositing, canvas-sized frame sequence
image interface ✅ ToImage(), *Webp implements image.Image, image.RegisterFormat("webp", ...)
Metadata editing ✅ rewrite with ICCP/EXIF/XMP chunks, pixels never re-encoded, byte-exact when untouched
VP8L lossless encode ✅ color-indexing / subtract-green / predictor / LZ77 / Huffman (≤15 bits)
VP8 lossy encode ❌ out of scope (mode decision, RD optimization, rate control)

Install

go get github.com/knoweather/simple-webp

Usage

Parse + decode
f, _ := os.Open("a.webp")
w, err := simple_webp.ParseWebp(f)
if err != nil {
    panic(err)
}
log.Println(w.Width, w.Height, w.HasAlpha, w.HasAnimation, len(w.ANMFs))

img, err := w.ToImage() // image.Image (*image.NRGBA)
Standard library

The format is registered in init(), so image.Decode works out of the box:

img, format, err := image.Decode(r)  // format == "webp"
cfg, err := image.DecodeConfig(r)    // header only, no pixels touched
Use directly as an image.Image
var img image.Image = w // Bounds / ColorModel / At (lazy decode, cached)
draw.Draw(dst, dst.Bounds(), w, image.Point{}, draw.Src)
Edit metadata, lossless write back
w.XMP = &simple_webp.XMP{Data: []byte(xmpPacket)}
w.DeleteRawChunk("EXIF")

var buf bytes.Buffer
_, err := w.WriteTo(&buf) // untouched chunks are copied byte for byte, pixels not re-encoded
Animation
frames, durations, err := w.Frames(0) // 0 = default pixel budget (1<<27)
for i, f := range frames {
    log.Println(i, durations[i], f.Bounds())
}
Encode
err := simple_webp.Encode(out, img, &simple_webp.EncodeOptions{
    Lossless: true,
    Method:   2, // 0 literals only / 1 +color-indexing+LZ77 / 2 +predictor
})
Custom chunks

The same extension point as simple-png:

type myChunk struct{ Data []byte }

func (c *myChunk) ChunkName() simple_webp.FourCC { return simple_webp.FourCC("ABCD") }
func (c *myChunk) Parse(chunk *simple_webp.RawChunk) error { c.Data = chunk.Data; return nil }

Layout

webp.go        Webp struct / ParseWebp / chunk consumption & in-order snapshot
riff.go        RIFF primitives: FourCC, little-endian lengths, padding, R/W
chunk.go       VP8X / VP8 / VP8L / ALPH / ANIM / ANMF / ICCP / EXIF / XMP
writer.go      WriteTo / MarshalBytes (in-order write back, lengths & padding recomputed)
decode.go      ToImage / ToImageWith, lossy & lossless dispatch
alpha.go       ALPH inflate, alpha row-filter inverse transform, compositing
animation.go   ANMF frame compositing (blend / dispose)
vp8.go         VP8 → NRGBA (9:3:3:1 fancy upsampling + fixed-point BT.601)
image.go       image.RegisterFormat / Decode / DecodeConfig / image.Image
encode.go      image.Image → VP8L → RIFF
internal/vp8/  VP8 lossy decoder (bool decode, prediction, IDCT, loop filter)
internal/vp8l/ VP8L lossless decoder + encoder (bitstream, Huffman, LZ77, transforms)
testdata/      fixtures generated against libwebp (.webp + .rgba references)

Correctness

Fixtures are all generated by libwebp (Pillow) and compared pixel by pixel:

  • lossless: gradients, random noise, palette, text, alpha, checkerboard, 1×1, 320×240
  • lossy: smooth, sharp edges, noise, with alpha
  • real data: 6778×6028 satellite image, 400×300 photo crop
  • animation: 3 frames

VP8/VP8L decode produces YUV/ARGB planes byte-identical to the reference implementation (loop filter and animation compositing included), and the final RGBA output is pixel-identical to libwebp (fancy upsampling and fixed-point BT.601 included). Encoded files decode correctly in libwebp and match the source pixel-for-pixel.

Benchmark

Windows/amd64, Ryzen 7 7840HS, go test -bench . -benchtime 1s:

Scenario simple-webp reference (x/image) libwebp 1.5.0 (C)
container parse (320×240, 37 KB) 8.6 µs (4.3 GB/s) — —
VP8L lossless decode (320×240) 3.45 ms (22.3 MP/s) 2.86 ms (26.9 MP/s) 1.3–3.4× faster
VP8 lossy decode, YUV only (400×300) 1.02 ms (117.8 MP/s) 1.08 ms (111.4 MP/s) —
VP8 lossy decode + RGB (400×300) 4.52 ms → 1.42 ms (optimized, CT) not supported 0.208 ms (single-thread ≈ 6.8× slower)

The libwebp column comes from a single thread on a different machine (libwebp 1.5.0 WebPDecodeRGBA), scale reference only. Lossy decoding got a YUV→RGB lookup-table + 16-row concurrent optimization, hence the optimized numbers.

Lossless decoding is ~20% slower than the reference (bit-by-bit Huffman, no table-driven fast path yet); core VP8 decoding is slightly faster. Encoding runs at ~4 MP/s with output sizes 1.5–2× of cwebp -lossless -m 6 (no meta-Huffman / color transform yet).

Known limitations

  • Lossy encoding not implemented — EncodeOptions.Lossless is accepted but ignored: every encode path writes lossless (VP8L) only
  • The encoder implements neither color transform nor meta-Huffman blocked coding, so compression is weaker than cwebp
  • ALPH preprocessing=1 (level reduction) is passed through as-is, without the reference dithering; cwebp does not produce that mode by default
  • Lossless Huffman decodes bit by bit — large images ~20% slower than the reference
  • Animation is read-only (no animation encoder)
  • WebP contains no VP9: WebP's two codecs are VP8 (lossy, VP8 chunk) and VP8L (lossless, VP8L chunk). VP9 is a different codec used in WebM/Matroska containers and unrelated to this library

Safety / pixel budget

To guard against decompression bombs (test data includes a few-dozen-byte malformed file claiming 16383×16383), every decode path checks the pixel budget after reading the header and before allocating pixel buffers:

  • DecodeOptions.MaxPixels (default 1<<27 ≈ 134M pixels ≈ 512 MB ARGB) is forwarded to vp8.DecodeLimit / vp8l.DecodeARGBLimit; exceeding it returns ErrTooLarge / ErrImageTooLarge
  • single chunk size is capped by maxChunkSize
  • covered by TestToImageWithDefaultBudget and single-worker fuzzing: the worst-case bogus header is rejected with zero allocations, no OOM

Tighten the budget yourself, e.g. trust only small images:

img, err := w.ToImageWith(simple_webp.DecodeOptions{MaxPixels: 4096 * 4096})

WebAssembly (read-only decode)

wasm/ ships a WebAssembly decoder only: it turns a WebP byte stream into RGBA pixels ready for <canvas> — no re-encoding, no writes. Encoding is out of scope.

  • Build: wasm/build.ps1 (Windows) or wasm/build.sh; output is wasm/example/main.wasm plus a copied wasm_exec.js.
  • JS API (installed on the global object by wasm.Register()):
    • webpDecode(bytes) -> { width, height, hasAlpha, hasAnimation, rgba } (returns { error } on failure; first frame for animations)
    • webpDecodeFrames(bytes) -> [ { width, height, duration, rgba }, … ] (all animation frames at canvas size; a still image yields one frame)
    • webpInfo(bytes) -> { width, height, hasAlpha, hasAnimation, hasICC, hasEXIF, hasXMP, frameCount } (no pixel decode; VP8X flags + frame count)
    • bytes may be a Uint8Array or ArrayBuffer; rgba is a straight-alpha Uint8Array that must be consumed synchronously (Go-managed memory).
  • Decoding covers the full extended format: VP8X alpha (lossless and lossy VP8X+ALPH), animation (VP8X+ANIM+ANMF), and the ICC/EXIF/XMP metadata flags. Full demo: wasm/example/index.html and wasm/README.md.

Testing

go test ./...                 # unit tests + pixel-by-pixel fixture comparison
go test -run XXX -bench .     # benchmarks
go test -fuzz FuzzParseWebp   # fuzz (goal: no panic, no OOM)

The internal/vp8 plane-level tests and bench_test.go use golang.org/x/image as a comparison oracle — test-only, never a runtime dependency.

References


Acknowledgements

Thanks to Tencent WorkBuddy for the free quota of the Step 5 Preview model that made this project possible.

Documentation

Overview

Package simple_webp 解析与编辑 WebP(RIFF)容器:读取 RIFF 头与全部 chunk, 解码容器级 chunk(VP8X、VP8、VP8L、ALPH、ANIM、ANMF、ICCP、EXIF、XMP), 并支持在不重新编码像素的前提下增删元数据 chunk、按原始顺序写回整个流。

Package simple_webp parses and edits WebP (RIFF) containers: it reads the RIFF header and every chunk, decodes the container-level chunks (VP8X, VP8, VP8L, ALPH, ANIM, ANMF, ICCP, EXIF, XMP), and supports adding or removing metadata chunks and writing the stream back in its original order without re-encoding the pixels.

Index

Constants

View Source
const (
	FlagICC       uint8 = 0x20
	FlagAlpha     uint8 = 0x10
	FlagEXIF      uint8 = 0x08
	FlagXMP       uint8 = 0x04
	FlagAnimation uint8 = 0x02
)

VP8X 特性标志位,位位置由 WebP 容器规范定义。 VP8X feature flags; the bit positions are defined by the container spec.

View Source
const (
	ALPHCompressionNone     uint8 = 0
	ALPHCompressionLossless uint8 = 1
)

ALPH 的 alpha 通道压缩方法常量。 ALPH alpha channel compression methods.

View Source
const (
	ALPHFilterNone       uint8 = 0
	ALPHFilterHorizontal uint8 = 1
	ALPHFilterVertical   uint8 = 2
	ALPHFilterGradient   uint8 = 3
)

ALPH 的 alpha 通道过滤方法常量。 ALPH alpha channel filtering methods.

Variables

View Source
var (
	// ErrNotWebP 在流不以 RIFF....WEBP 签名开头时返回。
	// ErrNotWebP is returned when the stream does not start with RIFF....WEBP.
	ErrNotWebP = errors.New("invalid webp: bad RIFF/WEBP signature")
	// ErrChunkNotFound 在 ParseChunk 找不到所请求类型的剩余 chunk 时返回。
	// ErrChunkNotFound is returned by ParseChunk when no chunk of the
	// requested type is left in the stream.
	ErrChunkNotFound = errors.New("chunk not found")
	// ErrUnsupported 对应格式合法但本库尚未实现的 WebP 特性。
	// ErrUnsupported is returned for valid but unimplemented webp features.
	ErrUnsupported = errors.New("unsupported webp feature")
	// ErrImageTooLarge 在图像像素数超出配置的像素预算时返回。
	// ErrImageTooLarge is returned when the image exceeds the configured
	// pixel budget.
	ErrImageTooLarge = errors.New("webp image too large")
)

解析器与解码器共用的哨兵错误,调用方可用 errors.Is 判断具体类型。

Sentinel errors returned by the parser and the decoders; callers can test them with errors.Is.

Functions

func Decode

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

Decode 读取 webp 流并返回解码后的图像,实现 image.Decode 签名。 动图会返回合成后的画布帧。

Decode reads a webp stream and returns the decoded image, implementing the image.Decode signature. For animated files it returns the composited canvas frame.

func DecodeConfig

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

DecodeConfig 只读取容器头部(RIFF 头 + 第一个决定尺寸的 chunk), 不解码任何像素数据,因此开销很小。VP8X、VP8、VP8L 三种 chunk 都被 支持;颜色模型固定返回 NRGBAModel。

DecodeConfig reads only the container header (the RIFF header plus the first chunk that determines the size) and decodes no pixel data at all, so it is cheap. It supports the VP8X, VP8 and VP8L chunks; the color model is always NRGBAModel.

func Encode

func Encode(w io.Writer, m image.Image, opt *EncodeOptions) error

Encode 把 m 编码为无损 WebP 文件并写入 w。

Encode writes m as a lossless WebP file.

func EncodeBytes

func EncodeBytes(m image.Image, opt *EncodeOptions) ([]byte, error)

EncodeBytes 把 m 编码为完整的无损 WebP 文件并保留在内存中返回。

EncodeBytes encodes m as a lossless WebP file held in memory.

Types

type ALPH

type ALPH struct {
	// Compression 是压缩方法,见 ALPHCompression* 常量。
	// Compression is the compression method, see the ALPHCompression* constants.
	Compression uint8
	// Filter 是过滤方法,见 ALPHFilter* 常量。
	// Filter is the filtering method, see the ALPHFilter* constants.
	Filter uint8
	// Preprocessing 是预处理方法(0 无,1 level reduction)。
	// Preprocessing is the preprocessing method (0 none, 1 level reduction).
	Preprocessing uint8
	// Data 是去掉首字节标志后的压缩 alpha 数据。
	// Data is the compressed alpha data without the leading flag byte.
	Data []byte
}

ALPH 携带有损图像的 alpha 平面:首字节为标志位,其余为压缩后的 alpha 数据。

0                   1
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+-+-+-+-+-+-+-+-+
|C|F|P|Rsv|

C: 压缩方法(0 none, 1 lossless);F: 过滤方法;P: 预处理(0 none, 1 level reduction)。

ALPH carries the alpha plane of a lossy image: one flag byte followed by the compressed alpha data. C: compression method (0 none, 1 lossless), F: filtering method, P: preprocessing (0 none, 1 level reduction).

func (*ALPH) ChunkName

func (a *ALPH) ChunkName() FourCC

ChunkName 返回 ALPH 的 chunk 类型码。 ChunkName returns the ALPH chunk type code.

func (*ALPH) Parse

func (a *ALPH) Parse(c *riffChunk) error

Parse 解析 ALPH 首字节中的压缩(bit0-1)、过滤(bit2-3)与预处理(bit4-5) 方法,其余字节作为压缩 alpha 数据保留;压缩方法大于 1 报错。 Parse decodes the compression (bits 0-1), filtering (bits 2-3) and preprocessing (bits 4-5) methods from the ALPH flag byte and keeps the remaining bytes as compressed alpha data; a compression method above 1 is rejected.

type ANIM

type ANIM struct {
	// BackgroundColor 按文件的 BGRA 字节序读入,Go 中记作 0xAARRGGBB。
	// BackgroundColor is read in the file's BGRA byte order and held in Go
	// as 0xAARRGGBB.
	BackgroundColor uint32
	// LoopCount 是播放完所有帧后的循环次数,0 表示无限循环。
	// LoopCount is how often the animation is played again after the last
	// frame; 0 means loop forever.
	LoopCount uint16
}

ANIM 保存整段动画的全局参数,6 字节载荷布局如下(WebP 容器规范):

Background Color: 32 bits (BGRA)
Loop Count:       16 bits

ANIM holds the global parameters of an animation. Its 6-byte payload is laid out as follows (WebP container spec): a 32-bit background colour in BGRA byte order and a 16-bit loop count.

func (*ANIM) ChunkName

func (a *ANIM) ChunkName() FourCC

ChunkName 返回 ANIM 的 chunk 类型码。 ChunkName returns the ANIM chunk type code.

func (*ANIM) Parse

func (a *ANIM) Parse(c *riffChunk) error

Parse 解析 6 字节 ANIM 载荷:4 字节背景色(BGRA 字节序)与 2 字节循环次数。 Parse decodes the 6-byte ANIM payload: the 32-bit background colour in BGRA byte order and the 16-bit loop count.

type ANMF

type ANMF struct {
	// X、Y 是帧左上角在画布上的坐标;文件中按一半存储,读出后乘 2。
	// X and Y are the frame's top-left position on the canvas; they are
	// stored halved in the file and doubled on read.
	X, Y int
	// Width、Height 是帧尺寸,文件中按减一存储,读出后加一。
	// Width and Height are the frame size, stored minus one and incremented on read.
	Width, Height int
	// Duration 是本帧的显示时长,单位毫秒。/ Duration is how long the frame is shown, in milliseconds.
	Duration int
	// Blend 为 true 时帧以 alpha 混合叠加到画布,为 false 时直接覆盖画布对应区域。
	// Blend is true when the frame is alpha blended onto the canvas, false
	// when it overwrites the canvas region.
	Blend bool
	// Dispose 为 true 时本帧显示完后把画布清为背景色。
	// Dispose is true when the canvas is cleared to the background color
	// after the frame was displayed.
	Dispose bool

	// ALPH 是本帧的 alpha 平面,可为 nil。/ ALPH is the frame's alpha plane, may be nil.
	ALPH *ALPH
	// VP8 与 VP8L 恰有一个非 nil,承载本帧的图像码流。
	// Exactly one of VP8 and VP8L is non-nil and carries the frame bitstream.
	VP8  *VP8
	VP8L *VP8L
}

ANMF 表示动画中的一帧:16 字节头部给出帧在画布上的位置、尺寸、显示时长与 混合/清理标志,其后嵌套本帧的图像数据 chunk(可选的 ALPH,加 VP8 或 VP8L 之一)。头部字段布局(WebP 容器规范)见下:

Frame X:      24 bits (actual x / 2)
Frame Y:      24 bits (actual y / 2)
Frame Width:  24 bits (width - 1)
Frame Height: 24 bits (height - 1)
Duration:     24 bits (milliseconds)
Flags:         8 bits (bit1 blending, bit0 disposal)
Frame Data:   ALPH (optional) + VP8 / VP8L

ANMF is one animation frame: a 16-byte header with the frame position, size, duration and blend/disposal flags, followed by the frame's nested image chunk (an optional ALPH plus either VP8 or VP8L); the header field layout is shown above (WebP container spec).

func (*ANMF) ChunkName

func (a *ANMF) ChunkName() FourCC

ChunkName 返回 ANMF 的 chunk 类型码。 ChunkName returns the ANMF chunk type code.

func (*ANMF) Parse

func (a *ANMF) Parse(c *riffChunk) error

Parse 解析 ANMF:位置 x/y 读出后乘 2、宽高读出后加一,时长单位为毫秒; 载荷剩余部分按嵌套 RIFF 流处理,依次解出其中的 ALPH、VP8、VP8L 子 chunk; 既无 VP8 也无 VP8L 时报错。 Parse decodes an ANMF chunk: the position gets doubled and the size incremented on read, the duration is in milliseconds; the rest of the payload is treated as a nested RIFF stream from which the ALPH, VP8 and VP8L sub-chunks are parsed. A frame without any image data is rejected.

type ChunkParse

type ChunkParse interface {
	// ChunkName 返回解析器接管的 chunk 类型码。
	// ChunkName returns the chunk type code the parser handles.
	ChunkName() FourCC
	// Parse 解码给定 chunk 的载荷,出错时返回非 nil error。
	// Parse decodes the payload of the given chunk and returns a non-nil
	// error on failure.
	Parse(chunk *riffChunk) error
}

ChunkParse 是自定义 chunk 解析器必须实现的接口,也是本包对外开放的扩展点 (与 simple-png 的做法一致):实现方在 ChunkName 中声明接管的 chunk 类型码, 由 Parse 解码该 chunk 的载荷。

ChunkParse is the interface a custom chunk parser has to implement, the same extension point simple-png exposes: an implementation states in ChunkName which chunk type code it takes over and decodes that chunk's payload in Parse.

type DecodeOptions

type DecodeOptions struct {
	// Format 选择返回的像素布局。
	//
	// Format selects the returned pixel layout.
	Format PixelFormat
	// Frame 选择动画文件的帧,0 表示第一帧。
	//
	// Frame selects the frame of an animated file; 0 is the first frame.
	Frame int
	// MaxPixels 拒绝像素数超过该值的图像,0 表示使用 defaultMaxPixels。
	//
	// MaxPixels rejects larger images; 0 uses defaultMaxPixels.
	MaxPixels int
}

DecodeOptions 调节像素解码行为。

DecodeOptions tunes the pixel decoding.

type EXIF

type EXIF struct {
	// Data 是原始 EXIF 字节。/ Data is the raw EXIF bytes.
	Data []byte
}

EXIF 携带内嵌的 EXIF 元数据。/ EXIF carries the embedded EXIF metadata.

func (*EXIF) ChunkName

func (e *EXIF) ChunkName() FourCC

ChunkName 返回 EXIF 的 chunk 类型码。/ ChunkName returns the EXIF chunk type code.

func (*EXIF) Parse

func (e *EXIF) Parse(c *riffChunk) error

Parse 原样保留 EXIF 载荷,不作解释。/ Parse keeps the EXIF payload verbatim without interpreting it.

type EncodeOptions

type EncodeOptions struct {
	// Lossless 仅为信息性字段:本库只写出无损 WebP,因此该值被接受但忽略。
	//
	// Lossless is informational: this library only writes lossless webp, so
	// the value is accepted and ignored.
	Lossless bool
	// Method 以速度换压缩率:0 只存储字面量,1 追加减绿(subtract green)
	// 变换、颜色索引和 LZ77,2(默认)再应用预测器变换。
	//
	// Method trades speed for size: 0 stores literals only, 1 adds the
	// subtract green transform, colour indexing and LZ77, 2 (the default)
	// also applies a predictor transform.
	Method int
}

EncodeOptions 调节无损编码器的行为。

EncodeOptions tunes the lossless encoder.

type FourCC

type FourCC string

FourCC is a 4 byte chunk type code, e.g. "VP8L".

const (
	RIFFChunk FourCC = "RIFF"
	WEBPChunk FourCC = "WEBP"

	VP8XChunk FourCC = "VP8X"
	VP8Chunk  FourCC = "VP8 "
	VP8LChunk FourCC = "VP8L"
	ALPHChunk FourCC = "ALPH"
	ANIMChunk FourCC = "ANIM"
	ANMFChunk FourCC = "ANMF"
	ICCPChunk FourCC = "ICCP"
	EXIFChunk FourCC = "EXIF"
	XMPChunk  FourCC = "XMP "
)

Chunk type codes of the WebP container, see https://developers.google.com/speed/webp/docs/riff_container

WebP 容器的各 chunk 类型码,见 https://developers.google.com/speed/webp/docs/riff_container

type ICCP

type ICCP struct {
	// Data 是原始 ICC 配置字节。/ Data is the raw ICC profile bytes.
	Data []byte
}

ICCP 携带内嵌的 ICC 色彩配置文件。/ ICCP carries the embedded ICC color profile.

func (*ICCP) ChunkName

func (i *ICCP) ChunkName() FourCC

ChunkName 返回 ICCP 的 chunk 类型码。/ ChunkName returns the ICCP chunk type code.

func (*ICCP) Parse

func (i *ICCP) Parse(c *riffChunk) error

Parse 原样保留 ICCP 载荷,不作解释。/ Parse keeps the ICCP payload verbatim without interpreting it.

type PixelFormat

type PixelFormat int

PixelFormat 选择解码结果的像素布局。

PixelFormat selects the pixel layout of the decoded image.

const (
	// PixelFormatNRGBA 返回 *image.NRGBA,即 straight alpha(非预乘)布局,
	// 与 png 解码器处理 RGBA 图像时使用的布局一致。这是默认值。
	//
	// PixelFormatNRGBA returns an *image.NRGBA, the straight alpha layout
	// used by the png decoder for RGBA images. It is the default.
	PixelFormatNRGBA PixelFormat = iota
	// PixelFormatRGBA 返回 *image.RGBA,即预乘 alpha(premultiplied)布局。
	//
	// PixelFormatRGBA returns an *image.RGBA, the premultiplied layout.
	PixelFormatRGBA
)

type RawChunk

type RawChunk struct {
	// Name 是 4 字节 chunk 类型码。/ Name is the 4-byte chunk type code.
	Name FourCC
	// Data 是去掉 chunk 头与对齐填充后的载荷。
	// Data is the chunk payload without the header and the pad byte.
	Data []byte
}

RawChunk 表示未被原生解码、以原始字节形式保留的 chunk(例如未知的应用私有 chunk);重编码容器时它会原样写回。

RawChunk is a chunk that was not decoded natively and is kept in its raw form, e.g. an application-specific chunk of unknown type; it is written back verbatim when the container is re-encoded.

type VP8

type VP8 struct {
	// Data 是整个 VP8 chunk 载荷,含帧头,原样保留。
	// Data is the whole VP8 chunk payload including the frame header, kept verbatim.
	Data []byte

	// Width、Height 是帧头声明的画布尺寸(各 16 位字段的低 14 位)。
	// Width and Height are the canvas dimensions declared by the frame
	// header (the low 14 bits of each 16-bit field).
	Width, Height int
	// Version 是帧头中的编码器版本(bit1-3)。/ Version is the encoder version from the frame header (bits 1-3).
	Version uint8
	// ShowFrame 表示该帧解码后立即显示。/ ShowFrame reports that the frame is displayed right after decoding.
	ShowFrame bool
	// FirstPartSize 是首分区的字节数。/ FirstPartSize is the size of the first partition in bytes.
	FirstPartSize int
}

VP8 是有损码流 chunk。载荷原样保留、不作改动:WebP 静态有损图像只有一帧 VP8 关键帧,这里仅解析帧头,不解码即可拿到图像尺寸。

VP8 is the lossy bitstream chunk. The payload is kept untouched: a WebP lossy still image is a single VP8 key frame, so parsing the frame header alone already yields the image dimensions without decoding the frame.

func (*VP8) ChunkName

func (v *VP8) ChunkName() FourCC

ChunkName 返回 VP8 的 chunk 类型码。 ChunkName returns the VP8 chunk type code.

func (*VP8) Parse

func (v *VP8) Parse(c *riffChunk) error

Parse 解析 VP8 帧头:3 字节 tag(bit0 为 0 表示关键帧,bit1-3 为版本, bit4 为 show_frame,bit5 起为首分区字节长)、随后 3 字节起始码 0x9d 0x01 0x2a、 以及宽高字段(各 16 位,取低 14 位,高 2 位为缩放系数,此处忽略)。 Data 保留整个载荷;非关键帧、坏起始码或零尺寸都判为损坏。 Parse decodes the VP8 frame header: the 3-byte tag (bit 0 clear means a key frame, bits 1-3 the version, bit 4 show_frame, bits 5+ the size of the first partition), the 3-byte start code 0x9d 0x01 0x2a that follows, and the dimension fields (16 bits each, the low 14 bits taken, the high 2 scale bits ignored here). Data keeps the whole payload; a non-key frame, a bad start code or a zero dimension is rejected.

type VP8L

type VP8L struct {
	// Data 是整个 VP8L chunk 载荷,含 5 字节头,原样保留。
	// Data is the whole VP8L chunk payload including the 5-byte header, kept verbatim.
	Data []byte

	// Width、Height 是从头部读出并加一后的实际尺寸。
	// Width and Height are the actual dimensions read from the header and incremented.
	Width, Height int
	// AlphaUsed 是头部的 alpha_is_used 标志位。
	// AlphaUsed is the alpha_is_used flag from the header.
	AlphaUsed bool
	// Version 是头部的版本号,非 0 会被 Parse 拒绝。
	// Version is the header version number; anything but 0 is rejected by Parse.
	Version uint8
}

VP8L 是无损码流 chunk,头部布局见无损码流规范 https://developers.google.com/speed/webp/docs/webp_lossless_bitstream_specification: 5 字节头按位排列如下,宽高按减一存储(读出后各加一),版本号必须为 0。

VP8L is the lossless bitstream chunk; its header layout follows the lossless bitstream specification at https://developers.google.com/speed/webp/docs/webp_lossless_bitstream_specification. The 5-byte header is laid out bit-wise as follows; the dimensions are stored minus one (and get incremented) and the version number must be 0.

signature       8 bits (0x2f)
width - 1      14 bits
height - 1     14 bits
alpha_is_used   1 bit
version_number  3 bits (must be 0)

func (*VP8L) ChunkName

func (v *VP8L) ChunkName() FourCC

ChunkName 返回 VP8L 的 chunk 类型码。 ChunkName returns the VP8L chunk type code.

func (*VP8L) Parse

func (v *VP8L) Parse(c *riffChunk) error

Parse 解析 VP8L 的 5 字节头:先校验签名 0x2f,再读出宽高(减一存储,各加一)、 alpha 使用标志与版本号;版本号非 0 报错。Data 保留整个载荷。 Parse decodes the 5-byte VP8L header: it checks the 0x2f signature first, then reads the dimensions (stored minus one, so incremented), the alpha-used flag and the version number; a version other than 0 is rejected. Data keeps the whole payload.

type VP8X

type VP8X struct {
	Flags        uint8
	CanvasWidth  int
	CanvasHeight int
}

VP8X 是扩展 chunk:10 字节载荷声明画布宽高(各按减一存储)以及文件包含哪些 扩展特性,其位布局如下(WebP 容器规范):

0                   1                   2                   3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Rsv|I|L|E|X|A|R|                   Reserved                    |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|          Canvas Width Minus One               |             ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
...  Canvas Height Minus One    |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

标志字母:I: ICC profile, L: alpha, E: EXIF, X: XMP, A: animation。

VP8X is the extended chunk: its 10-byte payload declares the canvas size (each dimension stored minus one) and which extended features the file carries. The bit layout above follows the WebP container spec; the flag letters are: I: ICC profile, L: alpha, E: EXIF, X: XMP, A: animation.

func (*VP8X) ChunkName

func (v *VP8X) ChunkName() FourCC

ChunkName 返回 VP8X 的 chunk 类型码。 ChunkName returns the VP8X chunk type code.

func (*VP8X) HasAlpha

func (v *VP8X) HasAlpha() bool

HasAlpha 报告图像是否带 alpha 通道。/ HasAlpha reports whether an alpha channel is present.

func (*VP8X) HasAnimation

func (v *VP8X) HasAnimation() bool

HasAnimation 报告该文件是否为动画。/ HasAnimation reports whether the file is an animation.

func (*VP8X) HasEXIF

func (v *VP8X) HasEXIF() bool

HasEXIF 报告文件是否内嵌 EXIF 元数据。/ HasEXIF reports whether EXIF metadata is embedded.

func (*VP8X) HasICC

func (v *VP8X) HasICC() bool

HasICC 报告文件是否内嵌 ICC 色彩配置。/ HasICC reports whether an ICC profile is embedded.

func (*VP8X) HasXMP

func (v *VP8X) HasXMP() bool

HasXMP 报告文件是否内嵌 XMP 元数据。/ HasXMP reports whether XMP metadata is embedded.

func (*VP8X) Parse

func (v *VP8X) Parse(c *riffChunk) error

Parse 解析 VP8X 载荷:读入标志字节与画布宽高;宽高按减一存储,因此读出后 各加一。载荷不足 10 字节视为损坏。 Parse decodes the VP8X payload: it reads the flag byte and the canvas dimensions, which are stored minus one and therefore get incremented. A payload shorter than 10 bytes is treated as corrupt.

type Webp

type Webp struct {
	Width, Height int
	HasAlpha      bool
	HasAnimation  bool

	VP8X  *VP8X
	VP8   *VP8
	VP8L  *VP8L
	ALPH  *ALPH
	ANIM  *ANIM
	ANMFs []*ANMF
	ICCP  *ICCP
	EXIF  *EXIF
	XMP   *XMP

	// OtherChunk 按 chunk 类型存放经 ParseChunk 解析出的自定义 chunk。
	// OtherChunk holds the custom chunks parsed through ParseChunk, grouped
	// by chunk type.
	OtherChunk map[FourCC][]ChunkParse

	sync.RWMutex
	sync.Once
	// contains filtered or unexported fields
}

Webp 是解析后的 WebP 容器。图像数据 chunk 的负载以原始字节保留, 各解析结构体只是对这些字节的视图,因此可以修改元数据并原样写回, 无需重新编码像素。

Webp is a parsed WebP container. The chunk payloads of the image data are kept as raw bytes and the parsed structs are views onto them, so metadata can be edited and written back without re-encoding the pixels.

func ParseWebp

func ParseWebp(r io.Reader, verifySize ...bool) (*Webp, error)

ParseWebp 解析 WebP 流:读取 RIFF 头与全部 chunk,并解码容器级 chunk (VP8X、VP8、VP8L、ALPH、ANIM、ANMF、ICCP、EXIF、XMP)。未知类型的 chunk 以原始形式保留,见 RawChunks。仅当 verifySize 为 true 时才校验 RIFF 大小字段与实际流长度是否一致。

ParseWebp parses a webp stream: it reads the RIFF header, all chunks and decodes the container chunks (VP8X, VP8, VP8L, ALPH, ANIM, ANMF, ICCP, EXIF, XMP). Chunks of unknown type are kept in raw form, see RawChunks.

The RIFF size field is validated only when verifySize is true.

func ParseWebpBytes

func ParseWebpBytes(b []byte) (*Webp, error)

ParseWebpBytes 解析内存中的 WebP 字节流,不校验 RIFF 大小字段 (等价于调用 ParseWebp 时不传 verifySize)。

ParseWebpBytes parses a webp stream held in memory; the RIFF size field is not verified (ParseWebp is called without verifySize).

func (*Webp) AddRawChunk

func (w *Webp) AddRawChunk(name FourCC, data []byte) error

AddRawChunk 把一个新的原始 chunk 插入流中,位置在第一个图像 chunk (VP8、VP8L 或 ANMF)之前;没有图像 chunk 时追加到末尾。可用于给已解析 的文件补充 EXIF 或 XMP 等 chunk。name 必须是合法 FourCC,data 不得超过 maxChunkSize,且 Webp 必须来自成功的 ParseWebp。 AddRawChunk inserts a new raw chunk into the stream before the first image chunk (VP8, VP8L or ANMF); if there is no image chunk it is appended. Use it e.g. to add an EXIF or XMP chunk to an already parsed file. name must be a valid FourCC, data must fit in maxChunkSize and the Webp must come from a successful ParseWebp.

func (*Webp) At

func (w *Webp) At(x, y int) color.Color

At 实现 image.Image。图像在首次调用时才解码并缓存;解码失败时 返回透明的 color.NRGBA{}。

At implements image.Image. The image is decoded lazily on the first call and cached afterwards; if decoding fails, a transparent color.NRGBA{} is returned.

func (*Webp) Bounds

func (w *Webp) Bounds() image.Rectangle

Bounds 实现 image.Image:返回文件的画布矩形(从原点开始,宽高来自头部)。

Bounds implements image.Image: it returns the canvas rectangle of the file (origin at (0,0), size taken from the header).

func (*Webp) ColorModel

func (w *Webp) ColorModel() color.Model

ColorModel 实现 image.Image。解码器输出的是 straight alpha(非预乘) NRGBA 像素,因此这里固定返回 color.NRGBAModel。

ColorModel implements image.Image. The decoders produce straight alpha (non-premultiplied) NRGBA pixels, so color.NRGBAModel is returned unconditionally.

func (*Webp) DeleteRawChunk

func (w *Webp) DeleteRawChunk(name FourCC) int

DeleteRawChunk 从流中删除所有指定类型的 chunk 并返回删除数量。 既适用于原生解码的元数据 chunk(ICCP、EXIF、XMP),也适用于未知的 原始 chunk;删除后调用 WriteTo 写出的流不再包含这些 chunk。 DeleteRawChunk removes every chunk of the given type from the stream and returns how many chunks were removed. It works for the metadata chunks that are decoded natively (ICCP, EXIF, XMP) as well as for unknown raw chunks; a WriteTo afterwards writes the stream without those chunks.

func (*Webp) Frames

func (w *Webp) Frames(maxPixels int) ([]*image.NRGBA, []int, error)

Frames 逐帧解码动图,返回按画布尺寸合成的各帧(NRGBA)及其显示时长 (毫秒)。每帧先解码为静止图像再混合到画布上,随后克隆整块画布作为输出帧。 maxPixels 像素预算会透传给子解码器,用于限制单帧解码规模。

Frames decodes every frame of an animated file and returns the composited frames at canvas size together with their display duration in milliseconds. Each frame is decoded as a still image, blended onto the canvas, and the whole canvas is then cloned as the output frame. The maxPixels budget is forwarded to the sub-decoders to cap per-frame decoding size.

func (*Webp) GetOtherChunkByName

func (w *Webp) GetOtherChunkByName(name FourCC) ([]ChunkParse, error)

GetOtherChunkByName 返回为某类 chunk 记录的自定义解析器列表; 该类型没有记录时返回 ErrChunkNotFound。 GetOtherChunkByName returns the custom chunk parsers recorded for a type; it returns ErrChunkNotFound when nothing was recorded for that type.

func (*Webp) Image

func (w *Webp) Image() (image.Image, error)

Image 只解码一次并缓存结果,是 Webp 实现 image.Image 的基础: 首次调用触发完整解码,之后(含失败原因)都复用同一结果。

Image decodes the image once and caches the result; it backs the image.Image implementation of Webp. The first call triggers the full decode; later calls reuse the same result, including any error.

func (*Webp) MarshalBytes

func (w *Webp) MarshalBytes() ([]byte, error)

MarshalBytes returns the webp stream of the current state as a byte slice, see WriteTo for the details.

MarshalBytes 以字节切片返回当前状态的 webp 流,细节见 WriteTo。

func (*Webp) ParseChunk

func (w *Webp) ParseChunk(c ChunkParse, notSave ...bool) error

ParseChunk 把第一个类型与 c.ChunkName() 匹配的剩余 chunk 解析进 c。 解析成功后该 chunk 被视为已消费并从原始 chunk 列表移除;除非 notSave 为 true,解析器实例会记录到 OtherChunk,之后可用 GetOtherChunkByName 取回。找不到匹配 chunk 时返回 ErrChunkNotFound。 ParseChunk parses the first remaining chunk whose type matches c.ChunkName() into c. After a successful parse the chunk is consumed and removed from the raw chunk list; unless notSave is true, the parser instance is recorded in OtherChunk and can be retrieved later with GetOtherChunkByName. It returns ErrChunkNotFound when no chunk matches.

func (*Webp) RawChunkByName

func (w *Webp) RawChunkByName(name FourCC) ([]byte, bool)

RawChunkByName 返回第一个仍存在的指定类型 chunk 的原始负载; 不存在时 ok 为 false。 RawChunkByName returns the raw payload of the first still-present chunk of the given type; ok is false when no such chunk remains.

func (*Webp) RawChunks

func (w *Webp) RawChunks() []RawChunk

RawChunks 返回基础解析后仍以原始形式保留的所有 chunk,按其在文件中 出现的顺序排列。 RawChunks returns all chunks that are still left after base parsing, in the order they appear in the file.

func (*Webp) ToImage

func (w *Webp) ToImage() (image.Image, error)

ToImage 解码图像并以 image.Image 返回。无损文件走 VP8L 解码器,有损文件走 VP8 解码器并叠加 ALPH alpha 通道;动画文件返回第一帧。

ToImage decodes the image and returns it as an image.Image. Lossless files are decoded by the VP8L decoder, lossy files by the VP8 decoder with the ALPH alpha channel applied. For animated files the first frame is returned.

func (*Webp) ToImageWith

func (w *Webp) ToImageWith(opt DecodeOptions) (image.Image, error)

ToImageWith 按给定选项解码图像。

ToImageWith decodes the image with the given options.

func (*Webp) WriteTo

func (w *Webp) WriteTo(out io.Writer) (int64, error)

WriteTo writes the webp stream of the current state, implementing io.WriterTo. Chunks that were consumed while parsing are re-serialized from their parsed structs, so modifications made to the structs (or chunks added/deleted through AddRawChunk / DeleteRawChunk) are reflected, while the chunks that were left raw are copied verbatim. Parsing and writing back a file without touching anything reproduces it byte for byte.

The image payload (VP8 / VP8L / ALPH) is never re-encoded, so editing the metadata chunks does not touch the pixels at all.

WriteTo 把当前状态的 webp 流写出,实现 io.WriterTo。解析时被消费(consumed)的 chunk 会从解析结构体重新序列化,因此对结构体的修改、以及通过 AddRawChunk / DeleteRawChunk 增删的 chunk 都会反映到输出;未被消费、保持原始形态的 chunk 则逐字节原样拷贝。 不做任何修改地解析再写回,可逐字节复现原文件。

图像载荷(VP8 / VP8L / ALPH)永远不会被重新编码,因此编辑元数据 chunk 完全不触及像素数据。

type XMP

type XMP struct {
	// Data 是原始 XMP 字节。/ Data is the raw XMP bytes.
	Data []byte
}

XMP 携带内嵌的 XMP 元数据。/ XMP carries the embedded XMP metadata.

func (*XMP) ChunkName

func (x *XMP) ChunkName() FourCC

ChunkName 返回 XMP 的 chunk 类型码。/ ChunkName returns the XMP chunk type code.

func (*XMP) Parse

func (x *XMP) Parse(c *riffChunk) error

Parse 原样保留 XMP 载荷,不作解释。/ Parse keeps the XMP payload verbatim without interpreting it.

Directories

Path Synopsis
internal
vp8
vp8l
Package vp8l 实现 WebP 无损(VP8L)位流解码器,规范见 https://developers.google.com/speed/webp/docs/webp_lossless_bitstream_specification
Package vp8l 实现 WebP 无损(VP8L)位流解码器,规范见 https://developers.google.com/speed/webp/docs/webp_lossless_bitstream_specification
Package wasm 将纯 Go 实现的 webp 解码器暴露给 WebAssembly / JavaScript。
Package wasm 将纯 Go 实现的 webp 解码器暴露给 WebAssembly / JavaScript。
main command
Command wasm is the WebAssembly entry point.
Command wasm is the WebAssembly entry point.

Jump to

Keyboard shortcuts

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