Created
December 1, 2018 05:10
-
-
Save JokerCatz/9790290776b71f7cff41f7bd060cd83b to your computer and use it in GitHub Desktop.
rpi-rgb-led-matrix unix socket client by Golang (animation gif reader)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"bufio" | |
"flag" | |
"fmt" | |
"image" | |
"image/draw" | |
"image/gif" | |
"log" | |
"net" | |
"os" | |
"strconv" | |
"strings" | |
"time" | |
) | |
func helpGetEnvStr(key string, defaultStr string) string { | |
var tempStr string | |
tempStr = os.Getenv(key) | |
if tempStr == "" { | |
return defaultStr | |
} | |
return tempStr | |
} | |
func helpGetEnvUint(key string, defaultUint uint) uint { | |
var tempStr string | |
tempStr = os.Getenv(key) | |
if tempStr == "" { | |
return defaultUint | |
} | |
tempInt, err := strconv.Atoi(tempStr) | |
if err != nil || tempInt < 0 || tempInt > 65535 { | |
return defaultUint | |
} | |
return uint(tempInt) | |
} | |
func helperIntAryJoin(input []int, delim string) string { | |
return strings.Trim(strings.Join(strings.Fields(fmt.Sprint(input)), delim), "[]") | |
} | |
func helperFillColor(frameBuffer *[frameIndexMax]byte, byteIndex *int, r byte, g byte, b byte) { | |
frameBuffer[*byteIndex] = byte(0x20) | byte(r>>4) | |
*byteIndex++ | |
frameBuffer[*byteIndex] = byte(0x30) | byte(r&0xF) | |
*byteIndex++ | |
// G | |
frameBuffer[*byteIndex] = byte(0x40) | byte(g>>4) | |
*byteIndex++ | |
frameBuffer[*byteIndex] = byte(0x50) | byte(g&0xF) | |
*byteIndex++ | |
// B | |
frameBuffer[*byteIndex] = byte(0x60) | byte(b>>4) | |
*byteIndex++ | |
frameBuffer[*byteIndex] = byte(0x70) | byte(b&0xF) | |
*byteIndex++ | |
} | |
const squareWidth = 64 // LED panel size | |
const squareHeight = 64 | |
const frameIndexMax = squareWidth*squareHeight*3*2 + 2 // width * height * 3color * 2byte + head & tail | |
var unixDomainSocketClient net.Conn | |
// flag | |
var pathGif = helpGetEnvStr("PATH_GIF", "./vendor/2.gif") | |
var pathSocket = helpGetEnvStr("PATH_SOCKET", "./tmp/socket") | |
var backgroundLight = helpGetEnvUint("BACKGROUND_LIGHT", 72) | |
var brightnessDown = helpGetEnvUint("BRIGHTNESS_DOWN", 2) | |
func init() { | |
flag.StringVar(&pathGif, "path_gif", pathGif, "gif file path") | |
flag.StringVar(&pathSocket, "path_socket", pathSocket, "socket path") | |
flag.UintVar(&backgroundLight, "background_light", backgroundLight, "background_light") | |
flag.UintVar(&brightnessDown, "brightness_down", brightnessDown, "brightness_down / N") | |
flag.Parse() | |
var err error | |
unixDomainSocketClient, err = net.Dial("unix", pathSocket) | |
if err != nil { | |
log.Fatal("Dial error", err) | |
} | |
} | |
func main() { | |
defer unixDomainSocketClient.Close() | |
inputFile, err := os.Open(pathGif) | |
defer inputFile.Close() | |
if err != nil { | |
panic(err) | |
} | |
gifImg, err := gif.DecodeAll(bufio.NewReader(inputFile)) | |
if err != nil { | |
panic(err) | |
} | |
brightnessDownByte := byte(brightnessDown) | |
fmt.Printf("loop count : %d \n", gifImg.LoopCount) | |
fmt.Printf("frame count : %d \n", len(gifImg.Delay)) | |
fmt.Printf("config.width : %d \n", gifImg.Config.Width) | |
fmt.Printf("config.height : %d \n", gifImg.Config.Height) | |
fmt.Printf("delay : (%s) \n", helperIntAryJoin(gifImg.Delay, ",")) | |
fmt.Println("Disposal : ", gifImg.Disposal) | |
fmt.Println("BackgroundIndex : ", gifImg.BackgroundIndex) | |
disposal := gifImg.Disposal | |
backgroundR := byte(backgroundLight) // bg R | |
backgroundG := byte(backgroundLight) // bg G | |
backgroundB := byte(backgroundLight) // bg B | |
imgWidth := gifImg.Config.Width // img size | |
imgHeight := gifImg.Config.Height | |
offsetLeft := (squareWidth - imgWidth) / 2 // border single side size | |
offsetTop := (squareHeight - imgHeight) / 2 | |
frameCount := len(gifImg.Image) | |
var loopCount int | |
if gifImg.LoopCount == 0 { | |
loopCount = -1 | |
} else { | |
loopCount = gifImg.LoopCount | |
} | |
delayAry := gifImg.Delay | |
// init default content : background color ... then skip | |
frameBuffer := [frameIndexMax]byte{} | |
/////////start | |
byteIndex := 0 | |
frameBuffer[byteIndex] = byte(0x80) // head cmd 100_0_0000 | |
byteIndex++ | |
/////////background content | |
for t := 0; t < squareWidth*squareHeight; t++ { | |
helperFillColor(&frameBuffer, &byteIndex, backgroundR, backgroundG, backgroundB) | |
} | |
frameBuffer[byteIndex] = byte(0xA0) // tail cmd 101_0_0000 | |
/////////end | |
frameIndex := 0 | |
t := 0 | |
x := 0 | |
y := 0 | |
sourceFrame := gifImg.Image[frameIndex] | |
outputFrame := image.NewRGBA(image.Rect(0, 0, imgWidth, imgHeight)) | |
for { // frame loop | |
// clone frame first | |
if disposal[frameIndex] == 2 { //full frame redraw | |
outputFrame = image.NewRGBA(image.Rect(0, 0, imgWidth, imgHeight)) | |
draw.Draw(outputFrame, outputFrame.Bounds(), sourceFrame, image.ZP, draw.Over) | |
} else if disposal[frameIndex] == 1 { //mask background | |
draw.Draw(outputFrame, outputFrame.Bounds(), sourceFrame, image.ZP, draw.Over) | |
} | |
byteIndex = 0 | |
byteIndex++ // skip head cmd 100_0_0000 | |
// top border offset | |
if offsetTop > 0 { | |
for t = 0; t < squareWidth*offsetTop; t++ { | |
byteIndex = byteIndex + 6 // skip RGB x 2byte | |
} | |
} | |
// middle content | |
for y = 0; y < imgHeight; y++ { | |
// left border offset | |
for t = 0; t < offsetLeft; t++ { | |
byteIndex = byteIndex + 6 // skip RGB x 2byte | |
} | |
// image content | |
for x = 0; x < imgWidth; x++ { | |
r, g, b, a := outputFrame.At(x, y).RGBA() | |
if a == 0 { // alpha background | |
helperFillColor( | |
&frameBuffer, | |
&byteIndex, | |
backgroundR, | |
backgroundG, | |
backgroundB, | |
) | |
} else { | |
helperFillColor( | |
&frameBuffer, | |
&byteIndex, | |
byte(r)/brightnessDownByte, | |
byte(g)/brightnessDownByte, | |
byte(b)/brightnessDownByte, | |
) | |
} | |
} | |
// right border offset | |
for t = 0; t < squareHeight-offsetLeft-imgWidth; t++ { | |
byteIndex = byteIndex + 6 // skip RGB x 2byte | |
} | |
} | |
// bottom border offset // do nothing | |
// tail cmd 101_0_0000 // do nothing | |
_, err := unixDomainSocketClient.Write(frameBuffer[:]) | |
if err != nil { | |
panic(err) | |
} | |
// delayAry[frameIndex] 100 = 1sec , so * 10 => 1000 (GIF spec) | |
time.Sleep(time.Duration(delayAry[frameIndex]*10) * time.Millisecond) //delay timer | |
// next round init | |
frameIndex = (frameIndex + 1) % frameCount | |
if frameIndex == 0 { | |
if loopCount > 0 { | |
loopCount-- | |
} else if loopCount == 0 { // count down break | |
break | |
} else { // -1 loop forever | |
// do nothing | |
} | |
} | |
sourceFrame = gifImg.Image[frameIndex] | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment