Add canvas, dither, sprites, clock and pixel elements
This commit is contained in:
11 files changed
+1274
-11
No files matched your search
+112
@@ -0,0 +1,112 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user