feat: image variants engine
This commit is contained in:
1 parent
f0845de643
commit
f1d56ef50e
80 files changed
+7312
-84
No files matched your search
@@ -0,0 +1,129 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
domain "RenewCMS/internal/domain/image"
|
||||
"bytes"
|
||||
"errors"
|
||||
"image"
|
||||
"image/color"
|
||||
"image/gif"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func bandedTallImage() *image.NRGBA {
|
||||
canvas := image.NewNRGBA(image.Rect(0, 0, 20, 60))
|
||||
for y := 0; y < 60; y++ {
|
||||
fill := variantGreen
|
||||
if y < 15 {
|
||||
fill = variantRed
|
||||
}
|
||||
if y >= 45 {
|
||||
fill = variantBlue
|
||||
}
|
||||
for x := 0; x < 20; x++ {
|
||||
canvas.SetNRGBA(x, y, fill)
|
||||
}
|
||||
}
|
||||
return canvas
|
||||
}
|
||||
|
||||
func globalPaletteGIF(t *testing.T, frames int) []byte {
|
||||
t.Helper()
|
||||
palette := color.Palette{color.White, color.Black, color.RGBA{R: 255, A: 255}, color.RGBA{B: 255, A: 255}}
|
||||
animation := &gif.GIF{Config: image.Config{ColorModel: palette, Width: 8, Height: 8}}
|
||||
for frame := 0; frame < frames; frame++ {
|
||||
paletted := image.NewPaletted(image.Rect(0, 0, 8, 8), palette)
|
||||
paletted.SetColorIndex(frame, frame, 1)
|
||||
animation.Image = append(animation.Image, paletted)
|
||||
animation.Delay = append(animation.Delay, 10)
|
||||
}
|
||||
var buffer bytes.Buffer
|
||||
if err := gif.EncodeAll(&buffer, animation); err != nil {
|
||||
t.Fatalf("gif encode failed: %v", err)
|
||||
}
|
||||
return buffer.Bytes()
|
||||
}
|
||||
|
||||
func TestVariantGeometryPixelGaps(t *testing.T) {
|
||||
t.Run("Cover on a tall source keeps the middle band", func(t *testing.T) {
|
||||
rendered := renderVariant(t, encodePNG(t, bandedTallImage()), variantSettings(20, 20, domain.FitCover, domain.FormatPNG))
|
||||
|
||||
decoded, _ := decodeImage(t, rendered.Content)
|
||||
if decoded.Bounds().Dx() != 20 || decoded.Bounds().Dy() != 20 {
|
||||
t.Fatalf("output = %v", decoded.Bounds())
|
||||
}
|
||||
for _, y := range []int{0, 10, 19} {
|
||||
if got := pixelAt(decoded, 10, y); got != variantGreen {
|
||||
t.Errorf("pixel 10,%d = %v, want green", y, got)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Inside on a tall source scales the whole picture", func(t *testing.T) {
|
||||
rendered := renderVariant(t, encodePNG(t, bandedTallImage()), variantSettings(10, 30, domain.FitInside, domain.FormatPNG))
|
||||
|
||||
decoded, _ := decodeImage(t, rendered.Content)
|
||||
if decoded.Bounds().Dx() != 10 || decoded.Bounds().Dy() != 30 {
|
||||
t.Fatalf("output = %v", decoded.Bounds())
|
||||
}
|
||||
want := map[int]color.NRGBA{1: variantRed, 15: variantGreen, 28: variantBlue}
|
||||
for y, expected := range want {
|
||||
assertNearColor(t, pixelAt(decoded, 5, y), expected)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Contain pads horizontally around a narrow source", func(t *testing.T) {
|
||||
rendered := renderVariant(t, encodePNG(t, variantSolidImage(20, 40, variantRed)), variantSettings(40, 40, domain.FitContain, domain.FormatPNG))
|
||||
|
||||
decoded, _ := decodeImage(t, rendered.Content)
|
||||
for y := 0; y < 40; y += 13 {
|
||||
for _, x := range []int{0, 9, 30, 39} {
|
||||
if got := pixelAt(decoded, x, y); got.A != 0 {
|
||||
t.Fatalf("pixel %d,%d = %v, want transparent", x, y, got)
|
||||
}
|
||||
}
|
||||
for _, x := range []int{10, 19, 29} {
|
||||
if got := pixelAt(decoded, x, y); got != variantRed {
|
||||
t.Fatalf("pixel %d,%d = %v, want red", x, y, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Contain keeps a box that is larger in one direction only", func(t *testing.T) {
|
||||
for _, row := range []struct{ width, height, wantWidth, wantHeight int }{
|
||||
{200, 100, 200, 100},
|
||||
{100, 200, 100, 200},
|
||||
} {
|
||||
rendered := renderVariant(t, solidPNG(t, 100, 100), variantSettings(row.width, row.height, domain.FitContain, domain.FormatPNG))
|
||||
|
||||
width, height := decodedSize(t, rendered.Content)
|
||||
if width != row.wantWidth || height != row.wantHeight {
|
||||
t.Errorf("output = %dx%d, want %dx%d", width, height, row.wantWidth, row.wantHeight)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestAnimatedGIFDetectionWithGlobalPalette(t *testing.T) {
|
||||
animated, err := domain.IsAnimatedGIF(bytes.NewReader(globalPaletteGIF(t, 2)))
|
||||
if err != nil || !animated {
|
||||
t.Errorf("two frames: animated = %v, err = %v", animated, err)
|
||||
}
|
||||
|
||||
animated, err = domain.IsAnimatedGIF(bytes.NewReader(globalPaletteGIF(t, 1)))
|
||||
if err != nil || animated {
|
||||
t.Errorf("one frame: animated = %v, err = %v", animated, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVariantRequestErrorMessages(t *testing.T) {
|
||||
unknown := domain.NewUnknownVariantError("thumb")
|
||||
if unknown.Error() != "unknown variant: thumb" || !errors.Is(unknown, domain.ErrUnknownVariant) {
|
||||
t.Errorf("unknown = %q", unknown.Error())
|
||||
}
|
||||
unsupported := domain.NewUnsupportedParameterError("w")
|
||||
if unsupported.Error() != "unsupported parameter: w" || !errors.Is(unsupported, domain.ErrUnsupportedParameter) {
|
||||
t.Errorf("unsupported = %q", unsupported.Error())
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user