aboutsummaryrefslogtreecommitdiff
path: root/unicorn
diff options
context:
space:
mode:
Diffstat (limited to 'unicorn')
-rw-r--r--unicorn/BaseFakeUnicorn.go78
-rw-r--r--unicorn/FakeUnicorn.go66
-rw-r--r--unicorn/FakeUnicorn2.go70
-rw-r--r--unicorn/RealUnicorn.go3
-rw-r--r--unicorn/RealUnicorn2.go69
-rw-r--r--unicorn/SpiRenderDevice.go19
-rw-r--r--unicorn/Unicorn2.go40
-rw-r--r--unicorn/Unicorn2_test.go61
-rw-r--r--unicorn/Unicorn_test2.go21
-rw-r--r--unicorn/bindata.go25
-rwxr-xr-xunicorn/build_bindata.sh1
-rw-r--r--unicorn/data/sample2.gifbin0 -> 1783 bytes
12 files changed, 331 insertions, 122 deletions
diff --git a/unicorn/BaseFakeUnicorn.go b/unicorn/BaseFakeUnicorn.go
new file mode 100644
index 0000000..7910814
--- /dev/null
+++ b/unicorn/BaseFakeUnicorn.go
@@ -0,0 +1,78 @@
+package unicorn
+
+import "github.com/veandco/go-sdl2/sdl"
+
+// BaseFakeUnicorn ...
+// The base for FakeUnicorn & FakeUnicorn2
+// Share the SDL code.
+type BaseFakeUnicorn struct {
+ displayWidth int32
+ displayHeight int32
+ window *sdl.Window
+ renderer *sdl.Renderer
+}
+
+func NewBaseFakeUnicorn(width, height int32) (*BaseFakeUnicorn, error) {
+ if err := sdl.Init(sdl.INIT_EVERYTHING); err != nil {
+ return nil, err
+ }
+
+ unicorn := &BaseFakeUnicorn{
+ displayWidth: width,
+ displayHeight: height,
+ window: nil,
+ renderer: nil,
+ }
+ if err := unicorn.createWindow(); err != nil {
+ unicorn.Close()
+ return nil, err
+ }
+ if err := unicorn.createRenderer(); err != nil {
+ unicorn.Close()
+ return nil, err
+ }
+ return unicorn, nil
+}
+
+func (f *BaseFakeUnicorn) createWindow() error {
+ window, err := sdl.CreateWindow("Fake Unicorn",
+ sdl.WINDOWPOS_UNDEFINED,
+ sdl.WINDOWPOS_UNDEFINED,
+ f.displayWidth,
+ f.displayHeight,
+ sdl.WINDOW_SHOWN)
+ f.window = window
+ return err
+}
+
+func (f *BaseFakeUnicorn) createRenderer() error {
+ renderer, err := sdl.CreateRenderer(f.window, -1, sdl.RENDERER_ACCELERATED)
+ f.renderer = renderer
+ return err
+}
+
+func (f *BaseFakeUnicorn) Close() error {
+ if f.window != nil {
+ f.window.Destroy()
+ }
+ if f.renderer != nil {
+ f.renderer.Destroy()
+ }
+ return nil
+}
+
+// MainLoop ...
+// Handle UI events so OS doesn't think we're frozen
+func (f *BaseFakeUnicorn) 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
+ }
+ }
+ }
+}
diff --git a/unicorn/FakeUnicorn.go b/unicorn/FakeUnicorn.go
index eaec4f2..8db71d5 100644
--- a/unicorn/FakeUnicorn.go
+++ b/unicorn/FakeUnicorn.go
@@ -8,10 +8,7 @@ import (
type FakeUnicorn struct {
BaseUnicorn
- displayWidth int32
- displayHeight int32
- window *sdl.Window
- renderer *sdl.Renderer
+ *BaseFakeUnicorn
}
// NewUnicorn ...
@@ -19,7 +16,9 @@ type FakeUnicorn struct {
func NewUnicorn() (*FakeUnicorn, error) {
width := uint8(16)
height := uint8(16)
- if err := sdl.Init(sdl.INIT_EVERYTHING); err != nil {
+
+ baseFake, err := NewBaseFakeUnicorn(300, 300)
+ if err != nil {
return nil, err
}
@@ -27,47 +26,10 @@ func NewUnicorn() (*FakeUnicorn, error) {
BaseUnicorn{
pixels: makePixels(width, height),
},
- 300,
- 300,
- nil,
- nil,
- }
- if err := unicorn.createWindow(); err != nil {
- unicorn.Close()
- return nil, err
- }
- if err := unicorn.createRenderer(); err != nil {
- unicorn.Close()
- return nil, err
+ baseFake,
}
- return unicorn, nil
-}
-func (f *FakeUnicorn) createWindow() error {
- window, err := sdl.CreateWindow("Fake Unicorn",
- sdl.WINDOWPOS_UNDEFINED,
- sdl.WINDOWPOS_UNDEFINED,
- f.displayWidth,
- f.displayHeight,
- sdl.WINDOW_SHOWN)
- f.window = window
- return err
-}
-
-func (f *FakeUnicorn) createRenderer() error {
- renderer, err := sdl.CreateRenderer(f.window, -1, sdl.RENDERER_ACCELERATED)
- f.renderer = renderer
- return err
-}
-
-func (f *FakeUnicorn) Close() error {
- if f.window != nil {
- f.window.Destroy()
- }
- if f.renderer != nil {
- f.renderer.Destroy()
- }
- return nil
+ return unicorn, nil
}
func (f *FakeUnicorn) Show() {
@@ -96,19 +58,3 @@ func (f *FakeUnicorn) Show() {
func (f *FakeUnicorn) Off() {
f.Close()
}
-
-// MainLoop ...
-// Handle UI events so OS doesn't think we're frozen
-func (f *FakeUnicorn) 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
- }
- }
- }
-}
diff --git a/unicorn/FakeUnicorn2.go b/unicorn/FakeUnicorn2.go
index 73ce8ab..27629d2 100644
--- a/unicorn/FakeUnicorn2.go
+++ b/unicorn/FakeUnicorn2.go
@@ -8,50 +8,42 @@ import (
type FakeUnicorn2 struct {
BaseUnicorn2
+ *BaseFakeUnicorn
}
-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 (u *FakeUnicorn2) renderImage(im image.Image) {
+ b := im.Bounds()
+ width, height := b.Dx(), b.Dy()
+ for x := 0; x < width; x++ {
+ for y := 0; y < height; y++ {
+ col := im.At(x, y)
+ r, g, b, _ := col.RGBA()
+ // Ignore alpha for now, not worked out how it should work on real unicorn
+ if err := u.renderer.SetDrawColor(uint8(r), uint8(g), uint8(b), uint8(255)); err != nil {
+ panic(err)
+ }
+ cellWidth := u.displayWidth / int32(width)
+ cellHeight := u.displayHeight / int32(height)
+ if err := u.renderer.FillRect(&sdl.Rect{
+ X: cellWidth * int32(x),
+ Y: u.displayHeight - (cellHeight * int32(y)) - cellHeight, // SDL Y coordinate is from the top
+ W: cellWidth,
+ H: cellHeight,
+ }); err != nil {
+ panic(err)
}
}
}
+ u.renderer.Present()
}
-func NewUnicorn2() *FakeUnicorn2 {
- return &FakeUnicorn2{}
+func NewUnicorn2() (*FakeUnicorn2, error) {
+ baseFake, err := NewBaseFakeUnicorn(300, 300)
+ if err != nil {
+ return nil, err
+ }
+ return &FakeUnicorn2{
+ BaseUnicorn2{},
+ baseFake,
+ }, nil
}
diff --git a/unicorn/RealUnicorn.go b/unicorn/RealUnicorn.go
index b3c1df3..6174ccf 100644
--- a/unicorn/RealUnicorn.go
+++ b/unicorn/RealUnicorn.go
@@ -3,11 +3,12 @@
package unicorn
import (
- "github.com/ecc1/spi"
"log"
"os"
"os/signal"
"syscall"
+
+ "github.com/ecc1/spi"
)
type RealUnicorn struct {
diff --git a/unicorn/RealUnicorn2.go b/unicorn/RealUnicorn2.go
new file mode 100644
index 0000000..991e79f
--- /dev/null
+++ b/unicorn/RealUnicorn2.go
@@ -0,0 +1,69 @@
+// +build linux,arm linux,arm64
+
+package unicorn
+
+import (
+ "image"
+ "log"
+ "os"
+ "os/signal"
+ "syscall"
+
+ "github.com/ecc1/spi"
+)
+
+type RealUnicorn2 struct {
+ BaseUnicorn2
+ device *spi.Device
+}
+
+func (u *RealUnicorn2) renderImage(im image.Image) {
+ b := im.Bounds()
+ width, height := b.Dx(), b.Dy()
+ sz := (width * height * 3) + 1
+ write := make([]byte, sz)
+
+ // Write leading bit
+ write[0] = 0x72
+
+ // Write color values
+ ix := 1
+ for x := 0; x < width; x++ {
+ for y := 0; y < height; y++ {
+ col := im.At(x, y)
+ r, g, b, _ := col.RGBA()
+ write[ix] = byte(r)
+ ix++
+ write[ix] = byte(g)
+ ix++
+ write[ix] = byte(b)
+ ix++
+ }
+ }
+ // Write to the device
+ err := u.device.Transfer(write)
+ if err != nil {
+ log.Printf("Error writing to SPI device %v", err)
+ }
+}
+
+// NewUnicorn2 ...
+// Constructs a new and improved unicorn from stuff and things
+func NewUnicorn2() (*RealUnicorn2, error) {
+ dev, err := spi.Open("/dev/spidev0.0", 9000000, 0)
+ if err != nil {
+ return nil, err
+ }
+ return &RealUnicorn2{
+ BaseUnicorn2{},
+ dev,
+ }, nil
+}
+
+// MainLoop ...
+// Just blocks until sigterm
+func (u *RealUnicorn2) MainLoop() {
+ c := make(chan os.Signal, 2)
+ signal.Notify(c, os.Interrupt, syscall.SIGTERM)
+ <-c
+}
diff --git a/unicorn/SpiRenderDevice.go b/unicorn/SpiRenderDevice.go
new file mode 100644
index 0000000..140326a
--- /dev/null
+++ b/unicorn/SpiRenderDevice.go
@@ -0,0 +1,19 @@
+package unicorn
+
+import (
+ "github.com/ecc1/spi"
+)
+
+type SpiRenderDevice struct {
+ device *spi.Device
+}
+
+func NewSpiRenderDevice() (*SpiRenderDevice, error) {
+ dev, err := spi.Open("/dev/spidev0.0", 9000000, 0)
+ if err != nil {
+ return nil, err
+ }
+ return &SpiRenderDevice{
+ device: dev,
+ }, nil
+}
diff --git a/unicorn/Unicorn2.go b/unicorn/Unicorn2.go
index 716fe4d..04722f7 100644
--- a/unicorn/Unicorn2.go
+++ b/unicorn/Unicorn2.go
@@ -4,21 +4,32 @@
package unicorn
import (
+ "image"
"image/gif"
+ "time"
)
// Unicorn2 ...
// Interface for interacting with the Unicorn HAT HD
// Implemented by both real & fake unicorns.
type Unicorn2 interface {
+ // Change the image
GetGif() *gif.GIF
SetGif(*gif.GIF)
+ // Starts the rendering goroutine
StartRender()
+
+ // Must be implemented to actually render the image to device
+ renderImage(image.Image)
+
// Required for os to not think we're stuck
MainLoop()
}
+// BaseUnicorn2 ...
+// Common to both real & fake unicorns!
+// timing code for rendering & stopping rendering
type BaseUnicorn2 struct {
g *gif.GIF
}
@@ -30,3 +41,32 @@ func (u *BaseUnicorn2) GetGif() *gif.GIF {
func (u *BaseUnicorn2) SetGif(g *gif.GIF) {
u.g = g
}
+
+// StartRender ...
+// Starts rendering the image. If it's an animated image,
+// renders animation frames. Return a channel to stop the
+// image rendering.
+func (u *FakeUnicorn2) StartRender() chan bool {
+ stopChan := make(chan bool)
+ go func() {
+ timer := time.NewTimer(0)
+ imageIndex := 0
+ running := true
+ for running {
+ select {
+ case <-stopChan:
+ timer.Stop()
+ running = false
+ case <-timer.C:
+ gf := u.GetGif()
+ im := gf.Image[imageIndex]
+ delay := gf.Delay[imageIndex] //100ths of a second, 10^-2
+ u.renderImage(im)
+
+ timer.Reset(time.Duration(delay * 10000000)) // nanoseconds 10^-9 sec
+ imageIndex = (imageIndex + 1) % len(gf.Image)
+ }
+ }
+ }()
+ return stopChan
+} \ No newline at end of file
diff --git a/unicorn/Unicorn2_test.go b/unicorn/Unicorn2_test.go
new file mode 100644
index 0000000..c74571f
--- /dev/null
+++ b/unicorn/Unicorn2_test.go
@@ -0,0 +1,61 @@
+package unicorn
+
+import (
+ "bytes"
+ "image/gif"
+ "testing"
+ "time"
+)
+
+func gifAsset(name string) (*gif.GIF, error) {
+ data, err := Asset(name)
+ if err != nil {
+ return nil, err
+ }
+
+ g, err := gif.DecodeAll(bytes.NewReader(data))
+ if err != nil {
+ return nil, err
+ }
+
+ return g, nil
+}
+
+func TestAnimated(t *testing.T) {
+ un, err := NewUnicorn2()
+ if err != nil {
+ t.Errorf("Failed to create fake unicorn :( %v", err)
+ return
+ }
+ defer un.Close()
+
+ g, err := gifAsset("data/sample.gif")
+ if err != nil {
+ t.Errorf("Failed to load asset %v", err)
+ return
+ }
+
+ un.SetGif(g)
+ stopChan := un.StartRender()
+
+ // Stop after 3
+ time.Sleep(3 * time.Second)
+ stopChan <- true
+
+ // Leave it for a sec
+ time.Sleep(1 * time.Second)
+ g2, err := gifAsset("data/sample2.gif")
+ if err != nil {
+ t.Errorf("Failed to load asset %v", err)
+ return
+ }
+ un.SetGif(g2)
+ stopChan = un.StartRender()
+
+ // Stop after 5
+ time.Sleep(5 * time.Second)
+ stopChan <- true
+
+ // Make sure it's stopped
+ time.Sleep(2 * time.Second)
+}
diff --git a/unicorn/Unicorn_test2.go b/unicorn/Unicorn_test2.go
deleted file mode 100644
index 74cffd1..0000000
--- a/unicorn/Unicorn_test2.go
+++ /dev/null
@@ -1,21 +0,0 @@
-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
index f4395cc..1bd572c 100644
--- a/unicorn/bindata.go
+++ b/unicorn/bindata.go
@@ -1,6 +1,7 @@
// Code generated by go-bindata. DO NOT EDIT.
// sources:
// data/sample.gif
+// data/sample2.gif
package unicorn
import (
@@ -81,7 +82,27 @@ func dataSampleGif() (*asset, error) {
return nil, err
}
- info := bindataFileInfo{name: "data/sample.gif", size: 1773, mode: os.FileMode(420), modTime: time.Unix(1527458100, 0)}
+ info := bindataFileInfo{name: "data/sample.gif", size: 1773, mode: os.FileMode(420), modTime: time.Unix(1527504134, 0)}
+ a := &asset{bytes: bytes, info: info}
+ return a, nil
+}
+
+var _dataSample2Gif = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x93\xb1\x8b\x5d\x45\x14\xc6\x3f\x31\xe8\x26\x0a\xee\x53\x14\x0b\x0b\xf3\x60\x21\x48\x90\xe4\x2d\x0b\x6a\x6c\xd6\xcc\x8b\x0c\xde\xcd\x2c\xea\x41\x2d\x2e\x6a\xb2\x5c\x08\xd8\x68\x91\x5b\x5a\xf8\xc4\x09\xa6\x30\xb9\x46\x1c\x08\x97\xd5\x05\x8b\xd3\x0a\x0b\x32\x8d\x5b\xb9\xc5\x01\xc5\x46\xb7\x18\xb0\x11\xb4\x19\x14\x04\x95\x45\x47\xce\x80\x45\xfe\x87\x7d\xcd\x39\x9c\xf3\x8a\xdf\xfc\xee\x77\x9e\xb7\x17\x9e\x7a\xfa\xcd\x65\x2c\x03\xc0\xc9\x72\xdf\xc5\xf9\xcb\x2f\x9d\x5f\xdf\x9c\xcf\x9e\x3c\x73\xf7\x5d\x3a\xfa\xfb\x18\x66\x00\x4e\xeb\x5e\xff\xf6\x81\x36\x30\x80\x07\x44\xab\x31\x30\x1e\x46\x74\xe0\x0d\xbc\x87\x17\x08\x20\x06\xe2\x21\x82\x94\x26\x93\x09\x40\x40\x00\x92\x56\x22\x34\x01\x36\xe9\x20\x34\x08\x01\x43\x42\x02\x92\x45\x1a\x90\x47\xe4\x3c\x9d\x4e\x81\x0e\x60\x20\x6b\xed\x3a\x10\xc3\x65\x1d\x30\x81\x19\x63\x46\x06\xb2\x43\x21\x14\x46\x29\xab\xab\xab\x40\x0f\x44\xa0\x68\xed\x7b\xb4\x11\x54\x74\x10\x5b\xc4\x08\x2e\x28\x30\x4a\xec\x0d\x44\xc1\xf5\xe7\x8d\x11\x7d\x81\x37\xc6\x7b\xe3\xc5\x08\x8c\x18\x23\xde\x88\x98\xca\x4d\xa0\x06\xc1\x22\x11\x81\x88\x9a\x26\x58\x9b\x9a\x80\x26\x34\x4d\x08\x76\x48\x36\xc1\x26\x6b\xd3\x60\x53\x72\x39\x2b\x2f\x3a\x02\x3b\xe4\xae\x43\xd7\x75\x44\xec\x5c\x26\x06\x31\x11\xb3\x1b\xb3\xcb\x70\xd9\xb9\x3c\x52\x61\x2a\x45\x79\xd1\xb7\x88\x84\xd2\xf7\xe8\xfb\xbe\x6d\x23\x51\x69\x23\xda\xd8\xb6\x31\x12\x17\x2a\xa0\x42\x5e\x15\x8b\x9a\x56\x74\xef\x8d\xa8\x72\xed\xbc\xf7\xe2\x05\x5e\x8c\x17\xef\x45\xf4\x83\xc0\x28\x26\xc2\x80\xa4\xa6\x9b\x26\x34\x61\xb0\x29\x04\x84\xd0\x84\x10\x86\x21\x0d\x09\x43\xb2\x43\x1a\x86\x94\x42\x35\xae\x98\xe0\x11\x59\x4d\x13\x31\xf1\xe8\x32\x33\x98\x89\x99\xc7\x31\x8f\x19\x63\x76\x63\x1e\xc7\x9c\xb9\x14\x15\xac\xae\x23\xa3\xa8\xe9\xb6\x8d\x6d\x64\x2a\x31\x22\xc6\x36\xc6\xc8\x5c\xb8\x80\x0b\x71\x61\x81\x6a\x95\x2a\x5d\x8c\x52\x4a\x7d\x83\x78\x91\xba\x93\xda\x49\x8d\x97\xc0\xa7\x1a\x0e\x8d\x86\xb5\xa9\xba\x4e\x03\xd4\x74\x45\x4f\x75\x97\x6a\xa7\x09\x82\x55\xaf\x35\x1c\xd9\x41\x05\x57\xd7\x9a\xa9\xd1\xe5\x8a\x9e\xeb\x2e\xd7\x2e\x6b\x56\xa0\x5e\x6b\x38\x34\x4a\x44\xa5\xba\xd6\x4c\x31\x95\x8a\x5e\xea\xae\xd4\x6e\x69\x03\xf8\x71\xf9\xd5\xef\x7e\x5a\x5f\x1c\x7f\xe8\x41\xb3\xbf\xf9\xd9\xf9\xf7\x27\x2b\x17\x1f\xfb\x6b\x73\xfa\x65\x2b\xa7\x66\x87\x7f\x3e\x70\xf9\xb9\xeb\x72\xe6\xea\x1f\xa7\x66\x3b\x5f\xdf\x28\x57\x6f\x6c\xfd\x3a\xc1\x37\x9f\x9c\x3d\xf7\xc5\xc7\x07\xb3\xcb\x1b\x37\x4f\x6e\xe4\x85\x7d\xe7\x87\xf9\xad\xe9\xda\x89\xfb\x97\x8e\x2e\xec\xe8\xc2\x8e\x2e\xec\xce\x0b\x7b\xfd\xff\x0b\xc3\x89\x63\x4f\x98\xfd\xdf\x1e\xdf\x5b\x60\x6b\x69\xe1\x3f\xfa\x7c\x7e\xed\xd1\xb5\x67\x9e\xdd\xff\xfd\xd2\x85\xf7\x1e\x5e\x7f\x64\x6b\xf7\xc3\x83\xbd\x6b\xf7\x86\x7f\x0e\x5f\xf9\xfe\x92\xed\x56\x5e\xdb\xbd\x7e\xe5\xc5\x6d\x79\x37\x76\xf9\xad\xe3\x6f\xac\xb8\xed\xd3\x6f\xdf\xf3\xf3\x95\x9b\xbf\x34\x9f\xae\xdd\xfa\xf7\xf6\x57\x2f\xec\x7c\x7b\xfb\xac\xd3\x9b\x3b\xf7\x5f\x00\x00\x00\xff\xff\x24\xe4\xf7\x2e\xf7\x06\x00\x00")
+
+func dataSample2GifBytes() ([]byte, error) {
+ return bindataRead(
+ _dataSample2Gif,
+ "data/sample2.gif",
+ )
+}
+
+func dataSample2Gif() (*asset, error) {
+ bytes, err := dataSample2GifBytes()
+ if err != nil {
+ return nil, err
+ }
+
+ info := bindataFileInfo{name: "data/sample2.gif", size: 1783, mode: os.FileMode(420), modTime: time.Unix(1527519351, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@@ -139,6 +160,7 @@ func AssetNames() []string {
// _bindata is a table, holding each asset generator, mapped to its name.
var _bindata = map[string]func() (*asset, error){
"data/sample.gif": dataSampleGif,
+ "data/sample2.gif": dataSample2Gif,
}
// AssetDir returns the file names below a certain
@@ -183,6 +205,7 @@ type bintree struct {
var _bintree = &bintree{nil, map[string]*bintree{
"data": &bintree{nil, map[string]*bintree{
"sample.gif": &bintree{dataSampleGif, map[string]*bintree{}},
+ "sample2.gif": &bintree{dataSample2Gif, map[string]*bintree{}},
}},
}}
diff --git a/unicorn/build_bindata.sh b/unicorn/build_bindata.sh
new file mode 100755
index 0000000..2265ce0
--- /dev/null
+++ b/unicorn/build_bindata.sh
@@ -0,0 +1 @@
+~/go/bin/go-bindata -pkg unicorn data/
diff --git a/unicorn/data/sample2.gif b/unicorn/data/sample2.gif
new file mode 100644
index 0000000..2dfc764
--- /dev/null
+++ b/unicorn/data/sample2.gif
Binary files differ