I want to load (and decode) PNG images and convert them into a one-dimensional array in Java. I can obviously do this with ImageIO.read() and then copy the pixels into the array, but that consumes twice the memory (the raster + the final array) and it involves more processing time than I would like.
How should I go about this? Worst-case I can implement the PNG spec myself, but that seems like a rather involved task. Ideally I'd like a PNG implementation that I can "plug" into. Less ideal but still fine would be an easy to understand (unlike the com.sun code) PNG reader that I can (and would be allowed to) modify.
If what you're trying to do is get the pixel data as an array, then you can use
ImageIO.read() to get a
BufferedImage and then use
BufferedImage.getRaster().getDataBuffer() to get the
DataBuffer. From there, you need to check what type of
BufferedImage you have in order to determine how to cast the
DataBuffer. E.g. if you have a
TYPE_INT_RGB image, then you should cast to a
DataBufferInt and then you can call
DataBufferInt.getData() to retrieve the
int which contains the data. What you get this way is not a copy---it's the actual array backing the
However, there is a big caveat to all of this:
ImageIO.read() will often NOT give you the type of image you want. E.g., you'll frequently get TYPE_CUSTOM, which you can't do anything useful with except copy to the image type you actually want.
So, you'll only be able to get your image data via
ImageIO.read() without making a second copy if you're fortunate enough that
ImageIO.read() gives you the image in the format you want. If you want to check what types are available from
ImageIO, you can call
ImageIO.getImageReaders() to get an
Iterator over all of the readers which claim to be able to read your stream, and then you can use
ImageReader.getImageTypes(0) to get an
Iterator over image types which the reader can read. You might find a reader which will give you the image type you want this way, but don't hold your breath.
Unless your images are huge, copying them is actually pretty fast. If your images are huge, though, you might have to resort to using
BufferedImage.getRGB() to write the raw image data out to disk one row at a time (but do it compressed, use a
GZIPOutputStream), let the original image be garbage collected, create a new
BufferedImage of the desired type, and finally read the rows back from disk, writing them to the new image using
BufferedImage.setRGB(). (I wrote something to do this just a few days ago, hence the details are rather fresh in my mind. The LGPL'd source for it is here.)
Why do you want a 1D array? What is the bigger purpose?
On that hinges any recommendations.
You can usually decode a PNG to a memory-accessible bitmap, (
ImageIO.read() returns a
BufferedImage, which is precisely that) and just access its pixels directly ... as a 1D array (using
use Java readfully()