badges/badge.go
2026-04-21 21:51:05 +02:00

360 lines
9.1 KiB
Go

package main
import (
_ "embed"
"image/color"
"strconv"
"time"
"unsafe"
"tinygo.org/x/tinydraw"
"tinygo.org/x/tinyfont"
"tinygo.org/x/tinyfont/freesans"
"tinygo.org/x/tinyfont/gophers"
qrcode "github.com/skip2/go-qrcode"
)
const (
logoDisplayTime = 10 * time.Second
)
var rainbow []color.RGBA
var quit bool
var selectedScreen uint8
//go:embed logo.bin
var badgeLogo string
func Badge() {
quit = false
selectedScreen = 0
display.FillScreen(colorBlack)
rainbow = make([]color.RGBA, 256)
for i := 0; i < 256; i++ {
rainbow[i] = getRainbowRGB(uint8(i))
}
for {
switch selectedScreen {
case 7: // last +1
selectedScreen = 0
fallthrough
case 0:
showLogoBin()
if quit {
break
}
case 1:
if hardwareName == "GOPHERBADGE" {
logopurpleHardware()
} else {
selectedScreen++
}
if quit {
break
}
case 2:
myNameIsRainbow(YourName)
if quit {
break
}
case 3:
blinkyRainbow(YourTitleA1, YourTitleA2)
if quit {
break
}
case 4:
scroll(YourMarqueeTop, YourMarqueeMiddle, YourMarqueeBottom)
if quit {
break
}
case 5:
QR(YourQRText)
if quit {
break
}
case 255: // max
selectedScreen = 6 // last one
fallthrough
case 6:
blinkyRainbow(YourTitleB1, YourTitleB2)
if quit {
break
}
default:
selectedScreen = 0
}
if quit {
break
}
}
}
func myNameIs(name string) {
display.FillScreen(colorWhite)
// black corners detail
display.FillRectangle(0, 0, cornerRadius, cornerRadius, colorBlack)
display.FillRectangle(0, displayHeight-cornerRadius, cornerRadius, cornerRadius, colorBlack)
display.FillRectangle(displayWidth-cornerRadius, 0, cornerRadius, cornerRadius, colorBlack)
display.FillRectangle(displayWidth-cornerRadius, displayHeight-cornerRadius, cornerRadius, cornerRadius, colorBlack)
// round corners
tinydraw.FilledCircle(&display, cornerRadius, cornerRadius, cornerRadius, colorRed)
tinydraw.FilledCircle(&display, displayWidth-cornerRadius-1, cornerRadius, cornerRadius, colorRed)
tinydraw.FilledCircle(&display, cornerRadius, displayHeight-cornerRadius-1, cornerRadius, colorRed)
tinydraw.FilledCircle(&display, displayWidth-cornerRadius-1, displayHeight-cornerRadius-1, cornerRadius, colorRed)
// top band
display.FillRectangle(cornerRadius, 0, displayWidth-2*cornerRadius-1, cornerRadius, colorRed)
display.FillRectangle(0, cornerRadius, displayWidth, int16(topBandHeight), colorRed)
// bottom band
display.FillRectangle(cornerRadius, displayHeight-cornerRadius-1, displayWidth-2*cornerRadius-1, cornerRadius+1, colorRed)
display.FillRectangle(0, displayHeight-int16(bottomBandHeight)-cornerRadius-1, displayWidth, int16(bottomBandHeight), colorRed)
// top text
if displayWidth >= 320 {
// gopherbadge: HELLO + my name is
w32, _ := tinyfont.LineWidth(&freesans.Bold18pt7b, "HELLO")
tinyfont.WriteLine(&display, &freesans.Bold18pt7b, (displayWidth-int16(w32))/2, int16(helloY), "HELLO", colorWhite)
w32, _ = tinyfont.LineWidth(&freesans.Oblique9pt7b, "my name is")
tinyfont.WriteLine(&display, &freesans.Oblique9pt7b, (displayWidth-int16(w32))/2, int16(myNameIsY), "my name is", colorWhite)
} else {
// pybadge: my NAME is
w32, _ := tinyfont.LineWidth(&freesans.Regular12pt7b, "my NAME is")
tinyfont.WriteLine(&display, &freesans.Regular12pt7b, (displayWidth-int16(w32))/2, int16(myNameIsY), "my NAME is", colorWhite)
}
writeLine(name, colorBlack, displayWidth/2, 6*displayHeight/10, displayWidth, true)
// gophers
if displayWidth >= 320 {
tinyfont.WriteLineColors(&display, &gophers.Regular58pt, displayWidth-int16(gophersX), int16(gophersY), "BE", []color.RGBA{getRainbowRGB(100), getRainbowRGB(200)})
} else {
tinyfont.WriteLineColors(&display, &gophers.Regular32pt, displayWidth-int16(gophersX), int16(gophersY), "BE", []color.RGBA{getRainbowRGB(100), getRainbowRGB(200)})
}
}
func myNameIsRainbow(name string) {
myNameIs(name)
for i := 0; i < 230; i++ {
writeLine(name, rainbow[i], displayWidth/2, 6*displayHeight/10, displayWidth, true)
i += 2
getInput()
if goBack() {
quit = true
return
}
}
selectedScreen++
}
func blinkyRainbow(topline, bottomline string) {
display.FillScreen(colorWhite)
// calculate the width of the text so we could center them later
for i := int16(0); i < 20; i++ {
writeLine(topline, getRainbowRGB(uint8(i*12)), displayWidth/2, 4*displayHeight/10, displayWidth, true)
writeLine(bottomline, getRainbowRGB(uint8(i*12)), displayWidth/2, 8*displayHeight/10, displayWidth, true)
getInput()
if goBack() {
quit = true
return
}
}
selectedScreen++
}
func scroll(topline, middleline, bottomline string) {
display.FillScreen(colorWhite)
writeLine(topline, getRainbowRGB(200), displayWidth/2, 3*displayHeight/10, displayWidth, true)
writeLine(middleline, getRainbowRGB(80), displayWidth/2, 6*displayHeight/10, displayWidth, true)
writeLine(bottomline, getRainbowRGB(120), displayWidth/2, 9*displayHeight/10, displayWidth, true)
display.SetScrollArea(0, 0)
defer func() {
display.SetScroll(0)
display.StopScroll()
}()
for k := 0; k < 4; k++ {
for i := int16(displayWidth - 1); i >= 0; i-- {
getInput()
if goBack() {
quit = true
return
}
display.SetScroll(i)
time.Sleep(10 * time.Millisecond)
}
}
selectedScreen++
}
func logopurpleHardware() {
bgColor := color.RGBA{109, 0, 140, 255}
white := color.RGBA{255, 255, 255, 255}
display.FillScreen(bgColor)
if displayWidth >= 320 {
// Full logo for gopherbadge
display.FillRectangle(6, 166, 308, 21, white)
tinydraw.FilledCircle(&display, 282, 130, 9, white)
tinydraw.Line(&display, 259, 110, 298, 149, bgColor)
tinydraw.Line(&display, 260, 110, 299, 149, bgColor)
tinydraw.Line(&display, 261, 110, 300, 149, bgColor)
tinydraw.Line(&display, 262, 110, 301, 149, bgColor)
tinydraw.Line(&display, 263, 110, 302, 149, bgColor)
tinydraw.Line(&display, 264, 110, 303, 149, bgColor)
tinydraw.Line(&display, 265, 110, 304, 149, bgColor)
display.FillRectangle(250, 98, 11, 63, white)
display.FillRectangle(250, 98, 44, 11, white)
display.FillRectangle(270, 150, 44, 11, white)
display.FillRectangle(303, 98, 11, 63, white)
tinyfont.WriteLine(&display, &freesans.Regular18pt7b, 6, 109, "purple", white)
tinyfont.WriteLine(&display, &freesans.Regular18pt7b, 6, 153, "Hardware by", white)
} else {
// Simplified logo for pybadge
tinyfont.WriteLine(&display, &freesans.Bold9pt7b, 10, 60, "purple", white)
tinyfont.WriteLine(&display, &freesans.Bold9pt7b, 10, 80, "Hardware", white)
}
refreshIntervals := uint8(7)
displayTime := logoDisplayTime / 3
for i := uint8(0); i < refreshIntervals; i++ {
time.Sleep(displayTime / time.Duration(refreshIntervals))
getInput()
if goBack() {
quit = true
return
}
}
selectedScreen++
}
func getRainbowRGB(i uint8) color.RGBA {
if i < 85 {
return color.RGBA{i * 3, 255 - i*3, 0, 255}
} else if i < 170 {
i -= 85
return color.RGBA{255 - i*3, 0, i * 3, 255}
}
i -= 170
return color.RGBA{0, i * 3, 255 - i*3, 255}
}
func showLogoBin() {
row := make([]color.RGBA, displayWidth)
unsafeBadgeLogo := unsafe.Slice(unsafe.StringData(badgeLogo), len(badgeLogo))
for i := 0; i < displayHeight; i++ {
for j := 0; j < displayWidth; j++ {
values, err := strconv.ParseUint(string(unsafeBadgeLogo[6*(displayWidth*i+j):6*(displayWidth*i+j+1)]), 16, 32)
if err != nil {
println(err)
}
row[j] = color.RGBA{
R: uint8(values >> 16),
G: uint8((values >> 8) & 0xFF),
B: uint8(values & 0xFF),
A: 255,
}
}
display.FillRectangleWithBuffer(0, int16(i), displayWidth, 1, row)
}
refreshIntervals := uint8(20)
for i := uint8(0); i < refreshIntervals; i++ {
time.Sleep(logoDisplayTime / time.Duration(refreshIntervals))
getInput()
if goBack() {
quit = true
return
}
}
selectedScreen++
}
func QR(msg string) {
qr, err := qrcode.New(msg, qrcode.Medium)
if err != nil {
println(err, 123)
}
qrbytes := qr.Bitmap()
size := int16(len(qrbytes))
factor := int16(displayHeight / len(qrbytes))
bx := (displayWidth - size*factor) / 2
by := (displayHeight - size*factor) / 2
display.FillScreen(color.RGBA{109, 0, 140, 255})
for y := int16(0); y < size; y++ {
for x := int16(0); x < size; x++ {
if qrbytes[y][x] {
display.FillRectangle(bx+x*factor, by+y*factor, factor, factor, colorBlack)
} else {
display.FillRectangle(bx+x*factor, by+y*factor, factor, factor, colorWhite)
}
}
}
refreshIntervals := uint8(20)
for i := uint8(0); i < refreshIntervals; i++ {
time.Sleep(logoDisplayTime / time.Duration(refreshIntervals))
getInput()
if goBack() {
quit = true
return
}
}
selectedScreen++
}
func getFontWidthSize(text string) (w32 uint32, size byte) {
w32, _ = tinyfont.LineWidth(&freesans.Bold24pt7b, text)
size = 24
if w32 < uint32(FontWidthThreshold) {
size = 24
} else {
w32, _ = tinyfont.LineWidth(&freesans.Bold18pt7b, text)
if w32 < uint32(FontWidthThreshold) {
size = 18
} else {
w32, _ = tinyfont.LineWidth(&freesans.Bold12pt7b, text)
if w32 < uint32(FontWidthThreshold) {
size = 12
} else {
w32, _ = tinyfont.LineWidth(&freesans.Bold9pt7b, text)
size = 9
}
}
}
return
}