aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Ashby <martin@martin-laptop.lan>2018-05-28 11:40:30 +0100
committerMartin Ashby <martin@martin-laptop.lan>2018-05-28 11:40:30 +0100
commit4ec7e493520d558dd05e911b58ea354d25f33f45 (patch)
tree0097c34ae8ba15c2d812e1da4ab42aad7d5097d0
parente6060fb69c32dcdf5904793e61e4b272bbd79de2 (diff)
downloadunicornpaint-4ec7e493520d558dd05e911b58ea354d25f33f45.tar.gz
unicornpaint-4ec7e493520d558dd05e911b58ea354d25f33f45.tar.bz2
unicornpaint-4ec7e493520d558dd05e911b58ea354d25f33f45.tar.xz
unicornpaint-4ec7e493520d558dd05e911b58ea354d25f33f45.zip
Started animated implementation
-rw-r--r--MakeGif.go106
-rw-r--r--unicorn/FakeUnicorn2.go57
-rw-r--r--unicorn/Unicorn2.go32
-rw-r--r--unicorn/Unicorn_test2.go21
-rw-r--r--unicorn/bindata.go235
-rw-r--r--unicorn/data/sample.gifbin0 -> 1773 bytes
6 files changed, 451 insertions, 0 deletions
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
--- /dev/null
+++ b/unicorn/data/sample.gif
Binary files differ