Previous | Next | Trail Map | Creating a User Interface (with Swing) | Working with Graphics

Improving the Appearance and Performance of Image Animation

You might have noticed two things about the animation on the previous page:

The problem of displaying partial images is easy to fix, using the MediaTracker class. MediaTracker also can decrease the amount of time that loading images takes. Another way to deal with the problem of slow image loading is to change the image format somehow; this page gives you some suggestions for doing so.

Using MediaTracker to Download Images and Delay Image Display

The MediaTracker class lets you easily download data for a group of images and find out when the images are fully loaded. Ordinarily, an image's data isn't downloaded until the image is drawn for the first time. To request that the data for a group of images be preloaded asynchronously, you can use the forms of checkID() and checkAll() that take a boolean argument, setting the argument to true. To load data synchronously (waiting for the data to arrive), use the waitForID() and waitForAll() methods. The MediaTracker methods that load data use several background threads to download the data, resulting in increased speed.

To check on the status of image loading, you can use the MediaTracker statusID() and statusAll() methods. To simply check whether any image data remains to be loaded, you can use the checkID() and checkAll() methods.

Here's a modified version of the example applet that uses the MediaTracker waitForAll() and checkAll() methods. Until every image is fully loaded, this applet simply displays a "Please wait..." message. See the MediaTracker documentation(in the API reference documentation) for an example that draws a background image immediately, but delays drawing the animated images.

Here is the applet in action:


Your browser can't run 1.0 Java applets. If it could, you'd see Duke waving at you.

Below is the changed code, which uses a MediaTracker to help delay image display. Differences are marked in a bold font.

...//Where instance variables are declared:
MediaTracker tracker;

...//In the init() method:
tracker = new MediaTracker(this);
for (int i = 1; i <= 10; i++) {
    images[i-1] = getImage(getCodeBase(),
                           "../../../images/duke/T"+i+".gif");
    tracker.addImage(images[i-1], 0);
}

...//At the beginning of the run() method:
try {
    //Start downloading the images. Wait until they're loaded.
    tracker.waitForAll();
} catch (InterruptedException e) {}

...//At the beginning of the update() method:
//If not all the images are loaded, just clear the background
//and display a status string.
if (!tracker.checkAll()) {
    g.clearRect(0, 0, d.width, d.height);
    g.drawString("Please wait...", 0, d.height/2);
}

//If all images are loaded, draw.
else {
    ...//same code as before...

Speeding Up Image Loading

Whether or not you use MediaTracker, loading images using URLs (as applets usually do) usually takes a long time. Most of the time is taken up by initiating HTTP connections. Each image file requires a separate HTTP connection, and each connection can take several seconds to initiate. The key to avoiding this performance hit is to combine the images in a single file. You can further improve performance by using some sort of compression scheme, especially one that's designed for moving images.

One simple way to combine images in a single file is to create an image strip -- a file that contains several images in a row. Here's an example of an image strip:

jack.gif:

To draw an image from the strip, you first set the clipping area to the size of one image. Then you draw the image strip, shifted to the left (if necessary) so that only the image you want appears within the clipping area. For example:

//imageStrip is the Image object representing the image strip.
//imageWidth is the size of an individual image in the strip.
//imageNumber is the number (from 0 to numImages) of the image to draw.
int stripWidth = imageStrip.getWidth(this);
int stripHeight = imageStrip.getHeight(this);
int imageWidth = stripWidth / numImages;
g.clipRect(0, 0, imageWidth, stripHeight);
g.drawImage(imageStrip, -imageNumber*imageWidth, 0, this);

If you want image loading to be faster still, you should look into image compression schemes, especially ones like Flic that perform inter-frame compression.


Previous | Next | Trail Map | Creating a User Interface (with Swing) | Working with Graphics