@@ -2,7 +2,12 @@ package libwebp
22
33/*
44#include <stdlib.h>
5+ #include <string.h> // for memset
6+ #ifndef LIBWEBP_NO_SRC
57#include <encode.h>
8+ #else
9+ #include <webp/encode.h>
10+ #endif
611
712static uint8_t* encodeNRGBA(WebPConfig* config, const uint8_t* rgba, int width, int height, int stride, size_t* output_size) {
813 WebPPicture pic;
@@ -27,8 +32,58 @@ static uint8_t* encodeNRGBA(WebPConfig* config, const uint8_t* rgba, int width,
2732 return wrt.mem;
2833}
2934
35+ static uint8_t* encodeGray(WebPConfig* config, uint8_t *y, int width, int height, int stride, size_t* output_size) {
36+ WebPPicture pic;
37+ WebPMemoryWriter wrt;
38+
39+ int ok;
40+ if (!WebPPictureInit(&pic)) {
41+ return NULL;
42+ }
43+
44+ pic.use_argb = 0;
45+ pic.width = width;
46+ pic.height = height;
47+ pic.y_stride = stride;
48+ pic.writer = WebPMemoryWrite;
49+ pic.custom_ptr = &wrt;
50+ WebPMemoryWriterInit(&wrt);
51+
52+ const int uvWidth = (int)(((int64_t)width + 1) >> 1);
53+ const int uvHeight = (int)(((int64_t)height + 1) >> 1);
54+ const int uvStride = uvWidth;
55+ const int uvSize = uvStride * uvHeight;
56+ const int gray = 128;
57+ uint8_t* chroma;
58+
59+ chroma = malloc(uvSize);
60+ if (!chroma) {
61+ return 0;
62+ }
63+ memset(chroma, gray, uvSize);
64+
65+ pic.y = y;
66+ pic.u = chroma;
67+ pic.v = chroma;
68+ pic.uv_stride = uvStride;
69+
70+ ok = WebPEncode(config, &pic);
71+
72+ free(chroma);
73+
74+ WebPPictureFree(&pic);
75+ if (!ok) {
76+ WebPMemoryWriterClear(&wrt);
77+ return NULL;
78+ }
79+ *output_size = wrt.size;
80+ return wrt.mem;
81+
82+ }
83+
3084*/
3185import "C"
86+
3287import (
3388 "errors"
3489 "image"
@@ -50,7 +105,7 @@ type (
50105//
51106// TODO(bep) ColorSpace
52107// TODO(bep) Can we handle *image.YCbCr without conversion?
53- // TODO(bep) Grayscale
108+ // TODO(bep) sharp YUV: https://www.ctrl.blog/entry/webp-sharp-yuv.html
54109func Encode (w io.Writer , src image.Image , o options.EncodingOptions ) error {
55110 config , err := encodingOptionsToCConfig (o )
56111 if err != nil {
@@ -59,35 +114,53 @@ func Encode(w io.Writer, src image.Image, o options.EncodingOptions) error {
59114
60115 var (
61116 bounds = src .Bounds ()
62- stride int
63- rgba * C. uint8_t
117+ output * C. uchar
118+ size C. size_t
64119 )
65120
66121 switch v := src .(type ) {
67122 case * image.RGBA :
68- rgba = (* C .uint8_t )(& v .Pix [0 ])
69- stride = v .Stride
123+ output = C .encodeNRGBA (
124+ config ,
125+ (* C .uint8_t )(& v .Pix [0 ]),
126+ C .int (bounds .Max .X ),
127+ C .int (bounds .Max .Y ),
128+ C .int (v .Stride ),
129+ & size ,
130+ )
70131 case * image.NRGBA :
71- rgba = (* C .uint8_t )(& v .Pix [0 ])
72- stride = v .Stride
132+ output = C .encodeNRGBA (
133+ config ,
134+ (* C .uint8_t )(& v .Pix [0 ]),
135+ C .int (bounds .Max .X ),
136+ C .int (bounds .Max .Y ),
137+ C .int (v .Stride ),
138+ & size ,
139+ )
140+ case * image.Gray :
141+ gray := (* C .uint8_t )(& v .Pix [0 ])
142+ output = C .encodeGray (
143+ config ,
144+ gray ,
145+ C .int (bounds .Max .X ),
146+ C .int (bounds .Max .Y ),
147+ C .int (v .Stride ),
148+ & size ,
149+ )
73150 default :
74- img := ConvertToNRGBA (src )
75- rgba = (* C .uint8_t )(& img .Pix [0 ])
76- stride = img .Stride
151+ rgba := ConvertToNRGBA (src )
152+ output = C .encodeNRGBA (
153+ config ,
154+ (* C .uint8_t )(& rgba .Pix [0 ]),
155+ C .int (bounds .Max .X ),
156+ C .int (bounds .Max .Y ),
157+ C .int (rgba .Stride ),
158+ & size ,
159+ )
77160 }
78161
79- var size C.size_t
80- output := C .encodeNRGBA (
81- config ,
82- rgba ,
83- C .int (bounds .Max .X ),
84- C .int (bounds .Max .Y ),
85- C .int (stride ),
86- & size ,
87- )
88-
89162 if output == nil || size == 0 {
90- return errors .New ("cannot encode webppicture " )
163+ return errors .New ("failed to encode " )
91164 }
92165 defer C .free (unsafe .Pointer (output ))
93166
@@ -107,19 +180,25 @@ func encodingOptionsToCConfig(o options.EncodingOptions) (*C.WebPConfig, error)
107180 cfg := & C.WebPConfig {}
108181 quality := C .float (o .Quality )
109182
183+ cfg .quality = quality
184+
110185 if C .WebPConfigPreset (cfg , C .WebPPreset (o .EncodingPreset ), quality ) == 0 {
111186 return nil , errors .New ("failed to init encoder config" )
112187 }
113188
114- cfg .quality = quality
115189 if quality == 0 {
116- cfg .lossless = C .int (1 )
190+ // Activate the lossless compression mode with the desired efficiency level
191+ // between 0 (fastest, lowest compression) and 9 (slower, best compression).
192+ // A good default level is '6', providing a fair tradeoff between compression
193+ // speed and final compressed size.
194+ if C .WebPConfigLosslessPreset (cfg , C .int (6 )) == 0 {
195+ return nil , errors .New ("failed to init lossless preset" )
196+ }
117197 }
118198
119199 if C .WebPValidateConfig (cfg ) == 0 {
120200 return nil , errors .New ("failed to validate config" )
121201 }
122202
123203 return cfg , nil
124-
125204}
0 commit comments