Add fx components 291-320 and the home scene; Kit.Shot, Kit.Toward
Palette cycling, sky, sun and moon, clouds, starfield, rain, snow, petals, leaves, fireflies, waterfall, ocean, campfire, lava, aurora, lightning, mountains and a season scene; scanlines, vignette, screen shake, flash, dither fades, iris wipes, dissolves, mosaics, glitches, sparkles, explosions and confetti. Home opens on a live scene in the palette's season and a strip of all eight palettes. pixel adds Canvas.Cycled, Sprite.At, SpriteOf, Kit.Toward and Kit.Shot, which spends a cue under Reduce Motion so it never replays; display exports Reduce. Gallery grids and the sidebar are one Tab stop each.
This commit is contained in:
56 files changed
+3324
-101
No files matched your search
@@ -18,23 +18,39 @@ func fill(w, h int, c color.Color) *image.RGBA {
|
||||
return img
|
||||
}
|
||||
|
||||
// dots are a sprite's slots, -1 where clear.
|
||||
func dots(sp *pixel.Sprite) []int {
|
||||
var out []int
|
||||
for y := range sp.H {
|
||||
for x := range sp.W {
|
||||
s, ok := sp.At(0, x, y, pixel.Ink)
|
||||
if !ok {
|
||||
out = append(out, -1)
|
||||
continue
|
||||
}
|
||||
out = append(out, int(s))
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func TestReduce(t *testing.T) {
|
||||
pal := pixel.SpringDay
|
||||
a := pal.Colors[pixel.Accent]
|
||||
for _, s := range reduce(fill(8, 8, color.RGBA{a.R, a.G, a.B, 255}), 4, 4, pal) {
|
||||
if s != int8(pixel.Accent) {
|
||||
for _, s := range dots(Reduce(fill(8, 8, color.RGBA{a.R, a.G, a.B, 255}), 4, 4, pal)) {
|
||||
if s != int(pixel.Accent) {
|
||||
t.Fatalf("a flat Accent image gave slot %d", s)
|
||||
}
|
||||
}
|
||||
for _, s := range reduce(fill(4, 4, color.RGBA{}), 4, 4, pal) {
|
||||
for _, s := range dots(Reduce(fill(4, 4, color.RGBA{}), 4, 4, pal)) {
|
||||
if s != -1 {
|
||||
t.Fatal("a clear image drew")
|
||||
}
|
||||
}
|
||||
i, b := pal.Colors[pixel.Info], pal.Colors[pixel.Back]
|
||||
mid := color.RGBA{uint8((int(i.R) + int(b.R)) / 2), uint8((int(i.G) + int(b.G)) / 2), uint8((int(i.B) + int(b.B)) / 2), 255}
|
||||
seen := map[int8]int{}
|
||||
for _, s := range reduce(fill(16, 16, mid), 16, 16, pal) {
|
||||
seen := map[int]int{}
|
||||
for _, s := range dots(Reduce(fill(16, 16, mid), 16, 16, pal)) {
|
||||
seen[s]++
|
||||
}
|
||||
if len(seen) < 2 {
|
||||
|
||||
+24
-41
@@ -20,7 +20,7 @@ type pictureCache struct {
|
||||
sum uint64
|
||||
pal *pixel.Palette
|
||||
w, h int
|
||||
dots []int8
|
||||
sp *pixel.Sprite
|
||||
}
|
||||
|
||||
// Picture reduces img to the palette with Bayer dither.
|
||||
@@ -30,55 +30,38 @@ func Picture(c *ui.Context, img image.Image, o PictureOptions) *ui.Element {
|
||||
w, h := pixel.Or("Picture W", o.W, b.Dx()), pixel.Or("Picture H", o.H, b.Dy())
|
||||
e := art(c, k, o.Label, w, h, nil)
|
||||
pc := ui.Local(e, pictureKey{}, func() pictureCache { return pictureCache{} })
|
||||
if sum := fingerprint(img); pc.dots == nil || pc.sum != sum || pc.pal != k.Palette() || pc.w != w || pc.h != h {
|
||||
*pc = pictureCache{sum, k.Palette(), w, h, reduce(img, w, h, k.Palette())}
|
||||
if sum := fingerprint(img); pc.sp == nil || pc.sum != sum || pc.pal != k.Palette() || pc.w != w || pc.h != h {
|
||||
*pc = pictureCache{sum, k.Palette(), w, h, Reduce(img, w, h, k.Palette())}
|
||||
}
|
||||
dots := pc.dots
|
||||
sp := pc.sp
|
||||
return e.Draw(func(p *ui.Painter, r ui.Rect) {
|
||||
g := k.Canvas(p, r)
|
||||
for y := range h {
|
||||
for x := 0; x < w; {
|
||||
s, end := dots[y*w+x], x+1
|
||||
for end < w && dots[y*w+end] == s {
|
||||
end++
|
||||
}
|
||||
if s >= 0 {
|
||||
g.Rect(x, y, end-x, 1, pixel.Slot(s))
|
||||
}
|
||||
x = end
|
||||
}
|
||||
}
|
||||
k.Canvas(p, r).Sprite(0, 0, sp, 0, pixel.Ink, 1)
|
||||
})
|
||||
}
|
||||
|
||||
// reduce maps img to w×h slots; -1 marks clear dots.
|
||||
func reduce(img image.Image, w, h int, pal *pixel.Palette) []int8 {
|
||||
// Reduce maps img onto pal as a dithered w×h sprite.
|
||||
func Reduce(img image.Image, w, h int, pal *pixel.Palette) *pixel.Sprite {
|
||||
b := img.Bounds()
|
||||
out := make([]int8, w*h)
|
||||
for y := range h {
|
||||
return pixel.SpriteOf(w, h, func(x, y int) (pixel.Slot, bool) {
|
||||
y0, y1 := span(b.Min.Y, b.Dy(), y, h)
|
||||
for x := range w {
|
||||
x0, x1 := span(b.Min.X, b.Dx(), x, w)
|
||||
var sum [4]uint32
|
||||
n := uint32(0)
|
||||
for sy := y0; sy < y1; sy++ {
|
||||
for sx := x0; sx < x1; sx++ {
|
||||
r, g, bl, a := img.At(sx, sy).RGBA()
|
||||
sum[0], sum[1], sum[2], sum[3] = sum[0]+r>>8, sum[1]+g>>8, sum[2]+bl>>8, sum[3]+a>>8
|
||||
n++
|
||||
}
|
||||
x0, x1 := span(b.Min.X, b.Dx(), x, w)
|
||||
var sum [4]uint32
|
||||
n := uint32(0)
|
||||
for sy := y0; sy < y1; sy++ {
|
||||
for sx := x0; sx < x1; sx++ {
|
||||
r, g, bl, a := img.At(sx, sy).RGBA()
|
||||
sum[0], sum[1], sum[2], sum[3] = sum[0]+r>>8, sum[1]+g>>8, sum[2]+bl>>8, sum[3]+a>>8
|
||||
n++
|
||||
}
|
||||
if sum[3]/n < 128 {
|
||||
out[y*w+x] = -1
|
||||
continue
|
||||
}
|
||||
nudge := (float64(pixel.Bayer(x, y))+0.5)/16 - 0.5
|
||||
out[y*w+x] = int8(nearest(pal, [3]float64{
|
||||
float64(sum[0]/n) + nudge*48, float64(sum[1]/n) + nudge*48, float64(sum[2]/n) + nudge*48,
|
||||
}))
|
||||
}
|
||||
}
|
||||
return out
|
||||
if sum[3]/n < 128 {
|
||||
return 0, false
|
||||
}
|
||||
nudge := (float64(pixel.Bayer(x, y))+0.5)/16 - 0.5
|
||||
return nearest(pal, [3]float64{
|
||||
float64(sum[0]/n) + nudge*48, float64(sum[1]/n) + nudge*48, float64(sum[2]/n) + nudge*48,
|
||||
}), true
|
||||
})
|
||||
}
|
||||
|
||||
// fingerprint mixes img's bounds and pixels, to notice edits.
|
||||
|
||||
Reference in new issue
Block a user