unicornpaint

A web-based painting app for raspberry PI and pimoroni Unicorn Hat HD
Log | Files | Refs | README

Unicorn2.go (2048B)


      1 // Version 2 of unicorn, uses gif.GIF as store of pixels
      2 // & a separate goroutine to render it so it can do
      3 // animated GIFs
      4 package unicorn
      5 
      6 import (
      7 	"image"
      8 	"image/color/palette"
      9 	"image/gif"
     10 	"time"
     11 )
     12 
     13 // Unicorn2 ...
     14 // Interface for interacting with the Unicorn HAT HD
     15 // Implemented by both real & fake unicorns.
     16 type Unicorn2 interface {
     17 	// Change the image
     18 	GetGif() *gif.GIF
     19 	SetGif(*gif.GIF)
     20 
     21 	// Starts the rendering goroutine
     22 	StartRender() chan bool
     23 
     24 	// Must be implemented to actually render the image to device
     25 	renderImage(image.Image)
     26 
     27 	// Required for os to not think we're stuck
     28 	MainLoop()
     29 }
     30 
     31 // BaseUnicorn2 ...
     32 // Common to both real & fake unicorns!
     33 // timing code for rendering & stopping rendering
     34 type BaseUnicorn2 struct {
     35 	g *gif.GIF
     36 }
     37 
     38 func (u *BaseUnicorn2) GetGif() *gif.GIF {
     39 	return u.g
     40 }
     41 
     42 func (u *BaseUnicorn2) SetGif(g *gif.GIF) {
     43 	u.g = g
     44 }
     45 
     46 func NewBaseUnicorn2() *BaseUnicorn2 {
     47 	im := image.NewPaletted(
     48 		image.Rect(0, 0, 16, 16),
     49 		palette.WebSafe)
     50 
     51 	gf := &gif.GIF{
     52 		Image:           []*image.Paletted{im},
     53 		Delay:           []int{50},
     54 		Disposal:        []byte{gif.DisposalBackground},
     55 		BackgroundIndex: 0, // This is black in the websafe palette
     56 	}
     57 
     58 	return &BaseUnicorn2{
     59 		g: gf,
     60 	}
     61 }
     62 
     63 // StartRenderBase ...
     64 // Deals with the timing aspect of animated GIFs
     65 func (u *BaseUnicorn2) StartRenderBase(renderImage func(image.Image)) chan bool {
     66 	stopChan := make(chan bool)
     67 	go func() {
     68 		timer := time.NewTimer(0)
     69 		imageIndex := 0
     70 		running := true
     71 		for running {
     72 			select {
     73 			case <-stopChan:
     74 				timer.Stop()
     75 				running = false
     76 			case <-timer.C:
     77 				gf := u.GetGif()
     78 
     79 				// Image could change underneath us, but there should always be 1 image at least.
     80 				if imageIndex >= len(gf.Image) {
     81 					imageIndex = 0
     82 				}
     83 
     84 				im := gf.Image[imageIndex]
     85 				delay := gf.Delay[imageIndex] //100ths of a second, 10^-2
     86 				renderImage(im)
     87 
     88 				timer.Reset(time.Duration(delay * 10000000)) // nanoseconds 10^-9 sec
     89 				imageIndex = (imageIndex + 1) % len(gf.Image)
     90 			}
     91 		}
     92 	}()
     93 	return stopChan
     94 }