SandpointsGitHook/vendor/github.com/bep/gowebp/internal/libwebp/a__encoder.go

214 lines
4.3 KiB
Go

package libwebp
/*
#include <stdlib.h>
#include <string.h> // for memset
#ifndef LIBWEBP_NO_SRC
#include <encode.h>
#else
#include <webp/encode.h>
#endif
static uint8_t* encodeNRGBA(WebPConfig* config, const uint8_t* rgba, int width, int height, int stride, size_t* output_size) {
WebPPicture pic;
WebPMemoryWriter wrt;
int ok;
if (!WebPPictureInit(&pic)) {
return NULL;
}
pic.use_argb = 1;
pic.width = width;
pic.height = height;
pic.writer = WebPMemoryWrite;
pic.custom_ptr = &wrt;
WebPMemoryWriterInit(&wrt);
ok = WebPPictureImportRGBA(&pic, rgba, stride) && WebPEncode(config, &pic);
WebPPictureFree(&pic);
if (!ok) {
WebPMemoryWriterClear(&wrt);
return NULL;
}
*output_size = wrt.size;
return wrt.mem;
}
static uint8_t* encodeGray(WebPConfig* config, uint8_t *y, int width, int height, int stride, size_t* output_size) {
WebPPicture pic;
WebPMemoryWriter wrt;
int ok;
if (!WebPPictureInit(&pic)) {
return NULL;
}
pic.use_argb = 0;
pic.width = width;
pic.height = height;
pic.y_stride = stride;
pic.writer = WebPMemoryWrite;
pic.custom_ptr = &wrt;
WebPMemoryWriterInit(&wrt);
const int uvWidth = (int)(((int64_t)width + 1) >> 1);
const int uvHeight = (int)(((int64_t)height + 1) >> 1);
const int uvStride = uvWidth;
const int uvSize = uvStride * uvHeight;
const int gray = 128;
uint8_t* chroma;
chroma = malloc(uvSize);
if (!chroma) {
return 0;
}
memset(chroma, gray, uvSize);
pic.y = y;
pic.u = chroma;
pic.v = chroma;
pic.uv_stride = uvStride;
ok = WebPEncode(config, &pic);
free(chroma);
WebPPictureFree(&pic);
if (!ok) {
WebPMemoryWriterClear(&wrt);
return NULL;
}
*output_size = wrt.size;
return wrt.mem;
}
*/
import "C"
import (
"errors"
"image"
"image/draw"
"io"
"unsafe"
"github.com/bep/gowebp/libwebp/webpoptions"
)
type (
Encoder struct {
config *C.WebPConfig
img *image.NRGBA
}
)
// Encode encodes src into w considering the options in o.
//
// Any src that isn't one of *image.RGBA, *image.NRGBA, or *image.Gray
// will be converted to *image.NRGBA using draw.Draw first.
//
func Encode(w io.Writer, src image.Image, o webpoptions.EncodingOptions) error {
config, err := encodingOptionsToCConfig(o)
if err != nil {
return err
}
var (
bounds = src.Bounds()
output *C.uchar
size C.size_t
)
switch v := src.(type) {
case *image.RGBA:
output = C.encodeNRGBA(
config,
(*C.uint8_t)(&v.Pix[0]),
C.int(bounds.Max.X),
C.int(bounds.Max.Y),
C.int(v.Stride),
&size,
)
case *image.NRGBA:
output = C.encodeNRGBA(
config,
(*C.uint8_t)(&v.Pix[0]),
C.int(bounds.Max.X),
C.int(bounds.Max.Y),
C.int(v.Stride),
&size,
)
case *image.Gray:
gray := (*C.uint8_t)(&v.Pix[0])
output = C.encodeGray(
config,
gray,
C.int(bounds.Max.X),
C.int(bounds.Max.Y),
C.int(v.Stride),
&size,
)
default:
rgba := ConvertToNRGBA(src)
output = C.encodeNRGBA(
config,
(*C.uint8_t)(&rgba.Pix[0]),
C.int(bounds.Max.X),
C.int(bounds.Max.Y),
C.int(rgba.Stride),
&size,
)
}
if output == nil || size == 0 {
return errors.New("failed to encode")
}
defer C.free(unsafe.Pointer(output))
_, err = w.Write(((*[1 << 30]byte)(unsafe.Pointer(output)))[0:int(size):int(size)])
return err
}
func ConvertToNRGBA(src image.Image) *image.NRGBA {
dst := image.NewNRGBA(src.Bounds())
draw.Draw(dst, dst.Bounds(), src, src.Bounds().Min, draw.Src)
return dst
}
func encodingOptionsToCConfig(o webpoptions.EncodingOptions) (*C.WebPConfig, error) {
cfg := &C.WebPConfig{}
quality := C.float(o.Quality)
if C.WebPConfigPreset(cfg, C.WebPPreset(o.EncodingPreset), quality) == 0 {
return nil, errors.New("failed to init encoder config")
}
if quality == 0 {
// Activate the lossless compression mode with the desired efficiency level
// between 0 (fastest, lowest compression) and 9 (slower, best compression).
// A good default level is '6', providing a fair tradeoff between compression
// speed and final compressed size.
if C.WebPConfigLosslessPreset(cfg, C.int(6)) == 0 {
return nil, errors.New("failed to init lossless preset")
}
}
cfg.use_sharp_yuv = boolToCInt(o.UseSharpYuv)
if C.WebPValidateConfig(cfg) == 0 {
return nil, errors.New("failed to validate config")
}
return cfg, nil
}
func boolToCInt(b bool) (result C.int) {
result = 0
if b {
result = 1
}
return
}