Skip to content

Commit 50a3286

Browse files
committed
Improve grayscale support
1 parent 5e38121 commit 50a3286

File tree

9 files changed

+129
-41
lines changed

9 files changed

+129
-41
lines changed

‎gen/main.go‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ func main() {
6060
if err := ioutil.WriteFile(target, []byte(fmt.Sprintf(`#ifndef LIBWEBP_NO_SRC
6161
#include "../../libwebp_src/src/%s"
6262
#endif
63-
`, filename)), 0644); err != nil {
63+
`, filename)), 0o644); err != nil {
6464
return err
6565
}
6666

@@ -70,5 +70,4 @@ func main() {
7070
if err != nil {
7171
log.Fatal(err)
7272
}
73-
7473
}

‎internal/libwebp/a__encoder.go‎

Lines changed: 103 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -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
712
static 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
*/
3185
import "C"
86+
3287
import (
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
54109
func 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
}

‎libwebp/encode_test.go‎

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,17 @@ package libwebp
22

33
import (
44
"bytes"
5-
"fmt"
65
"image"
76
"image/draw"
87
"image/jpeg"
8+
_ "image/jpeg"
9+
_ "image/png"
910
"io"
1011
"io/ioutil"
1112
"os"
13+
"path/filepath"
1214
"runtime"
15+
"strings"
1316
"testing"
1417

1518
"github.com/bep/gowebp/internal/libwebp"
@@ -21,24 +24,28 @@ import (
2124
func TestEncode(t *testing.T) {
2225
writeGolden := false
2326
for _, test := range []struct {
24-
name string
25-
opts options.EncodingOptions
27+
name string
28+
inputFile string
29+
opts options.EncodingOptions
2630
}{
27-
{"lossy", options.EncodingOptions{Quality: 75}},
28-
{"lossless", options.EncodingOptions{}},
31+
{"lossy", "source.jpg", options.EncodingOptions{Quality: 75}},
32+
{"lossless", "source.jpg", options.EncodingOptions{}},
33+
{"bw", "bw-gopher.png", options.EncodingOptions{Quality: 75}},
2934
} {
3035
t.Run(test.name, func(t *testing.T) {
31-
r, err := os.Open("../test_data/images/source.jpg")
36+
r, err := os.Open(filepath.Join("../test_data/images", test.inputFile))
3237
if err != nil {
3338
t.Fatal(err)
3439
}
3540

36-
img, err := jpeg.Decode(r)
41+
img, _, err := image.Decode(r)
3742
if err != nil {
3843
t.Fatal(err)
3944
}
4045

41-
targetFilename := fmt.Sprintf("../test_data/images/target-%s.webp", test.name)
46+
targetName := strings.TrimSuffix(test.inputFile, filepath.Ext(test.inputFile)) + "-" + test.name + ".webp"
47+
48+
targetFilename := filepath.Join("../test_data/images/golden", targetName)
4249
b := &bytes.Buffer{}
4350
var w io.Writer = b
4451

@@ -72,7 +79,6 @@ func TestEncode(t *testing.T) {
7279
}
7380
})
7481
}
75-
7682
}
7783

7884
func BenchmarkEncode(b *testing.B) {

‎libwebp/options/options.go‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ const (
1010
)
1111

1212
type (
13-
EncodingPreset int
14-
EncodingOptions struct {
13+
EncodingPreset int
14+
EncodingOptions struct {
1515

1616
// Quality is a number between 0 and 100. Set to 0 for lossless.
1717
Quality int

‎libwebp_src/swig/libwebp.go‎

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,17 @@
1010

1111
package libwebp
1212

13-
import _ "runtime/cgo"
14-
import "unsafe"
13+
import (
14+
_ "runtime/cgo"
15+
"unsafe"
16+
)
1517

1618
type _ unsafe.Pointer
1719

18-
type _swig_fnptr *byte
19-
type _swig_memberptr *byte
20+
type (
21+
_swig_fnptr *byte
22+
_swig_memberptr *byte
23+
)
2024

2125
//extern libwebpSwigCgocall
2226
func SwigCgocall()

‎test_data/images/bw-gopher.png‎

546 Bytes
Loading
1.12 KB
Loading
File renamed without changes.

0 commit comments

Comments
 (0)