All formats

Magick++ (ImageMagick)

LanguageC++ (MagickWand and MagickCore for C)
LicenseImageMagick License (Apache 2.0 style, permissive)
Repositorygithub.com/ImageMagick/ImageMagick
DocumentationMagick++ API
Depends onImageMagick (MagickCore, MagickWand); libpng for PNG entries
Read / writeyes / yes, every entry, BMP and PNG entries; CUR read (written files are always type 1, icon)

ImageMagick’s ICO coder reads every image in an ICO or CUR file, whether it’s stored as a BMP bitmap with an AND mask or as PNG. Each entry becomes one image in a list. When writing, each image in the list becomes one entry, stored as BMP by default (PNG only above 256x256). Set icon:png-compression-size=256 to store the 256x256 entry as PNG, which makes the file much smaller. The icon:auto-resize define generates all sizes from one image.

Magick++ is the C++ API. It’s a large dependency for icon work alone, but it also converts icons to and from any other format ImageMagick knows.

Installation#

Shell
# Debian / Ubuntu
sudo apt install libmagick++-dev
# Fedora
sudo dnf install ImageMagick-c++-devel
# macOS
brew install imagemagick
# Windows: the installer from imagemagick.org, with "Install development headers and libraries"

Compile with pkg-config:

Shell
g++ -o icotool icotool.cpp $(pkg-config --cflags --libs Magick++)

Basic usage: list the sizes in an ICO file#

C++
#include <Magick++.h>
#include <iostream>
#include <vector>

int main(int argc, char **argv) {
    Magick::InitializeMagick(*argv);
    try {
        std::vector<Magick::Image> entries;
        Magick::readImages(&entries, "app.ico");  // one Image per ICONDIRENTRY
        for (size_t i = 0; i < entries.size(); i++) {
            const Magick::Image &img = entries[i];
            std::cout << i << ": " << img.columns() << " x " << img.rows()
                      << ", depth " << img.depth() << "\n";
        }
    } catch (const Magick::Exception &e) {
        std::cerr << e.what() << "\n";
        return 1;
    }
    return 0;
}

How to convert ICO to PNG#

Save the largest entry:

C++
std::vector<Magick::Image> entries;
Magick::readImages(&entries, "app.ico");
size_t best = 0;
for (size_t i = 1; i < entries.size(); i++)
    if (entries[i].columns() * entries[i].rows() > entries[best].columns() * entries[best].rows())
        best = i;
entries[best].write("app.png");

To save every size, write each image in a loop (entries[i].write("icon-" + std::to_string(i) + ".png")).

How to create a multi-size favicon.ico from one image#

icon:auto-resize makes ImageMagick resize the image to each listed size (16 to 512). Without a list it writes 256, 192, 128, 96, 64, 48, 40, 32, 24 and 16. Start from a square image:

C++
Magick::Image logo("logo.png");
logo.resize(Magick::Geometry(256, 256));
logo.defineValue("icon", "auto-resize", "256,64,48,32,16");
logo.defineValue("icon", "png-compression-size", "256");  // 256x256 entry as PNG
logo.write("favicon.ico");

How to build an ICO from hand-drawn sizes#

When you have a separate image per size (sharper than scaling), put them in a list and write it once:

C++
std::vector<Magick::Image> sizes;
for (const char *f : {"icon16.png", "icon32.png", "icon48.png", "icon256.png"})
    sizes.emplace_back(f);
Magick::writeImages(sizes.begin(), sizes.end(), "app.ico");

Each image must be 512x512 or smaller; keep to 256x256 for Windows.

Tips#