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 BufferedImage
.
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.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With