All formats

TwelveMonkeys ImageIO

LanguageJava 8 or later
LicenseBSD 3-Clause
Repositorygithub.com/haraldk/TwelveMonkeys
DocumentationBMP plugin wiki page
Depends onimageio-core and common-* modules of TwelveMonkeys (pulled in by Maven)
Read / writeICO yes / yes (BMP or PNG entries); CUR yes / no

TwelveMonkeys is a set of plugins for the standard javax.imageio API. The imageio-bmp module adds an ICO reader, an ICO writer and a CUR reader. Once the jar is on the classpath, ImageIO.read opens .ico files; each icon entry is one image index, so you can read all sizes through an ImageReader.

The writer builds multi-size icons with the ImageIO sequence API. Entries are stored as BMP by default, or as PNG with the BI_PNG compression type. Each image must be 256x256 or smaller, and a file holds at most 8 entries.

Installation#

Maven:

XML
<dependency>
    <groupId>com.twelvemonkeys.imageio</groupId>
    <artifactId>imageio-bmp</artifactId>
    <version>3.15.3</version>
</dependency>

Gradle:

implementation("com.twelvemonkeys.imageio:imageio-bmp:3.15.3")

In a web application, also add imageio-servlet and register IIOProviderContextListener so the plugins are found and released correctly.

Basic usage: read all sizes of an ICO file#

Java
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Iterator;

public class ListIcons {
    public static void main(String[] args) throws IOException {
        try (ImageInputStream in = ImageIO.createImageInputStream(new File("app.ico"))) {
            Iterator<ImageReader> readers = ImageIO.getImageReaders(in);
            if (!readers.hasNext()) throw new IOException("no ICO reader: add imageio-bmp");
            ImageReader reader = readers.next();
            try {
                reader.setInput(in);
                int n = reader.getNumImages(true);
                for (int i = 0; i < n; i++) {
                    BufferedImage img = reader.read(i);
                    System.out.printf("%d: %d x %d%n", i, img.getWidth(), img.getHeight());
                    ImageIO.write(img, "png", new File("icon-" + i + ".png"));
                }
            } finally {
                reader.dispose();
            }
        }
    }
}

How to convert ICO to PNG#

ImageIO.read returns the first entry. To get the largest, loop over the entries as above and keep the biggest; for a single-entry file this is enough:

Java
BufferedImage img = ImageIO.read(new File("favicon.ico"));
ImageIO.write(img, "png", new File("favicon.png"));

How to get icon sizes without decoding#

Java
reader.setInput(in);
for (int i = 0; i < reader.getNumImages(true); i++) {
    System.out.println(reader.getWidth(i) + " x " + reader.getHeight(i));
}

How to write a multi-size ICO#

Java
import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.stream.ImageOutputStream;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

public class WriteIco {
    public static void main(String[] args) throws IOException {
        String[] inputs = {"icon16.png", "icon32.png", "icon48.png", "icon256.png"};
        ImageWriter writer = ImageIO.getImageWritersByFormatName("ico").next();
        try (ImageOutputStream out = ImageIO.createImageOutputStream(new File("app.ico"))) {
            writer.setOutput(out);
            writer.prepareWriteSequence(null);
            for (String name : inputs) {
                BufferedImage img = ImageIO.read(new File(name));
                ImageWriteParam param = null;  // null: BMP entry
                if (img.getWidth() >= 256) {   // store the large entry as PNG
                    param = writer.getDefaultWriteParam();
                    param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
                    param.setCompressionType("BI_PNG");
                }
                writer.writeToSequence(new IIOImage(img, null, null), param);
            }
            writer.endWriteSequence();
        } finally {
            writer.dispose();
        }
    }
}

How to read a cursor hotspot#

Java
import com.twelvemonkeys.imageio.plugins.bmp.CURImageReader;
import java.awt.Point;

CURImageReader reader = new CURImageReader();
try (ImageInputStream in = ImageIO.createImageInputStream(new File("pointer.cur"))) {
    reader.setInput(in);
    Point hotspot = reader.getHotSpot(0);
    System.out.println("hotspot: " + hotspot.x + ", " + hotspot.y);
} finally {
    reader.dispose();
}

Tips#