Magick++ (ImageMagick)
| Language | C++ (MagickWand and MagickCore for C) |
|---|---|
| License | ImageMagick License (Apache 2.0 style, permissive) |
| Repository | github.com/ImageMagick/ImageMagick |
| Documentation | Magick++ API |
| Depends on | ImageMagick (MagickCore, MagickWand); libpng for PNG entries |
| Read / write | yes / 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#
# 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:
g++ -o icotool icotool.cpp $(pkg-config --cflags --libs Magick++)
Basic usage: list the sizes in an ICO file#
#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:
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:
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:
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#
- The command line does the same:
magick logo.png -define icon:auto-resize=256,48,32,16 -define icon:png-compression-size=256 favicon.ico, andmagick identify app.icolists the entries. magick app.ico[2] out.pngpicks one entry by index.- Set a resource limit (
Magick::ResourceLimits::memory(...)) before reading untrusted files.