Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 0 additions & 45 deletions _examples/color_swap/color_swap.go

This file was deleted.

7 changes: 0 additions & 7 deletions _examples/color_swap/firefly.toml

This file was deleted.

9 changes: 0 additions & 9 deletions _examples/color_swap/go.mod

This file was deleted.

4 changes: 0 additions & 4 deletions _examples/color_swap/go.sum

This file was deleted.

Binary file removed _examples/color_swap/image.png
Binary file not shown.
106 changes: 18 additions & 88 deletions firefly/graphics.go
Original file line number Diff line number Diff line change
Expand Up @@ -427,14 +427,9 @@ func (i Image) Sub(p Point, s Size) SubImage {
return SubImage{raw: i.raw, point: p, size: s}
}

// Bits per pixel. One of: 1, 2, or 4.
func (i Image) BPP() uint8 {
return i.raw[1]
}

// The color used for transparency. If no transparency, returns [ColorNone].
func (i Image) Transparency() Color {
c := i.raw[4]
c := i.raw[3]
if c > 15 {
return ColorNone
}
Expand All @@ -446,21 +441,21 @@ func (i Image) Transparency() Color {
// Pass ColorNone to disable transparency.
func (i Image) SetTransparency(c Color) {
if c == ColorNone {
i.raw[4] = 16
i.raw[3] = 16
}
i.raw[4] = byte(c) - 1
i.raw[3] = byte(c) - 1
}

// The number of pixels the image has.
func (i Image) Pixels() int {
bpp := int(i.BPP())
headerSize := 5 + (1 << (bpp - 1))
return (len(i.raw) - headerSize) * 8 / bpp
const ppb = 2
const headerSize = 4
return (len(i.raw) - headerSize) * ppb
}

// The image width in pixels.
func (i Image) Width() int {
return int(i.raw[2]) | int(i.raw[3])<<8
return int(i.raw[1]) | int(i.raw[2])<<8
}

// The image height in pixels.
Expand All @@ -484,23 +479,6 @@ func (i Image) Size() Size {
}
}

// Get the color used to represent the given pixel value.
func (i Image) GetColor(p uint8) Color {
if p > 15 {
return ColorNone
}
byteVal := i.raw[5+p/2]
if p%2 == 0 {
byteVal >>= 4
}
byteVal &= 0b1111
transp := i.raw[4]
if byteVal == transp {
return ColorNone
}
return Color(byteVal + 1)
}

// Get color of a pixel in the image.
//
// Returns [ColorNone] if out of bounds.
Expand All @@ -512,55 +490,14 @@ func (i Image) GetPixel(point Point) Color {
if point.X >= size.W || point.Y >= size.H {
return ColorNone
}
bpp := i.raw[1]
headerLen := 5 + (1 << (bpp - 1))
body := i.raw[headerLen:]

pixelIndex := point.X + point.Y*size.W
bodyIndex := pixelIndex * int(bpp) / 8
pixelValue := body[bodyIndex]

switch bpp {
case 1:
byteOffset := 1 * (7 - pixelIndex%8)
pixelValue = (pixelValue >> byte(byteOffset)) & 0b1
case 2:
byteOffset := 2 * (3 - pixelIndex%4)
pixelValue = (pixelValue >> byte(byteOffset)) & 0b11
case 4:
byteOffset := 4 * (1 - pixelIndex%2)
pixelValue = (pixelValue >> byte(byteOffset)) & 0b1111
default:
panic("invalid bpp")
}

return i.GetColor(pixelValue)
}

// Set color to be used to represent the given pixel value.
func (i Image) SetColor(p uint8, c Color) {
if p > 15 || c == ColorNone {
return
}
byteIdx := 5 + p/2
byteVal := i.raw[byteIdx]
colorVal := byte(c) - 1
if p%2 == 0 {
byteVal = (colorVal << 4) | (byteVal & 0b_0000_1111)
} else {
byteVal = (byteVal & 0b_1111_0000) | colorVal
}
i.raw[byteIdx] = byteVal
}

// Replace the old color with the new value.
func (i Image) ReplaceColor(oldC, newC Color) {
var p uint8
for p = range 16 {
if i.GetColor(p) == oldC {
i.SetColor(p, newC)
}
bodyIndex := pixelIndex / 2
pixelValue := i.raw[4+bodyIndex]
if pixelIndex%2 == 0 {
pixelValue >>= 4
}
color := pixelValue & 0b1111
return Color(color + 1)
}

// A subregion of an image. Constructed using [Image.Sub].
Expand Down Expand Up @@ -609,20 +546,13 @@ type Canvas struct {
}

func NewCanvas(s Size) Canvas {
const headerSize = 5 + 8
const headerSize = 4
bodySize := s.W * s.H / 2
raw := make([]byte, headerSize+bodySize)
raw[0] = 0x21 // magic number
raw[1] = 4 // BPP
raw[2] = byte(s.W) // width
raw[3] = byte(s.W >> 8) // width
raw[4] = 255 // transparency

// color swaps
var i byte
for i = range 8 {
raw[5+i] = ((i * 2) << 4) | (i*2 + 1)
}
raw[0] = 0x22 // magic number
raw[1] = byte(s.W) // width
raw[2] = byte(s.W >> 8) // width
raw[3] = 255 // transparency
return Canvas{raw}
}

Expand Down
94 changes: 13 additions & 81 deletions firefly/graphics_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,40 +6,10 @@ import (
"github.com/firefly-zero/firefly-go/firefly"
)

// image examples taken from docs: https://docs.fireflyzero.com/internal/formats/image/
var image1BPP = []byte{
// header
0x21, // magic number
0x01, // bits per pixel
var testImage = []byte{
0x22, // magic number
0x04, 0x00, // image width
0xff, // transparency
0x42, // color palette swap
// body
0xc3, // row 1 & row 2, 0b1100_0011
0x9b, // row 3 & row 4, 0b1001_1011
}

var image2BPP = []byte{
// header
0x21, // magic number
0x02, // bits per pixel
0x04, 0x00, // image width
0xff, // transparency
0x2B, 0x5A, // color palette swap
// body
0xec, // row 1
0xaf, // row 2
0x50, // row 3
0x91, // row 4
}

var image4BPP = []byte{
// header
0x21, // magic number
0x04, // bits per pixel
0x04, 0x00, // image width
0x01, // transparency
0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, // color palette swap
0x01, // transparency
// body
0x01, 0x23, // row 1
0x45, 0x67, // row 2
Expand All @@ -56,35 +26,11 @@ func TestImage_GetPixel(t *testing.T) {
pixel firefly.Point
want firefly.Color
}{
{name: "negative point", raw: image1BPP, pixel: P(-1, -1), want: firefly.ColorNone},
{name: "point out of bounds", raw: image1BPP, pixel: P(100, 100), want: firefly.ColorNone},

// 1 BPP
{name: "1 BPP/0x0", raw: image1BPP, pixel: P(0, 0), want: firefly.ColorRed},
{name: "1 BPP/1x0", raw: image1BPP, pixel: P(1, 0), want: firefly.ColorRed},
{name: "1 BPP/2x0", raw: image1BPP, pixel: P(2, 0), want: firefly.ColorYellow},
{name: "1 BPP/3x0", raw: image1BPP, pixel: P(3, 0), want: firefly.ColorYellow},
{name: "1 BPP/0x1", raw: image1BPP, pixel: P(0, 1), want: firefly.ColorYellow},

// 2 BPP
{name: "2 BPP/0x0", raw: image2BPP, pixel: P(0, 0), want: firefly.ColorLightBlue},
{name: "2 BPP/1x0", raw: image2BPP, pixel: P(1, 0), want: firefly.ColorLightGreen},
{name: "2 BPP/2x0", raw: image2BPP, pixel: P(2, 0), want: firefly.ColorLightBlue},
{name: "2 BPP/3x0", raw: image2BPP, pixel: P(3, 0), want: firefly.ColorRed},
{name: "2 BPP/0x1", raw: image2BPP, pixel: P(0, 1), want: firefly.ColorLightGreen},
{name: "2 BPP/1x1", raw: image2BPP, pixel: P(1, 1), want: firefly.ColorLightGreen},
{name: "2 BPP/2x1", raw: image2BPP, pixel: P(2, 1), want: firefly.ColorLightBlue},
{name: "2 BPP/3x1", raw: image2BPP, pixel: P(3, 1), want: firefly.ColorLightBlue},
{name: "2 BPP/0x2", raw: image2BPP, pixel: P(0, 2), want: firefly.ColorCyan},
{name: "2 BPP/1x2", raw: image2BPP, pixel: P(1, 2), want: firefly.ColorCyan},
{name: "2 BPP/2x2", raw: image2BPP, pixel: P(2, 2), want: firefly.ColorRed},
{name: "2 BPP/3x2", raw: image2BPP, pixel: P(3, 2), want: firefly.ColorRed},
{name: "2 BPP/2x3", raw: image2BPP, pixel: P(2, 3), want: firefly.ColorRed},

// 4 BPP
{name: "4 BPP/0x0", raw: image4BPP, pixel: P(0, 0), want: firefly.ColorBlack},
{name: "4 BPP/1x1", raw: image4BPP, pixel: P(1, 1), want: firefly.ColorLightGreen},
{name: "4 BPP/2x3", raw: image4BPP, pixel: P(2, 3), want: firefly.ColorGray},
{name: "negative point", raw: testImage, pixel: P(-1, -1), want: firefly.ColorNone},
{name: "point out of bounds", raw: testImage, pixel: P(100, 100), want: firefly.ColorNone},
{name: "x0y0", raw: testImage, pixel: P(0, 0), want: firefly.ColorBlack},
{name: "x1y1", raw: testImage, pixel: P(1, 1), want: firefly.ColorLightGreen},
{name: "x2y3", raw: testImage, pixel: P(2, 3), want: firefly.ColorGray},
}

for _, test := range tests {
Expand All @@ -101,24 +47,10 @@ func TestImage_GetPixel(t *testing.T) {

func TestImagePixels(t *testing.T) {
t.Parallel()
tests := []struct {
name string
raw []byte
want int
}{
{name: "1 BPP", raw: image1BPP, want: 16},
{name: "2 BPP", raw: image2BPP, want: 16},
{name: "4 BPP", raw: image4BPP, want: 16},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
t.Parallel()
image := firefly.File{test.raw}.Image()
got := image.Pixels()
if got != test.want {
t.Errorf("want %d, but got %d", test.want, got)
}
})
image := firefly.File{testImage}.Image()
got := image.Pixels()
want := 16
if got != want {
t.Errorf("want %d, but got %d", want, got)
}
}