From: Simon Heimlicher Date: Mon, 29 Dec 2025 15:47:43 +0000 (+0100) Subject: images: Fix WebP quality and hint parameters being ignored X-Git-Url: http://git.maquefel.me/?a=commitdiff_plain;h=ea9675f6e7c10b12e082eae2eabd1ea586bc56d3;p=brevno-suite%2Fhugo images: Fix WebP quality and hint parameters being ignored Fixes #14316 --- diff --git a/internal/warpc/webp.go b/internal/warpc/webp.go index f30588577..fe0df8e30 100644 --- a/internal/warpc/webp.go +++ b/internal/warpc/webp.go @@ -202,6 +202,10 @@ func (d *WebpCodec) DecodeConfig(r io.Reader) (image.Config, error) { } func (d *WebpCodec) Encode(w io.Writer, img image.Image) error { + return d.EncodeOptions(w, img, nil) +} + +func (d *WebpCodec) EncodeOptions(w io.Writer, img image.Image, options map[string]any) error { b := img.Bounds() if b.Dx() >= 1<<16 || b.Dy() >= 1<<16 { 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 { // encodeGray // decode // config + + opts := map[string]any{ + "quality": d.quality, // a number between 0 and 100. Set to 0 for lossless. + "hint": d.hint, // drawing, icon, photo, picture, or text + "useSharpYuv": true, // Use sharp (and slow) RGB->YUV conversion. + } + + // Override with per-image options if provided. + for _, key := range []string{"quality", "hint"} { + if v, ok := options[key]; ok { + opts[key] = v + } + } + message := Message[WebpInput]{ Header: Header{ Version: 1, @@ -310,11 +328,7 @@ func (d *WebpCodec) Encode(w io.Writer, img image.Image) error { Data: WebpInput{ Source: bytes.NewReader(imageBytes), Destination: w, - Options: map[string]any{ - "quality": d.quality, // a number between 0 and 100. Set to 0 for lossless. - "hint": d.hint, // drawing, icon, photo, picture, or text - "useSharpYuv": true, // Use sharp (and slow) RGB->YUV conversion. - }, + Options: opts, Params: map[string]any{ "width": bounds.Max.X, "height": bounds.Max.Y, diff --git a/resources/images/codec.go b/resources/images/codec.go index 7fe1ccf5b..087643c26 100644 --- a/resources/images/codec.go +++ b/resources/images/codec.go @@ -46,6 +46,14 @@ type ToEncoder interface { EncodeTo(conf ImageConfig, w io.Writer, src image.Image) error } +// EncoderWithOptions defines the encoding of an image format with options. +// This is currently only used for WebP and the options are passed as a map +// to match the internal WASM API. The options map may be nil when no options +// need to be overridden. +type EncoderWithOptions interface { + EncodeOptions(w io.Writer, src image.Image, options map[string]any) error +} + // CodecStdlib defines both decoding and encoding of an image format as defined by the standard library. type CodecStdlib interface { Decoder @@ -123,6 +131,19 @@ func (d *Codec) EncodeTo(conf ImageConfig, w io.Writer, img image.Image) error { case BMP: return bmp.Encode(w, img) case WEBP: + if enc, ok := d.webp.(EncoderWithOptions); ok { + var opts map[string]any + if conf.qualitySetForImage || conf.hintSetForImage { + opts = make(map[string]any) + if conf.qualitySetForImage { + opts["quality"] = conf.Quality + } + if conf.hintSetForImage { + opts["hint"] = conf.Hint + } + } + return enc.EncodeOptions(w, img, opts) + } return d.webp.Encode(w, img) default: return errors.New("format not supported") diff --git a/resources/images/config.go b/resources/images/config.go index f09805307..32940021c 100644 --- a/resources/images/config.go +++ b/resources/images/config.go @@ -226,6 +226,7 @@ func DecodeImageConfig(options []string, defaults *config.ConfigNamespace[Imagin c.Filter = filter } else if _, ok := hints[part]; ok { c.Hint = part + c.hintSetForImage = true } else if part[0] == '#' { c.BgColor, err = hexStringToColorGo(part[1:]) if err != nil { @@ -236,8 +237,8 @@ func DecodeImageConfig(options []string, defaults *config.ConfigNamespace[Imagin if err != nil { return c, err } - if c.Quality < 1 || c.Quality > 100 { - return c, errors.New("quality ranges from 1 to 100 inclusive") + if c.Quality < 0 || c.Quality > 100 { + return c, errors.New("quality ranges from 0 to 100 inclusive") } c.qualitySetForImage = true } else if part[0] == 'r' { @@ -305,8 +306,9 @@ func DecodeImageConfig(options []string, defaults *config.ConfigNamespace[Imagin c.TargetFormat = sourceFormat } - if c.Quality <= 0 && c.TargetFormat.RequiresDefaultQuality() { - // We need a quality setting for all JPEGs and WEBPs. + if !c.qualitySetForImage && c.Quality <= 0 && c.TargetFormat.RequiresDefaultQuality() { + // We need a quality setting for all JPEGs and WEBPs, + // unless the user explicitly set quality (e.g., q0 for lossless WebP). c.Quality = defaults.Config.Imaging.Quality } @@ -358,7 +360,8 @@ type ImageConfig struct { // Hint about what type of picture this is. Used to optimize encoding // when target is set to webp. - Hint string + Hint string + hintSetForImage bool // Whether the above is set for this image. Width int Height int diff --git a/resources/images/images_golden_integration_test.go b/resources/images/images_golden_integration_test.go index ff2c3d207..d5a707024 100644 --- a/resources/images/images_golden_integration_test.go +++ b/resources/images/images_golden_integration_test.go @@ -388,6 +388,14 @@ Home. {{ template "process" (dict "spec" "png" "img" $highContrast) }} {{ template "process" (dict "spec" "resize 300x300" "img" $giphy) }} {{ template "process" (dict "spec" "resize 300x300 webp" "img" $giphy) }} +{{ template "process" (dict "spec" "resize 300x300 webp q0" "img" $sunset) }} +{{ template "process" (dict "spec" "resize 300x300 webp q1" "img" $sunset) }} +{{ template "process" (dict "spec" "resize 300x300 webp q33" "img" $sunset) }} +{{ template "process" (dict "spec" "resize 300x300 webp q75" "img" $sunset) }} +{{ template "process" (dict "spec" "resize 300x300 webp q100" "img" $sunset) }} +{{ template "process" (dict "spec" "resize 300x300 webp drawing" "img" $sunset) }} +{{ template "process" (dict "spec" "resize 300x300 webp icon" "img" $sunset) }} +{{ template "process" (dict "spec" "resize 300x300 webp q50 drawing" "img" $sunset) }} {{ template "process" (dict "spec" "resize 400x" "img" $highContrast) }} {{ define "process"}} diff --git a/resources/images/testdata/images_golden/process/webp/resize-300x300-webp-drawing.webp b/resources/images/testdata/images_golden/process/webp/resize-300x300-webp-drawing.webp new file mode 100644 index 000000000..4a94f7214 Binary files /dev/null and b/resources/images/testdata/images_golden/process/webp/resize-300x300-webp-drawing.webp differ diff --git a/resources/images/testdata/images_golden/process/webp/resize-300x300-webp-icon.webp b/resources/images/testdata/images_golden/process/webp/resize-300x300-webp-icon.webp new file mode 100644 index 000000000..cd13d1db4 Binary files /dev/null and b/resources/images/testdata/images_golden/process/webp/resize-300x300-webp-icon.webp differ diff --git a/resources/images/testdata/images_golden/process/webp/resize-300x300-webp-q0.webp b/resources/images/testdata/images_golden/process/webp/resize-300x300-webp-q0.webp new file mode 100644 index 000000000..ee53c6030 Binary files /dev/null and b/resources/images/testdata/images_golden/process/webp/resize-300x300-webp-q0.webp differ diff --git a/resources/images/testdata/images_golden/process/webp/resize-300x300-webp-q1.webp b/resources/images/testdata/images_golden/process/webp/resize-300x300-webp-q1.webp new file mode 100644 index 000000000..2d1b9f86a Binary files /dev/null and b/resources/images/testdata/images_golden/process/webp/resize-300x300-webp-q1.webp differ diff --git a/resources/images/testdata/images_golden/process/webp/resize-300x300-webp-q100.webp b/resources/images/testdata/images_golden/process/webp/resize-300x300-webp-q100.webp new file mode 100644 index 000000000..cc4a5c2fe Binary files /dev/null and b/resources/images/testdata/images_golden/process/webp/resize-300x300-webp-q100.webp differ diff --git a/resources/images/testdata/images_golden/process/webp/resize-300x300-webp-q33.webp b/resources/images/testdata/images_golden/process/webp/resize-300x300-webp-q33.webp new file mode 100644 index 000000000..55180b074 Binary files /dev/null and b/resources/images/testdata/images_golden/process/webp/resize-300x300-webp-q33.webp differ diff --git a/resources/images/testdata/images_golden/process/webp/resize-300x300-webp-q50-drawing.webp b/resources/images/testdata/images_golden/process/webp/resize-300x300-webp-q50-drawing.webp new file mode 100644 index 000000000..1a8e1cf3b Binary files /dev/null and b/resources/images/testdata/images_golden/process/webp/resize-300x300-webp-q50-drawing.webp differ diff --git a/resources/images/testdata/images_golden/process/webp/resize-300x300-webp-q75.webp b/resources/images/testdata/images_golden/process/webp/resize-300x300-webp-q75.webp new file mode 100644 index 000000000..3d81589c2 Binary files /dev/null and b/resources/images/testdata/images_golden/process/webp/resize-300x300-webp-q75.webp differ