views:

139

answers:

3

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.

+2  A: 

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 BufferedImage.getRaster().getDataBuffer()).

Will
A: 

use Java readfully()

plan9assembler
This makes sense only if he wants the raw bytes from the PNG file, instead of the *decoded* image data.
uckelman
Which *is* what I want (I amended the question).
Wouter Lievens
+3  A: 

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.)

uckelman
+1 I like the detailed cases and caveats...
eee