From 4ec7e493520d558dd05e911b58ea354d25f33f45 Mon Sep 17 00:00:00 2001 From: Martin Ashby Date: Mon, 28 May 2018 11:40:30 +0100 Subject: Started animated implementation --- MakeGif.go | 106 +++++++++++++++++++++ unicorn/FakeUnicorn2.go | 57 ++++++++++++ unicorn/Unicorn2.go | 32 +++++++ unicorn/Unicorn_test2.go | 21 +++++ unicorn/bindata.go | 235 +++++++++++++++++++++++++++++++++++++++++++++++ unicorn/data/sample.gif | Bin 0 -> 1773 bytes 6 files changed, 451 insertions(+) create mode 100644 MakeGif.go create mode 100644 unicorn/FakeUnicorn2.go create mode 100644 unicorn/Unicorn2.go create mode 100644 unicorn/Unicorn_test2.go create mode 100644 unicorn/bindata.go create mode 100644 unicorn/data/sample.gif diff --git a/MakeGif.go b/MakeGif.go new file mode 100644 index 0000000..3119b94 --- /dev/null +++ b/MakeGif.go @@ -0,0 +1,106 @@ +package main + +import ( + "encoding/json" + "image" + "image/color" + "image/color/palette" + "image/draw" + "image/gif" + "io/ioutil" + "time" + + "github.com/MFAshby/unicornpaint/unicorn" +) + +var ( + un unicorn.Unicorn +) + +func imageFromPixels(pixels [][][]uint8) image.Image { + width := len(pixels) + height := len(pixels[0]) + im1 := image.NewRGBA(image.Rect(0, 0, width, height)) + for x := 0; x < width; x++ { + for y := 0; y < height; y++ { + r, g, b := unicorn.Rgb(pixels[x][y]) + col := color.RGBA{ + R: r, + G: g, + B: b, + A: 100, + } + im1.Set(x, y, col) + } + } + return im1 +} + +func toPaletted(im image.Image) *image.Paletted { + b := im.Bounds() + pm := image.NewPaletted(b, palette.Plan9[:256]) + draw.FloydSteinberg.Draw(pm, b, im, image.ZP) + return pm +} + +var ( + stop bool +) + +func renderImage(un unicorn.Unicorn, im image.Image) { + b := im.Bounds() + width := b.Dx() + height := b.Dy() + for x := 0; x < width; x++ { + for y := 0; y < height; y++ { + r, g, b, _ := im.At(x, y).RGBA() + un.SetPixel(uint8(x), uint8(y), uint8(r), uint8(g), uint8(b)) + } + } + un.Show() +} + +func renderGif(un unicorn.Unicorn, gf *gif.GIF) { + for !stop { + for i := 0; i < len(gf.Image); i++ { + im := gf.Image[i] + delay := gf.Delay[i] //100ths of a second + renderImage(un, im) + time.Sleep(time.Duration(delay * 10000000)) // nanoseconds 10^-9 sec + } + } +} + +func main() { + b1, _ := ioutil.ReadFile("saves/modern") + b2, _ := ioutil.ReadFile("saves/modern2") + + px1 := [][][]uint8{} + json.Unmarshal(b1, &px1) + px2 := [][][]uint8{} + json.Unmarshal(b2, &px2) + + im1 := toPaletted(imageFromPixels(px1)) + im2 := toPaletted(imageFromPixels(px2)) + + gf := &gif.GIF{ + Image: []*image.Paletted{im1, im2}, + Delay: []int{50, 50}, // 100ths of a second + } + + // f1, err := os.Create("saves/modern.gif") + // if err != nil { + // log.Fatalf("Error opening GIF file to write %v", err) + // } + // defer f1.Close() + // err = gif.EncodeAll(f1, gf) + // if err != nil { + // log.Printf("Error writing GIF %v", err) + // } + + un, _ = unicorn.NewUnicorn() + + go renderGif(un, gf) + + un.MainLoop() +} diff --git a/unicorn/FakeUnicorn2.go b/unicorn/FakeUnicorn2.go new file mode 100644 index 0000000..73ce8ab --- /dev/null +++ b/unicorn/FakeUnicorn2.go @@ -0,0 +1,57 @@ +package unicorn + +import ( + "image" + + "github.com/veandco/go-sdl2/sdl" +) + +type FakeUnicorn2 struct { + BaseUnicorn2 +} + +func renderImage(im image.Image) { + // b := im.Bounds() + // width := b.Dx() + // height := b.Dy() + // for x := 0; x < width; x++ { + // for y := 0; y < height; y++ { + // r, g, b, _ := im.At(x, y).RGBA() + // un.SetPixel(uint8(x), uint8(y), uint8(r), uint8(g), uint8(b)) + // } + // } + // un.Show() +} + +func render() { + // for !stop { + // for i := 0; i < len(gf.Image); i++ { + // im := gf.Image[i] + // delay := gf.Delay[i] //100ths of a second + // renderImage(un, im) + // time.Sleep(time.Duration(delay * 10000000)) // nanoseconds 10^-9 sec + // } + // } +} + +func (u *FakeUnicorn2) StartRender() { + +} + +func MainLoop() { + running := true + for running { + for event := sdl.PollEvent(); event != nil; event = sdl.PollEvent() { + switch event.(type) { + case *sdl.QuitEvent: + println("Quit") + running = false + break + } + } + } +} + +func NewUnicorn2() *FakeUnicorn2 { + return &FakeUnicorn2{} +} diff --git a/unicorn/Unicorn2.go b/unicorn/Unicorn2.go new file mode 100644 index 0000000..716fe4d --- /dev/null +++ b/unicorn/Unicorn2.go @@ -0,0 +1,32 @@ +// Version 2 of unicorn, uses gif.GIF as store of pixels +// & a separate goroutine to render it so it can do +// animated GIFs +package unicorn + +import ( + "image/gif" +) + +// Unicorn2 ... +// Interface for interacting with the Unicorn HAT HD +// Implemented by both real & fake unicorns. +type Unicorn2 interface { + GetGif() *gif.GIF + SetGif(*gif.GIF) + + StartRender() + // Required for os to not think we're stuck + MainLoop() +} + +type BaseUnicorn2 struct { + g *gif.GIF +} + +func (u *BaseUnicorn2) GetGif() *gif.GIF { + return u.g +} + +func (u *BaseUnicorn2) SetGif(g *gif.GIF) { + u.g = g +} diff --git a/unicorn/Unicorn_test2.go b/unicorn/Unicorn_test2.go new file mode 100644 index 0000000..74cffd1 --- /dev/null +++ b/unicorn/Unicorn_test2.go @@ -0,0 +1,21 @@ +package unicorn + +import ( + "testing" +) + +func TestAnimated(t *testing.T) { + // data, err := Asset("sample.gif") + // if err != nil { + // t.Errorf("Failed to load asset %v", err) + // } + + // g, err := gif.DecodeAll(bytes.NewReader(data)) + // if err != nil { + // t.Errorf("Failed to decode gif from asset %v", err) + // } + + // un := unicorn.NewUnicorn2() + // un.SetGif(g) + // un.MainLoop() +} diff --git a/unicorn/bindata.go b/unicorn/bindata.go new file mode 100644 index 0000000..f4395cc --- /dev/null +++ b/unicorn/bindata.go @@ -0,0 +1,235 @@ +// Code generated by go-bindata. DO NOT EDIT. +// sources: +// data/sample.gif +package unicorn + +import ( + "bytes" + "compress/gzip" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "strings" + "time" +) + +func bindataRead(data []byte, name string) ([]byte, error) { + gz, err := gzip.NewReader(bytes.NewBuffer(data)) + if err != nil { + return nil, fmt.Errorf("Read %q: %v", name, err) + } + + var buf bytes.Buffer + _, err = io.Copy(&buf, gz) + clErr := gz.Close() + + if err != nil { + return nil, fmt.Errorf("Read %q: %v", name, err) + } + if clErr != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +type asset struct { + bytes []byte + info os.FileInfo +} + +type bindataFileInfo struct { + name string + size int64 + mode os.FileMode + modTime time.Time +} + +func (fi bindataFileInfo) Name() string { + return fi.name +} +func (fi bindataFileInfo) Size() int64 { + return fi.size +} +func (fi bindataFileInfo) Mode() os.FileMode { + return fi.mode +} +func (fi bindataFileInfo) ModTime() time.Time { + return fi.modTime +} +func (fi bindataFileInfo) IsDir() bool { + return false +} +func (fi bindataFileInfo) Sys() interface{} { + return nil +} + +var _dataSampleGif = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x93\xb1\x8b\x24\x45\x14\xc6\x3f\xef\x0e\x6f\x0e\x9d\x73\x47\x0d\x9c\xc3\x64\xe6\x98\x40\x3c\x44\x6e\x40\xee\x14\x11\xb1\x76\x8e\x92\x76\x5b\xb9\x7b\x61\x07\x1e\xd2\x62\x28\x08\x6d\x20\xb2\xc8\xe8\x15\xee\x8a\x0b\x1d\x48\x25\x8d\xa2\xd1\xcb\x5c\x35\xb1\x44\x10\x84\x65\xe1\x09\x86\x9b\x94\xa8\x20\x1a\x2c\x85\x60\xe2\x82\x50\xf2\x2a\xd2\xff\x61\x27\x79\x1f\xef\x4d\xf0\xd5\xaf\xbf\xef\x86\x5d\x5d\xbb\xfe\xf2\x06\x36\x00\x60\x96\xef\xd9\xda\xbc\x75\xf3\xb9\x67\x5f\xdc\xbc\xfa\xd8\xe3\x67\xef\xd2\xd5\xc9\x39\x5c\x05\x70\x45\xef\xfa\xb7\x3b\x2a\x60\x00\x07\x88\x4e\x63\x60\x1c\x8c\xe8\xc2\x19\x38\x07\x27\x10\x40\x0c\xc4\x41\x04\x31\x4e\x26\x13\x80\x00\x0f\x44\x9d\x44\xa8\x3c\x6c\xd4\x85\xaf\xe0\x3d\xfa\x88\x08\x44\x8b\xd8\x23\x0d\x48\x69\x3e\x9f\x03\x2d\xc0\x40\xd2\xd9\xb6\x20\x46\x9d\x74\xc1\x04\x66\x0c\x09\x09\x48\x35\x32\x21\x33\x72\x5e\x2e\x97\x40\x07\x04\x20\xeb\xec\x3a\x34\x01\x94\x75\x11\x1a\x84\x00\xce\xc8\x30\xea\xd8\x19\x88\x1a\xd7\x9f\x33\x46\xf4\x05\xce\x18\xe7\x8c\x13\x23\x30\x62\x8c\x38\x23\x62\x8a\x6f\x02\x55\xf0\x16\x91\x08\x44\x54\x55\xde\xda\x58\x79\x54\xbe\xaa\xbc\xb7\x7d\xb4\x11\x36\x5a\x1b\x7b\x1b\x63\x9d\x92\xfa\x45\x4b\xe0\x1a\xa9\x6d\xd1\xb6\x2d\x11\xd7\x75\x22\x06\x31\x11\x73\x3d\xa4\x3a\xa1\x4e\x75\x9d\x06\xca\x4c\x39\xab\x5f\x74\x0d\x02\x21\x77\x1d\xba\xae\x6b\x9a\x40\x94\x9b\x80\x26\x34\x4d\x08\xc4\x99\x32\x28\x93\x53\xc4\xa2\xa4\xd5\xba\x73\x46\x14\xb9\x2a\xe7\x9c\x38\x81\x13\xe3\xc4\x39\x11\xfd\x20\x30\x6a\x13\xbe\x47\x54\xd2\x55\xe5\x2b\xdf\xdb\xe8\x3d\xbc\xaf\xbc\xf7\x7d\x1f\xfb\x88\x3e\xda\x3e\xf6\x7d\x8c\xbe\x10\x57\x9b\xe0\x01\x49\x49\x13\x31\xf1\x50\x27\x66\x30\x13\x33\x0f\x43\x1a\x12\x86\x54\x0f\x69\x18\x52\xe2\x9c\x15\xb0\xb2\x0e\x8c\xac\xa4\x9b\x26\x34\x81\x29\x87\x80\x10\x9a\x10\x02\x73\xe6\x0c\xce\xc4\x99\x05\x8a\x55\x0a\x74\x31\xea\x52\xca\x1b\xc4\x89\x94\x9b\x14\x25\x25\x5e\x02\x17\x4b\x38\x34\x1a\xd6\xc6\xc2\x3a\xf6\x50\xd2\xc5\x7a\x2c\xb7\x58\x94\x26\x08\x56\xb9\x96\x70\xa4\x1a\x0a\xb8\xb0\xd6\x4c\x0d\x75\x2a\xd6\x53\xb9\xa5\xa2\x92\x66\x05\xca\xb5\x84\x43\xa3\x44\x94\x0b\x6b\xcd\x14\x53\x2e\xd6\x73\xb9\xe5\xa2\x46\x5b\xff\x9c\xbb\xf8\xf0\x08\xdb\xe3\xf5\xe8\x64\xfa\xca\xd1\x8f\x9f\x7c\xf0\xee\xc5\xc5\xa5\xf5\xb5\xd7\x6f\x6f\xee\x9c\x7f\xe0\xf6\x37\x47\x7b\x9f\xae\xd6\xe7\x9f\x5e\xbd\xf5\xd0\xfe\xec\x70\xe7\x89\x2b\xfc\xe0\xe1\xde\x6f\xcf\x7f\x38\x5d\x7c\x7b\xfc\xc7\xfd\xdb\xf7\x4e\x1f\xa5\xfd\xef\x1f\xf9\x62\xb6\xf5\xf1\xf5\xf1\xdd\xe3\x4b\x1b\xa7\x15\x3b\xad\xd8\x69\xc5\xfe\x5f\xb1\x5b\x7f\x95\x8a\xe1\xcc\x7a\xf4\xbb\x56\x6c\xf6\xce\x7f\x2b\xf6\x93\x7b\xf3\xf3\xd9\xea\xfd\x0b\xf7\xbd\x7d\xfc\xf7\xe4\xbb\xf1\x9d\xc5\x62\xfe\xa5\xdb\xfe\xf5\x60\xf7\xf2\xf4\xeb\xdd\x0b\xcb\xcb\x3f\x7c\xf4\xcc\xcf\x5f\xbd\x61\x6f\xbe\xfa\xc2\x7b\x4f\xfe\x32\x8e\xaf\xfd\x79\xf0\xd2\x67\x37\xce\x9e\xc1\x53\xff\x06\x00\x00\xff\xff\x92\xfe\xa3\x1e\xed\x06\x00\x00") + +func dataSampleGifBytes() ([]byte, error) { + return bindataRead( + _dataSampleGif, + "data/sample.gif", + ) +} + +func dataSampleGif() (*asset, error) { + bytes, err := dataSampleGifBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "data/sample.gif", size: 1773, mode: os.FileMode(420), modTime: time.Unix(1527458100, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +// Asset loads and returns the asset for the given name. +// It returns an error if the asset could not be found or +// could not be loaded. +func Asset(name string) ([]byte, error) { + cannonicalName := strings.Replace(name, "\\", "/", -1) + if f, ok := _bindata[cannonicalName]; ok { + a, err := f() + if err != nil { + return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err) + } + return a.bytes, nil + } + return nil, fmt.Errorf("Asset %s not found", name) +} + +// MustAsset is like Asset but panics when Asset would return an error. +// It simplifies safe initialization of global variables. +func MustAsset(name string) []byte { + a, err := Asset(name) + if err != nil { + panic("asset: Asset(" + name + "): " + err.Error()) + } + + return a +} + +// AssetInfo loads and returns the asset info for the given name. +// It returns an error if the asset could not be found or +// could not be loaded. +func AssetInfo(name string) (os.FileInfo, error) { + cannonicalName := strings.Replace(name, "\\", "/", -1) + if f, ok := _bindata[cannonicalName]; ok { + a, err := f() + if err != nil { + return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err) + } + return a.info, nil + } + return nil, fmt.Errorf("AssetInfo %s not found", name) +} + +// AssetNames returns the names of the assets. +func AssetNames() []string { + names := make([]string, 0, len(_bindata)) + for name := range _bindata { + names = append(names, name) + } + return names +} + +// _bindata is a table, holding each asset generator, mapped to its name. +var _bindata = map[string]func() (*asset, error){ + "data/sample.gif": dataSampleGif, +} + +// AssetDir returns the file names below a certain +// directory embedded in the file by go-bindata. +// For example if you run go-bindata on data/... and data contains the +// following hierarchy: +// data/ +// foo.txt +// img/ +// a.png +// b.png +// then AssetDir("data") would return []string{"foo.txt", "img"} +// AssetDir("data/img") would return []string{"a.png", "b.png"} +// AssetDir("foo.txt") and AssetDir("notexist") would return an error +// AssetDir("") will return []string{"data"}. +func AssetDir(name string) ([]string, error) { + node := _bintree + if len(name) != 0 { + cannonicalName := strings.Replace(name, "\\", "/", -1) + pathList := strings.Split(cannonicalName, "/") + for _, p := range pathList { + node = node.Children[p] + if node == nil { + return nil, fmt.Errorf("Asset %s not found", name) + } + } + } + if node.Func != nil { + return nil, fmt.Errorf("Asset %s not found", name) + } + rv := make([]string, 0, len(node.Children)) + for childName := range node.Children { + rv = append(rv, childName) + } + return rv, nil +} + +type bintree struct { + Func func() (*asset, error) + Children map[string]*bintree +} +var _bintree = &bintree{nil, map[string]*bintree{ + "data": &bintree{nil, map[string]*bintree{ + "sample.gif": &bintree{dataSampleGif, map[string]*bintree{}}, + }}, +}} + +// RestoreAsset restores an asset under the given directory +func RestoreAsset(dir, name string) error { + data, err := Asset(name) + if err != nil { + return err + } + info, err := AssetInfo(name) + if err != nil { + return err + } + err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755)) + if err != nil { + return err + } + err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) + if err != nil { + return err + } + err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime()) + if err != nil { + return err + } + return nil +} + +// RestoreAssets restores an asset under the given directory recursively +func RestoreAssets(dir, name string) error { + children, err := AssetDir(name) + // File + if err != nil { + return RestoreAsset(dir, name) + } + // Dir + for _, child := range children { + err = RestoreAssets(dir, filepath.Join(name, child)) + if err != nil { + return err + } + } + return nil +} + +func _filePath(dir, name string) string { + cannonicalName := strings.Replace(name, "\\", "/", -1) + return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...) +} + diff --git a/unicorn/data/sample.gif b/unicorn/data/sample.gif new file mode 100644 index 0000000..73be4e7 Binary files /dev/null and b/unicorn/data/sample.gif differ -- cgit v1.2.3-ZIG