| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146 |
- // Command icon draws the MPCkit app icon: a 4x4 pad grid on the app's dark
- // background, with pad 1 (bottom left, as on the device) lit in the accent
- // colour. It writes a 1024x1024 PNG; `wails3 generate icons` makes the
- // Windows and macOS icons from it.
- //
- // go run ./tools/icon build/appicon.png
- //
- // With -dmg it writes the background of the macOS disk image instead: a
- // plain light 540x380 image (Finder labels the icons with dark text).
- //
- // go run ./tools/icon -dmg build/darwin/dmg-background.png
- package main
- import (
- "flag"
- "fmt"
- "image"
- "image/color"
- "image/png"
- "math"
- "os"
- )
- const (
- size = 1024
- ss = 4 // supersampling per axis, for smooth edges
- )
- // Colours from the app (frontend/src/app.css).
- var (
- bg = rgb(0x15, 0x17, 0x1A)
- edge = rgb(0x35, 0x3A, 0x43)
- pad = rgb(0x33, 0x38, 0x42)
- padEdge = rgb(0x5A, 0x61, 0x6E)
- accent = rgb(0xF5, 0xA5, 0x24)
- )
- func rgb(r, g, b uint8) color.NRGBA { return color.NRGBA{r, g, b, 0xFF} }
- type rect struct{ x0, y0, x1, y1, r float64 }
- // inside reports whether (x, y) is inside the rounded rectangle.
- func (q rect) inside(x, y float64) bool {
- if x < q.x0 || x >= q.x1 || y < q.y0 || y >= q.y1 {
- return false
- }
- cx := math.Max(q.x0+q.r, math.Min(x, q.x1-q.r))
- cy := math.Max(q.y0+q.r, math.Min(y, q.y1-q.r))
- return (x-cx)*(x-cx)+(y-cy)*(y-cy) <= q.r*q.r
- }
- func (q rect) inset(d float64) rect {
- return rect{q.x0 + d, q.y0 + d, q.x1 - d, q.y1 - d, math.Max(q.r-d, 0)}
- }
- type layer struct {
- shape rect
- col color.NRGBA
- }
- func main() {
- dmg := flag.Bool("dmg", false, "write the disk image background instead of the icon")
- flag.Parse()
- if flag.NArg() != 1 {
- fmt.Fprintln(os.Stderr, "usage: icon [-dmg] out.png")
- os.Exit(2)
- }
- if *dmg {
- img := image.NewNRGBA(image.Rect(0, 0, 540, 380))
- for i := 0; i < len(img.Pix); i += 4 {
- img.Pix[i], img.Pix[i+1], img.Pix[i+2], img.Pix[i+3] = 0xF2, 0xF3, 0xF5, 0xFF
- }
- write(flag.Arg(0), img)
- return
- }
- // Body: a rounded square with a margin, as macOS icons have; the
- // same image works for Windows and Linux.
- body := rect{64, 64, 960, 960, 200}
- layers := []layer{{body, edge}, {body.inset(10), bg}}
- // 4x4 pads, 140 px with 32 px gaps, centred in the body.
- const cell, gap = 140.0, 32.0
- start := (size - (4*cell + 3*gap)) / 2
- for row := 0; row < 4; row++ {
- for col := 0; col < 4; col++ {
- x := start + float64(col)*(cell+gap)
- y := start + float64(row)*(cell+gap)
- r := rect{x, y, x + cell, y + cell, 28}
- if row == 3 && col == 0 { // pad 1: bottom left
- layers = append(layers, layer{r, accent})
- continue
- }
- layers = append(layers, layer{r, padEdge}, layer{r.inset(8), pad})
- }
- }
- img := image.NewNRGBA(image.Rect(0, 0, size, size))
- for py := 0; py < size; py++ {
- for px := 0; px < size; px++ {
- var r, g, b, a float64
- for sy := 0; sy < ss; sy++ {
- for sx := 0; sx < ss; sx++ {
- x := float64(px) + (float64(sx)+0.5)/ss
- y := float64(py) + (float64(sy)+0.5)/ss
- // Topmost layer that covers the point wins.
- for i := len(layers) - 1; i >= 0; i-- {
- if layers[i].shape.inside(x, y) {
- c := layers[i].col
- r += float64(c.R)
- g += float64(c.G)
- b += float64(c.B)
- a++
- break
- }
- }
- }
- }
- if a == 0 {
- continue // transparent outside the body
- }
- img.SetNRGBA(px, py, color.NRGBA{
- uint8(r/a + 0.5), uint8(g/a + 0.5), uint8(b/a + 0.5),
- uint8(255*a/(ss*ss) + 0.5),
- })
- }
- }
- write(flag.Arg(0), img)
- }
- func write(path string, img image.Image) {
- f, err := os.Create(path)
- if err != nil {
- fmt.Fprintln(os.Stderr, err)
- os.Exit(1)
- }
- if err := png.Encode(f, img); err != nil {
- fmt.Fprintln(os.Stderr, err)
- os.Exit(1)
- }
- if err := f.Close(); err != nil {
- fmt.Fprintln(os.Stderr, err)
- os.Exit(1)
- }
- }
|