When I read a PNG image in Java using javax.imageio.ImageIO.read()
, the resulting BufferedImage
is of TYPE_3BYTE_BGR
or TYPE_4BYTE_ABGR
depending on transparency.
I'm processing very large images (64+ megapixels), and need them in TYPE_INT_RGB
/ TYPE_INT_ARGB
format, which requires an expensive and very-large-chunk-of-memory hogging repainting of the image onto a new image in the correct format, which is causing OOMs.
It would be much better if I could somehow persuade ImageIO
to read the image in the desired format from the get-go - is there any way of doing that? Thanks!
Yes, it is possible to read into a predefined type of BufferedImage
, given that the type is supported by and compatible with the reader plugin. Most often the TYPE_#BYTE_*
types are compatible with the TYPE_INT_*
types, and this is the case for the standard PNGImageReader
.
To make it work, you need access to the ImageReader
directly, and use the read
method that takes an ImageReadParam
to control the type of image. It's possible to read into a pre-allocated image by using the ImageReadParam.setDestination(..)
method, or to just specify the type of image and let the reader plugin allocate it for you by using ImageReadParam.setDestinationType(..)
like I will show below.
Here's a short stand-alone code sample that shows how to read into a specific image type:
public static void main(String[] args) throws IOException {
File input = new File(args[0]);
try (ImageInputStream stream = ImageIO.createImageInputStream(input)) {
// Find a suitable reader
Iterator<ImageReader> readers = ImageIO.getImageReaders(stream);
if (!readers.hasNext()) {
throw new IIOException("No reader for " + input);
}
ImageReader reader = readers.next();
try {
reader.setInput(stream);
// Query the reader for types and select the best match
ImageTypeSpecifier intPackedType = getIntPackedType(reader);
System.out.println("intPackedType = " + intPackedType);
// Pass the type to the reader using read param
ImageReadParam param = reader.getDefaultReadParam();
param.setDestinationType(intPackedType);
// Finally read the image
BufferedImage image = reader.read(0, param);
System.out.println("image = " + image);
}
finally {
reader.dispose();
}
}
}
private static ImageTypeSpecifier getIntPackedType(ImageReader reader) throws IOException {
Iterator<ImageTypeSpecifier> types = reader.getImageTypes(0);
while (types.hasNext()) {
ImageTypeSpecifier spec = types.next();
switch (spec.getBufferedImageType()) {
case BufferedImage.TYPE_INT_RGB:
case BufferedImage.TYPE_INT_ARGB:
return spec;
default:
// continue searching
}
}
return null;
}
Sample output from one of my runs using a PNG as input:
intPackedType = javax.imageio.ImageTypeSpecifier$Packed@707084ba
image = BufferedImage@45ff54e6: type = 2 DirectColorModel: rmask=ff0000 gmask=ff00 bmask=ff amask=ff000000 IntegerInterleavedRaster: width = 100 height = 100 #Bands = 4 xOff = 0 yOff = 0 dataOffset[0] 0
Where type = 2
means BufferedImage.TYPE_INT_ARGB
.