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 }