package main import ( "bytes" b64 "encoding/base64" "encoding/json" "fmt" "image" "image/color" "log" "os" "time" rgbmatrix "gitea.wagshome.duckdns.org/publicWagsHome/go-rpi-rgb-led-matrix" "github.com/disintegration/imaging" mqtt "github.com/eclipse/paho.mqtt.golang" "github.com/fogleman/gg" ) // contents of struct mostly don't matter for toolkit. type incomingMessage struct { Type string `json:"type"` Image string `json:"image"` } type Animation struct { ctx *gg.Context position image.Point dir image.Point height int width int stroke int image []image.Image images map[string]image.Image updown string mqmsg chan mqtt.Message msg string countDown int } // animator is a wrapping function for go routine that can receive an mq channel func animator(tk *rgbmatrix.ToolKit, mqMessages chan mqtt.Message) { //Playanimation comes from the toolkit, all it takes is an animation struct tk.PlayAnimation(NewAnimation(image.Point{127, 64}, mqMessages)) } // initializes the struct for the an play animation function, this could all be dumped into function that's wrapping go routine if I wanted func NewAnimation(sz image.Point, mqMessages chan mqtt.Message) *Animation { imageMap := make(map[string]image.Image) reader, err := os.Open("marioUp.png") if err != nil { log.Fatal(err) } rawMario, _, err := image.Decode(reader) //marioUp := imaging.FlipH(imaging.Resize(rawMario, 16, 16, imaging.Lanczos)) marioUp := imaging.Resize(rawMario, 16, 16, imaging.Lanczos) imageMap["marioUp"] = marioUp reader, err = os.Open("marioDown.png") if err != nil { log.Fatal(err) } rawMario, _, err = image.Decode(reader) if err != nil { log.Fatal(err) } //marioDown := imaging.FlipH(imaging.Resize(rawMario, 16, 16, imaging.Lanczos)) marioDown := imaging.Resize(rawMario, 16, 16, imaging.Lanczos) imageMap["marioDown"] = marioDown return &Animation{ ctx: gg.NewContext(sz.X, sz.Y), dir: image.Point{1, 1}, height: 8, width: 8, stroke: 8, images: imageMap, updown: "marioUp", mqmsg: mqMessages, countDown: 5000, } } func appendImage(img string, a *Animation) { baseImage, _ := b64.StdEncoding.DecodeString(img) bigImage, _, _ := image.Decode(bytes.NewReader(baseImage)) a.images["doorbell"] = imaging.Resize(bigImage, 64, 64, imaging.Lanczos) } // what happens each frame, at an interval of 50 milliseconds func (a *Animation) Next() (image.Image, <-chan time.Time, error) { var incoming incomingMessage if a.countDown == 5000 { a.animateMario() } if a.images["doorbell"] != nil { if a.countDown > 0 { a.ctx.DrawImageAnchored(a.images["doorbell"], 0, 0, 0, 0) a.countDown -= 50 } else { //a.image = a.image[:len(a.image)-1] delete(a.images, "doorbell") a.countDown = 5000 } } a.ctx.SetColor(color.White) select { case msg := <-a.mqmsg: json.Unmarshal([]byte(string(msg.Payload())), &incoming) fmt.Println(incoming.Type) if incoming.Type == "doorbell" { go appendImage(incoming.Image, a) } else { a.ctx.DrawString(a.msg, 5, 9) } default: } return a.ctx.Image(), time.After(time.Millisecond * 50), nil } func (a *Animation) animateMario() { defer a.updateMarioPosition() a.ctx.SetColor(color.Black) a.ctx.Clear() if a.dir.X == 1 { a.ctx.DrawImageAnchored(a.images[a.updown], a.position.X, a.position.Y, 0.5, 0.5) } else { a.ctx.DrawImageAnchored(imaging.FlipH(a.images[a.updown]), a.position.X, a.position.Y, 0.5, 0.5) } } // what mario does every frame func (a *Animation) updateMarioPosition() { a.position.X += 1 * a.dir.X a.position.Y += 1 * a.dir.Y if a.position.Y+a.height > a.ctx.Height() { a.dir.Y = -1 a.updown = "marioUp" } else if a.position.Y-a.height < 0 { a.updown = "marioDown" a.dir.Y = 1 } if a.position.X+a.width > a.ctx.Width() { a.dir.X = -1 } else if a.position.X-a.width < 0 { a.dir.X = 1 } }