Browse Source

Now pixel package

master v0.1.2
maze 7 months ago
parent
commit
dfbff8e2c7
23 changed files with 583 additions and 94 deletions
  1. +194
    -28
      color.go
  2. +273
    -0
      color_test.go
  3. +4
    -0
      doc.go
  4. +10
    -10
      draw.go
  5. +1
    -1
      draw_test.go
  6. +1
    -1
      fx.go
  7. +1
    -1
      fx_bit.go
  8. +1
    -1
      fx_test.go
  9. +1
    -1
      go.mod
  10. +44
    -9
      image.go
  11. +1
    -1
      image_test.go
  12. +24
    -24
      pixeleffect/fade.go
  13. +5
    -5
      pixeleffect/fade_test.go
  14. +0
    -0
      pixeleffect/testdata/gopher.png
  15. +7
    -6
      pixelfont/font.go
  16. +1
    -1
      pixelfont/font_glcd_5x8.go
  17. +5
    -3
      pixelfont/font_test.go
  18. +0
    -0
      pixelfont/testdata/16bit.ttf
  19. +0
    -0
      pixelfont/testdata/8bit.ttf
  20. +0
    -0
      pixelfont/testdata/pixelmix.ttf
  21. +8
    -0
      pixelfont/util.go
  22. +1
    -1
      util.go
  23. +1
    -1
      util_test.go

+ 194
- 28
color.go View File

@ -1,14 +1,90 @@
package bitmap
package pixel
import "image/color"
import (
"image/color"
)
// Color models supported by this package.
var (
BitModel = color.ModelFunc(convertBit)
RGB565Model = color.ModelFunc(convertRGB565)
RGB888Model = color.ModelFunc(convertRGB888)
BitModel = color.ModelFunc(bitModel)
RGB332Model = color.ModelFunc(rgb332Model)
RGB565Model = color.ModelFunc(rgb565Model)
RGB888Model = color.ModelFunc(rgb888Model)
RGBA4444Model = color.ModelFunc(rgba4444Model)
RGBA5551Model = color.ModelFunc(rgba5551Model)
)
func bitModel(c color.Color) color.Color {
if _, ok := c.(Bit); ok {
return c
}
r, g, b, _ := c.RGBA()
// These coefficients (the fractions 0.299, 0.587 and 0.114) are the same
// as those given by the JFIF specification and used by func RGBToYCbCr in
// ycbcr.go.
//
// Note that 19595 + 38470 + 7471 equals 65536.
y := (19595*r + 38470*g + 7471*b + 1<<15) >> 24
return Bit(uint8(y) >= 0x80)
//return Bit((r | g | b) >= 0x8000)
}
func rgb332Model(c color.Color) color.Color {
if _, ok := c.(RGB332); ok {
return c
}
r, g, b, _ := c.RGBA()
r >>= 13
g >>= 13
b >>= 14
return RGB332(r<<5 | g<<2 | b)
}
func rgb565Model(c color.Color) color.Color {
if _, ok := c.(RGB565); ok {
return c
}
r, g, b, _ := c.RGBA()
// RRRRRGGGGGGBBBBB
return RGB565((r & 0xf800) | ((g & 0xfc00) >> 5) | ((b & 0xf800) >> 11))
}
func rgb888Model(c color.Color) color.Color {
if _, ok := c.(RGB888); ok {
return c
}
r, g, b, _ := c.RGBA()
return RGB888{uint8(r), uint8(g), uint8(b)}
}
func rgba4444Model(c color.Color) color.Color {
if _, ok := c.(RGBA4444); ok {
return c
}
r, g, b, a := c.RGBA()
r >>= 12
g >>= 12
b >>= 12
a >>= 12
return RGBA4444(r<<12 | g<<8 | b<<4 | a)
}
func rgba5551Model(c color.Color) color.Color {
if _, ok := c.(RGBA5551); ok {
return c
}
r, g, b, a := c.RGBA()
r >>= 11
g >>= 11
b >>= 11
if a > 0 {
a = 1
}
return RGBA5551(r<<11 | g<<6 | b<<1 | a)
}
// Bit is a 1-bit color.
type Bit bool
@ -25,11 +101,7 @@ func (c Bit) RGBA() (r, g, b, a uint32) {
return 0, 0, 0, 0xffff
}
func convertBit(c color.Color) color.Color {
return convertBitColor(c)
}
func convertBitColor(c color.Color) Bit {
func toBit(c color.Color) Bit {
switch c := c.(type) {
case Bit:
return c
@ -39,6 +111,36 @@ func convertBitColor(c color.Color) Bit {
}
}
// RGB332 is a 8-bit RGB color with no alpha channel.
type RGB332 uint8 // 3-3-2 RGB
func (c RGB332) RGBA() (r, g, b, a uint32) {
r = uint32(c&0b111_000_00) >> 4 // ............rrr0
r |= r << 3 // .........rrrrrr0
r |= r << 6 // ......rrrrrrrrr0
r |= r << 12 // rrrrrrrrrrrrrrr0
g = uint32(c&0b000_111_00) >> 1 // ............ggg0
g |= g << 3 // .........gggggg0
g |= g << 6 // ......ggggggggg0
g |= g << 12 // ggggggggggggggg0
b = uint32(c&0b000_000_11) >> 0 // ..............bb
b |= b << 2 // ............bbbb
b |= b << 4 // ........bbbbbbbb
b |= b << 8 // bbbbbbbbbbbbbbbb
return
}
func toRGB332(c color.Color) RGB332 {
if c, ok := c.(RGB332); ok {
return c
}
r, g, b, _ := c.RGBA()
r >>= 13
g >>= 13
b >>= 14
return RGB332(r<<5 | g<<2 | b)
}
// RGB565 is a 16-bit RGB color with no alpha channel.
type RGB565 uint16 // 5-6-5 RGB
@ -60,9 +162,9 @@ func (c RGB565) RGBA() (r, g, b, a uint32) {
//
// Alpha is always 100% opaque since this model does not support
// transparency.
rBits := uint32(c & 0xF800) // RRRRR00000000000
gBits := uint32(c & 0x7E0) // 00000GGGGGG00000
bBits := uint32(c & 0x1F) // 00000000000BBBBB
rBits := uint32(c & 0b11111_000000_00000)
gBits := uint32(c & 0b00000_111111_00000)
bBits := uint32(c & 0b00000_000000_11111)
r = uint32(rBits | rBits>>5 | rBits>>10 | rBits>>15)
g = uint32(gBits<<5 | gBits>>1 | gBits>>7)
b = uint32(bBits<<11 | bBits<<6 | bBits<<1 | bBits>>4)
@ -70,32 +172,96 @@ func (c RGB565) RGBA() (r, g, b, a uint32) {
return
}
func convertRGB565(c color.Color) color.Color {
return convertRGB565Color(c)
}
func convertRGB565Color(c color.Color) RGB565 {
func toRGB565(c color.Color) RGB565 {
r, g, b, _ := c.RGBA()
// RRRRRGGGGGGBBBBB
return RGB565((r & 0xf800) + ((g & 0xfc00) >> 5) + ((b & 0xf800) >> 11))
}
// RGB888 is a 24-bit color with no alpha channel.
type RGB888 [3]byte // 8-8-8 RGB
type RGB888 struct {
R, G, B uint8
}
func (c RGB888) RGBA() (r, g, b, a uint32) {
r = uint32(c[0]) | uint32(c[0])<<8
g = uint32(c[1]) | uint32(c[1])<<8
b = uint32(c[2]) | uint32(c[2])<<8
a = 0xffff
r = uint32(c.R)
r |= r << 8
g = uint32(c.G)
g |= g << 8
b = uint32(c.B)
b |= b << 8
return
}
func convertRGB888(c color.Color) color.Color {
return convertRGB888Color(c)
}
func convertRGB888Color(c color.Color) RGB888 {
func toRGB888(c color.Color) RGB888 {
r, g, b, _ := c.RGBA()
return RGB888{byte(r), byte(g), byte(b)}
}
// RGBA4444 is a 16-bit RGB color with alpha channel.
type RGBA4444 uint16
func (c RGBA4444) RGBA() (r, g, b, a uint32) {
r = uint32(c&0b1111_0000_0000_0000) >> 12
r |= r << 4
r |= r << 8
r |= r << 12
g = uint32(c&0b0000_1111_0000_0000) >> 8
g |= g << 4
g |= g << 8
g |= g << 12
b = uint32(c&0b0000_0000_1111_0000) >> 4
b |= b << 4
b |= b << 8
b |= b << 12
a = uint32(c&0b0000_0000_0000_1111) >> 0
a |= a << 4
a |= a << 8
a |= a << 12
return
}
func toRGBA4444(c color.Color) RGBA4444 {
if c, ok := c.(RGBA4444); ok {
return c
}
r, g, b, a := c.RGBA()
r >>= 12
g >>= 12
b >>= 12
a >>= 12
return RGBA4444(r<<12 | g<<8 | b<<4 | a)
}
// RGBA5551 is a 16-bit RGB color with alpha channel.
type RGBA5551 uint16
func (c RGBA5551) RGBA() (r, g, b, a uint32) {
var (
rBits = uint32(c&0b11111_00000_00000_0) >> 11
gBits = uint32(c&0b00000_11111_00000_0) >> 6
bBits = uint32(c&0b00000_00000_11111_0) >> 1
aBits = uint32(c&0b00000_00000_00000_1) >> 0
)
r = (rBits | rBits<<5 | rBits<<10) << 1
g = (gBits | gBits<<5 | gBits<<10) << 1
b = (bBits | bBits<<5 | bBits<<10) << 1
if aBits == 1 {
a = 0xffff
}
return
}
func toRGBA5551(c color.Color) RGBA5551 {
if c, ok := c.(RGBA5551); ok {
return c
}
r, g, b, a := c.RGBA()
r >>= 11
g >>= 11
b >>= 11
if a > 0 {
a = 1
}
return RGBA5551(r<<11 | g<<6 | b<<1 | a)
}

+ 273
- 0
color_test.go View File

@ -0,0 +1,273 @@
package pixel
import (
"image/color"
"math/bits"
"testing"
)
var (
testBlack = color.RGBA{0x00, 0x00, 0x00, 0x00}
testWhite = color.RGBA{0xff, 0xff, 0xff, 0x00}
testAmber = color.RGBA{0xff, 0x7f, 0x00, 0x00}
testIndigo = color.RGBA{0x50, 0x00, 0x82, 0x00}
testTurquoise = color.RGBA{0x00, 0xCE, 0xD1, 0x00}
testGray25 = color.RGBA{0x3f, 0x3f, 0x3f, 0x3f}
testGray50 = color.RGBA{0x7f, 0x7f, 0x7f, 0x7f}
testGray75 = color.RGBA{0xbf, 0xbf, 0xbf, 0xbf}
testColorSink color.Color
testColors = []struct {
Name string
Color color.Color
}{
{"black", testBlack}, // black
{"white", testWhite}, // white
{"amber", testAmber}, // amber
{"indigo", testIndigo}, // indigo
{"turquoise", testTurquoise}, // turquoise
{"gray25", testGray25}, // gray 25%
{"gray50", testGray50}, // gray 50%
{"gray75", testGray75}, // gray 75%
}
)
func TestRGB323(t *testing.T) {
tests := []struct {
Name string
Test RGB332
Want color.RGBA
}{
{"black", 0b000_000_00, testBlack},
{"white", 0b111_111_11, testWhite},
{"amber", 0b111_011_00, testAmber},
{"indigo", 0b010_000_10, testIndigo},
{"turquoise", 0b000_110_11, testTurquoise},
{"gray25", 0b001_001_00, testGray25},
{"gray50", 0b011_011_01, testGray50},
{"gray75", 0b101_101_10, testGray75},
}
for _, test := range tests {
t.Run(test.Name, func(it *testing.T) {
testColorSink = color.RGBAModel.Convert(test.Test)
})
}
}
func TestRGB332Model(t *testing.T) {
want := map[string]RGB332{
"black": 0b000_000_00,
"white": 0b111_111_11,
"amber": 0b111_011_00,
"indigo": 0b010_000_10,
"turquoise": 0b000_110_11,
"gray25": 0b001_001_00,
"gray50": 0b011_011_01,
"gray75": 0b101_101_10,
}
for _, test := range testColors {
t.Run(test.Name, func(it *testing.T) {
v := RGB332Model.Convert(test.Color).(RGB332)
if v != want[test.Name] {
it.Fatalf("expected %q (%+v) to return %#08b, got %#08b", test.Name, test.Color, want[test.Name], v)
}
})
}
}
func TestRGB565(t *testing.T) {
tests := []struct {
Name string
Test RGB565
Want color.RGBA
}{
{"black", 0b00000_000000_00000, testBlack},
{"white", 0b11111_111111_11111, testWhite},
{"amber", 0b11111_011111_00000, testAmber},
{"indigo", 0b01010_000000_10000, testIndigo},
{"turquoise", 0b00000_110011_11010, testTurquoise},
{"gray25", 0b00111_001111_00111, testGray25},
{"gray50", 0b01111_011111_01111, testGray50},
{"gray75", 0b10111_101111_10111, testGray75},
}
for _, test := range tests {
t.Run(test.Name, func(it *testing.T) {
v := color.RGBAModel.Convert(test.Test).(color.RGBA)
testColorBitErrors(it, test.Want, v, 3, false)
})
}
}
func TestRGB565Model(t *testing.T) {
want := map[string]RGB565{
"black": 0b00000_000000_00000,
"white": 0b11111_111111_11111,
"amber": 0b11111_011111_00000,
"indigo": 0b01010_000000_10000,
"turquoise": 0b00000_110011_11010,
"gray25": 0b00111_001111_00111,
"gray50": 0b01111_011111_01111,
"gray75": 0b10111_101111_10111,
}
for _, test := range testColors {
t.Run(test.Name, func(it *testing.T) {
v := RGB565Model.Convert(test.Color).(RGB565)
if v != want[test.Name] {
it.Fatalf("expected %q (%+v) to return %#016b, got %#016b", test.Name, test.Color, want[test.Name], v)
}
})
}
}
func TestRGB888(t *testing.T) {
tests := []struct {
Name string
Test RGB888
Want color.RGBA
}{
{"black", RGB888{0x00, 0x00, 0x00}, testBlack},
{"white", RGB888{0xff, 0xff, 0xff}, testWhite},
{"amber", RGB888{0xff, 0x7f, 0x00}, testAmber},
{"indigo", RGB888{0x50, 0x00, 0x82}, testIndigo},
{"turquoise", RGB888{0x00, 0xCE, 0xD1}, testTurquoise},
{"gray25", RGB888{0x3f, 0x3f, 0x3f}, testGray25},
{"gray50", RGB888{0x7f, 0x7f, 0x7f}, testGray50},
{"gray75", RGB888{0xbf, 0xbf, 0xbf}, testGray75},
}
for _, test := range tests {
t.Run(test.Name, func(it *testing.T) {
v := color.RGBAModel.Convert(test.Test).(color.RGBA)
testColorBitErrors(it, test.Want, v, 2, false)
})
}
}
func TestRGB888Model(t *testing.T) {
want := map[string]RGB888{
"black": {0x00, 0x00, 0x00}, // black
"white": {0xff, 0xff, 0xff}, // white
"amber": {0xff, 0x7f, 0x00}, // amber
"indigo": {0x50, 0x00, 0x82}, // indigo
"turquoise": {0x00, 0xCE, 0xD1}, // turquoise
"gray25": {0x3f, 0x3f, 0x3f}, // gray 25%
"gray50": {0x7f, 0x7f, 0x7f}, // gray 50%
"gray75": {0xbf, 0xbf, 0xbf}, // gray 75%
}
for _, test := range testColors {
t.Run(test.Name, func(it *testing.T) {
v := RGB888Model.Convert(test.Color).(RGB888)
if v != want[test.Name] {
it.Fatalf("expected %q (%+v) to return %+v, got %+v", test.Name, test.Color, want[test.Name], v)
}
})
}
}
func TestRGBA4444(t *testing.T) {
tests := []struct {
Name string
Test RGBA4444
Want color.RGBA
}{
{"black", 0b0000_0000_0000_0000, testBlack},
{"white", 0b1111_1111_1111_0000, testWhite},
{"amber", 0b1111_0111_0000_0000, testAmber},
{"indigo", 0b0101_0000_1000_0000, testIndigo},
{"turquoise", 0b0000_1100_1101_0000, testTurquoise},
{"gray25", 0b0011_0011_0011_0011, testGray25},
{"gray50", 0b0111_0111_0111_0111, testGray50},
{"gray75", 0b1011_1011_1011_1011, testGray75},
}
for _, test := range tests {
t.Run(test.Name, func(it *testing.T) {
v := color.RGBAModel.Convert(test.Test).(color.RGBA)
testColorBitErrors(it, test.Want, v, 2, true)
})
}
}
func TestRGBA444Model(t *testing.T) {
want := map[string]RGBA4444{
"black": 0b0000_0000_0000_0000, // black
"white": 0b1111_1111_1111_0000, // white
"amber": 0b1111_0111_0000_0000, // amber
"indigo": 0b0101_0000_1000_0000, // indigo
"turquoise": 0b0000_1100_1101_0000, // turquoise
"gray25": 0b0011_0011_0011_0011, // gray 50%
"gray50": 0b0111_0111_0111_0111, // gray 50%
"gray75": 0b1011_1011_1011_1011, // gray 50%
}
for _, test := range testColors {
t.Run(test.Name, func(it *testing.T) {
v := RGBA4444Model.Convert(test.Color).(RGBA4444)
if v != want[test.Name] {
it.Fatalf("expected %q (%+v) to return %#016b, got %#016b", test.Name, test.Color, want[test.Name], v)
}
})
}
}
func TestRGBA5551(t *testing.T) {
tests := []struct {
Name string
Test RGBA5551
Want color.RGBA // A not used
Alpha bool
}{
{"black", 0b00000_00000_00000_0, testBlack, false},
{"white", 0b11111_11111_11111_0, testWhite, false},
{"amber", 0b11111_01111_00000_0, testAmber, false},
{"indigo", 0b01010_00000_10000_0, testIndigo, false},
{"turquoise", 0b00000_11001_11010_0, testTurquoise, false},
{"gray25", 0b00111_00111_00111_1, testGray25, true},
{"gray50", 0b01111_01111_01111_1, testGray50, true},
{"gray75", 0b10111_10111_10111_1, testGray75, true},
}
for _, test := range tests {
t.Run(test.Name, func(it *testing.T) {
v := color.RGBAModel.Convert(test.Test).(color.RGBA)
testColorBitErrors(it, test.Want, v, 3, false)
if v.A > 0 != test.Alpha {
it.Errorf("expected alpha %t, got %t: %+v", test.Alpha, v.A > 0, v)
}
})
}
}
func TestRGBA5551Model(t *testing.T) {
want := map[string]RGBA5551{
"black": 0b00000_00000_00000_0, // black
"white": 0b11111_11111_11111_0, // white
"amber": 0b11111_01111_00000_0, // amber
"indigo": 0b01010_00000_10000_0, // indigo
"turquoise": 0b00000_11001_11010_0, // turquoise
"gray25": 0b00111_00111_00111_1, // gray 25%
"gray50": 0b01111_01111_01111_1, // gray 50%
"gray75": 0b10111_10111_10111_1, // gray 75%
}
for _, test := range testColors {
t.Run(test.Name, func(it *testing.T) {
v := RGBA5551Model.Convert(test.Color).(RGBA5551)
if v != want[test.Name] {
it.Fatalf("expected %q (%+v) to return %#016b, got %#016b", test.Name, test.Color, want[test.Name], v)
}
})
}
}
func testColorBitErrors(t *testing.T, want color.RGBA, v color.RGBA, errors int, alpha bool) {
t.Helper()
if n := bits.OnesCount8(v.R ^ want.R); n > errors {
t.Errorf("R channel has %d bit errors: want %#02x (%#08b), got %#02x (%#08b)", n, want.R, want.R, v.R, v.R)
}
if n := bits.OnesCount8(v.G ^ want.G); n > errors {
t.Errorf("G channel has %d bit errors: want %#02x (%#08b), got %#02x (%#08b)", n, want.G, want.G, v.G, v.G)
}
if n := bits.OnesCount8(v.B ^ want.B); n > errors {
t.Errorf("B channel has %d bit errors: want %#02x (%#08b), got %#02x (%#08b)", n, want.B, want.B, v.B, v.B)
}
if alpha {
if n := bits.OnesCount8(v.A ^ want.A); n > errors {
t.Errorf("A channel has %d bit errors: want %#02x (%#08b), got %#02x (%#08b)", n, want.A, want.A, v.A, v.A)
}
}
}

+ 4
- 0
doc.go View File

@ -0,0 +1,4 @@
/*
Package pixel contains common pixel formats.
*/
package pixel

+ 10
- 10
draw.go View File

@ -1,4 +1,4 @@
package bitmap
package pixel
import (
"encoding/binary"
@ -9,18 +9,18 @@ import (
func Fill(b Image, c color.Color) {
switch b := b.(type) {
case *Bitmap:
if convertBitColor(c) {
if toBit(c) {
memset(b.Pix, 0xff)
} else {
memset(b.Pix, 0x00)
}
case *RGB565Image:
var v [2]byte
binary.BigEndian.PutUint16(v[:], uint16(convertRGB565Color(c)))
binary.BigEndian.PutUint16(v[:], uint16(toRGB565(c)))
memsetSlice(b.Pix, v[:])
case *RGB888Image:
v := convertRGB888Color(c)
memsetSlice(b.Pix, v[:])
v := toRGB888(c)
memsetSlice(b.Pix, []byte{v.R, v.G, v.B})
default:
// Naive and slow set pixel implementation.
r := b.Bounds()
@ -43,7 +43,7 @@ func fillRectangle(b Image, x, y, width, height int, c color.Color) {
switch b.Format {
case MHMSBFormat:
var v byte
if convertBitColor(c) {
if toBit(c) {
v = 0x01
}
for xx := x; xx < x+width; xx++ {
@ -57,7 +57,7 @@ func fillRectangle(b Image, x, y, width, height int, c color.Color) {
case MVLSBFormat:
var v byte
if convertBitColor(c) {
if toBit(c) {
v = 0x01
}
for ; height > 0; height, y = height-1, y+1 {
@ -77,7 +77,7 @@ func fillRectangle(b Image, x, y, width, height int, c color.Color) {
r = b.Bounds()
xe = min(x+width, r.Max.X)
ye = min(y+height, r.Max.Y)
c = convertRGB565Color(c)
c = toRGB565(c)
v [2]byte
)
binary.BigEndian.PutUint16(v[:], uint16(c))
@ -91,11 +91,11 @@ func fillRectangle(b Image, x, y, width, height int, c color.Color) {
r = b.Bounds()
xe = min(x+width, r.Max.X)
ye = min(y+height, r.Max.Y)
v = convertRGB888Color(c)
v = toRGB888(c)
)
for ; y < ye; y++ {
o := y*b.Stride + x*3
memsetSlice(b.Pix[o:o+(xe-x)*3], v[:])
memsetSlice(b.Pix[o:o+(xe-x)*3], []byte{v.R, v.G, v.B})
}
default:


+ 1
- 1
draw_test.go View File

@ -1,4 +1,4 @@
package bitmap
package pixel
import (
"image"


+ 1
- 1
fx.go View File

@ -1,4 +1,4 @@
package bitmap
package pixel
/*
func Scroll(buffer Image, xstep, ystep int) {


+ 1
- 1
fx_bit.go View File

@ -1,4 +1,4 @@
package bitmap
package pixel
import "time"


+ 1
- 1
fx_test.go View File

@ -1,4 +1,4 @@
package bitmap
package pixel
import (
"image"


+ 1
- 1
go.mod View File

@ -1,4 +1,4 @@
module maze.io/x/bitmap
module maze.io/x/pixel
go 1.14


bitmap.go → image.go View File

@ -1,4 +1,4 @@
package bitmap
package pixel
import (
"encoding/binary"
@ -89,7 +89,7 @@ func (b *Bitmap) PixOffset(x, y int) (int, uint) {
}
func (b *Bitmap) Set(x, y int, c color.Color) {
b.SetBit(x, y, convertBitColor(c))
b.SetBit(x, y, toBit(c))
}
func (b *Bitmap) SetBit(x, y int, c Bit) {
@ -110,6 +110,42 @@ type RGBImage struct {
Stride int
}
type RGB332Image struct {
Rect image.Rectangle
Pix []byte
Stride int
}
func (b *RGB332Image) Bounds() image.Rectangle {
return b.Rect
}
func (b *RGB332Image) At(x, y int) color.Color {
if !(image.Point{X: x, Y: y}).In(b.Rect) {
return RGB332(0)
}
return RGB332(b.Pix[y*b.Stride+x])
}
func (b *RGB332Image) Set(x, y int, c color.Color) {
if !(image.Point{X: x, Y: y}).In(b.Rect) {
return
}
b.Pix[y*b.Stride+x] = byte(toRGB332(c))
}
func (RGB332Image) ColorModel() color.Model {
return RGB332Model
}
func NewRGB332(w, h int) *RGB332Image {
return &RGB332Image{
Rect: image.Rectangle{Max: image.Point{X: w, Y: h}},
Stride: w,
Pix: make([]byte, w*h),
}
}
type RGB565Image struct {
Rect image.Rectangle
Pix []byte
@ -122,7 +158,7 @@ func (b *RGB565Image) Bounds() image.Rectangle {
func (b *RGB565Image) At(x, y int) color.Color {
if !(image.Point{X: x, Y: y}).In(b.Rect) {
return color.Black
return RGB565(0)
}
return RGB565(binary.BigEndian.Uint16(b.Pix[b.OffsetOf(x, y):]))
}
@ -131,7 +167,7 @@ func (b *RGB565Image) Set(x, y int, c color.Color) {
if !(image.Point{X: x, Y: y}).In(b.Rect) {
return
}
binary.BigEndian.PutUint16(b.Pix[b.OffsetOf(x, y):], uint16(convertRGB565Color(c)))
binary.BigEndian.PutUint16(b.Pix[b.OffsetOf(x, y):], uint16(toRGB565(c)))
}
func (b *RGB565Image) OffsetOf(x, y int) (offset int) {
@ -172,17 +208,16 @@ func (b *RGB888Image) At(x, y int) color.Color {
if !(image.Point{X: x, Y: y}).In(b.Rect) {
return color.Black
}
var c RGB888
copy(c[:], b.Pix[b.OffsetOf(x, y):])
return c
v := b.Pix[b.OffsetOf(x, y):]
return RGB888{v[0], v[1], v[2]}
}
func (b *RGB888Image) Set(x, y int, c color.Color) {
if !(image.Point{X: x, Y: y}).In(b.Rect) {
return
}
v := convertRGB888Color(c)
copy(b.Pix[b.OffsetOf(x, y):], v[:])
v := toRGB888(c)
copy(b.Pix[b.OffsetOf(x, y):], []byte{v.R, v.G, v.B})
}
func (b *RGB888Image) OffsetOf(x, y int) (offset int) {

bitmap_test.go → image_test.go View File

@ -1,4 +1,4 @@
package bitmap
package pixel
import (
"fmt"

fx/fade.go → pixeleffect/fade.go View File

@ -1,4 +1,4 @@
package fx
package pixeleffect
import (
"image"
@ -6,7 +6,7 @@ import (
"image/draw"
"time"
"maze.io/x/bitmap"
"maze.io/x/pixel"
)
const (
@ -35,14 +35,14 @@ func repeatWithin(duration time.Duration, n int, f func(int)) <-chan time.Time {
}
// FadeOutDither dims the colors by dithering, most useful for 1-bit bitmaps.
func FadeOutDither(duration time.Duration, im bitmap.Image) <-chan time.Time {
func FadeOutDither(duration time.Duration, im pixel.Image) <-chan time.Time {
return repeatWithin(duration, len(ditherMasks), func(step int) {
DitherStep(im, step)
})
}
// FadeOutVertical draws horizontal raster lines to fade out the image.
func FadeOutHorizontal(duration time.Duration, im bitmap.Image) <-chan time.Time {
func FadeOutHorizontal(duration time.Duration, im pixel.Image) <-chan time.Time {
var (
bounds = im.Bounds()
width = bounds.Dx()
@ -57,13 +57,13 @@ func FadeOutHorizontal(duration time.Duration, im bitmap.Image) <-chan time.Time
offset0 = y * 2
offset1 = height - offset0 - 1
)
bitmap.Line(im, image.Point{Y: offset0}, image.Point{X: width, Y: offset0}, color.Black)
bitmap.Line(im, image.Point{Y: offset1}, image.Point{X: width, Y: offset1}, color.Black)
pixel.Line(im, image.Point{Y: offset0}, image.Point{X: width, Y: offset0}, color.Black)
pixel.Line(im, image.Point{Y: offset1}, image.Point{X: width, Y: offset1}, color.Black)
})
}
// FadeOutVertical draws vertical raster lines to fade out the image.
func FadeOutVertical(duration time.Duration, im bitmap.Image) <-chan time.Time {
func FadeOutVertical(duration time.Duration, im pixel.Image) <-chan time.Time {
var (
bounds = im.Bounds()
width = bounds.Dx()
@ -78,12 +78,12 @@ func FadeOutVertical(duration time.Duration, im bitmap.Image) <-chan time.Time {
offset0 = x * 2
offset1 = width - offset0 - 1
)
bitmap.Line(im, image.Point{X: offset0}, image.Point{X: offset0, Y: height}, color.Black)
bitmap.Line(im, image.Point{X: offset1}, image.Point{X: offset1, Y: height}, color.Black)
pixel.Line(im, image.Point{X: offset0}, image.Point{X: offset0, Y: height}, color.Black)
pixel.Line(im, image.Point{X: offset1}, image.Point{X: offset1, Y: height}, color.Black)
})
}
func FadeOutDiagonal(duration time.Duration, im bitmap.Image) <-chan time.Time {
func FadeOutDiagonal(duration time.Duration, im pixel.Image) <-chan time.Time {
var (
bounds = im.Bounds()
width = bounds.Dx()
@ -104,8 +104,8 @@ func FadeOutDiagonal(duration time.Duration, im bitmap.Image) <-chan time.Time {
offset0 = i * 2
offset1 = max - offset0 - 1
)
bitmap.Line(im, image.Point{X: offset0}, image.Point{Y: offset0}, color.Black)
bitmap.Line(im, image.Point{X: offset1}, image.Point{Y: offset1}, color.Black)
pixel.Line(im, image.Point{X: offset0}, image.Point{Y: offset0}, color.Black)
pixel.Line(im, image.Point{X: offset1}, image.Point{Y: offset1}, color.Black)
})
}
@ -186,7 +186,7 @@ var ditherMasks = [8]*ditherMask{
},
}
func DitherStep(im bitmap.Image, step int) {
func DitherStep(im pixel.Image, step int) {
if step < 0 || step >= len(ditherMasks) {
// Nothing to do here.
return
@ -196,18 +196,18 @@ func DitherStep(im bitmap.Image, step int) {
}
// FadeOutShift dims the colors by bit shifting. 1-bit bitmaps are ignored.
func FadeOutShift(duration time.Duration, im bitmap.Image) <-chan time.Time {
func FadeOutShift(duration time.Duration, im pixel.Image) <-chan time.Time {
switch im := im.(type) {
case *bitmap.Bitmap:
case *pixel.Bitmap:
// pointless
signal := make(chan time.Time)
close(signal)
return signal
case *bitmap.RGB565Image:
case *pixel.RGB565Image:
return repeatWithin(duration, shiftRightRGB565MaxSteps, func(_ int) { shiftRightRGB565(im) })
case *bitmap.RGB888Image:
case *pixel.RGB888Image:
return repeatWithin(duration, shiftRightRGB888MaxSteps, func(_ int) { shiftRightRGB888(im) })
default:
@ -215,15 +215,15 @@ func FadeOutShift(duration time.Duration, im bitmap.Image) <-chan time.Time {
}
}
func ShiftRight(im bitmap.Image) {
func ShiftRight(im pixel.Image) {
switch im := im.(type) {
case *bitmap.Bitmap:
case *pixel.Bitmap:
// Pointless, ignored.
case *bitmap.RGB565Image:
case *pixel.RGB565Image:
shiftRightRGB565(im)
case *bitmap.RGB888Image:
case *pixel.RGB888Image:
shiftRightRGB888(im)
default:
@ -232,7 +232,7 @@ func ShiftRight(im bitmap.Image) {
}
// Naive approach by converting the color to RGBA and shifting.
func shiftRightRGB(im bitmap.Image) {
func shiftRightRGB(im pixel.Image) {
b := im.Bounds()
for y := b.Min.Y; y < b.Max.Y; y++ {
for x := b.Min.X; x < b.Max.X; x++ {
@ -247,7 +247,7 @@ func shiftRightRGB(im bitmap.Image) {
}
}
func shiftRightRGB565(im *bitmap.RGB565Image) {
func shiftRightRGB565(im *pixel.RGB565Image) {
for i, l := 0, len(im.Pix); i < l; i += 2 {
var (
hi = im.Pix[i+0]
@ -263,7 +263,7 @@ func shiftRightRGB565(im *bitmap.RGB565Image) {
}
}
func shiftRightRGB888(im *bitmap.RGB888Image) {
func shiftRightRGB888(im *pixel.RGB888Image) {
for i, l := 0, len(im.Pix); i < l; i += 3 {
im.Pix[i+0] >>= 1
im.Pix[i+1] >>= 1

fx/fade_test.go → pixeleffect/fade_test.go View File

@ -1,4 +1,4 @@
package fx
package pixeleffect
import (
"fmt"
@ -12,7 +12,7 @@ import (
_ "image/png" // PNG codec
"maze.io/x/bitmap"
"maze.io/x/pixel"
)
func TestDitherStep(t *testing.T) {
@ -48,12 +48,12 @@ func TestFadeOutVertical(t *testing.T) {
}
for i := 0; i < im.Bounds().Dy()/2; i++ {
offset := i * 2
bitmap.VLine(im, image.Point{X: offset}, height, color.Transparent)
bitmap.VLine(im, image.Point{X: height - offset - 1}, height, color.Transparent)
pixel.VLine(im, image.Point{X: offset}, height, color.Transparent)
pixel.VLine(im, image.Point{X: height - offset - 1}, height, color.Transparent)
}
}
func testLoadImage(t *testing.T, name string) bitmap.Image {
func testLoadImage(t *testing.T, name string) pixel.Image {
f, err := os.Open(name)
if err != nil {
t.Fatal(err)

fx/testdata/gopher.png → pixeleffect/testdata/gopher.png View File


font.go → pixelfont/font.go View File

@ -1,13 +1,14 @@
package bitmap
package pixelfont
import (
"image"
"image/draw"
"io"
"io/ioutil"
"log"
"os"
"maze.io/x/pixel"
"github.com/golang/freetype/truetype"
"golang.org/x/image/font"
"golang.org/x/image/math/fixed"
@ -76,11 +77,11 @@ func (f *bitmapFont) Glyph(r rune) image.Image {
if o < 0 || o >= len(f.pix) {
return nil
}
b := NewBitmap(f.rect.Max.X, f.rect.Max.Y)
b := pixel.NewBitmap(f.rect.Max.X, f.rect.Max.Y)
for x := 0; x < f.rect.Max.X; x++ {
v := f.pix[o+x]
for y := 0; y < f.rect.Max.Y; y++ {
b.Set(x, y, Bit((v>>y)&0x01 == 0x01))
b.Set(x, y, pixel.Bit((v>>y)&0x01 == 0x01))
}
}
return b
@ -145,8 +146,8 @@ func (f ttfFont) Glyph(r rune) image.Image {
if !ok {
return nil
}
glyph := NewBitmap(dr.Dx(), dr.Dy())
log.Printf("glyph: %s, dr: %s, mask: %s, mask point: %s", glyph.Bounds(), dr, mask.Bounds(), maskPoint)
glyph := pixel.NewBitmap(dr.Dx(), dr.Dy())
// log.Printf("glyph: %s, dr: %s, mask: %s, mask point: %s", glyph.Bounds(), dr, mask.Bounds(), maskPoint)
draw.DrawMask(glyph, glyph.Bounds(), image.White, image.Point{}, mask, maskPoint, draw.Over)
return glyph
}

font_glcd_5x8.go → pixelfont/font_glcd_5x8.go View File

@ -1,4 +1,4 @@
package bitmap
package pixelfont
var fontGLCD5x8 = []byte{
0x00, 0x00, 0x00, 0x00, 0x00,

font_test.go → pixelfont/font_test.go View File

@ -1,10 +1,12 @@
package bitmap
package pixelfont
import (
"image"
"image/draw"
"path/filepath"
"testing"
"maze.io/x/pixel"
)
func TestBitmapFont(t *testing.T) {
@ -50,7 +52,7 @@ func testFont(t *testing.T, f Font, w, h int) {
logBitmap(t, test)
}
func logBitmap(t *testing.T, b Image) {
func logBitmap(t *testing.T, b pixel.Image) {
t.Helper()
var (
bounds = b.Bounds()
@ -58,7 +60,7 @@ func logBitmap(t *testing.T, b Image) {
)
for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
for x := bounds.Min.X; x < bounds.Max.X; x++ {
if convertBitColor(b.At(x, y)) {
if pixel.BitModel.Convert(b.At(x, y)).(pixel.Bit) {
row[x] = '#'
} else {
row[x] = ' '

testdata/16bit.ttf → pixelfont/testdata/16bit.ttf View File


testdata/8bit.ttf → pixelfont/testdata/8bit.ttf View File


testdata/pixelmix.ttf → pixelfont/testdata/pixelmix.ttf View File


+ 8
- 0
pixelfont/util.go View File

@ -0,0 +1,8 @@
package pixelfont
func max(a, b int) int {
if a > b {
return a
}
return b
}

+ 1
- 1
util.go View File

@ -1,4 +1,4 @@
package bitmap
package pixel
func abs(v int) int {
if v < 0 {


+ 1
- 1
util_test.go View File

@ -1,4 +1,4 @@
package bitmap
package pixel
import (
"bytes"


Loading…
Cancel
Save