All formats

go-ico

LanguageGo
LicenseBSD 3-Clause
Repositorygithub.com/sergeymakinen/go-ico
Documentationpkg.go.dev, cur subpackage
Depends ongithub.com/sergeymakinen/go-bmp
Read / writeyes / yes, BMP and PNG entries, CUR read and write

go-ico follows the style of the standard library’s image codecs: Decode returns the largest icon as an image.Image, DecodeAll returns every entry and DecodeConfig reads the size without decoding pixels. Importing the package registers the ico format, so image.Decode recognizes .ico files too.

Encode and EncodeAll write one or more images. 256x256 images are stored as 32-bit PNG and smaller ones as BMP, the layout Windows expects; images larger than 256x256 are rejected. The cur subpackage does the same for cursors and returns hotspots from DecodeAll.

Installation#

Shell
go get github.com/sergeymakinen/go-ico

Basic usage: convert ICO to PNG#

Go
package main

import (
	"image/png"
	"log"
	"os"

	"github.com/sergeymakinen/go-ico"
)

func main() {
	f, err := os.Open("app.ico")
	if err != nil {
		log.Fatal(err)
	}
	defer f.Close()

	img, err := ico.Decode(f) // the largest entry
	if err != nil {
		log.Fatal(err)
	}

	out, err := os.Create("app.png")
	if err != nil {
		log.Fatal(err)
	}
	defer out.Close()
	if err := png.Encode(out, img); err != nil {
		log.Fatal(err)
	}
}

How to read every size in an ICO file#

Go
f, err := os.Open("app.ico")
if err != nil {
	log.Fatal(err)
}
defer f.Close()

images, err := ico.DecodeAll(f)
if err != nil {
	log.Fatal(err)
}
for i, img := range images {
	b := img.Bounds()
	fmt.Printf("%d: %d x %d\n", i, b.Dx(), b.Dy())
}

How to get the icon size without decoding#

Go
cfg, err := ico.DecodeConfig(f) // size of the largest entry
if err != nil {
	log.Fatal(err)
}
fmt.Println(cfg.Width, cfg.Height)

How to create a multi-size ICO from PNG files#

Go
package main

import (
	"image"
	"image/png"
	"log"
	"os"

	"github.com/sergeymakinen/go-ico"
)

func load(name string) image.Image {
	f, err := os.Open(name)
	if err != nil {
		log.Fatal(err)
	}
	defer f.Close()
	img, err := png.Decode(f)
	if err != nil {
		log.Fatal(err)
	}
	return img
}

func main() {
	var images []image.Image
	for _, name := range []string{"icon16.png", "icon32.png", "icon48.png", "icon256.png"} {
		images = append(images, load(name))
	}

	out, err := os.Create("app.ico")
	if err != nil {
		log.Fatal(err)
	}
	defer out.Close()
	if err := ico.EncodeAll(out, images); err != nil {
		log.Fatal(err)
	}
}

To resize one large image to each size first, use golang.org/x/image/draw (draw.CatmullRom.Scale).

How to read a cursor and its hotspot#

Go
import "github.com/sergeymakinen/go-ico/cur"

f, err := os.Open("pointer.cur")
if err != nil {
	log.Fatal(err)
}
defer f.Close()

c, err := cur.DecodeAll(f)
if err != nil {
	log.Fatal(err)
}
for i, img := range c.Cursor {
	fmt.Printf("%v, hotspot %d,%d\n", img.Bounds().Size(), c.Hotspot[i].X, c.Hotspot[i].Y)
}

cur.Encode and cur.EncodeAll write cursors with the hotspot at 0,0.

Tips#