go-libwebp

Experimental translation from libwebp to Go source.
Log | Files | Refs | README | LICENSE

commit 6be5ab5f797e71adea62b6aec71b9bad343b7cf0
parent cb025bcd4e24edd37e5bb7687c15c9576a3fde9c
Author: Jack Mordaunt <jackmordaunt.dev@gmail.com>
Date:   Sat, 29 Jan 2022 17:35:34 +0800

webp: bifurcate encoding and decoding logic

Signed-off-by: Jack Mordaunt <jackmordaunt.dev@gmail.com>

Diffstat:
Awebp/decode.go | 16++++++++++++++++
Awebp/encode.go | 105+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mwebp/webp.go | 109-------------------------------------------------------------------------------
3 files changed, 121 insertions(+), 109 deletions(-)

diff --git a/webp/decode.go b/webp/decode.go @@ -0,0 +1,16 @@ +package webp + +import ( + "fmt" + "image" + "io" +) + +// Decode a webp into an image. +func Decode(r io.Reader) (image.Image, error) { + return nil, fmt.Errorf("unimplemented") +} + +func DecodeConfig(r io.Reader) (image.Config, error) { + return image.Config{}, fmt.Errorf("unimplemented") +} diff --git a/webp/encode.go b/webp/encode.go @@ -0,0 +1,105 @@ +package webp + +import ( + "fmt" + "image" + "io" + "runtime" + "unsafe" + + "git.sr.ht/~jackmordaunt/go-libwebp/lib" + "modernc.org/libc" +) + +// Encode an image into webp with default settings. +func Encode(w io.Writer, m image.Image, opt ...EncodeOption) error { + var enc Encoder + for _, op := range opt { + op(&enc) + } + return enc.Encode(w, m) +} + +// EncodeOption configures the encoder. +type EncodeOption func(*Encoder) + +// Quality in the range (0,1]. +func Quality(q float32) EncodeOption { + return func(enc *Encoder) { + enc.Quality = q + } +} + +// Lossless will ignore quality. +func Lossless() EncodeOption { + return func(enc *Encoder) { + enc.Lossless = true + } +} + +// 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. + Quality float32 + // Lossless indicates whether to use the lossless compression + // strategy. If true, the Quality field is ignored. + Lossless bool +} + +// Encode specified image as webp to w. +func (enc *Encoder) Encode(w io.Writer, m image.Image) error { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + if enc.Quality <= 0.0 || enc.Quality > 1 { + enc.Quality = 1.0 + } + rgbaImage := image.NewRGBA(m.Bounds()) + rect := m.Bounds() + for y := rect.Min.Y; y < rect.Max.Y; y++ { + for x := rect.Min.X; x < rect.Max.X; x++ { + rgbaImage.Set(x, y, m.At(x, y)) + } + } + var ( + // out buffer to contain webp data. + out *byte = nil + ) + size := lib.Encode( + libc.NewTLS(), + uintptr(unsafe.Pointer(&rgbaImage.Pix[0])), + int32(rect.Dx()), + int32(rect.Dy()), + int32(rgbaImage.Stride), + // Function pointers are generated by taking a pointer to + // a struct who's first field is that function. + *(*uintptr)(unsafe.Pointer( + &struct { + f func(tls *libc.TLS, picture uintptr, rgba uintptr, rgba_stride int32) int32 + }{ + lib.WebPPictureImportRGBA, + }, + )), + // quality for libwebp is [0,100]. + float32(enc.Quality*100), + boolToInt32(enc.Lossless), + uintptr(unsafe.Pointer(&out)), + ) + if size == 0 { + return fmt.Errorf("encoding webp image: size %d", size) + } + if out == nil { + return fmt.Errorf("failed to allocate memory; probably errored") + } + if _, err := w.Write(libc.GoBytes(uintptr(unsafe.Pointer(out)), int(size))); err != nil { + return fmt.Errorf("writing webp data: %w", err) + } + return nil +} + +func boolToInt32(b bool) int32 { + if b { + return 1 + } + return 0 +} diff --git a/webp/webp.go b/webp/webp.go @@ -2,118 +2,9 @@ package webp import ( - "fmt" "image" - "io" - "runtime" - "unsafe" - - "git.sr.ht/~jackmordaunt/go-libwebp/lib" - "modernc.org/libc" ) func init() { image.RegisterFormat("webp", "RIFF????WEBP", Decode, DecodeConfig) } - -// EncodeOption configures the encoder. -type EncodeOption func(*Encoder) - -// Quality in the range (0,1]. -func Quality(q float32) EncodeOption { - return func(enc *Encoder) { - enc.Quality = q - } -} - -// Lossless will ignore quality. -func Lossless() EncodeOption { - return func(enc *Encoder) { - enc.Lossless = true - } -} - -// Encode an image into webp with default settings. -func Encode(w io.Writer, m image.Image, opt ...EncodeOption) error { - var enc Encoder - for _, op := range opt { - op(&enc) - } - return enc.Encode(w, m) -} - -// Decode a webp into an image. -func Decode(r io.Reader) (image.Image, error) { - return nil, fmt.Errorf("unimplemented") -} - -func DecodeConfig(r io.Reader) (image.Config, error) { - return image.Config{}, fmt.Errorf("unimplemented") -} - -// 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. - Quality float32 - // Lossless indicates whether to use the lossless compression - // strategy. If true, the Quality field is ignored. - Lossless bool -} - -// Encode specified image as webp to w. -func (enc *Encoder) Encode(w io.Writer, m image.Image) error { - runtime.LockOSThread() - defer runtime.UnlockOSThread() - if enc.Quality <= 0.0 || enc.Quality > 1 { - enc.Quality = 1.0 - } - rgbaImage := image.NewRGBA(m.Bounds()) - rect := m.Bounds() - for y := rect.Min.Y; y < rect.Max.Y; y++ { - for x := rect.Min.X; x < rect.Max.X; x++ { - rgbaImage.Set(x, y, m.At(x, y)) - } - } - var ( - // out buffer to contain webp data. - out *byte = nil - ) - size := lib.Encode( - libc.NewTLS(), - uintptr(unsafe.Pointer(&rgbaImage.Pix[0])), - int32(rect.Dx()), - int32(rect.Dy()), - int32(rgbaImage.Stride), - // Function pointers are generated by taking a pointer to - // a struct who's first field is that function. - *(*uintptr)(unsafe.Pointer( - &struct { - f func(tls *libc.TLS, picture uintptr, rgba uintptr, rgba_stride int32) int32 - }{ - lib.WebPPictureImportRGBA, - }, - )), - // quality for libwebp is [0,100]. - float32(enc.Quality*100), - boolToInt32(enc.Lossless), - uintptr(unsafe.Pointer(&out)), - ) - if size == 0 { - return fmt.Errorf("encoding webp image: size %d", size) - } - if out == nil { - return fmt.Errorf("failed to allocate memory; probably errored") - } - if _, err := w.Write(libc.GoBytes(uintptr(unsafe.Pointer(out)), int(size))); err != nil { - return fmt.Errorf("writing webp data: %w", err) - } - return nil -} - -func boolToInt32(b bool) int32 { - if b { - return 1 - } - return 0 -}