Files
8bitMyGoUI/pixel/sprite.go
T

113 lines
2.3 KiB
Go

package pixel
import (
"fmt"
"strings"
)
// Sprite is hand-placed art in palette slots, in frames.
type Sprite struct {
W, H int
frames [][]int8
}
const (
cellClear int8 = -1
cellInk int8 = -2
)
// Art letters naming slots; '.' is clear, '#' the ink.
const artSlots = "kbflsenwaAigyrcm"
// MustSprite parses string art; blank lines split frames.
func MustSprite(art string) *Sprite {
var frames [][]string
var cur []string
for _, line := range strings.Split(strings.TrimSpace(art), "\n") {
line = strings.TrimSpace(line)
if line == "" {
if cur != nil {
frames, cur = append(frames, cur), nil
}
continue
}
cur = append(cur, line)
}
if cur == nil {
panic("pixel: empty sprite")
}
frames = append(frames, cur)
sp := &Sprite{W: len(frames[0][0]), H: len(frames[0])}
for _, f := range frames {
if len(f) != sp.H {
panic(fmt.Sprintf("pixel: sprite frames of %d and %d rows", sp.H, len(f)))
}
px := make([]int8, 0, sp.W*sp.H)
for _, row := range f {
if len(row) != sp.W {
panic(fmt.Sprintf("pixel: sprite row %q is not %d wide", row, sp.W))
}
for i := range len(row) {
px = append(px, cell(row[i]))
}
}
sp.frames = append(sp.frames, px)
}
return sp
}
func cell(c byte) int8 {
switch c {
case '.':
return cellClear
case '#':
return cellInk
}
if i := strings.IndexByte(artSlots, c); i >= 0 {
return int8(i)
}
panic(fmt.Sprintf("pixel: sprite character %q", c))
}
func (s *Sprite) Frames() int { return len(s.frames) }
// Mirror returns s flipped left to right.
func (s *Sprite) Mirror() *Sprite {
m := &Sprite{W: s.W, H: s.H}
for _, px := range s.frames {
out := make([]int8, len(px))
for y := range s.H {
for x := range s.W {
out[y*s.W+x] = px[y*s.W+s.W-1-x]
}
}
m.frames = append(m.frames, out)
}
return m
}
// Sprite draws frame of sp; # takes ink; frames wrap.
func (g Canvas) Sprite(x, y int, sp *Sprite, frame int, ink Slot, scale int) {
checkScale(scale)
n := len(sp.frames)
px := sp.frames[(frame%n+n)%n]
for row := range sp.H {
line := px[row*sp.W : (row+1)*sp.W]
for col := 0; col < sp.W; {
v := line[col]
end := col + 1
for end < sp.W && line[end] == v {
end++
}
if v != cellClear {
s := ink
if v >= 0 {
s = Slot(v)
}
g.Rect(x+col*scale, y+row*scale, (end-col)*scale, scale, s)
}
col = end
}
}
}