mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-08-14 20:03:40 +00:00
pixel: add Grayscale2bit color (#817)
* pixel: add GrayScale2bit color * pixel: fix spelling of 'GrayScale' to 'Grayscale'
This commit is contained in:
@@ -149,6 +149,20 @@ func (img Image[T]) setPixel(index int, c T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
|
case zeroColor.BitsPerPixel() == 2:
|
||||||
|
// Grayscale2bit.
|
||||||
|
offset := index / 4 // 4 pixels per byte
|
||||||
|
shift := 6 - (index%4)*2 // bits: 6, 4, 2, 0
|
||||||
|
|
||||||
|
ptr := (*byte)(unsafe.Add(img.data, offset))
|
||||||
|
|
||||||
|
raw := *(*uint8)(unsafe.Pointer(&c))
|
||||||
|
gray := raw & 0b11
|
||||||
|
|
||||||
|
mask := byte(0b11 << shift)
|
||||||
|
*ptr = (*ptr &^ mask) | (gray << shift)
|
||||||
|
return
|
||||||
|
|
||||||
case zeroColor.BitsPerPixel()%8 == 0:
|
case zeroColor.BitsPerPixel()%8 == 0:
|
||||||
// Each color starts at a whole byte offset.
|
// Each color starts at a whole byte offset.
|
||||||
// This is the easy case.
|
// This is the easy case.
|
||||||
@@ -206,6 +220,13 @@ func (img Image[T]) Get(x, y int) T {
|
|||||||
ptr := (*byte)(unsafe.Add(img.data, offset))
|
ptr := (*byte)(unsafe.Add(img.data, offset))
|
||||||
c = ((*ptr >> (7 - uint8(bits))) & 0x1) > 0
|
c = ((*ptr >> (7 - uint8(bits))) & 0x1) > 0
|
||||||
return any(c).(T)
|
return any(c).(T)
|
||||||
|
case zeroColor.BitsPerPixel() == 2:
|
||||||
|
// Grayscale2bit.
|
||||||
|
offset := index / 4 // 4 pixels per byte
|
||||||
|
shift := 6 - (index%4)*2 // bits: 6, 4, 2, 0
|
||||||
|
ptr := (*byte)(unsafe.Add(img.data, offset))
|
||||||
|
value := ((*ptr) >> shift) & 0b11
|
||||||
|
return any(Grayscale2bit(value)).(T)
|
||||||
case zeroColor.BitsPerPixel()%8 == 0:
|
case zeroColor.BitsPerPixel()%8 == 0:
|
||||||
// Colors like RGB565, RGB888, etc.
|
// Colors like RGB565, RGB888, etc.
|
||||||
offset := index * int(unsafe.Sizeof(zeroColor))
|
offset := index * int(unsafe.Sizeof(zeroColor))
|
||||||
|
|||||||
@@ -107,6 +107,66 @@ func TestImageRGB444BE(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestImageGrayscale2bit(t *testing.T) {
|
||||||
|
image := pixel.NewImage[pixel.Grayscale2bit](128, 64)
|
||||||
|
|
||||||
|
if width, height := image.Size(); width != 128 || height != 64 {
|
||||||
|
t.Errorf("image.Size(): expected 128, 64 but got %d, %d", width, height)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Define test colors representing 4 Grayscale levels.
|
||||||
|
testColors := []color.RGBA{
|
||||||
|
{R: 0x00, G: 0x00, B: 0x00, A: 0xff}, // black
|
||||||
|
{R: 0x55, G: 0x55, B: 0x55, A: 0xff}, // dark gray
|
||||||
|
{R: 0xaa, G: 0xaa, B: 0xaa, A: 0xff}, // light gray
|
||||||
|
{R: 0xff, G: 0xff, B: 0xff, A: 0xff}, // white
|
||||||
|
}
|
||||||
|
|
||||||
|
// Single pixel roundtrip test at a fixed coordinate.
|
||||||
|
for _, c := range testColors {
|
||||||
|
encoded := pixel.NewColor[pixel.Grayscale2bit](c.R, c.G, c.B)
|
||||||
|
image.Set(5, 3, encoded)
|
||||||
|
actual := image.Get(5, 3).RGBA()
|
||||||
|
if actual != c {
|
||||||
|
t.Errorf("failed to roundtrip color: expected %v but got %v", c, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Multi-coordinate test across the image.
|
||||||
|
for x := 0; x < 8; x++ {
|
||||||
|
for y, c := range testColors {
|
||||||
|
encoded := pixel.NewColor[pixel.Grayscale2bit](c.R, c.G, c.B)
|
||||||
|
image.Set(x, y, encoded)
|
||||||
|
actual := image.Get(x, y).RGBA()
|
||||||
|
if actual != c {
|
||||||
|
t.Errorf("Set/Get mismatch at (%d,%d): expected %v but got %v", x, y, c, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewGrayscale2bitMapping(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
input color.RGBA
|
||||||
|
expect pixel.Grayscale2bit
|
||||||
|
}{
|
||||||
|
{color.RGBA{R: 0x00, G: 0x00, B: 0x00}, 0}, // 0
|
||||||
|
{color.RGBA{R: 0x3F, G: 0x3F, B: 0x3F}, 0}, // 63
|
||||||
|
{color.RGBA{R: 0x40, G: 0x40, B: 0x40}, 1}, // 64
|
||||||
|
{color.RGBA{R: 0x7F, G: 0x7F, B: 0x7F}, 1}, // 127
|
||||||
|
{color.RGBA{R: 0x80, G: 0x80, B: 0x80}, 2}, // 128
|
||||||
|
{color.RGBA{R: 0xBF, G: 0xBF, B: 0xBF}, 2}, // 191
|
||||||
|
{color.RGBA{R: 0xC0, G: 0xC0, B: 0xC0}, 3}, // 192
|
||||||
|
{color.RGBA{R: 0xFF, G: 0xFF, B: 0xFF}, 3}, // 255
|
||||||
|
}
|
||||||
|
for _, tc := range testCases {
|
||||||
|
actual := pixel.NewColor[pixel.Grayscale2bit](tc.input.R, tc.input.G, tc.input.B)
|
||||||
|
if actual != tc.expect {
|
||||||
|
t.Errorf("NewGrayscale2bit(%#v) = %d, want %d", tc.input, actual, tc.expect)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestImageMonochrome(t *testing.T) {
|
func TestImageMonochrome(t *testing.T) {
|
||||||
image := pixel.NewImage[pixel.Monochrome](128, 64)
|
image := pixel.NewImage[pixel.Monochrome](128, 64)
|
||||||
if width, height := image.Size(); width != 128 || height != 64 {
|
if width, height := image.Size(); width != 128 || height != 64 {
|
||||||
@@ -236,6 +296,9 @@ func TestImageNoise(t *testing.T) {
|
|||||||
t.Run("RGB444BE", func(t *testing.T) {
|
t.Run("RGB444BE", func(t *testing.T) {
|
||||||
testImageNoiseN[pixel.RGB444BE](t)
|
testImageNoiseN[pixel.RGB444BE](t)
|
||||||
})
|
})
|
||||||
|
t.Run("Grayscale2bit", func(t *testing.T) {
|
||||||
|
testImageNoiseN[pixel.Grayscale2bit](t)
|
||||||
|
})
|
||||||
t.Run("Monochrome", func(t *testing.T) {
|
t.Run("Monochrome", func(t *testing.T) {
|
||||||
testImageNoiseN[pixel.Monochrome](t)
|
testImageNoiseN[pixel.Monochrome](t)
|
||||||
})
|
})
|
||||||
|
|||||||
+31
-1
@@ -16,7 +16,7 @@ import (
|
|||||||
// particular display. Each pixel is at least 1 byte in size.
|
// particular display. Each pixel is at least 1 byte in size.
|
||||||
// The color format is sRGB (or close to it) in all cases except for 1-bit.
|
// The color format is sRGB (or close to it) in all cases except for 1-bit.
|
||||||
type Color interface {
|
type Color interface {
|
||||||
RGB888 | RGB565BE | RGB555 | RGB444BE | Monochrome
|
RGB888 | RGB565BE | RGB555 | RGB444BE | Grayscale2bit | Monochrome
|
||||||
|
|
||||||
BaseColor
|
BaseColor
|
||||||
}
|
}
|
||||||
@@ -50,6 +50,8 @@ func NewColor[T Color](r, g, b uint8) T {
|
|||||||
return any(NewRGB555(r, g, b)).(T)
|
return any(NewRGB555(r, g, b)).(T)
|
||||||
case RGB444BE:
|
case RGB444BE:
|
||||||
return any(NewRGB444BE(r, g, b)).(T)
|
return any(NewRGB444BE(r, g, b)).(T)
|
||||||
|
case Grayscale2bit:
|
||||||
|
return any(NewGrayscale2bit(r, g, b)).(T)
|
||||||
case Monochrome:
|
case Monochrome:
|
||||||
return any(NewMonochrome(r, g, b)).(T)
|
return any(NewMonochrome(r, g, b)).(T)
|
||||||
default:
|
default:
|
||||||
@@ -204,6 +206,34 @@ func (c RGB444BE) RGBA() color.RGBA {
|
|||||||
return color
|
return color
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Grayscale2bit represents a 2-bit Grayscale value (4 levels: black, dark gray, light gray, white).
|
||||||
|
type Grayscale2bit uint8
|
||||||
|
|
||||||
|
func NewGrayscale2bit(r, g, b uint8) Grayscale2bit {
|
||||||
|
// Convert RGB to luminance using standard weights (approximation of human perception)
|
||||||
|
// Use shift-based operations to reduce processing time.
|
||||||
|
// luminance := (299*uint32(r) + 587*uint32(g) + 114*uint32(b)) / 1000
|
||||||
|
luminance := (77*uint32(r) + 150*uint32(g) + 29*uint32(b)) >> 8
|
||||||
|
// Map to 2-bit value: 0–63 => 0, 64–127 => 1, 128–191 => 2, 192–255 => 3
|
||||||
|
return Grayscale2bit((luminance >> 6) & 0b11)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Grayscale2bit) BitsPerPixel() int {
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Grayscale2bit) RGBA() color.RGBA {
|
||||||
|
// Expand 2-bit Grayscale back to 8-bit (0–255) using multiplication
|
||||||
|
// 0 → 0x00, 1 → 0x55, 2 → 0xAA, 3 → 0xFF (i.e., multiply by 85)
|
||||||
|
gray := uint8(c&0b11) * 85
|
||||||
|
return color.RGBA{
|
||||||
|
R: gray,
|
||||||
|
G: gray,
|
||||||
|
B: gray,
|
||||||
|
A: 255,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type Monochrome bool
|
type Monochrome bool
|
||||||
|
|
||||||
func NewMonochrome(r, g, b uint8) Monochrome {
|
func NewMonochrome(r, g, b uint8) Monochrome {
|
||||||
|
|||||||
Reference in New Issue
Block a user