138 lines
4.5 KiB
Go
138 lines
4.5 KiB
Go
package platform
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/binary"
|
|
"image"
|
|
"image/color"
|
|
"image/jpeg"
|
|
"image/png"
|
|
"testing"
|
|
)
|
|
|
|
func TestAssetPreviewDownscalesWithoutChangingOriginal(t *testing.T) {
|
|
source := image.NewNRGBA(image.Rect(0, 0, 1600, 900))
|
|
for y := 0; y < 900; y++ {
|
|
for x := 0; x < 1600; x++ {
|
|
source.SetNRGBA(x, y, color.NRGBA{uint8(x), uint8(y), uint8(x * y), 255})
|
|
}
|
|
}
|
|
var encoded bytes.Buffer
|
|
if err := png.Encode(&encoded, source); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
original := append([]byte(nil), encoded.Bytes()...)
|
|
preview, err := makeAssetPreview(encoded.Bytes(), "image/png", 512)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
config, format, err := image.DecodeConfig(bytes.NewReader(preview.data))
|
|
if err != nil || config.Width != 512 || config.Height != 288 || format != "jpeg" {
|
|
t.Fatalf("bad preview %s %dx%d %v", format, config.Width, config.Height, err)
|
|
}
|
|
if len(preview.data) >= len(original) || !bytes.Equal(original, encoded.Bytes()) {
|
|
t.Fatal("preview must shrink without changing original")
|
|
}
|
|
cached, err := cachedAssetPreview(context.Background(), original, "image/png", 512)
|
|
if err != nil || !bytes.Equal(cached.data, preview.data) {
|
|
t.Fatal("cache result mismatch")
|
|
}
|
|
}
|
|
func TestAssetPreviewPreservesTransparencyAndAnimation(t *testing.T) {
|
|
source := image.NewNRGBA(image.Rect(0, 0, 800, 400))
|
|
source.SetNRGBA(1, 1, color.NRGBA{255, 0, 0, 255})
|
|
var encoded bytes.Buffer
|
|
_ = png.Encode(&encoded, source)
|
|
preview, err := makeAssetPreview(encoded.Bytes(), "image/png", 256)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
decoded, format, err := image.Decode(bytes.NewReader(preview.data))
|
|
if err != nil || format != "png" {
|
|
t.Fatal("transparent image must stay PNG")
|
|
}
|
|
_, _, _, alpha := decoded.At(200, 100).RGBA()
|
|
if alpha != 0 {
|
|
t.Fatal("transparent background was lost")
|
|
}
|
|
original := []byte("GIF original animation bytes")
|
|
gif, err := makeAssetPreview(original, "image/gif", 512)
|
|
if err != nil || !bytes.Equal(gif.data, original) {
|
|
t.Fatal("GIF animation changed")
|
|
}
|
|
}
|
|
func TestAssetPreviewRejectsUnboundedWidths(t *testing.T) {
|
|
for _, value := range []string{"0", "1", "256", "512", "2048", "999999", "-1", "abc"} {
|
|
if _, err := previewWidth("preview", value); err == nil {
|
|
t.Errorf("accepted width %s", value)
|
|
}
|
|
}
|
|
for _, value := range []string{"", "1024"} {
|
|
if _, err := previewWidth("preview", value); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
if n, err := previewWidth("", ""); err != nil || n != 0 {
|
|
t.Fatal("original request changed")
|
|
}
|
|
if _, err := previewWidth("other", ""); err == nil {
|
|
t.Fatal("unknown variant accepted")
|
|
}
|
|
if _, err := makeAssetPreview([]byte("invalid"), "image/png", 512); err == nil {
|
|
t.Fatal("malformed image accepted")
|
|
}
|
|
}
|
|
func TestAssetPreviewAppliesJPEGOrientation(t *testing.T) {
|
|
source := image.NewNRGBA(image.Rect(0, 0, 800, 400))
|
|
for y := 0; y < 400; y++ {
|
|
for x := 0; x < 800; x++ {
|
|
source.SetNRGBA(x, y, color.NRGBA{255, 0, 0, 255})
|
|
}
|
|
}
|
|
var original bytes.Buffer
|
|
_ = jpeg.Encode(&original, source, nil)
|
|
tiff := make([]byte, 26)
|
|
copy(tiff, "II")
|
|
binary.LittleEndian.PutUint16(tiff[2:], 42)
|
|
binary.LittleEndian.PutUint32(tiff[4:], 8)
|
|
binary.LittleEndian.PutUint16(tiff[8:], 1)
|
|
binary.LittleEndian.PutUint16(tiff[10:], 0x112)
|
|
binary.LittleEndian.PutUint16(tiff[12:], 3)
|
|
binary.LittleEndian.PutUint32(tiff[14:], 1)
|
|
binary.LittleEndian.PutUint16(tiff[18:], 6)
|
|
exif := append([]byte("Exif\x00\x00"), tiff...)
|
|
segment := append([]byte{0xff, 0xe1, 0, byte(len(exif) + 2)}, exif...)
|
|
encoded := append(append(append([]byte{}, original.Bytes()[:2]...), segment...), original.Bytes()[2:]...)
|
|
if jpegOrientation(encoded) != 6 {
|
|
t.Fatal("EXIF orientation not decoded")
|
|
}
|
|
preview, err := makeAssetPreview(encoded, "image/jpeg", 256)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
config, _, err := image.DecodeConfig(bytes.NewReader(preview.data))
|
|
if err != nil || config.Width != 128 || config.Height != 256 {
|
|
t.Fatalf("rotated dimensions %dx%d %v", config.Width, config.Height, err)
|
|
}
|
|
}
|
|
func TestAllAssetOrientationMappings(t *testing.T) {
|
|
source := image.NewNRGBA(image.Rect(0, 0, 3, 2))
|
|
source.SetNRGBA(0, 0, color.NRGBA{255, 0, 0, 255})
|
|
for orientation := 1; orientation <= 8; orientation++ {
|
|
oriented := orientedAsset{source, orientation}
|
|
red := 0
|
|
for y := 0; y < oriented.Bounds().Dy(); y++ {
|
|
for x := 0; x < oriented.Bounds().Dx(); x++ {
|
|
r, _, _, _ := oriented.At(x, y).RGBA()
|
|
if r > 0 {
|
|
red++
|
|
}
|
|
}
|
|
}
|
|
if red != 1 {
|
|
t.Errorf("orientation %d loses/repeats pixels", orientation)
|
|
}
|
|
}
|
|
}
|