Images in Java
Sections in this page
Images in Java
Memory requirements
Let's get the size issue out of the way first. We have seen in another section that a typical photographic image these days occupies some 30 to 100 megabytes (Mb) of memory when it is loaded for processing. That has an implication for the JVM because by default that will allocate only 64Mb to an application when it starts and increase that in steps of 16Mb up to 128Mb when necessary. If it needs any more it will throw an OutOfMemoryError, which is fatal. To prevent this it is necessary to specify initial and maximum memory sizes in the command line for running the application. Eg,
java -Xms256m -Xmx1024m classname
would start with 256 Mb and be able to expand to 1024 Mb (= 1 Gb).
The class representing images
The class to instantiate for holding an image in memory is java.awt.image.BufferedImage. The Sun API documentation shows that this can hold various kinds of image with different numbers of channels and bits per channel. When loading an image from a disc file the type is set for us (see the image reading and writing page). It is important to ensure that when we come to display (a scaled down version of) the BufferedImage type it is compatible with the display hardware. Otherwise it can take a very long time for an image to be displayed or repainted. If we create an empty image it is necessary to use
java.awt.GraphicsEnvironment ge = java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment ();
java.awt.GraphicsDevice gd = ge.getDefaultScreenDevice ();
java.awt.GraphicsConfiguration gc = gd.getDefaultConfiguration ();
java.awt.image.BufferedImage bim = gc.createCompatibleImage (width, height);
Similarly, when cloning an image or creating a destination image for an operation on an already loaded image:
BufferedImage dstBim = new BufferedImage (srcBim.getColorModel (), srcBim.getRaster ().createCompatibleWritableRaster (dstWd, dstHt), srcBim.isAlphaPremultiplied (), null);
Rasters
Contained within every BufferedImage object is a java.awt.image.WritableRaster object containing the multi-channel arrays of pixel values. The getWritableRaster () method of BufferedImage gets a reference to the raster. If you only want to read the pixel values and want to avoid inadvertently changing them, use instead the getRaster () method of BufferedImage, which gets a read-only reference to the raster, of the super-type java.awt.image.Raster.
We use a common pattern throughout our software for reading and writing pixel data in rasters. Here is one of the simplest instances of the pattern, from class ImProcess:
public static void invert (BufferedImage im)
{
long startTime = System.nanoTime ();
WritableRaster wr = im.getRaster ();
int nb = getNBands (im);
int [] px = new int [nb];
int maxLevel = getMaxLevel (im);
for (int y = 0; y < wr.getHeight (); y++)
{
for (int x = 0; x < wr.getWidth (); x++)
{
wr.getPixel (x, y, px);
for (int b = 0; b < nb; b++)
{
px [b] = maxLevel - px [b];
}
wr.setPixel (x, y, px);
}
}
long estimatedTime = System.nanoTime () - startTime;
GRIP.getLogger ().info ("Improcess.invert () took " + estimatedTime + " ns.");
} // invert

