simple_webp

package module
v0.2.8 Latest Latest
Warning

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

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

README

The Go gopher wearing a red scarf, holding up a framed picture

simple-webp

Pure-Go WebP decode + encode — VP8 lossy, VP8L lossless, ALPH, animation — plus a WebAssembly (WASM) decoder for the browser and Node.js. No cgo, no libwebp: the codec is written from scratch and returns standard image.Image (registered with image.RegisterFormat), and the only third-party module is github.com/pkg/errors for error wrapping.

CI Go Report Card OpenSSF Scorecard Go Reference Go version License Latest tag CGO disabled WASM

English | 简体中文

img, err := webp.Decode(r)          // VP8 lossy, VP8L lossless, ALPH
if err != nil {
    return err
}

var out bytes.Buffer
if err := webp.Encode(&out, img, &webp.EncodeOptions{}); err != nil { // lossless
    return err
}
// lossy instead: webp.EncodeLossy(&out, img, &webp.LossyOptions{Quality: 75})
  • Byte-exact rewriting: parse → edit (SetImage / AddFrame / AddRawChunk / DeleteRawChunk) → write; unknown chunks and untouched pixels survive unchanged, metadata chunks (ICCP/EXIF/XMP) are carried through and never re-encoded.
  • Complete codec: VP8/VP8L/ALPH/animation decoding plus an encoder with loop filter, target-size rate control, per-segment quantization, color transform, color cache, meta-Huffman groups and animation output.
  • WebAssembly (WASM): the decoder compiled to WASM for the browser and Node.js — decode WebP to RGBA for <canvas> with no libwebp and no cgo (wasm/).
  • Strict when you want it: ParseOptions.Strict enforces the container spec's MUST rules (chunk order, VP8X first, reserved bits, canvas and frame geometry, flags matching the payload); the default stays lenient.

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

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
Strict parsing ✅ ParseOptions.Strict checks the spec MUST rules (chunk order, VP8X first, VP8X/ALPH/ANMF reserved bits, canvas area, frame inside canvas, exactly one image chunk, VP8X flags matching the data, no ALPH on VP8L, zero padding); the default stays lenient
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, FrameAt decodes one frame without cloning the others
image interface ✅ ToImage(), *Webp implements image.Image, image.RegisterFormat("webp", ...), PixelFormatYCbCr for lossy stills, DecodeWebp decodes straight from an io.Reader, DecodeInto reuses a caller buffer
Metadata editing ✅ rewrite with ICCP/EXIF/XMP chunks, pixels never re-encoded, byte-exact when untouched; new chunks land where the spec puts their type
Container editing ✅ NewWebp + SetImage build a file from scratch, AddFrame/RemoveFrame edit animations, DeleteRawChunk removes consumed chunks, InvalidateCache drops the decode cache
VP8L lossless encode ✅ color-indexing / subtract-green / predictor / color transform / color cache / meta-Huffman groups / LZ77 / Huffman (≤15 bits); the cheaper of the variants is kept
VP8 lossy encode ✅ intra only: rate-distortion mode decision, 16x16 or 4x4 luma modes, 8x8 chroma modes, loop filter, target-size rate control, per-segment quantization (LossyOptions.Segment); lossy + alpha (VP8X+ALPH)
Animation encode ✅ EncodeAnimation writes VP8X+ANIM+ANMF, lossless or lossy frames, per-frame ALPH

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

New chunks go where the container spec puts their type: VP8X first, ICCP right after it and before ANIM, ANMF and the image data, ANIM before the first ANMF, ALPH before VP8 /VP8L, and EXIF, XMP or unknown types at the end. A single-instance chunk that is already present is refused (a second copy would make the file invalid), and so is a chunk combination the spec forbids — VP8 next to VP8L, ALPH next to VP8L. DeleteRawChunk also removes the chunks parsing already consumed (ALPH, ANIM, ANMF, VP8X, VP8, VP8L) and clears the matching VP8X flags. A removal that would leave the file invalid — the only image data, ANIM while frames remain, VP8X while an extended feature remains — is refused: nothing is deleted and the call returns 0. Unknown sub-chunks inside an ANMF frame keep their original position, so frame-level application data survives a parse/write cycle. An ICCP/EXIF/XMP/ALPH/ANIM assigned directly to its struct is written even when the source file had none; WriteTo returns an error for an odd frame position, a duration beyond 24 bits, an ANIM without frames or an ALPH next to a VP8L.

Decode straight from a stream
img, err := simple_webp.DecodeWebp(resp.Body, simple_webp.DecodeOptions{})

DecodeWebp parses and decodes from any io.Reader — a file, a network response or a bytes.Reader — streaming the container in chunk by chunk, so the caller never has to hold the whole file in memory first. (*Webp).DecodeInto decodes one still image into an *image.NRGBA you already own, which saves the result allocation when a pipeline decodes image after image; a size mismatch, a nil buffer or an animation is an error and leaves the buffer untouched.

Strict parsing
w, err := simple_webp.ParseWebpWith(r, simple_webp.ParseOptions{Strict: true, VerifySize: true})

Strict enables the spec MUST checks and reports the first violation; the default (and ParseWebp) stays lenient so real world files that break the rules still parse. The checks cover the order of the reconstructing chunks, the reserved bits of VP8X, ALPH and ANMF, the canvas area, the frame rectangles, the "exactly one image chunk" rule and the agreement between the VP8X feature flags and the data in the file.

Two things are deliberately not rejected, because the spec allows them: metadata and unknown chunks may appear out of order, and an ANIM chunk without the animation flag must be ignored rather than treated as an error.

Decode to YCbCr
img, err := w.ToImageWith(simple_webp.DecodeOptions{Format: simple_webp.PixelFormatYCbCr})

Lossy stills hand back the decoder's *image.YCbCr without the YUV to RGB conversion. Lossless bitstreams, alpha planes and animations have no YCbCr form and return ErrUnsupported.

Encode with metadata
err := simple_webp.Encode(out, img, &simple_webp.EncodeOptions{
    ICCP: iccProfile,
    EXIF: exifPacket,
    XMP:  xmpPacket,
})

The metadata turns into a flagged VP8X plus the chunks in container order; an encode without metadata stays byte identical to before. LossyOptions carries the same three fields.

Lossy + alpha, and animation
err := simple_webp.EncodeLossy(out, img, &simple_webp.LossyOptions{
    Quality:      80,
    FilterLevel:  20,              // VP8 loop filter 0..63; 0 derives it from the quality
    TargetBytes:  0,               // >0: pick the quality closest to a size
    AlphaFilter:  simple_webp.ALPHFilterGradient,
    Segment:      true,            // per-segment quantisation, can only shrink the file
})

A transparent input is written as VP8X(alpha) + ALPH + VP8 ; the alpha plane is lossless. Animations are written from a canvas and a frame list:

err := simple_webp.EncodeAnimation(out, &simple_webp.Animation{
    CanvasWidth: 320, CanvasHeight: 240,
    LoopCount: 0,
    Frames: []simple_webp.Frame{{Image: frame, Duration: 100, Blend: true}},
}, &simple_webp.AnimationOptions{Lossy: true})
Build or edit a container
w := simple_webp.NewWebp(0, 0)
w.SetImage(img, &simple_webp.ImageOptions{Lossy: true})
_ = w.AddFrame(frame)          // animations: append a frame
_ = w.RemoveFrame(0)           // …and drop one
w.InvalidateCache()            // after mutating parsed structs directly
out, err := w.MarshalBytes()
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())
}

Frames rejects a canvas larger than the pixel budget with ErrImageTooLarge, and because it returns every frame at once it also bounds canvas pixels × frames through FrameOptions.MaxTotalPixels; FramesWith(FrameOptions{...}) sets both, and the canvas is checked before any pixel buffer is allocated.

Encode
err := simple_webp.Encode(out, img, &simple_webp.EncodeOptions{
    Lossless: true,
    Method:   2, // 0 literals only / 1 +color-indexing+LZ77 / 2 +predictor
})

Lossy output goes through the VP8 encoder:

err := simple_webp.EncodeLossy(out, img, &simple_webp.LossyOptions{
    Quality: 80, // 0 (smallest) .. 100 (best); 0 means the default, 75
})

A transparent input makes the lossy encoder write the extended format (VP8X with the alpha flag, ALPH, VP8 ) with a lossless alpha plane; use EncodeBytes when you want lossless pixels instead.

The lossy encoder chooses its modes by rate distortion. Every candidate mode is predicted, transformed and quantized, and the candidates are scored on the distortion of the reconstruction plus lambda times the number of bits their tokens take; the cheapest one wins. The candidates are ranked by their absolute prediction error first, and only the cheapest ones are scored in full: two of the four 16×16 modes, and four of the ten 4×4 modes, which fall back to the single cheapest one when its error is already below a flatness threshold. The luma of a macroblock is either one 16×16 mode or sixteen 4×4 modes, whichever comes out cheaper, and the chroma uses one of the four 8×8 modes. On a set of five test images the size at matched PSNR drops by 6.7% against plain SAD mode selection (a 320×240 photo −3%, a smooth gradient −14%, a sharp synthetic image −38%, noise −0%).

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 }

WebAssembly (WASM): decode WebP in the browser and Node

wasm/ is a decoder only WebAssembly (WASM) build: a WebP decoder for the browser and Node.js that turns a WebP byte stream into RGBA pixels ready for <canvas>, with no libwebp, no cgo and no re-encoding. 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.

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
strict.go      spec MUST validation behind ParseOptions.Strict
edit.go        NewWebp / SetImage / AddFrame / RemoveFrame / InvalidateCache
writer.go      WriteTo / MarshalBytes (in-order write back, lengths & padding recomputed)
container_write.go  RIFF assembly shared by the encoders
decode.go      ToImage / ToImageWith / lossy & lossless dispatch / YCbCr output
alpha.go       ALPH inflate, alpha row-filter inverse transform, compositing
alpha_encode.go  ALPH payload: filtering and VP8L compression of alpha
animation.go   ANMF frame compositing (blend / dispose), FrameAt / AnimationInfo
animation_encode.go  animation writer (VP8X + ANIM + ANMF)
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
encode_lossy.go  image.Image → VP8 (+ ALPH) → RIFF
internal/vp8/  VP8 lossy decoder + encoder (bool coder, prediction, IDCT, loop filter, RD mode decision, rate control, segment quantisation)
internal/vp8l/ VP8L lossless decoder + encoder (bitstream, Huffman, LZ77, transforms, colour cache, colour transform, meta-Huffman)
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 are checked by decoding them with this library and by the strict container validation; a fresh libwebp cross-check was not possible in the environment this series was developed in, so it is not claimed here.

Benchmark

Windows/amd64, go test -run XXX -bench . -benchtime 200ms, re-measured after the container and encoder work in this series:

Scenario simple-webp reference (x/image)
container parse (320×240, 37 KB) 17.3 µs (2.2 GB/s) —
VP8L lossless decode (320×240, 320×240 photo) 2.15 ms (35.7 MP/s) 1.61 ms (47.6 MP/s)
VP8 lossy decode (400×300) 0.88 ms (136 MP/s) 0.67 ms (179 MP/s)
VP8L lossless encode (320×240) 35.6 ms (2.16 MP/s) not supported

The numbers above come from one run on the machine used for this series and are rounded; earlier libwebp comparisons were measured on a different machine and are not reproduced here because cwebp/dwebp are not available in this environment.

Lossless decoding is slower than the reference (bit-by-bit Huffman, no table-driven fast path yet); core VP8 decoding is slower after the loop filter was added on the encode side, which does not change the decoder. Lossless encoding now tries the colour-transform, colour-cache and meta-Huffman variants and keeps the shortest stream, so it is slower than before and its output is smaller; the size ratio against cwebp was not re-measured here.

SIMD kernels

A VP8 decode ends with two per-pixel loops: the 9:3:3:1 "fancy" chroma upsampling and the BT.601 YUV to RGBA conversion. Together they were about a third of a 400×300 decode on the machine used for this series, so both now dispatch to a per-architecture kernel while the original loops stay in the tree as the reference implementation and as the fallback.

  • the seam: decodeBand hands a whole row to yuvRowToNRGBA, and upsampleChromaRow keeps its signature but runs a kernel over the interior samples, leaving the two horizontally clamped ends to scalar code
  • amd64: AVX2 behind a CPUID + XGETBV probe rather than behind GOAMD64, so a single binary still runs on any x86-64 machine (yuvrgb_amd64.s, chroma_amd64.s)
  • wasm: SIMD128 written as raw WebAssembly stack instructions over v128 locals (yuvrgb_wasm.s, chroma_wasm.s); there is no runtime probe, the host decides whether SIMD128 exists
  • arm64: a NEON chroma kernel (chroma_arm64.s); the YUV kernel stays scalar because Go's arm64 assembler lacks VMOVL/VMLAL/VQMOVN/VMIN/VMAX
  • every other target, and any build with -tags purego, keeps the scalar loops
Kernel, 1920-pixel row scalar AVX2 (amd64) SIMD128 (wasm)
upsampleChromaRow 3.1 µs 0.23 µs (13.6×) 0.52 µs (18×)
yuvRowToNRGBA 8.9 µs 0.68 µs (13×) 1.53 µs (14×)

End to end, photo_real.webp (400×300) decodes in 0.88 ms instead of 1.11 ms on amd64 (1.26×); under Node 22 the same file drops from 6.75 ms to 2.38 ms (2.8×). Lossless decoding and encoding are untouched.

Output is unchanged: the decode fixtures still compare pixel for pixel, and TestYUVRowMatchesScalar / TestChromaRowMatchesScalar compare every kernel against its scalar reference across row lengths 1..257 and chroma widths 1..48. go test -bench 'YUVRow|ChromaRow' reproduces the kernel numbers, and -tags purego reproduces the scalar path on any machine.

Known limitations

  • EncodeOptions.Lossless is accepted but ignored: Encode always writes lossless (VP8L) and EncodeLossy is the lossy path. The flag cannot be honoured as it stands because its zero value is false, so every caller that never set it would silently switch to lossy
  • Lossy encoding is intra only. The VP8 encoder picks its modes by rate distortion: every candidate goes through prediction, the forward transform and quantisation, and the candidates are scored on the quantised distortion plus the exact number of bits their tokens would take. The luma uses either one 16x16 mode or sixteen 4x4 modes, whichever is cheaper and the chroma uses 8x8 modes. The loop filter and a target-size rate control are in; the rate control moves the global quantiser only, and LossyOptions.Segment adds per-segment steps on top (opt-in, and the encoder races the segmented and unsegmented plans so it can only shrink a file), so the size at a given quality still depends on the content. The quality to quantiser curve itself follows libwebp's, so a given Quality selects about the same step as the matching cwebp -q
  • Lossy encoding runs twice: the first pass collects token statistics and the second encodes with probabilities adapted to them, which costs about 1.8x the time of a single pass. The mode search runs only in the first pass, the second reuses the modes it found. With Segment on, the encoder races three plans (unsegmented, regular step, double step), so it costs up to three times that again
  • The rate distortion search is what makes the encoder slow: scoring ten 4x4 modes for every block of every macroblock means running the transform and the quantiser once per candidate
  • Lossless encoding now encodes up to four variants (colour transform on or off, colour cache, meta-Huffman groups) and keeps the shortest stream, so it costs several times the old single-pass encode; the container tests only assert the decoded pixels, not the exact bytes
  • The meta-Huffman implementation uses two groups of 16×16 blocks clustered by their symbol histograms. It wins on content whose regions use different symbol sets, but with the Method 2 predictor a single tree set usually wins, in which case the stream is left unchanged
  • ALPH preprocessing=1 (level reduction) is passed through as-is, without the reference dithering; cwebp does not produce that mode by default. The encoder writes preprocessing=0 and does not quantise alpha at all
  • Lossless Huffman decodes bit by bit — large images slower than the reference
  • DeleteRawChunk reports how many chunks it removed but not why a removal was refused: 0 means either no chunk of that type was present or the removal would have left the file invalid
  • DeleteRawChunk cannot report an error because its signature returns an int; a refused removal therefore looks like "nothing removed"
  • 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

Roadmap

The lossy encoder is intra only by design, and the WebP bitstreams themselves are conformant: the VP8 and VP8L encoders produce streams that an independent decoder (golang.org/x/image, a port of libwebp) reproduces sample for sample. What is left between this library and cwebp is listed below, ordered by "how much it costs to fix" versus "how much it buys".

The quality to quantiser curve now follows libwebp's — a piecewise linear map followed by a cube root — so a given Quality buys roughly the same step as the matching cwebp -q. On a smooth 320×240 test image the same quality setting now reaches about 1.5–3.7 dB more PSNR than the old linear rule did; both paths meet at quality 100.

LossyOptions.Segment adds per-segment quantization on top: the frame is split into four segments by macroblock activity and the flat side is quantised one or two steps coarser while busy regions keep the base step. The encoder races the unsegmented, regular-step and double-step plans and keeps the smallest stream, so the option can only shrink a file, never grow it. The gain is content dependent — flat regions are skipped outright by this encoder and gain nothing, so the win shows on low amplitude texture (about 5 % on the noise fixture, nothing on pure gradients).

# Item Where it stands Plan
1 Cost model for token probability updates adaptTokenProb uses a flat 12 bit gain threshold and never looks at tokenUpdateProb. About 40 % of the table entries carry an update probability of 255, which RFC 6386 §13.5 marks as "never updated"; signalling those costs ~16 bits, not the ~9 the model assumes, so an update can end up making the stream larger Price the signalling per entry, as libwebp does in enc/frame_enc.c
2 First partition size overflow The 19 bit first-partition length in the frame tag is neither masked nor range checked, so a very large canvas silently corrupts the stream Return an error when it does not fit, like libwebp's VP8_ENC_ERROR_PARTITION0_OVERFLOW
3 LossyOptions.Sharpness internal/vp8 writes the RFC's 3 bit sharpness_level field correctly, but LossyOptions has no such field, so the value can never be set from outside the package Add the field to LossyOptions (and fix the comment: like cwebp -sharpness, 0 is the sharpest)
4 Animation encoder ignores AlphaFilter The static lossy path honours LossyOptions.AlphaFilter; the animation path hardcodes ALPHFilterNone Read LossyOpt.AlphaFilter in the animation encoder too
5 Animation Duration overflow The 24 bit field is written with a silent truncation (1<<24 becomes 0, a negative value wraps) Validate the range before encoding and return an error
6 VP8L colour indexing alpha The spec sets every alpha of the index image to 255; the encoder writes 0. Decoding is unaffected because the inverse transform substitutes the whole pixel Write 255
7 Duplicate VP8L transforms The spec allows each transform type once; the decoder accepts repeats, libwebp rejects them as corrupt Reject duplicate transform types while decoding
8 VP8L two dimensional distance codes The decoder implements the full 120 entry neighbourhood map, but the encoder always writes a plain linear distance: a one pixel distance costs ~10 bits where a neighbourhood code costs 1. This is the largest single source of the remaining lossless size gap Add DistanceToPlaneCode and the lookup table to the encoder, as in libwebp's src/enc/vp8l_enc.c
9 More Huffman groups and better clustering The meta prefix codes use two groups; libwebp clusters into more groups and also handles the mixed photo/text case that way Cluster into more groups
10 Animation frame differencing Every animation frame is encoded in full: there is no frame rectangle cropping and no inter-frame differencing Add differencing and minimisation, as in libwebp's WebPAnimEncoder
11 Multi-token partitions The frame still uses a single residual partition, which limits decoder parallelism but not size Emit more than one partition

A few further libwebp capabilities are private to it and are not part of the WebP bitstream, so the spec does not ask for them: -near_lossless source pre-filtering, -alpha_q lossy alpha, -sns spatial noise shaping, -pass multi-pass optimisation, -partition_limit and -mt multi-threading. On the decode side the library streams the container from any io.Reader (DecodeWebp) and can decode into a caller buffer (DecodeInto), but it has no append-driven incremental decoder (libwebp's WebPIDecoder) and no scaled or cropped decode yet.

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
  • a chunk payload is streamed into a growing buffer instead of being allocated from its declared length, so a tiny file whose only chunk claims 1 GB fails without that allocation; a chunk that promises more data than the stream holds is an error rather than a silent end of stream
  • ParseOptions.MaxChunkSize (default 1<<30) tightens the per-chunk cap for untrusted input; the nested sub-chunks inside an ANMF are bound by it too
  • data after the RIFF File Size is ignored, as the spec allows; verifySize still rejects a stream whose size field does not match the actual length
  • Frames checks the VP8X canvas against the budget before allocating it and bounds the total canvas × frames pixels, so a 16383×16383 canvas carrying a 1×1 frame is rejected without the allocation
  • 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})

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.

The Go gopher was designed by Renee French and is licensed under CC BY 3.0. The logo in docs/logo.svg is a derivative work based on that design.

Documentation

Overview

Package simple_webp 解析与编辑 WebP(RIFF)容器:读取 RIFF 头与全部 chunk, 解码容器级 chunk(VP8X、VP8、VP8L、ALPH、ANIM、ANMF、ICCP、EXIF、XMP), 并支持在不重新编码像素的前提下增删元数据 chunk、按原始顺序写回整个流。 纯 Go 实现,不依赖 cgo 与 libwebp;浏览器与 Node.js 用的 WebAssembly (WASM)解码前端在 wasm 子包里。

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. It is pure Go — no cgo, no libwebp — and the WebAssembly (WASM) decoder front end for the browser and Node.js lives in the wasm subpackage.

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 DecodeWebp added in v0.2.8

func DecodeWebp(r io.Reader, opt DecodeOptions) (image.Image, error)

DecodeWebp 从任意 io.Reader 流式解析并解码一个 WebP 文件,等价于 ParseWebp(r) 后接 ToImageWith(opt)。容器按 chunk 逐个流入,调用方不需要 事先把整个文件读进内存——文件、网络流、bytes.Reader 都可以直接喂进来。

DecodeWebp parses and decodes a webp file from any io.Reader, the streaming counterpart of ParseWebp(r) followed by ToImageWith(opt). The container streams in chunk by chunk, so the caller never needs the whole file in memory: files, network streams and bytes.Reader all feed straight in.

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 EncodeAnimation added in v0.2.7

func EncodeAnimation(w io.Writer, a *Animation, opt *AnimationOptions) error

EncodeAnimation 把 a 编码为动图 WebP 并写入 w。

EncodeAnimation writes a as an animated WebP file.

func EncodeAnimationBytes added in v0.2.7

func EncodeAnimationBytes(a *Animation, opt *AnimationOptions) ([]byte, error)

EncodeAnimationBytes 把 a 编码为完整的动图 WebP 文件并保留在内存中返回。 每帧独立编码(不做帧间差分),带透明度的帧写出 ALPH 子 chunk,容器顺序为 VP8X、ANIM、ANMF…;画布尺寸、偶数坐标与帧矩形落在画布内都会在编码前校验。

EncodeAnimationBytes encodes a as an animated WebP file held in memory. Every frame is encoded on its own (no inter-frame differencing), a transparent frame carries an ALPH sub-chunk, and the container order is VP8X, ANIM, ANMF…; the canvas size, the even positions and the frame rectangles are validated before encoding.

func EncodeBytes

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

EncodeBytes 把 m 编码为完整的无损 WebP 文件并保留在内存中返回。 非空的 ICCP/EXIF/XMP 直接随文件写出;三者都为空时输出仍是不带 VP8X 的简单容器。

EncodeBytes encodes m as a lossless WebP file held in memory. Non-empty ICCP/EXIF/XMP are written straight into the file; when all three are empty the output is still the simple container without a VP8X chunk.

func EncodeLossy added in v0.2.2

func EncodeLossy(w io.Writer, m image.Image, opt *LossyOptions) error

EncodeLossy 把 m 编码为有损 WebP 文件并写入 w。

EncodeLossy writes m as a lossy WebP file.

func EncodeLossyBytes added in v0.2.2

func EncodeLossyBytes(m image.Image, opt *LossyOptions) ([]byte, error)

EncodeLossyBytes 把 m 编码为完整的有损 WebP 文件并保留在内存中返回。 非空的 ICCP/EXIF/XMP 直接随文件写出;三者都为空时输出仍是不带 VP8X 的简单容器。

带透明度的输入写成扩展格式:VP8X(Alpha 位)+ ALPH + VP8,alpha 平面无损 压缩(或按 AlphaUncompressed 原样存储),完全不透明的输入仍走简单格式。

EncodeLossyBytes encodes m as a lossy WebP file held in memory. Non-empty ICCP/EXIF/XMP are written straight into the file; when all three are empty the output is still the simple container without a VP8X chunk.

An input with transparency is written in the extended format: VP8X (alpha flag) + ALPH + VP8, with the alpha plane compressed losslessly (or stored verbatim with AlphaUncompressed); a fully opaque input still uses the simple format.

A Quality outside 0..100 returns an error rather than being clamped.

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
	// contains filtered or unexported fields
}

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 *RawChunk) 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 *RawChunk) 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
	// contains filtered or unexported fields
}

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 *RawChunk) 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 Animation added in v0.2.7

type Animation struct {
	// CanvasWidth、CanvasHeight 是画布尺寸,帧矩形必须落在画布内。
	//
	// CanvasWidth and CanvasHeight are the canvas size; every frame must fit.
	CanvasWidth, CanvasHeight int
	// BackgroundColor 是背景色,写在 ANIM 里。
	//
	// BackgroundColor is written into ANIM.
	BackgroundColor color.NRGBA
	// LoopCount 是循环次数,0 表示无限循环。
	//
	// LoopCount is the number of repeats; 0 loops forever.
	LoopCount uint16
	// Frames 按显示顺序排列,至少一帧。
	//
	// Frames are in display order and hold at least one frame.
	Frames []Frame
}

Animation 是 EncodeAnimation 的输入:画布参数与帧序列。

Animation is the EncodeAnimation input: the canvas parameters and the frames.

type AnimationInfo added in v0.2.7

type AnimationInfo struct {
	// Width、Height 是画布的像素尺寸。/ Width and Height are the canvas size in pixels.
	Width, Height int
	// BackgroundColor 是 ANIM 声明的背景色(straight alpha)。
	// BackgroundColor is the background colour ANIM declares (straight alpha).
	BackgroundColor color.NRGBA
	// LoopCount 是最后一帧之后的循环次数,0 表示无限循环。
	// LoopCount is how often playback repeats after the last frame; 0 loops forever.
	LoopCount uint16
	// FrameCount 是 ANMF 帧的数量。/ FrameCount is the number of ANMF frames.
	FrameCount int
}

AnimationInfo 描述一段动画的画布参数与帧数,不涉及像素解码。

AnimationInfo describes the canvas parameters and the frame count of an animation without decoding any pixels.

type AnimationOptions added in v0.2.7

type AnimationOptions struct {
	// Lossy 为 true 时每帧用 VP8 有损编码,否则用 VP8L 无损编码。
	//
	// Lossy encodes every frame with VP8; false uses VP8L lossless.
	Lossy bool
	// Encode 是无损帧的编码选项。/ Encode holds the lossless options.
	Encode *EncodeOptions
	// LossyOpt 是有损帧的编码选项。/ LossyOpt holds the lossy options.
	LossyOpt *LossyOptions
}

AnimationOptions 选择每一帧的编码方式。

AnimationOptions selects how every frame is encoded.

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 *RawChunk) 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 选择返回的像素布局。PixelFormatYCbCr 只适用于没有 alpha 平面的
	// 静态有损码流,其余情况返回 ErrUnsupported。
	//
	// Format selects the returned pixel layout. PixelFormatYCbCr applies only to
	// a still lossy bitstream without an alpha plane; other cases return
	// ErrUnsupported.
	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 *RawChunk) error

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

type EncodeOptions

type EncodeOptions struct {
	// Lossless 仅为兼容保留:Encode 始终写出无损 WebP,需要有损输出请改用
	// EncodeLossy。之所以不按该值分叉,是因为它的零值是 false——一旦生效,
	// 所有没有显式设置它的调用方都会静默地从无损变成有损。
	//
	// Lossless is kept for compatibility: Encode always writes lossless webp and
	// EncodeLossy is the lossy entry point. The value is not honoured because
	// its zero value is false, so making it meaningful would silently turn every
	// caller that never set it into a lossy encoder.
	Lossless bool
	// Method 以速度换压缩率:0 只存储字面量,1 追加减绿(subtract green)
	// 变换、颜色索引和 LZ77,2(默认)再应用预测器变换。取值必须是 0、1 或 2;
	// 越界的 Method 不会被静默兜底,而是返回错误。
	//
	// 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. Only 0, 1 and 2 are accepted; an
	// out-of-range Method is not silently fallen back to the default but
	// returns an error.
	Method int
	// ICCP 是内嵌的 ICC 色彩配置原始字节;非空时写出带标志位的 VP8X 与 ICCP。
	//
	// ICCP is the embedded ICC profile; when it is non-empty a flagged VP8X and
	// an ICCP chunk are written.
	ICCP []byte
	// EXIF 是内嵌的 EXIF 元数据原始字节;非空时写出带标志位的 VP8X 与 EXIF。
	//
	// EXIF is the embedded EXIF metadata; when it is non-empty a flagged VP8X
	// and an EXIF chunk are written.
	EXIF []byte
	// XMP 是内嵌的 XMP 元数据原始字节;非空时写出带标志位的 VP8X 与 XMP。
	//
	// XMP is the embedded XMP metadata; when it is non-empty a flagged VP8X and
	// an XMP chunk are written.
	XMP []byte
}

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 Frame added in v0.2.7

type Frame struct {
	// Image 是这一帧的像素,尺寸即帧矩形的大小。
	//
	// Image holds the frame pixels; its size is the frame rectangle.
	Image image.Image
	// X、Y 是帧左上角在画布上的坐标,必须是偶数(文件中按一半存储)。
	//
	// X and Y are the frame's top-left position, which must be even (the file
	// stores them halved).
	X, Y int
	// Duration 是这一帧的显示时长,单位毫秒。
	//
	// Duration is how long the frame is shown, in milliseconds.
	Duration int
	// Blend 为 true 时帧与画布按 alpha 混合,为 false 时直接覆盖。
	//
	// Blend composites the frame onto the canvas with alpha; false overwrites.
	Blend bool
	// Dispose 为 true 时本帧显示完后把画布清为背景色。
	//
	// Dispose clears the canvas to the background colour after the frame.
	Dispose bool
}

Frame 是 EncodeAnimation 的一帧输入:画布上的位置、显示时长、混合与清理 方式,以及这一帧的像素。

Frame is one input frame of EncodeAnimation: its position on the canvas, its display duration, its blend and disposal mode, and its pixels.

type FrameOptions added in v0.2.7

type FrameOptions struct {
	// MaxPixels 拒绝画布像素数超过该值的动画,0 表示 defaultMaxPixels。
	// 画布尺寸来自 VP8X,可能远大于实际帧,所以它必须单独受预算约束。
	//
	// MaxPixels rejects animations whose canvas is larger; 0 uses
	// defaultMaxPixels. The canvas comes from VP8X and can be far larger than
	// the frames themselves, so it needs a budget of its own.
	MaxPixels int
	// MaxTotalPixels 拒绝画布像素 × 帧数超过该值的动画,0 表示
	// defaultMaxTotalPixels。Frames 会一次性返回全部帧,总量必须有上限。
	//
	// MaxTotalPixels rejects animations whose canvas times frame count exceeds
	// it; 0 uses defaultMaxTotalPixels. Frames returns every frame at once, so
	// the total has to be bounded.
	MaxTotalPixels int
}

FrameOptions 调节动图解码。

FrameOptions tunes animation decoding.

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 *RawChunk) error

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

type ImageOptions added in v0.2.7

type ImageOptions struct {
	// Lossy 为 true 时用 VP8 有损编码,否则用 VP8L 无损编码。
	//
	// Lossy selects the VP8 lossy encoder; false selects VP8L lossless.
	Lossy bool
	// Encode 是无损编码选项,Lossy 为 false 时使用。
	//
	// Encode holds the lossless options, used when Lossy is false.
	Encode *EncodeOptions
	// LossyOpt 是有损编码选项,Lossy 为 true 时使用。
	//
	// LossyOpt holds the lossy options, used when Lossy is true.
	LossyOpt *LossyOptions
}

ImageOptions 选择 SetImage 的编码方式。

ImageOptions selects how SetImage encodes the pixels.

type LossyOptions added in v0.2.2

type LossyOptions struct {
	// Quality 取 0(体积最小)到 100(质量最高);超出 0..100 返回错误,不静默钳制。
	// 零值默认等同于 75,除非 QualityZero 为 true。
	//
	// Quality runs from 0 (smallest) to 100 (best); a value outside 0..100
	// returns an error instead of being silently clamped. The zero value means
	// the default 75 unless QualityZero is set.
	Quality int
	// QualityZero 让 Quality 的零值表示「质量 0」而不是默认的 75。
	// 不设置时保持旧语义:Quality == 0 等于默认质量。
	//
	// QualityZero makes the zero value of Quality mean quality 0 instead of
	// the default 75. When it is false the old meaning stays: Quality == 0
	// selects the default quality.
	QualityZero bool
	// ICCP 是内嵌的 ICC 色彩配置原始字节;非空时写出带标志位的 VP8X 与 ICCP。
	//
	// ICCP is the embedded ICC profile; when it is non-empty a flagged VP8X and
	// an ICCP chunk are written.
	ICCP []byte
	// EXIF 是内嵌的 EXIF 元数据原始字节;非空时写出带标志位的 VP8X 与 EXIF。
	//
	// EXIF is the embedded EXIF metadata; when it is non-empty a flagged VP8X
	// and an EXIF chunk are written.
	EXIF []byte
	// XMP 是内嵌的 XMP 元数据原始字节;非空时写出带标志位的 VP8X 与 XMP。
	//
	// XMP is the embedded XMP metadata; when it is non-empty a flagged VP8X and
	// an XMP chunk are written.
	XMP []byte
	// AlphaFilter 选择 ALPH 的行滤波方式:0 不滤波(默认)、1 水平、2 垂直、
	// 3 梯度;其它值返回错误。滤波只影响 alpha 的压缩率,不改变像素值。
	//
	// AlphaFilter selects the ALPH row filter: 0 none (the default), 1
	// horizontal, 2 vertical, 3 gradient; any other value is an error. The
	// filter changes only the compressed size, never the alpha values.
	AlphaFilter uint8
	// AlphaUncompressed 为 true 时 alpha 平面按 C=0 原样存储;默认按 C=1 用
	// VP8L 无损压缩。两种方式都不量化 alpha。
	//
	// AlphaUncompressed stores the alpha plane verbatim (C=0); by default it is
	// compressed losslessly with VP8L (C=1). Neither mode quantises the alpha.
	AlphaUncompressed bool
	// FilterLevel 是 VP8 环路滤波强度 1..63;0(默认)按质量自动选择,
	// -1 关闭滤波。强度越大块效应越弱,细节也更软。
	//
	// FilterLevel is the VP8 loop filter strength, 1..63. Zero (the default)
	// derives it from the quality, -1 disables the filter. A stronger filter
	// removes more blocking and softens detail.
	FilterLevel int
	// SimpleFilter 为 true 时用简单滤波(只处理亮度边缘),否则用普通滤波。
	//
	// SimpleFilter selects the simple filter (luma edges only).
	SimpleFilter bool
	// TargetBytes 打开码率控制:在质量 0..100 之间二分,取最接近该体积的编码
	// 结果(只对 VP8 码流生效,alpha 与元数据不计入)。0 表示按 Quality 编码。
	//
	// TargetBytes enables rate control: the quality is bisected over 0..100 and the
	// encode closest to that size wins (it covers the VP8 bitstream only, alpha
	// and metadata are not counted). 0 encodes at Quality.
	TargetBytes int
	// Segment 打开逐段量化:按宏块活动度把帧分成四段,平坦区域用更粗的
	// 量化、繁忙区域保持基础量化。编码器会同时试一遍不分段的版本并只保留
	// 更小的,体积不会大于关闭时。默认关闭。
	//
	// Segment enables per-segment quantization: the frame is split into four
	// segments by macroblock activity, flat regions getting a coarser
	// quantizer while busy ones keep the base. The encoder also tries one
	// unsegmented pass and keeps the smaller stream, so the size never grows
	// over having it off. It is off by default.
	Segment bool
}

LossyOptions 调节有损(VP8)编码器的行为。

LossyOptions tunes the lossy (VP8) encoder.

type ParseOptions added in v0.2.7

type ParseOptions struct {
	// Strict 打开规范 MUST 校验:VP8X 必须第一个、重建类 chunk 顺序、
	// VP8X 保留位为 0、动画位必须有 ANIM、帧矩形落在画布内、VP8L 帧不得带
	// ALPH、填充字节为 0。默认关闭,以免拒绝真实世界里的脏文件。
	//
	// Strict enables the spec MUST checks: VP8X first, the order of the
	// reconstructing chunks, zeroed VP8X reserved bits, an ANIM chunk behind
	// the animation flag, frames inside the canvas, no ALPH on a VP8L frame,
	// and zero padding bytes. It is off by default so real world files that
	// violate these rules still parse.
	Strict bool
	// VerifySize 要求 RIFF File Size 与实际流长度一致,也就是 ParseWebp 的
	// verifySize 参数。
	//
	// VerifySize requires the RIFF File Size to match the actual stream length,
	// the verifySize argument of ParseWebp.
	VerifySize bool
	// MaxChunkSize 限制单个 chunk 载荷的字节数,0 表示默认的 1<<30。
	// 解析不可信输入时调小它可以更早拒绝畸形文件;载荷按流式读入,
	// 超限的 chunk 在分配任何内存之前就被拒绝。
	//
	// MaxChunkSize caps the payload of a single chunk in bytes; 0 means the
	// default 1<<30. Turn it down when parsing untrusted input to reject
	// malformed files earlier; payloads are streamed in, so an oversized
	// chunk is refused before any memory is allocated for it.
	MaxChunkSize int
}

ParseOptions 调节容器解析。零值等价于 ParseWebp 的宽松行为。

ParseOptions tunes container parsing. The zero value matches the lenient behaviour of ParseWebp.

type PixelFormat

type PixelFormat int

PixelFormat 选择解码结果的像素布局:NRGBA(默认)、RGBA,或有损码流的 YCbCr 平面。

PixelFormat selects the pixel layout of the decoded image: NRGBA (the default), RGBA, or the YCbCr planes of a lossy bitstream.

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
	// PixelFormatYCbCr 返回有损码流解码得到的 *image.YCbCr(4:2:0),不做 YUV→RGB
	// 转换。无损码流、带 alpha 平面的图以及动画没有 YCbCr 表示,会返回 ErrUnsupported。
	//
	// PixelFormatYCbCr returns the *image.YCbCr (4:2:0) planes decoded from a
	// lossy bitstream without a YUV to RGB conversion. Lossless bitstreams,
	// images with an alpha plane and animations have no YCbCr representation and
	// return ErrUnsupported.
	PixelFormatYCbCr
)

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 *RawChunk) 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 *RawChunk) 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 *RawChunk) 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 NewWebp added in v0.2.7

func NewWebp(width, height int) *Webp

NewWebp 创建一个空容器,画布尺寸为 width × height。容器没有任何 chunk, 需要先用 SetImage 或 AddFrame 放入图像数据再 WriteTo;写一个空容器会报错。

NewWebp creates an empty container with a width times height canvas. It holds no chunk yet, so SetImage or AddFrame has to supply the image data before WriteTo; writing an empty container is an error.

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 ParseWebpBytesWith added in v0.2.7

func ParseWebpBytesWith(b []byte, opt ParseOptions) (*Webp, error)

ParseWebpBytesWith 按给定选项解析内存中的 WebP 字节流,见 ParseOptions。

ParseWebpBytesWith parses a webp stream held in memory with the given options, see ParseOptions.

func ParseWebpWith added in v0.2.7

func ParseWebpWith(r io.Reader, opt ParseOptions) (*Webp, error)

ParseWebpWith 按给定选项解析 WebP 流,见 ParseOptions。

ParseWebpWith parses a webp stream with the given options, see ParseOptions.

func (*Webp) AddFrame added in v0.2.7

func (w *Webp) AddFrame(f *ANMF) error

AddFrame 把一帧追加到动画容器末尾:帧坐标必须为偶数、帧矩形必须落在画布内, 帧内必须有 VP8 或 VP8L 码流。容器里已有静态图像时返回错误,尚未有 ANIM 时 会自动补一个默认的 ANIM。

AddFrame appends a frame to an animated container: the frame position must be even, the frame rectangle must fit the canvas and the frame must carry a VP8 or VP8L bitstream. A container that already holds a still image is an error; a missing ANIM chunk is created with default values.

func (*Webp) AddRawChunk

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

AddRawChunk 把一个新的原始 chunk 按类型排位插入流中:跳过所有排位不大于 自身的已有 chunk,即 VP8X 最前,ICCP 在 VP8X 之后、ANIM/ANMF/图像数据之前, ANIM 在第一个 ANMF 之前,ALPH 在 ANIM/ANMF 之后、VP8/VP8L 之前,VP8/VP8L 之后是 EXIF、XMP 等尾部 chunk;排位 6 一律追加到末尾。可用于给已解析的文件 补充 EXIF 或 XMP 等 chunk。name 必须是合法 FourCC,data 不得超过 maxChunkSize,且 Webp 必须来自成功的 ParseWebp。

单实例 chunk(VP8X/VP8/VP8L/ALPH/ANIM/ICCP/EXIF/XMP)在流中已存在时拒绝 重复插入——两份会让文件非法;ANMF 可以追加,但载荷必须是一帧合法的帧 (手工构造帧请用 AddFrame)。

AddRawChunk inserts a new raw chunk at the position its type dictates: it skips every existing chunk whose rank is not greater than its own, so VP8X comes first, ICCP follows VP8X and precedes ANIM, ANMF and the image data, ANIM precedes the first ANMF, ALPH follows ANIM/ANMF and precedes VP8/VP8L, and EXIF, XMP or unknown types land at the end. 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.

A single-instance chunk (VP8X/VP8/VP8L/ALPH/ANIM/ICCP/EXIF/XMP) that is already present is refused, because a second copy makes the file invalid; an ANMF may be appended, but its payload has to be a valid frame (use AddFrame to build one).

func (*Webp) AnimationInfo added in v0.2.7

func (w *Webp) AnimationInfo() (AnimationInfo, error)

AnimationInfo 返回动画的画布尺寸、背景色、循环次数与帧数;非动画文件返回错误。

AnimationInfo returns the canvas size, background colour, loop count and frame count of the animation; a non-animated file is an error.

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) DecodeInto added in v0.2.8

func (w *Webp) DecodeInto(dst *image.NRGBA) error

DecodeInto 把一张静态图解到调用方提供的 NRGBA 缓冲里:除色度上采样的暂存 行外不再分配图像内存,连续解码多张图时可以复用同一块缓冲。dst 的边界必须 恰好是 image.Rect(0, 0, w.Width, w.Height);尺寸不符、nil 目标或动画文件 都会返回错误,dst 内容不被修改。像素预算取默认值(1<<27 个像素)。

DecodeInto decodes one still image into an NRGBA buffer supplied by the caller: apart from the chroma upsampling scratch rows no image memory is allocated, so a pipeline that decodes image after image can reuse one buffer. The bounds of dst must be exactly image.Rect(0, 0, w.Width, w.Height); a size mismatch, a nil target or an animated file is an error and leaves dst untouched. The pixel budget is the default one (1<<27 pixels).

func (*Webp) DeleteRawChunk

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

DeleteRawChunk 从流中删除所有指定类型的 chunk 并返回删除数量。 既适用于原生解码的元数据 chunk(ICCP、EXIF、XMP),也适用于解析时已被 消费的 ALPH、ANIM、ANMF、VP8X、VP8、VP8L,以及未知的原始 chunk;删除后 调用 WriteTo 写出的流不再包含这些 chunk,VP8X 的对应标志位同步清除, 保持标志与数据一致。三条例外都会产出非法流,因此不删除任何东西,返回 0: 还有 ANMF 帧时删 ANIM、还有扩展特性时删 VP8X、以及删掉唯一的图像数据。 DeleteRawChunk removes every chunk of the given type from the stream and returns how many chunks were removed. It covers the metadata chunks decoded natively (ICCP, EXIF, XMP), the chunks already consumed by parsing (ALPH, ANIM, ANMF, VP8X, VP8, VP8L), and unknown raw chunks; a WriteTo afterwards writes the stream without those chunks, and the matching VP8X flag bit is cleared so the flags stay consistent with the data. Three cases are refused because they would produce an invalid stream: deleting ANIM while ANMF frames still exist, deleting VP8X while an extended feature still needs it, and deleting the only image data chunk. In all three cases nothing is removed and the result is 0.

func (*Webp) FrameAt added in v0.2.7

func (w *Webp) FrameAt(i int, opt FrameOptions) (*image.NRGBA, int, error)

FrameAt 只合成到第 i 帧(0 起)并返回该帧与它的显示时长:中间帧不克隆, 因此内存只需要一块画布,而不是「帧数 × 画布」。像素预算与 FramesWith 相同, 总量按 i+1 帧计算;i 越界返回错误。

FrameAt composites only up to frame i (zero based) and returns that frame and its display duration. The intermediate frames are never cloned, so one canvas is enough instead of frames times canvas. The budgets are the ones of FramesWith, with the total counted over i+1 frames; an out-of-range index is an error.

func (*Webp) Frames

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

Frames 逐帧解码动图,返回按画布尺寸合成的各帧(NRGBA)及其显示时长 (毫秒)。maxPixels 像素预算同时约束画布尺寸与单帧解码规模,0 表示 defaultMaxPixels;等价于 FramesWith(FrameOptions{MaxPixels: maxPixels})。

Frames decodes every frame of an animated file and returns the composited frames at canvas size together with their display duration in milliseconds. The maxPixels budget caps both the canvas size and the per-frame decoding size; 0 means defaultMaxPixels. It is FramesWith(FrameOptions{MaxPixels: maxPixels}).

func (*Webp) FramesWith added in v0.2.7

func (w *Webp) FramesWith(opt FrameOptions) ([]*image.NRGBA, []int, error)

FramesWith 按给定选项逐帧解码动图,见 Frames。每帧先解码为静止图像再混合到 画布上,随后克隆整块画布作为输出帧;画布尺寸与「画布 × 帧数」的总量都会在 分配像素缓冲之前按预算预检,超限返回 ErrImageTooLarge。

FramesWith decodes an animated file frame by frame with the given options, see Frames. Each frame is decoded as a still image, blended onto the canvas, and the whole canvas is then cloned as the output frame. The canvas size and the canvas-times-frames total are checked against the budget before any pixel buffer is allocated; exceeding it returns ErrImageTooLarge.

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) InvalidateCache added in v0.2.7

func (w *Webp) InvalidateCache()

InvalidateCache 丢弃 Image/At 的解码缓存;直接修改像素结构体后调用它, 下一次 Image/At 会重新解码。

InvalidateCache drops the decode cache of Image/At; call it after mutating the pixel structs directly so the next Image/At decodes again.

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) RemoveFrame added in v0.2.7

func (w *Webp) RemoveFrame(i int) error

RemoveFrame 删除第 i 帧(0 起);删掉最后一帧时同时清掉 ANIM 与 animation 标志位,输出会退化成没有图像数据的容器,因此需要再 AddFrame 或 SetImage。

RemoveFrame drops frame i (zero based); removing the last frame also clears the ANIM chunk and the animation flag, leaving a container without image data, so a later AddFrame or SetImage has to supply some.

func (*Webp) SetImage added in v0.2.7

func (w *Webp) SetImage(m image.Image, opt *ImageOptions) error

SetImage 用 m 替换容器里的图像数据:按 opt 选择有损或无损编码,重编码像素, 同步画布尺寸与 VP8X,保留已有的 ICCP/EXIF/XMP 元数据,并丢弃解码缓存。 动画容器不能直接替换静态图像,请用 RemoveFrame/AddFrame 编辑帧。

SetImage replaces the image data of the container: it re-encodes the pixels with the lossy or lossless encoder selected by opt, syncs the canvas size and the VP8X chunk, keeps the ICCP/EXIF/XMP metadata and drops the decode cache. An animated container cannot take a still image; edit its frames with RemoveFrame/AddFrame instead.

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.

An ICCP/EXIF/XMP/ALPH/ANIM assigned to its struct is written even when the original file had none; combinations that would produce an invalid stream (an ALPH next to a VP8L, an ANIM without frames, an odd frame position, a duration beyond 24 bits) are errors instead of a file whose meaning quietly changed.

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

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

直接赋值给结构体的 ICCP/EXIF/XMP/ALPH/ANIM 即使原文件没有也会被写出;会 产出非法流的组合(ALPH 配 VP8L、没有帧的 ANIM、奇数帧坐标、超出 24 位的 时长)返回错误,而不是写出一个语义被悄悄改变的文件。

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 *RawChunk) 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