TwelveMonkeys ImageIO
| Language | Java 8 or later |
|---|---|
| License | BSD 3-Clause |
| Repository | github.com/haraldk/TwelveMonkeys |
| Documentation | BMP plugin wiki page |
| Depends on | imageio-core and common-* modules of TwelveMonkeys (pulled in by Maven) |
| Read / write | ICO 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:
<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#
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:
BufferedImage img = ImageIO.read(new File("favicon.ico"));
ImageIO.write(img, "png", new File("favicon.png"));
How to get icon sizes without decoding#
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#
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#
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#
- If
ImageIO.readreturnsnullfor an.icofile, the plugin isn’t on the classpath (or, in an app server, wasn’t registered: callImageIO.scanForPlugins()). - The writer accepts at most 256x256 per entry and throws
IIOExceptionfor larger images. It writes up to 8 entries per file. - Pass
nullas the write param for BMP entries: the default param isn’t in explicit compression mode, and the writer asks it for the compression type.