Files
Ayke van Laethem 7d983647ad pixel: fix Image[Monochrome].Set for larger images
For bigger images, the pixel index might not fit in a int16. Therefore,
int is needed during the calculation.

While fixing this bug, I've added a few tests that verify the Image
implementation by creating images, filling them with random data, and
then checking whether they still contain the same data. This test failed
before the patch.
2024-05-25 17:09:48 +02:00

165 lines
4.6 KiB
Go

package pixel_test
import (
goimage "image"
"image/color"
"math/rand"
"testing"
"tinygo.org/x/drivers/pixel"
)
func TestImageRGB565BE(t *testing.T) {
image := pixel.NewImage[pixel.RGB565BE](5, 3)
if width, height := image.Size(); width != 5 && height != 3 {
t.Errorf("image.Size(): expected 5, 3 but got %d, %d", width, height)
}
for _, c := range []color.RGBA{
{R: 0xff, A: 0xff},
{G: 0xff, A: 0xff},
{B: 0xff, A: 0xff},
{R: 0x10, A: 0xff},
{G: 0x10, A: 0xff},
{B: 0x10, A: 0xff},
} {
image.Set(4, 2, pixel.NewColor[pixel.RGB565BE](c.R, c.G, c.B))
c2 := image.Get(4, 2).RGBA()
if c2 != c {
t.Errorf("failed to roundtrip color: expected %v but got %v", c, c2)
}
}
}
func TestImageRGB444BE(t *testing.T) {
image := pixel.NewImage[pixel.RGB444BE](5, 3)
if width, height := image.Size(); width != 5 && height != 3 {
t.Errorf("image.Size(): expected 5, 3 but got %d, %d", width, height)
}
for _, c := range []color.RGBA{
{R: 0xff, A: 0xff},
{G: 0xff, A: 0xff},
{B: 0xff, A: 0xff},
{R: 0x11, A: 0xff},
{G: 0x11, A: 0xff},
{B: 0x11, A: 0xff},
} {
encoded := pixel.NewColor[pixel.RGB444BE](c.R, c.G, c.B)
image.Set(0, 0, encoded)
image.Set(0, 1, encoded)
encoded2 := image.Get(0, 0)
encoded3 := image.Get(0, 1)
if encoded != encoded2 {
t.Errorf("failed to roundtrip color %v: expected %d but got %d", c, encoded, encoded2)
}
if encoded != encoded3 {
t.Errorf("failed to roundtrip color %v: expected %d but got %d", c, encoded, encoded3)
}
c2 := encoded2.RGBA()
if c2 != c {
t.Errorf("failed to roundtrip color: expected %v but got %v", c, c2)
}
c3 := encoded3.RGBA()
if c3 != c {
t.Errorf("failed to roundtrip color: expected %v but got %v", c, c3)
}
}
}
func TestImageMonochrome(t *testing.T) {
image := pixel.NewImage[pixel.Monochrome](5, 3)
if width, height := image.Size(); width != 5 && height != 3 {
t.Errorf("image.Size(): expected 5, 3 but got %d, %d", width, height)
}
for _, expected := range []color.RGBA{
{R: 0xff, G: 0xff, B: 0xff},
{G: 0xff},
{R: 0xff, G: 0xff},
{G: 0xff, B: 0xff},
{R: 0x00},
{G: 0x00, A: 0xff},
{B: 0x00, A: 0xff},
} {
encoded := pixel.NewColor[pixel.Monochrome](expected.R, expected.G, expected.B)
image.Set(4, 2, encoded)
actual := image.Get(4, 2).RGBA()
switch {
case expected.R == 0 && expected.G == 0 && expected.B == 0:
// should be false eg black
if actual.R != 0 || actual.G != 0 || actual.B != 0 {
t.Errorf("failed to roundtrip color: expected %v but got %v", expected, actual)
}
case int(expected.R)+int(expected.G)+int(expected.B) > 128*3:
// should be true eg white
if actual.R == 0 || actual.G == 0 || actual.B == 0 {
t.Errorf("failed to roundtrip color: expected %v but got %v", expected, actual)
}
}
}
}
// Test pixel formats by filling them with noise and checking whether they
// contain the same data afterwards.
func TestImageNoise(t *testing.T) {
t.Run("RGB888", func(t *testing.T) {
testImageNoise[pixel.RGB888](t)
})
t.Run("RGB565BE", func(t *testing.T) {
testImageNoise[pixel.RGB565BE](t)
})
t.Run("RGB555", func(t *testing.T) {
testImageNoise[pixel.RGB555](t)
})
t.Run("RGB444BE", func(t *testing.T) {
testImageNoise[pixel.RGB444BE](t)
})
t.Run("Monochrome", func(t *testing.T) {
testImageNoise[pixel.Monochrome](t)
})
}
func testImageNoise[T pixel.Color](t *testing.T) {
// Create an image of a random width/height for extra testing.
width := rand.Int()%500 + 10
height := rand.Int()%500 + 10
t.Log("image size:", width, height)
// Create two images: the to-be-tested image object and a reference image.
img := pixel.NewImage[T](width, height)
ref := goimage.NewRGBA(goimage.Rect(0, 0, width, height))
// Fill the two images with noise.
for y := 0; y < height; y++ {
for x := 0; x < width; x++ {
// Set a random color in both images.
c := pixel.NewColor[T](uint8(rand.Uint32()), uint8(rand.Uint32()), uint8(rand.Uint32()))
img.Set(x, y, c)
ref.Set(x, y, c.RGBA())
}
}
// Compare the two images. They should match.
mismatch := 0
firstX := 0
firstY := 0
var firstExpected, firstActual color.RGBA
for y := 0; y < height; y++ {
for x := 0; x < width; x++ {
c := img.Get(x, y).RGBA()
r2, g2, b2, _ := ref.At(x, y).RGBA()
c2 := color.RGBA{R: uint8(r2 >> 8), G: uint8(g2 >> 8), B: uint8(b2 >> 8), A: 255}
if c != c2 {
mismatch++
if mismatch == 1 {
firstX = x
firstY = y
firstExpected = c
firstActual = c2
}
}
}
}
if mismatch != 0 {
t.Errorf("mismatch found: %d pixels are different (first diff at (%d, %d), expected %v, actual %v)", mismatch, firstX, firstY, firstExpected, firstActual)
}
}