commit e4a3608cbc8d9fe44478cce306710d3e07b7a1e7
parent 82741fa8693af21b7c819edd160275b50302b60e
Author: Jack Mordaunt <jackmordaunt.dev@gmail.com>
Date: Wed, 3 Jan 2024 16:37:45 +0800
webp: use new lib API
Signed-off-by: Jack Mordaunt <jackmordaunt.dev@gmail.com>
Diffstat:
2 files changed, 32 insertions(+), 52 deletions(-)
diff --git a/webp/encode.go b/webp/encode.go
@@ -1,11 +1,8 @@
package webp
import (
- "fmt"
"image"
"io"
- "runtime"
- "unsafe"
dynamic "git.sr.ht/~jackmordaunt/go-libwebp/lib/dynamic/webp"
transpiled "git.sr.ht/~jackmordaunt/go-libwebp/lib/transpiled/webp"
@@ -18,7 +15,7 @@ func Encode(w io.Writer, m image.Image, opt ...EncodeOption) error {
op(&enc)
}
if enc.Quality <= 0.0 {
- enc.Quality = 0.1
+ enc.Quality = 0.9
}
if enc.Quality > 1.0 {
enc.Quality = 1.0
@@ -50,7 +47,7 @@ func Lossless() EncodeOption {
// Encoder implements webp encoding of an image.
type Encoder struct {
// Quality is in the range (0,1]. Values outside of this
- // range will be treated as 1.
+ // range will be treated as 1. Default 0.9.
Quality float32
// Lossless indicates whether to use the lossless compression
// strategy. If true, the Quality field is ignored.
@@ -60,7 +57,6 @@ type Encoder struct {
// Encode specified image as webp to w.
// If the image is RGBA, the pixel buffer will be encoded directly.
// If the image is not RGBA, it will be converted to RGBA first.
-// Not threadsafe.
func (enc *Encoder) Encode(w io.Writer, m image.Image) error {
if enc.Quality <= 0.0 || enc.Quality > 1 {
enc.Quality = 1.0
@@ -80,45 +76,7 @@ func (enc *Encoder) Encode(w io.Writer, m image.Image) error {
func (enc *Encoder) encode(w io.Writer, m *image.RGBA) error {
if err := dynamic.Init(); err == nil {
- return encode(w, m, enc.Quality, dynamic.EncodeImpl, dynamic.FreeImpl)
+ return dynamic.EncodeImpl(w, m, enc.Quality)
}
- return encode(w, m, enc.Quality, transpiled.EncodeImpl, transpiled.FreeImpl)
-}
-
-// encode an RGBA image losslessly into the provided writer.
-func encode(w io.Writer, m *image.RGBA, q float32, enc encodeFunc, free freeFunc) error {
- out := new(*byte)
-
- p := runtime.Pinner{}
- defer p.Unpin()
-
- p.Pin(&m.Pix[0])
- p.Pin(out)
-
- size := enc(
- unsafe.Pointer(&m.Pix[0]),
- int32(m.Bounds().Dx()),
- int32(m.Bounds().Dy()),
- int32(m.Stride),
- q,
- unsafe.Pointer(out),
- )
-
- if size == 0 {
- return fmt.Errorf("empty result")
- }
-
- if *out == nil {
- return fmt.Errorf("failed to allocate output buffer")
- }
-
- defer free(unsafe.Pointer(*out))
-
- buf := unsafe.Slice(*out, size)
-
- if _, err := w.Write(buf); err != nil {
- return fmt.Errorf("writing the encoded webp: %w", err)
- }
-
- return nil
+ return transpiled.EncodeImpl(w, m, enc.Quality)
}
diff --git a/webp/webp_test.go b/webp/webp_test.go
@@ -6,10 +6,13 @@ import (
"image"
"image/color"
"image/png"
+ "io"
"math/rand"
"os"
"path/filepath"
"testing"
+
+ stdwebp "golang.org/x/image/webp"
)
//go:embed testdata/golden-in.png
@@ -24,17 +27,30 @@ func TestLossless(t *testing.T) {
t.Fatalf("decoding image: %v", err)
}
- buf := bytes.NewBuffer(nil)
+ buf1 := bytes.NewBuffer(nil)
+ buf2 := bytes.NewBuffer(nil)
- if err := Encode(buf, m, Lossless()); err != nil {
+ if err := Encode(io.MultiWriter(buf1, buf2), m, Lossless()); err != nil {
t.Fatalf("encoding webp: %v", err)
}
- if err := os.WriteFile(filepath.Join("testdata", "golden-got.webp"), buf.Bytes(), 0o644); err != nil {
+ if err := os.WriteFile(filepath.Join("testdata", "golden-got.webp"), buf1.Bytes(), 0o644); err != nil {
t.Errorf("writing output png: %v", err)
}
- out, err := Decode(buf)
+ t.Run("stdlib webp outupt", func(t *testing.T) {
+ assertOutput(t, m, buf1, stdwebp.Decode)
+ })
+
+ t.Run("transpiled webp output", func(t *testing.T) {
+ assertOutput(t, m, buf2, Decode)
+ })
+}
+
+func assertOutput(t *testing.T, m image.Image, src io.Reader, decode func(io.Reader) (image.Image, error)) {
+ t.Helper()
+
+ out, err := decode(src)
if err != nil {
t.Fatalf("decoding webp: %v", err)
}
@@ -42,13 +58,19 @@ func TestLossless(t *testing.T) {
outb := out.Bounds()
for xx := outb.Min.X; xx < outb.Max.X; xx++ {
for yy := outb.Min.Y; yy < outb.Max.Y; yy++ {
- if out.At(xx, yy) != m.At(xx, yy) {
- t.Errorf("color mismatch after lossless encode: (%d, %d)", xx, yy)
+ if got, want := out.At(xx, yy), m.At(xx, yy); !colorEqual(got, want) {
+ t.Fatalf("color mismatch after lossless encode: Point = (%d, %d) Got = %v Want = %v", xx, yy, got, want)
}
}
}
}
+func colorEqual(left, right color.Color) bool {
+ lr, lg, lb, la := left.RGBA()
+ rr, rg, rb, ra := right.RGBA()
+ return lr == rr && lg == rg && lb == rb && la == ra
+}
+
func FuzzEncode(f *testing.F) {
f.Add(uint16(0), uint16(0), uint16(0), uint16(0), int64(0), float32(0))
f.Add(uint16(0), uint16(0), uint16(1), uint16(1), int64(1), float32(1))