icns

Easily create .icns files (Mac Icons) with this Go library or the included CLI.
Log | Files | Refs | LICENSE

commit 23ec0ba0840b4bef57b45266e6ea7482080dce46
parent 335a96d4f5a847f6dba50f5572b012bc2909d756
Author: Jack Mordaunt <jackmordaunt.dev@gmail.com>
Date:   Fri, 18 Sep 2026 16:09:33 -0400

docs: describe per-slot encoding and single icon decoding

Neither entry point is discoverable from the existing examples, and the slot
rather than pixel size keying needs saying: it is the part a reader would
otherwise get wrong. Payload is also the only route to a JPEG 2000 icon.

Diffstat:
Mdoc.go | 5+++++
Mreadme.md | 40++++++++++++++++++++++++++++++++++++++++
2 files changed, 45 insertions(+), 0 deletions(-)

diff --git a/doc.go b/doc.go @@ -15,6 +15,11 @@ // using this library from the command line. It supports piping, which is // something `iconutil` does not do, making it substantially easier to wrap. // +// Encode resizes one image into every size. EncodeSlots takes a drawing per +// slot, as an iconset directory holds, and resizes only the slots left empty. +// NewDecoder reads the other way, identifying the icons in a file so that a +// caller can decode only the size it wants. +// // Note: icns files are written with an icon at every size macOS draws, the // retina OSTypes for the larger ones and the colour and mask pair Apple still // uses at 16 and 32 pixels. diff --git a/readme.md b/readme.md @@ -98,6 +98,10 @@ Standard `icnsify -i icon.icns -o icon.png` +From an iconset directory, which uses each drawing where it is given instead of resizing one image for every size + +`icnsify -i MyIcon.iconset -o MyIcon.icns` + ## Library `go get github.com/jackmordaunt/icns/v4` @@ -124,6 +128,42 @@ func main() { } ``` +### Per-size artwork + +Icons are usually hand tuned at the small sizes rather than reduced from the large one. `EncodeSlots` takes a drawing per slot and resizes only the slots left empty, filling them from the largest image given. + +```go +images := map[icns.Slot]image.Image{ + {Points: 16, Scale: 1}: small, // icon_16x16.png + {Points: 512, Scale: 2}: large, // icon_512x512@2x.png +} +if err := icns.NewEncoder(dest).EncodeSlots(images); err != nil { + log.Fatalf("encoding icns: %v", err) +} +``` + +A slot is a size and a display scale, because 16x16@2x and 32x32 are both 32 pixels of artwork but fill different elements. `icns.Slots()` lists the ten a file holds and `icns.ParseSlot` reads iconset file names. + +### Reading one size + +`NewDecoder` identifies the icons without decoding any of them, so reading a single size does not pay for the rest. + +```go +d, err := icns.NewDecoder(src) +if err != nil { + log.Fatalf("reading icns: %v", err) +} +for _, icon := range d.Icons() { // Largest first. + if icon.Size > 128 { + continue + } + img, err := icon.Decode() + ... +} +``` + +`Entry.Payload` returns the bytes the file stores, which is how to reach a JPEG 2000 icon: this package identifies that format but cannot decode it. + ## Development The repository is a Go workspace of three modules: