To be an image observer, you have to implement the single method,
imageUpdate(), defined by the
java.awt.image.ImageObserver interface:
public boolean imageUpdate(Image image, int flags, int x, int y,
int width, int height)imageUpdate() is called by the consumer, as needed,
to pass the observer information about the construction of its view of
the image. Essentially, any time the image changes, the consumer tells
the observer so that the observer can perform any necessary actions,
like repainting. image holds a reference to the
Image object the consumer is
processing. flags is an integer whose bits specify
what information about the image is now available. The values of the
flags are defined as static identifiers in the
ImageObserver interface, as shown in
Table 17.1.
| Flag | Description |
|---|---|
HEIGHT | The height of the image is ready. |
WIDTH | The width of the image is ready. |
FRAMEBITS | A frame is complete. |
SOMEBITS | An arbitrary number of pixels have arrived. |
ALLBITS | The image is complete. |
ABORT | The image loading has been aborted. |
ERROR | An error occurred during image processing; attempts to display the image will fail. |
The flags determine which of the other parameters,
x, y, width,
and height, hold valid data and what that data means. To
test whether a particular flag in the flags integer
is set, we have to resort to some binary shenanigans. The following
class, MyObserver, implements the
ImageObserver interface and prints its information
as it's called:
import java.awt.*;
import java.awt.image.*;
class MyObserver implements ImageObserver {
public boolean imageUpdate( Image image, int flags, int x, int y,
int width, int height) {
if ( (flags & HEIGHT) !=0 )
System.out.println("Image height = " + height );
if ( (flags & WIDTH ) !=0 )
System.out.println("Image width = " + width );
if ( (flags & FRAMEBITS) != 0 )
System.out.println("Another frame finished.");
if ( (flags & SOMEBITS) != 0 )
System.out.println("Image section :"
+ new Rectangle( x, y, width, height ) );
if ( (flags & ALLBITS) != 0 ) {
System.out.println("Image finished!");
return false;
}
if ( (flags & ABORT) != 0 ) {
System.out.println("Image load aborted...");
return false;
}
return true;
}
} The imageUpdate() method of
MyObserver is called by the consumer periodically,
and prints simple status messages about the construction of the
image. Notice that width and
height play a dual role. If
SOMEBITS is set, they represent the size of the
chunk of the image that has just been delivered. If
HEIGHT or WIDTH is set, however,
they represent the overall image dimensions. Just for amusement, we
have used the java.awt.Rectangle class to help us
print the bounds of a rectangular region.
imageUpdate() returns a
boolean value indicating whether or not it's
interested in future updates. If the image is finished or aborted,
imageUpdate() returns false to
indicate it isn't interested in further updates. Otherwise,
it returns true.
The following example uses MyObserver to print
information about an image as AWT loads it:
import java.awt.*;
public class ObserveImage extends java.applet.Applet {
Image img;
public void init() {
img = getImage( getClass().getResource(getParameter("img")) );
MyObserver mo = new MyObserver();
img.getWidth( mo );
img.getHeight( mo );
prepareImage( img, mo );
}
}
After requesting the Image object with
getImage(), we perform three operations on it to
kick-start the loading process. getWidth() and
getHeight() ask for the image's width and
height. If the image hasn't been loaded yet, or its size
can't be determined until loading is finished, our observer will
be called when the data is ready. prepareImage()
asks that the image be readied for display on the component. It's a
general mechanism for getting AWT started loading,
converting, and possibly scaling the image. If the image hasn't
been otherwise prepared or displayed, this happens asynchronously, and
our image observer will be notified as the data is constructed.
You may be wondering where the image consumer is, since we
never see a call to imageUpdate(). That's a
good question, but for now I'd like you to take it on faith that
the consumer exists. As you'll see later, image consumers are
rather mysterious objects that tend to hide beneath the surface of
image-processing applications. In this case, the consumer is hiding
deep inside the implementation of Applet.
You should be able to see how we could implement all sorts of
sophisticated image loading and tracking schemes. The two most obvious
strategies, however, are to draw an image progressively, as it's
constructed, or to wait until it's complete and draw it in its
entirety. We have already seen that the Component
class implements the first scheme. Another class,
java.awt.MediaTracker, is a general utility that
tracks the loading of a number of images or other media types for
us. We'll look at it next.