icns

package module
v4.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 20, 2026 License: MIT Imports: 14 Imported by: 0

README

icns

CI Go Reference

Easily convert .jpg and .png to .icns with the command line tool icnsify, or use the library to convert from any image.Image to .icns.

go get github.com/jackmordaunt/icns/v4

icns files allow for high resolution icons to make your apps look sexy. The most common ways to generate icns files are:

  1. iconutil, which is a Mac native cli utility.
  2. ImageMagick which adds a large dependency to your project for such a simple use case.

With this library you can use pure Go to create icns files from any source image, given that you can decode it into an image.Image, without any heavyweight dependencies or subprocessing required. You can also use it to create icns files on windows and linux (thanks Go).

A small CLI app icnsify is provided allowing you to create icns files using this library from the command line. It supports piping, which is something iconutil does not do, making it substantially easier to wrap or chuck into a shell pipeline.

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. Decoding reaches further back than writing does. Alongside PNG it understands the is32, il32, ih32 and it32 colour and mask elements, the ARGB sidebar and toolbar icons, and the 1-, 4- and 8-bit indexed icons of System 7 through Mac OS 8. Where a file holds the same icon at several depths, the richest is returned first.

Elements that hold a whole image file are passed to image.Decode, so they read in whatever formats the program has registered. That is how a JPEG 2000 icon is handled: import a decoder for it and those icons start decoding, while a program that does not carries no codec and skips past them to a size it can read.

GUI

preview is a gui for displaying icns files cross-platform.

Go Tool
go install github.com/jackmordaunt/icns/cmd/preview@latest
Clone
git clone https://github.com/jackmordaunt/icns
cd icns/cmd/preview && go install .

Note: Gio cannot be cross-compiled right now, so there are no preview builds in releases. Note: preview has its own go.mod and therefore is versioned independently (unversioned).

preview

Windows Explorer thumbnails

cmd/shell-extension is a Windows shell extension that renders .icns thumbnails in Explorer. It is a COM server written in Go and built as a DLL; see its readme for build and registration steps.

Command Line

Go Tool
go install github.com/jackmordaunt/icns/v4/cmd/icnsify@latest
Scoop
scoop bucket add extras # Ensure bucket is added first
scoop install icnsify

Or from my personal bucket:

scoop bucket add jackmordaunt https://github.com/jackmordaunt/scoop-bucket 
scoop install jackmordaunt/icns # Name is defaulted to repo name. 
Winget
winget install icnsify
Brew
brew tap jackmordaunt/homebrew-tap # Ensure tap is added first.
brew install icnsify
Clone
git clone https://github.com/jackmordaunt/icns
cd icns && go install ./cmd/icnsify

Pipe it

cat icon.png | icnsify > icon.icns

cat icon.icns | icnsify > icon.png

Standard

icnsify -i icon.png -o icon.icns

icnsify -i icon.icns -o icon.png

Windows icons, which the output path names, or --format where there is no path to name them

icnsify -i icon.png -o icon.ico

cat icon.png | icnsify -f ico > icon.ico

Between the two icon formats, in either direction

icnsify -i icon.icns -o icon.ico

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

icnsify -i MyIcon.iconset -o MyIcon.ico

Library

go get github.com/jackmordaunt/icns/v4

func main() {
        pngf, err := os.Open("path/to/icon.png")
        if err != nil {
                log.Fatalf("opening source image: %v", err)
        }
        defer pngf.Close()
        srcImg, _, err := image.Decode(pngf)
        if err != nil {
                log.Fatalf("decoding source image: %v", err)
        }
        dest, err := os.Create("path/to/icon.icns")
        if err != nil {
                log.Fatalf("opening destination file: %v", err)
        }
        defer dest.Close()
        if err := icns.Encode(dest, srcImg); err != nil {
                log.Fatalf("encoding icns: %v", err)
        }
}
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.

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.

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, for handling an element yourself.

Windows icons

ico is a sibling package for the Windows .ico format, with the same shape as the icns API, so one mental model covers both.

import "github.com/jackmordaunt/icns/v4/ico"

if err := ico.Encode(dest, srcImg); err != nil {
        log.Fatalf("encoding ico: %v", err)
}

A file is written with an icon at 256, 128, 64, 48, 32, 24 and 16 pixels, skipping any larger than the source. The 256 is a PNG, since a bitmap at that size is a quarter of a megabyte on its own; the rest are 32-bit bitmaps with the one-bit mask Windows still reads. NewEncoder(dest).EncodeSizes(images) takes a drawing per size, as EncodeSlots does for icns.

Decoding reads PNG icons and bitmaps at 1, 4, 8, 24 and 32 bits per pixel, taking the colour table from the file and the alpha from the mask where the pixels carry none. NewDecoder, Icons, Entry.Decode and Entry.Payload work as their icns counterparts do, and the package registers itself with image.Decode.

icnsify writes .ico too, so the format is reachable from the command line without writing a program.

Development

The repository is a Go workspace of three modules:

Module Contents Why separate
github.com/jackmordaunt/icns/v4 (root) The library and cmd/icnsify Depends only on golang.org/x/image; one tag versions both
github.com/jackmordaunt/icns/cmd/preview The Gio GUI Keeps Gio's dependency tree out of library consumers' module graphs
github.com/jackmordaunt/icns/cmd/shell-extension The Windows DLL Windows-only and needs cgo (mingw)

go.work ties them together, so every build inside the checkout uses the working-tree library and gopls sees the whole repository. Note that ./... only matches the module you are in; to cover all three from the root, name them:

go test ./... ./cmd/preview/... ./cmd/shell-extension/...

The two command modules require the library at a published tag. Inside the workspace that requirement only shapes the module graph; the code always comes from the working tree, so the tag can lag behind without affecting development.

CI builds and tests the library on Linux, macOS and Windows, runs the encoder under the race detector, fuzzes the decoder, and reports gofmt, go vet and govulncheck. Two oracles check the formats against the systems that own them: iconutil on the macOS runner, and the Windows Imaging Component and System.Drawing on the Windows one. The shell extension is tested on Windows, where cgo can reach a C compiler, and preview is built on macOS and Windows, since Gio needs a long list of X11 and Wayland headers on Linux. Both command modules are also built with GOWORK=off, which is what go install sees.

Releasing: tag the root module (vX.Y.Z), which releases the library and icnsify together. When go install .../cmd/preview@latest, or a shell extension built outside the checkout, should pick up a newer library, bump that module's requirement and tidy it outside the workspace:

$env:GOWORK = 'off'; go get github.com/jackmordaunt/icns/v4@latest; go mod tidy; Remove-Item Env:GOWORK

Roadmap

  • Encoder: image.Image -> .icns
  • Command Line Interface
    • Encoding
    • Pipe support
    • Decoding
  • Implement Decoder: .icns -> image.Image
  • Symmetric test: decode(encode(img)) == img
  • Windows Explorer thumbnails
  • Windows .ico encoder and decoder

Coffee

If this software is useful to you, consider buying me a coffee!

https://liberapay.com/JackMordaunt

Documentation

Overview

Package icns implements an encoder for Apple's `.icns` file format. Reference: "https://en.wikipedia.org/wiki/Apple_Icon_Image_format".

icns files allow for high resolution icons to make your apps look sexy. The most common ways to generate icns files are 1. use `iconutil` which is a Mac native cli utility, or 2. use tools that wrap `ImageMagick` which adds a large dependency to your project for such a simple use case.

With this library you can use pure Go to create icns files from any source image, given that you can decode it into an `image.Image`, without any heavyweight dependencies or subprocessing required. You can also use this library to create icns files on windows and linux.

A small CLI app `icnsify` is provided to allow you to create icns files 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.

Index

Constants

View Source
const (
	// Nearest-neighbor interpolation
	NearestNeighbor = resample.NearestNeighbor
	// Bilinear interpolation
	Bilinear = resample.Bilinear
	// Bicubic interpolation (with cubic hermite spline)
	Bicubic = resample.Bicubic
	// Mitchell-Netravali interpolation
	MitchellNetravali = resample.MitchellNetravali
	// Lanczos interpolation (a=2)
	Lanczos2 = resample.Lanczos2
	// Lanczos interpolation (a=3)
	Lanczos3 = resample.Lanczos3
)

InterpolationFunction constants, ordered from fastest to highest quality.

Variables

View Source
var (
	// ErrInvalidHeader means the data does not begin with an icns header.
	ErrInvalidHeader = errors.New("invalid header for icns file")
	// ErrMalformed means an element's declared length disagrees with the
	// data present; the file is truncated or corrupt.
	ErrMalformed = errors.New("malformed icns file")
	// ErrNoIcons means the file is well formed but contains no icons of a
	// type this package understands.
	ErrNoIcons = errors.New("no icons found")
	// ErrUnsupportedFormat means every icon present uses an image format
	// this package cannot decode (JPEG 2000).
	ErrUnsupportedFormat = errors.New("unsupported image format")
)

Errors returned by the decoder. They are wrapped with detail, so compare with errors.Is.

Functions

func Decode

func Decode(r io.Reader) (image.Image, error)

Decode returns the largest icon in the icns file that can be decoded, ignoring all other sizes. An icon in a format no registered decoder reads is passed over, so the result may be smaller than the largest present.

func DecodeAll

func DecodeAll(r io.Reader) (images []image.Image, err error)

DecodeAll extracts every icon resolution present in the icns data that can be decoded. An icon in a format no registered decoder reads is ignored.

func Encode

func Encode(wr io.Writer, img image.Image) error

Encode writes img to wr in ICNS format. img is assumed to be a rectangle; non-square dimensions will be squared without preserving the aspect ratio. Uses nearest neighbor as interpolation algorithm.

Types

type Decoder added in v4.1.0

type Decoder struct {
	// contains filtered or unexported fields
}

Decoder reads an icns file and decodes its icons on demand, so a caller after one size does not pay for the rest.

func NewDecoder added in v4.1.0

func NewDecoder(r io.Reader) (*Decoder, error)

NewDecoder reads r and identifies the icons it holds without decoding any of their pixels.

func (*Decoder) Icons added in v4.1.0

func (d *Decoder) Icons() []Entry

Icons returns the icons in the file, largest first.

type Encoder

type Encoder struct {
	Wr        io.Writer
	Algorithm InterpolationFunction
}

Encoder encodes ICNS files from a source image.

func NewEncoder

func NewEncoder(wr io.Writer) *Encoder

NewEncoder initialises an encoder.

func (*Encoder) Encode

func (enc *Encoder) Encode(img image.Image) error

Encode icns with the given configuration.

func (*Encoder) EncodeSlots added in v4.1.0

func (enc *Encoder) EncodeSlots(images map[Slot]image.Image) error

EncodeSlots icns from artwork supplied per slot, so hand tuned art is used where it is given rather than resized from a single source.

func (*Encoder) WithAlgorithm

func (enc *Encoder) WithAlgorithm(a InterpolationFunction) *Encoder

WithAlgorithm applies the interpolation function used to resize the image.

type Entry added in v4.1.0

type Entry struct {
	IconDescription
	// contains filtered or unexported fields
}

Entry is one icon in an icns file, before its pixels are decoded.

func (Entry) Decode added in v4.1.0

func (e Entry) Decode() (image.Image, error)

Decode decodes the icon's pixels. The formats icns defines itself are decoded here; an element holding a whole image file is passed to image.Decode, so it is read by whatever the program has registered.

func (Entry) Payload added in v4.1.0

func (e Entry) Payload() []byte

Payload returns the bytes the file stores for the icon. For PNG and JPEG 2000 icons it is a complete image file; for the colour and mask types it is the run-length encoded colour planes, without the mask that holds their alpha.

The bytes are not copied, and must not be modified.

type ErrImageTooSmall

type ErrImageTooSmall struct {
	// contains filtered or unexported fields
}

ErrImageTooSmall is returned when the image is too small to process.

func (ErrImageTooSmall) Error

func (err ErrImageTooSmall) Error() string

type Icon

type Icon struct {
	Type  OsType
	Image image.Image
	// contains filtered or unexported fields
}

Icon encodes an icns icon.

func (*Icon) WriteTo

func (i *Icon) WriteTo(wr io.Writer) (int64, error)

WriteTo encodes the icon into wr.

type IconDescription

type IconDescription struct {
	OsType
	ImageFormat
}

IconDescription describes an icon.

func Probe

func Probe(r io.Reader) (desc []IconDescription, _ error)

Probe extracts descriptions of the icons in the icns, largest first.

func (IconDescription) String

func (desc IconDescription) String() string

type IconSet

type IconSet struct {
	Icons []*Icon
}

IconSet encodes a set of icons into an ICNS file.

func NewIconSet

func NewIconSet(img image.Image, interp InterpolationFunction) (*IconSet, error)

NewIconSet uses the source image to create an IconSet. If width != height, the image will be resized using the largest side without preserving the aspect ratio.

func NewIconSetFrom added in v4.1.0

func NewIconSetFrom(images map[Slot]image.Image, interp InterpolationFunction) (*IconSet, error)

NewIconSetFrom uses artwork supplied per slot to create an IconSet, which is what an iconset directory holds. A slot given artwork of the wrong size has it resized; a slot given none is filled from the largest image supplied, which also sets the largest icon written.

func (*IconSet) WriteTo

func (s *IconSet) WriteTo(wr io.Writer) (int64, error)

WriteTo writes the ICNS file to wr.

type ImageFormat

type ImageFormat int

ImageFormat specifies the type of image data associated with an icon.

const (
	ImageFormatPNG ImageFormat = iota
	ImageFormatJPEG2000
	// ImageFormatRGB is 24-bit colour in run-length encoded channel planes,
	// with alpha held in a separate mask element.
	ImageFormatRGB
	// ImageFormatARGB is run-length encoded channel planes that carry their
	// own alpha, behind an "ARGB" header.
	ImageFormatARGB
	// ImageFormatBitmap is one bit per pixel with a one bit mask.
	ImageFormatBitmap
	// ImageFormatIndexed is an index per pixel into a fixed colour table.
	ImageFormatIndexed
)

func (ImageFormat) String

func (f ImageFormat) String() string

type InterpolationFunction

type InterpolationFunction = resample.Function

InterpolationFunction is the algorithm used to resize the image.

type OsType

type OsType struct {
	ID   string
	Size uint
	// contains filtered or unexported fields
}

OsType is a 4 character identifier used to differentiate icon types.

func (OsType) String

func (t OsType) String() string

type Slot added in v4.1.0

type Slot struct {
	// Points is the size the icon is drawn at.
	Points uint
	// Scale is the display scale, 1 for a plain display and 2 for retina.
	Scale uint
}

Slot identifies an icon by the size it is drawn at and the display scale it is drawn for, the way an iconset names its files. Artwork for 16x16@2x and for 32x32 is 32 pixels either way, but the two fill different slots and need not be the same drawing.

func ParseSlot added in v4.1.0

func ParseSlot(name string) (Slot, bool)

ParseSlot reads an iconset file name, such as "icon_16x16@2x.png". The boolean reports whether the name is one.

func Slots added in v4.1.0

func Slots() []Slot

Slots returns the slots an encoded icns holds, largest artwork first.

func (Slot) Pixels added in v4.1.0

func (s Slot) Pixels() uint

Pixels returns the dimensions artwork for this slot has.

func (Slot) String added in v4.1.0

func (s Slot) String() string

String renders the slot the way an iconset names it, such as "16x16@2x".

Directories

Path Synopsis
cmd
icnsify command
wasm command
Command wasm exposes the icon encoders and decoders to a web page as the global "icns", so a conversion runs in the browser rather than on a server.
Command wasm exposes the icon encoders and decoders to a web page as the global "icns", so a conversion runs in the browser rather than on a server.
Package ico implements an encoder and decoder for the Windows icon format.
Package ico implements an encoder and decoder for the Windows icon format.
internal
resample
Package resample scales images for the icon formats in this module.
Package resample scales images for the icon formats in this module.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL
⌂ 换网址