From 7d983647ad1444074a071837fb7bc1710bcc449a Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Sat, 25 May 2024 15:42:11 +0200 Subject: [PATCH] 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. --- pixel/image.go | 6 ++-- pixel/image_test.go | 68 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 3 deletions(-) diff --git a/pixel/image.go b/pixel/image.go index 0a3f727..1a1d26f 100644 --- a/pixel/image.go +++ b/pixel/image.go @@ -104,9 +104,9 @@ func (img Image[T]) setPixel(index int, c T) { switch { case zeroColor.BitsPerPixel() == 1: // Monochrome. - x := int16(index) % img.width - y := int16(index) / img.width - offset := x + (y/8)*img.width + x := index % int(img.width) + y := index / int(img.width) + offset := x + (y/8)*int(img.width) ptr := (*byte)(unsafe.Add(img.data, offset)) if c != zeroColor { *((*byte)(ptr)) |= 1 << uint8(y%8) diff --git a/pixel/image_test.go b/pixel/image_test.go index e7f165e..bb7afc8 100644 --- a/pixel/image_test.go +++ b/pixel/image_test.go @@ -1,7 +1,9 @@ package pixel_test import ( + goimage "image" "image/color" + "math/rand" "testing" "tinygo.org/x/drivers/pixel" @@ -94,3 +96,69 @@ func TestImageMonochrome(t *testing.T) { } } } + +// 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) + } +}