Files
hugo/internal/warpc/avif_integration_test.go
T
Bjørn Erik Pedersen 341f575d2d images: Make AVIF chroma subsampling content-aware via the hint
Encode photo/picture hints (and the default) as YUV420 instead of YUV444,
keeping 444 for text/icon/drawing. Lossless stays 444.

This roughly halves the encoder's peak memory (42 -> 27 MiB/MP) and the
output size, while 444 remains available for sharp-edged content. A
3000x3000 image now needs ~239 MiB to encode, down from ~381 MiB (which
sat right at the 384 MiB WASM cap).

Closes #14987

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 18:54:56 +02:00

116 lines
3.4 KiB
Go

package warpc_test
import (
"strings"
"testing"
qt "github.com/frankban/quicktest"
"github.com/gohugoio/hugo/hugolib"
)
func TestAvifBasic(t *testing.T) {
files := `
-- hugo.toml --
-- assets/sunset.avif --
sourcefilename: ../../resources/testdata/bep/dock-75-hdr.avif
-- layouts/home.html --
{{ $image := resources.Get "sunset.avif" }}
Width/Height: {{ $image.Width }}/{{ $image.Height }}|
Decode avif: {{ $jpeg := $image.Process "jpeg" }}|{{ $jpeg.RelPermalink }}|
Encode avif from JPEG: {{ $avif := $jpeg.Process "avif" }}|{{ $avif.RelPermalink }}|
Encode avif from avif: {{ $avif := $image.Process "avif" }}|{{ $avif.RelPermalink }}|
`
b, err := hugolib.TestE(t, files)
b.Assert(err, qt.IsNil)
b.AssertFileContent("public/index.html", "Width/Height: 1024/683")
}
// giphy.avif is an animated AVIF transcoded from giphy.gif (14 frames @ 200ms,
// infinite loop) via avifenc. Verifies that decode preserves animation, the
// pipeline runs every frame through resize, the GIF encoder writes a multi-frame
// GIF, and the libavif "repetitionCount" → Go "LoopCount" inversion (libavif
// -1 = infinite, image/gif 0 = infinite) is handled at the AVIF decoder boundary.
func TestAvifAnimatedToGif(t *testing.T) {
files := `
-- hugo.toml --
disableKinds = ["page", "section", "taxonomy", "term", "sitemap", "robotsTXT", "404"]
-- assets/giphy.avif --
sourcefilename: ../../resources/testdata/giphy.avif
-- layouts/home.html --
{{ $img := resources.Get "giphy.avif" }}
{{ $gif := $img.Resize "x60 gif" }}
{{ $gif.Publish }}
gif:{{ $gif.RelPermalink }}
`
b := hugolib.Test(t, files)
rel := b.FileContent("public/index.html")
var gifFile string
for line := range strings.SplitSeq(rel, "\n") {
if rest, ok := strings.CutPrefix(line, "gif:"); ok {
gifFile = "public" + rest
break
}
}
b.Assert(gifFile, qt.Not(qt.Equals), "")
durations := make([]int, 14)
for i := range durations {
durations[i] = 200
}
b.ImageHelper(gifFile).
AssertFormat("gif").
AssertIsAnimated(true).
AssertLoopCount(0).
AssertFrameDurations(durations)
}
// See issue 14987.
func TestAvifEncodeHintSubsampling(t *testing.T) {
files := `
-- hugo.toml --
-- assets/logo.png --
sourcefilename: ../../resources/testdata/gohugoio24.png
-- layouts/home.html --
{{ $img := resources.Get "logo.png" }}
default:{{ ($img.Process "avif photo").RelPermalink }}|
photo:{{ ($img.Process "avif photo").RelPermalink }}|
text:{{ ($img.Process "avif text").RelPermalink }}|
`
b := hugolib.Test(t, files)
b.AssertFileContent("public/index.html",
"default:/logo_hu_add3d7dfc55b7f80.avif|",
"photo:/logo_hu_add3d7dfc55b7f80.avif|",
"text:/logo_hu_b7080fe2a2e2a664.avif|")
}
// See issue 14985.
func TestAvifEncodeOutOfMemory(t *testing.T) {
files := `
-- assets/gopher.png --
sourcefilename: ../../resources/testdata/bw-gopher.png
-- layouts/home.html --
{{ $img := resources.Get "gopher.png" }}
{{ $r := try ($img.Resize "3000x3000 avif") }}
{{ with $r.Err }}BigErr: {{ . }}|{{ else }}BigOK|{{ end }}
{{ $small := $img.Resize "32x32 avif" }}
SmallAfter: {{ $small.RelPermalink }}|
`
b := hugolib.Test(t, files, hugolib.TestOptWithConfig(func(c *hugolib.IntegrationTestConfig) {
c.WarpcMemory = 8
}))
// The big resize must fail gracefully (caught by try) ...
b.AssertFileContent("public/index.html",
"BigErr:",
"out of memory allocating",
"for blob data",
// ... and the dispatcher must still process images afterwards.
"SmallAfter: /gopher_",
)
}