Skip to content

Commit

Permalink
feat: use firacode font
Browse files Browse the repository at this point in the history
  • Loading branch information
MrMarble committed Jan 16, 2023
1 parent 72c875b commit 30a25d3
Show file tree
Hide file tree
Showing 2 changed files with 149 additions and 66 deletions.
Binary file added internal/img/FiraCode-Bold.ttf
Binary file not shown.
215 changes: 149 additions & 66 deletions internal/img/generate.go
Original file line number Diff line number Diff line change
@@ -1,23 +1,27 @@
package img

import (
_ "embed"
"fmt"
"image"
"image/color"
"image/draw"
"math"
"os"
"strings"

"github.com/fogleman/gg"
"github.com/golang/freetype/truetype"
"github.com/mrmarble/zmk-viewer/pkg/keyboard"
keyboard "github.com/mrmarble/zmk-viewer/pkg/infojson"
"github.com/mrmarble/zmk-viewer/pkg/keymap"
"github.com/rs/zerolog/log"
"golang.org/x/image/font/gofont/goregular"
)

//go:embed FiraCode-Bold.ttf
var firaCode []byte

const (
keySize = 60.0
keySize = 70.0
spacer = 5.0
margin = keySize / 8
radius = 5.0
Expand Down Expand Up @@ -100,9 +104,8 @@ func (i *Image) GenerateSingle() (image.Image, error) {
for _, layer := range layers {
if first {
first = false
rect = image.Rect(0, 0, layer.Bounds().Dx(), layer.Bounds().Dy()*(len(layers)-1))
rect = image.Rect(0, 0, layer.Bounds().Dx(), layer.Bounds().Dy()*len(layers))
output = image.NewRGBA(rect)
continue
}
draw.Draw(output, image.Rect(0, height, layer.Bounds().Dx(), layer.Bounds().Dy()+height), layer, image.Point{0, 0}, draw.Src)
height += layer.Bounds().Dy()
Expand All @@ -114,19 +117,28 @@ func (i *Image) GenerateUnified() (image.Image, error) {
for _, layout := range i.keyboard.Layouts {
layout := layout
ctx := createContext(&layout)
err := drawLayout(ctx, i.transparent, layout)
if err != nil {
return nil, err
if !i.transparent {
ctx.SetHexColor("#eeeeee")
ctx.Clear()
}

base := ctx.Image()
if keymap, ok := parseKeymap(i.keymap); ok {
keys := make([]*keycap, len(layout.Layout))
for layerIndex, layer := range keymap.Device.Keymap.Layers {
err := drawKeymap(ctx, layout, layer, i.raw, layerIndex)
if err != nil {
return nil, err
for keyIndex, key := range layer.Bindings {
if layerIndex == 0 {
log.Debug().Msgf("Adding key %d", keyIndex)
x, y := getKeyCoords(layout.Layout[keyIndex])
keys[keyIndex] = newKeycap(x, y, keySize, keySize).fromKey(key, !i.raw)
} else {
log.Debug().Msgf("Updating key %d", keyIndex)
keys[keyIndex].setLayer(layerIndex, key, !i.raw)
}
}
}
for _, key := range keys {
key.draw(ctx)
}
}
return base, nil
}
Expand Down Expand Up @@ -176,7 +188,19 @@ func createContext(layout *keyboard.Layout) *gg.Context {

log.Debug().Int("Image Width", imageW).Int("Image Height", imageH).Float64("Max X", mx).Float64("Max Y", my).Send()

return gg.NewContext(imageW, imageH)
ctx := gg.NewContext(imageW, imageH)
f, err := truetype.Parse(firaCode)
if err != nil {
log.Err(err).Send()
}

face := truetype.NewFace(f, &truetype.Options{
Size: 12.0,
// Hinting: font.HintingFull,
})
ctx.SetFontFace(face)

return ctx
}

// drawLaout of the keyboard. Blank keys.
Expand All @@ -196,36 +220,21 @@ func drawLayout(ctx *gg.Context, transparent bool, layout keyboard.Layout) error
if key.W != nil {
w = *key.W * keySize
}
ctx.DrawRoundedRectangle(x, y, w, h, radius)
ctx.SetRGB(0., 0., 0.)
ctx.StrokePreserve()
ctx.SetHexColor("#888888")
ctx.Fill()
ctx.DrawRoundedRectangle(x+(margin*2)/2, y+2, w-(margin*2), h-(margin*2), radius)
ctx.SetHexColor("#a7a7a7")
ctx.Fill()
newKeycap(x, y, w, h).draw(ctx)
}
return nil
}

// drawKeymap of the keyboard. Legend on top of the keys.
func drawKeymap(ctx *gg.Context, layout keyboard.Layout, layer *keymap.Layer, raw bool, layerNum int) error {
font, err := truetype.Parse(goregular.TTF)
if err != nil {
return err
}

face := truetype.NewFace(font, &truetype.Options{Size: 10})
ctx.SetFontFace(face)

ctx.SetRGB(0., 0., 0.)
if layerNum == -1 {
ctx.DrawString(layer.Name, spacer, fontSize+spacer)
}

for i, key := range layout.Layout {
x, y := getKeyCoords(key)
drawBehavior(ctx, layer.Bindings[i], x+margin+3, y+margin*2.5, raw, layerNum)
newKeycap(x, y, keySize, keySize).fromKey(layer.Bindings[i], !raw).draw(ctx)
}
return nil
}
Expand All @@ -237,50 +246,124 @@ func getKeyCoords(key keyboard.Key) (float64, float64) {
return x, y
}

func drawBehavior(ctx *gg.Context, key *keymap.Behavior, x float64, y float64, raw bool, layerNum int) {
log.Debug().Str("Action", key.Action).Interface("Params", key.Params).Send()
type keycap struct {
x float64
y float64
w float64
h float64
base string
layer1 string
layer2 string
layer3 string
mod string
}

func newKeycap(x, y, w, h float64) *keycap {
return &keycap{x: x, y: y, w: w, h: h}
}

func (k *keycap) fromKey(key *keymap.Behavior, parseKeyCode bool) *keycap {
if key.Params == nil || len(key.Params) == 0 {
return
return k
}
leyend := key.Params[0]

k.base = formatKeyCode(key.Params[0], parseKeyCode)
if len(key.Params) > 1 {
leyend = key.Params[1]
k.base = formatKeyCode(key.Params[1], parseKeyCode)
k.mod = formatKeyCode(key.Params[0], parseKeyCode)
}
if key.Action == "mo" {
k.base = "L" + k.base
}
return k
}

func formatKeyCode(key *keymap.List, parseKeyCode bool) string {
str := ""
if leyend.KeyCode == nil {
str += fmt.Sprintf("%v", *leyend.Number)
} else if raw {
str += *leyend.KeyCode
if key.KeyCode == nil {
str += fmt.Sprintf("%v", *key.Number)
} else if parseKeyCode {
str += keymap.GetSymbol(*key.KeyCode)
} else {
str += keymap.GetSymbol(*leyend.KeyCode)
}

w, h := ctx.MeasureString(str)
dx, dy := 0., 0.
if layerNum == 1 {
ctx.SetHexColor("#5ff84a")
dx, dy = 38-w, 0
} else if layerNum == 2 {
ctx.SetHexColor("#f84a4a")
dx, dy = 38-w, 28
} else if layerNum == 3 {
ctx.SetHexColor("#482af8")
dx, dy = 0, 28
}
ctx.DrawString(str, x+dx, y-h/2.+dy)
if len(key.Params) > 1 {
ctx.SetHexColor("#eeeeee")
str := ""
if key.Params[0].KeyCode == nil {
str += fmt.Sprintf("%v", *key.Params[0].Number)
} else if raw {
str += *key.Params[0].KeyCode
} else {
str += keymap.GetSymbol(*key.Params[0].KeyCode)
str += *key.KeyCode
}
if strings.HasPrefix(str, "LC") {
str = "⌃" + str[3:len(str)-1]
}

return str
}

func (k *keycap) setLayer(layer int, key *keymap.Behavior, parseKeyCode bool) {
if key.Params == nil || len(key.Params) == 0 {
return
}
switch layer {
case 1:
k.layer1 = formatKeyCode(key.Params[0], parseKeyCode)
if key.Action == "mo" {
k.layer1 = "L" + k.layer1
}
case 2:
k.layer2 = formatKeyCode(key.Params[0], parseKeyCode)
if key.Action == "mo" {
k.layer2 = "L" + k.layer2
}
ctx.DrawString(str, x, y-h/2.+44)
ctx.SetRGB(0., 0., 0.)
case 3:
k.layer3 = formatKeyCode(key.Params[0], parseKeyCode)
if key.Action == "mo" {
k.layer3 = "L" + k.layer3
}
}
}

func (k *keycap) draw(ctx *gg.Context) {
log.Debug().Str("Base", k.base).Str("MOD", k.mod).Str("Layer1", k.layer1).Str("Layer2", k.layer2).Str("Layer3", k.layer3).Msg("Drawing key")
ctx.DrawRoundedRectangle(k.x, k.y, k.w, k.h, radius)

// Border
ctx.SetColor(color.Black)
ctx.StrokePreserve()

// Shadow
ctx.SetColor(color.RGBA{R: 0x88, G: 0x88, B: 0x88, A: 0xff})
ctx.Fill()

// Highlight
ctx.DrawRoundedRectangle(k.x+margin, k.y+4, k.w-(margin*2), k.h-(margin*2), radius/2.0)
ctx.SetColor(color.RGBA{R: 0xa7, G: 0xa7, B: 0xa7, A: 0xff})
ctx.Fill()

if k.base != "" {
ctx.SetColor(color.Black)
_, sh := ctx.MeasureString(k.base)
ctx.DrawString(k.base, k.x+margin+3, k.y+margin+sh)
}

if k.mod != "" {
sw, _ := ctx.MeasureString(k.mod)
ctx.SetColor(color.RGBA{R: 0xee, G: 0xee, B: 0xee, A: 0xff})
ctx.DrawString(k.mod, k.x+(k.w/2)-sw/2, k.y+k.h-3)
}

if k.layer1 != "" {
ctx.SetColor(color.RGBA{R: 0x05, G: 0x90, B: 0x33, A: 0xff})
sw, sh := ctx.MeasureString(k.layer1)
ctx.DrawString(k.layer1, k.x+k.w-sw-margin-3, k.y+margin+sh)
}

if k.layer2 != "" {
ctx.SetColor(color.RGBA{R: 0x05, G: 0x06, B: 0xb1, A: 0xff})
sw, sh := ctx.MeasureString(k.layer2)
ctx.DrawString(k.layer2, k.x+k.w-sw-margin-3, k.y+k.h-sh-margin)
}

if k.layer3 != "" {
ctx.SetColor(color.RGBA{R: 0xe3, G: 0x00, B: 0x52, A: 0xff})
_, sh := ctx.MeasureString(k.layer3)
ctx.DrawString(k.layer3, k.x+margin+3, k.y+k.h-sh-margin)
}

}

func maxX(l []keyboard.Key) float64 {
Expand Down

0 comments on commit 30a25d3

Please sign in to comment.