go-libwebp

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

common.go (2265B)


      1 // Package common captures the unsafe orchestration that needs to occur when interacting
      2 // with libwebp. The function implementations can be swapped out based on the desired
      3 // implementation.
      4 package common
      5 
      6 import (
      7 	"fmt"
      8 	"image"
      9 	"io"
     10 	"runtime"
     11 	"unsafe"
     12 )
     13 
     14 type EncodeFunc func(in uintptr, w int32, h int32, bps int32, q float32, out uintptr) (size uint64)
     15 
     16 type DecoderFunc func(data uintptr, data_size uint64, width uintptr, height uintptr) (rgba uintptr)
     17 
     18 type FreeFunc func(uintptr)
     19 
     20 // Encode an RGBA webp image into the provided writer.
     21 func Encode(w io.Writer, m *image.NRGBA, q float32, enc EncodeFunc, free FreeFunc) error {
     22 	p := runtime.Pinner{}
     23 	defer p.Unpin()
     24 
     25 	out := new(*byte)
     26 	pix := unsafe.SliceData(m.Pix)
     27 
     28 	p.Pin(pix)
     29 	p.Pin(out)
     30 
     31 	size := enc(
     32 		uintptr(unsafe.Pointer(pix)),
     33 		int32(m.Bounds().Dx()),
     34 		int32(m.Bounds().Dy()),
     35 		int32(m.Stride),
     36 		q,
     37 		uintptr(unsafe.Pointer(out)),
     38 	)
     39 
     40 	if size == 0 {
     41 		return fmt.Errorf("empty result")
     42 	}
     43 
     44 	if *out == nil {
     45 		return fmt.Errorf("failed to allocate output buffer")
     46 	}
     47 
     48 	defer free(uintptr(unsafe.Pointer(*out)))
     49 
     50 	buf := unsafe.Slice(*out, size)
     51 
     52 	if _, err := w.Write(buf); err != nil {
     53 		return fmt.Errorf("writing the encoded webp: %w", err)
     54 	}
     55 
     56 	return nil
     57 }
     58 
     59 // Decode the webp data into an [image.Image].
     60 func Decode(buf []byte, dec DecoderFunc, free FreeFunc) (image.Image, error) {
     61 	p := runtime.Pinner{}
     62 	defer p.Unpin()
     63 
     64 	data := unsafe.SliceData(buf)
     65 	width := new(int32)
     66 	height := new(int32)
     67 
     68 	p.Pin(data)
     69 	p.Pin(width)
     70 	p.Pin(height)
     71 
     72 	samples := (*uint8)(unsafe.Pointer(dec(
     73 		uintptr(unsafe.Pointer(data)),
     74 		uint64(len(buf)),
     75 		uintptr(unsafe.Pointer(width)),
     76 		uintptr(unsafe.Pointer(height)),
     77 	)))
     78 
     79 	if samples == nil || width == nil || height == nil {
     80 		return nil, fmt.Errorf("failed decoding webp into rgba")
     81 	}
     82 
     83 	defer free(uintptr(unsafe.Pointer(samples)))
     84 
     85 	w, h := int(*width), int(*height)
     86 
     87 	size := w * h * 4
     88 
     89 	pix := make([]uint8, size)
     90 
     91 	// PERF: I wonder if we could use a finalizer to free the memory when the image is no
     92 	// longer reachable. That way we could avoid this copy.
     93 	copy(pix, unsafe.Slice(samples, size))
     94 
     95 	m := &image.NRGBA{
     96 		Pix:    pix,
     97 		Rect:   image.Rectangle{Max: image.Point{X: w, Y: h}},
     98 		Stride: w * 4,
     99 	}
    100 
    101 	return m, nil
    102 }