Skip to content

Commit ea9675f

Browse files
images: Fix WebP quality and hint parameters being ignored
Fixes #14316
1 parent b1f7e35 commit ea9675f

12 files changed

+56
-10
lines changed

‎internal/warpc/webp.go‎

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,10 @@ func (d *WebpCodec) DecodeConfig(r io.Reader) (image.Config, error) {
202202
}
203203

204204
func (d *WebpCodec) Encode(w io.Writer, img image.Image) error {
205+
return d.EncodeOptions(w, img, nil)
206+
}
207+
208+
func (d *WebpCodec) EncodeOptions(w io.Writer, img image.Image, options map[string]any) error {
205209
b := img.Bounds()
206210
if b.Dx() >= 1<<16 || b.Dy() >= 1<<16 {
207211
return errors.New("webp: image is too large to encode")
@@ -299,6 +303,20 @@ func (d *WebpCodec) Encode(w io.Writer, img image.Image) error {
299303
// encodeGray
300304
// decode
301305
// config
306+
307+
opts := map[string]any{
308+
"quality": d.quality, // a number between 0 and 100. Set to 0 for lossless.
309+
"hint": d.hint, // drawing, icon, photo, picture, or text
310+
"useSharpYuv": true, // Use sharp (and slow) RGB->YUV conversion.
311+
}
312+
313+
// Override with per-image options if provided.
314+
for _, key := range []string{"quality", "hint"} {
315+
if v, ok := options[key]; ok {
316+
opts[key] = v
317+
}
318+
}
319+
302320
message := Message[WebpInput]{
303321
Header: Header{
304322
Version: 1,
@@ -310,11 +328,7 @@ func (d *WebpCodec) Encode(w io.Writer, img image.Image) error {
310328
Data: WebpInput{
311329
Source: bytes.NewReader(imageBytes),
312330
Destination: w,
313-
Options: map[string]any{
314-
"quality": d.quality, // a number between 0 and 100. Set to 0 for lossless.
315-
"hint": d.hint, // drawing, icon, photo, picture, or text
316-
"useSharpYuv": true, // Use sharp (and slow) RGB->YUV conversion.
317-
},
331+
Options: opts,
318332
Params: map[string]any{
319333
"width": bounds.Max.X,
320334
"height": bounds.Max.Y,

‎resources/images/codec.go‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,14 @@ type ToEncoder interface {
4646
EncodeTo(conf ImageConfig, w io.Writer, src image.Image) error
4747
}
4848

49+
// EncoderWithOptions defines the encoding of an image format with options.
50+
// This is currently only used for WebP and the options are passed as a map
51+
// to match the internal WASM API. The options map may be nil when no options
52+
// need to be overridden.
53+
type EncoderWithOptions interface {
54+
EncodeOptions(w io.Writer, src image.Image, options map[string]any) error
55+
}
56+
4957
// CodecStdlib defines both decoding and encoding of an image format as defined by the standard library.
5058
type CodecStdlib interface {
5159
Decoder
@@ -123,6 +131,19 @@ func (d *Codec) EncodeTo(conf ImageConfig, w io.Writer, img image.Image) error {
123131
case BMP:
124132
return bmp.Encode(w, img)
125133
case WEBP:
134+
if enc, ok := d.webp.(EncoderWithOptions); ok {
135+
var opts map[string]any
136+
if conf.qualitySetForImage || conf.hintSetForImage {
137+
opts = make(map[string]any)
138+
if conf.qualitySetForImage {
139+
opts["quality"] = conf.Quality
140+
}
141+
if conf.hintSetForImage {
142+
opts["hint"] = conf.Hint
143+
}
144+
}
145+
return enc.EncodeOptions(w, img, opts)
146+
}
126147
return d.webp.Encode(w, img)
127148
default:
128149
return errors.New("format not supported")

‎resources/images/config.go‎

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,7 @@ func DecodeImageConfig(options []string, defaults *config.ConfigNamespace[Imagin
226226
c.Filter = filter
227227
} else if _, ok := hints[part]; ok {
228228
c.Hint = part
229+
c.hintSetForImage = true
229230
} else if part[0] == '#' {
230231
c.BgColor, err = hexStringToColorGo(part[1:])
231232
if err != nil {
@@ -236,8 +237,8 @@ func DecodeImageConfig(options []string, defaults *config.ConfigNamespace[Imagin
236237
if err != nil {
237238
return c, err
238239
}
239-
if c.Quality < 1 || c.Quality > 100 {
240-
return c, errors.New("quality ranges from 1 to 100 inclusive")
240+
if c.Quality < 0 || c.Quality > 100 {
241+
return c, errors.New("quality ranges from 0 to 100 inclusive")
241242
}
242243
c.qualitySetForImage = true
243244
} else if part[0] == 'r' {
@@ -305,8 +306,9 @@ func DecodeImageConfig(options []string, defaults *config.ConfigNamespace[Imagin
305306
c.TargetFormat = sourceFormat
306307
}
307308

308-
if c.Quality <= 0 && c.TargetFormat.RequiresDefaultQuality() {
309-
// We need a quality setting for all JPEGs and WEBPs.
309+
if !c.qualitySetForImage && c.Quality <= 0 && c.TargetFormat.RequiresDefaultQuality() {
310+
// We need a quality setting for all JPEGs and WEBPs,
311+
// unless the user explicitly set quality (e.g., q0 for lossless WebP).
310312
c.Quality = defaults.Config.Imaging.Quality
311313
}
312314

@@ -358,7 +360,8 @@ type ImageConfig struct {
358360

359361
// Hint about what type of picture this is. Used to optimize encoding
360362
// when target is set to webp.
361-
Hint string
363+
Hint string
364+
hintSetForImage bool // Whether the above is set for this image.
362365

363366
Width int
364367
Height int

‎resources/images/images_golden_integration_test.go‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,14 @@ Home.
388388
{{ template "process" (dict "spec" "png" "img" $highContrast) }}
389389
{{ template "process" (dict "spec" "resize 300x300" "img" $giphy) }}
390390
{{ template "process" (dict "spec" "resize 300x300 webp" "img" $giphy) }}
391+
{{ template "process" (dict "spec" "resize 300x300 webp q0" "img" $sunset) }}
392+
{{ template "process" (dict "spec" "resize 300x300 webp q1" "img" $sunset) }}
393+
{{ template "process" (dict "spec" "resize 300x300 webp q33" "img" $sunset) }}
394+
{{ template "process" (dict "spec" "resize 300x300 webp q75" "img" $sunset) }}
395+
{{ template "process" (dict "spec" "resize 300x300 webp q100" "img" $sunset) }}
396+
{{ template "process" (dict "spec" "resize 300x300 webp drawing" "img" $sunset) }}
397+
{{ template "process" (dict "spec" "resize 300x300 webp icon" "img" $sunset) }}
398+
{{ template "process" (dict "spec" "resize 300x300 webp q50 drawing" "img" $sunset) }}
391399
{{ template "process" (dict "spec" "resize 400x" "img" $highContrast) }}
392400
393401
{{ define "process"}}
3.3 KB
Loading
3.29 KB
Loading
59.3 KB
Loading
752 Bytes
Loading
17.2 KB
Loading
1.65 KB
Loading

0 commit comments

Comments
 (0)