Image Processing

Contents:
ImageObserver
ColorModel
ImageProducer
ImageConsumer
ImageFilter

The image processing parts of Java are buried within the java.awt.image package. The package consists of three interfaces and eleven classes, two of which are abstract. They are as follows:

The classes in the java.awt.image package let you create Image objects at run-time. These classes can be used to rotate images, make images transparent, create image viewers for unsupported graphics formats, and more.

ImageObserver

As you may recall from Simple Graphics, the last parameter to the drawImage() method is the image's ImageObserver. However, in Simple Graphics I also said that you can use this as the image observer and forget about it. Now it's time to ask the obvious questions: what is an image observer, and what is it for?

Because getImage() acquires an image asynchronously, the entire Image object might not be fully loaded when drawImage() is called. The ImageObserver interface provides the means for a component to be told asynchronously when additional information about the image is available. The Component class implements the imageUpdate() method (the sole method of the ImageObserver interface), so that method is inherited by any component that renders an image. Therefore, when you call drawImage(), you can pass this as the final argument; the component on which you are drawing serves as the ImageObserver for the drawing process. The communication between the image observer and the image consumer happens behind the scenes; you never have to worry about it, unless you want to write your own imageUpdate() method that does something special as the image is being loaded.

If you call drawImage() to display an image created in local memory (either for double buffering or from a MemoryImageSource), you can set the ImageObserver parameter of drawImage() to null because no asynchrony is involved; the entire image is available immediately, so an ImageObserver isn't needed.

ImageObserver Interface

Constants

The various flags associated with the ImageObserver are used for the infoflags argument to imageUpdate(). The flags indicate what kind of information is available and how to interpret the other arguments to imageUpdate(). Two or more flags are often combined (by an OR operation) to show that several kinds of information are available.

Method

Overriding imageUpdate

Instead of bothering with the MediaTracker class, you can override the imageUpdate() method and use it to notify you when an image is completely loaded. Example 12.1 demonstrates the use of imageUpdate(), along with a way to force your images to load immediately. Here's how it works: the init() method calls getImage() to request image loading at some time in the future. Instead of waiting for drawImage() to trigger the loading process, init() forces loading to start by calling prepareImage(), which also registers an image observer. prepareImage() is a method of the Component class discussed in Components.

The paint() method doesn't attempt to draw the image until the variable loaded is set to true. The imageUpdate() method checks the infoflags argument to see whether ALLBITS is set; when it is set, imageUpdate() sets loaded to true, and schedules a call to paint(). Thus, paint() doesn't call drawImage() until the method imageUpdate() has discovered that the image is fully loaded.

Example 12.1: imageUpdate Override.

import java.applet.*; import java.awt.*; import java.awt.image.ImageObserver;
public class imageUpdateOver extends Applet {
 Image image; boolean loaded = false;
public void init () {
 image = getImage (getDocumentBase(), "rosey.jpg"); prepareImage (image, -1, -1, this);
}
public void paint (Graphics g) {
 if (loaded) g.drawImage (image, 0, 0, this);
}
public void update (Graphics g) {
 paint (g);
}
public synchronized boolean imageUpdate (Image image, int infoFlags, int x, int y, int width, int height) {
 if ((infoFlags & ImageObserver.ALLBITS) != 0) {
 loaded = true; repaint();
return false;
}
else {
 return true;
}
}
}

Note that the call to prepareImage() is absolutely crucial. It is needed both to start image loading and to register the image observer. If prepareImage() were omitted, imageUpdate() would never be called, loaded would not be set, and paint() would never attempt to draw the image. As an alternative, you could use the MediaTracker class to force loading to start and monitor the loading process; that approach might give you some additional flexibility.