package main import ( "encoding/binary" "fmt" "image" "image/png" "os" "path/filepath" "golang.org/x/image/draw" ) func main() { in := filepath.Join("assets", "navis-icon.png") out := filepath.Join("assets", "navis.ico") f, err := os.Open(in) if err != nil { fatal(err) } defer f.Close() src, err := png.Decode(f) if err != nil { fatal(err) } sizes := []int{16, 32, 48, 64, 128, 256} type entry struct { size int png []byte } var entries []entry for _, size := range sizes { dst := image.NewRGBA(image.Rect(0, 0, size, size)) draw.CatmullRom.Scale(dst, dst.Bounds(), src, src.Bounds(), draw.Over, nil) tmp := filepath.Join(os.TempDir(), fmt.Sprintf("navis-%d.png", size)) tf, err := os.Create(tmp) if err != nil { fatal(err) } if err := png.Encode(tf, dst); err != nil { tf.Close() fatal(err) } tf.Close() b, err := os.ReadFile(tmp) _ = os.Remove(tmp) if err != nil { fatal(err) } entries = append(entries, entry{size: size, png: b}) } var blob []byte offset := 6 + 16*len(entries) for _, e := range entries { blob = append(blob, e.png...) } buf := make([]byte, offset+len(blob)) binary.LittleEndian.PutUint16(buf[0:], 0) // reserved binary.LittleEndian.PutUint16(buf[2:], 1) // icon binary.LittleEndian.PutUint16(buf[4:], uint16(len(entries))) dataOff := offset for i, e := range entries { dir := buf[6+i*16:] w, h := e.size, e.size if w >= 256 { dir[0], dir[1] = 0, 0 } else { dir[0], dir[1] = byte(w), byte(h) } dir[2] = 0 // colors dir[3] = 0 binary.LittleEndian.PutUint16(dir[4:], 1) // planes binary.LittleEndian.PutUint16(dir[6:], 32) binary.LittleEndian.PutUint32(dir[8:], uint32(len(e.png))) binary.LittleEndian.PutUint32(dir[12:], uint32(dataOff)) copy(buf[dataOff:], e.png) dataOff += len(e.png) } if err := os.WriteFile(out, buf, 0o644); err != nil { fatal(err) } fmt.Println("wrote", out) } func fatal(err error) { fmt.Fprintln(os.Stderr, err) os.Exit(1) }