1.124 Lecture 16 11/2/2000

Working with Images

Contents

1. Loading and Displaying Images

(Ref. Java Tutorial: http://java.sun.com/docs/books/tutorial/uiswing/painting/usingImages.html)

Images provide a way to augment the aethetic appeal of a Java program.  Java provides support for two common image formats: GIF and JPEG.   An image that is in one of these formats can be loaded by using either a URL or a filename.

The basic class for representing an image is java.awt.Image.   Packages that are relevant to image handling are java.applet, java.awt and java.awt.image.
 

Loading an image

Images can be loaded using the getImage() method.  There are several versions of getImage().  When we create an applet by subclassing javax.swing.JApplet, we inherit the following methods from java.awt.Applet. These methods only work after the applet's constructor has been called.  A good place to call them is in the applet's init() method.  Here are some examples:

    // In a method in an Applet subclass, such as the init() method:
    Image image1 = getImage(getCodeBase(), "imageFile.gif");
    Image image2 = getImage(getDocumentBase(), "anImageFile.jpeg");
    Image image3 = getImage(new URL("http://java.sun.com/graphics/people.gif"));

In the first example, the code base is the URL of the directory that contains the applets .class file.  In the second example, the document base is the URL of the directory containing the HTML document that loads the applet.

Alternatively, we may use the getImage() methods provided by the Toolkit class.

This approach can be used either in an applet or an application. e.g.

    Toolkit toolkit = Toolkit.getDefaultToolkit();
    Image image1 = toolkit.getImage("imageFile.gif");
    Image image2 = toolkit.getImage(new URL("http://java.sun.com/graphics/people.gif"));

In general, applets cannot read files that are on the local machine for reasons of security.  Thus, applets typically download any images they need from the server.

Note that getImage() returns immediately without waiting for the image to load.  The image loading process occurs lazily, in that the image doesn't start to load until the first time we try to display it.
 

Displaying an image

Images can be displayed by calling one of the drawImage() methods supplied by the Graphics object that gets passed in to the paintComponent() method.

This version draws an image at the specified position using its natural size:

    boolean drawImage(Image img, int x, int y, ImageObserver observer)

This version draws an image at the specified position, and scales it to the specified width and height:

    boolean drawImage(Image img, int x, int y, int width, int height, ImageObserver observer)

The ImageObserver is a mechanism for tracking the loading of an image (see below).  One of the uses for an ImageObserver is to ensure that the image is properly displayed once it has finished loading.  The return value from drawImage() is rarely used: this value is true if the image has been completely loaded and thus completely painted, and false otherwise.

Here is a simple example of loading and displaying images.

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

// This applet displays a single image twice,
// once at its normal size and once much wider.

public class ImageDisplayer extends JApplet {
    static String imageFile = "images/rocketship.gif";

    public void init() {
        Image image = getImage(getCodeBase(), imageFile);
        ImagePanel imagePanel = new ImagePanel(image);
        getContentPane().add(imagePanel, BorderLayout.CENTER);
    }
}

class ImagePanel extends JPanel {
    Image image;

    public ImagePanel(Image image) {
        this.image = image;
    }

    public void paintComponent(Graphics g) {
        super.paintComponent(g);  // Paint background

        // Draw image at its natural size first.
        g.drawImage(image, 0, 0, this); //85x62 image

        // Now draw the image scaled.
        g.drawImage(image, 90, 0, 300, 62, this);
    }
}
 

2. Tracking Image Loading

The most frequent reason to track image loading is to find out when an image or group of images is fully loaded.  At a minimum, we will want to make sure that each image is redrawn after it finishes loading, otherwise only a part of the image will be visible.  We may even wish to wait until image loading is complete, before attempting to do any drawingat all.  There are two ways to track images: using the MediaTracker class and by implementing the ImageObserver interface.

Media trackers

(Ref. Java Tutorial: http://java.sun.com/docs/books/tutorial/uiswing/painting/improvingImageAnim.html)

The MediaTracker class provides a relatively simple way to delay drawing until the image loading process is complete.  We can modify the ImageDisplayer applet to perform the following steps:


import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

// This applet displays a single image twice,
// once at its normal size and once much wider.

public class ImageDisplayer extends JApplet {
    static String imageFile = "images/rocketship.gif";

    public void init() {
        Image image = getImage(getCodeBase(), imageFile);

        // Create a media tracker and add the image to it.  If we had several
        // images to load, they could all be added to the same media tracker.
        MediaTracker tracker = new MediaTracker(this);
        tracker.addImage(image, 0);

        // Start downloading the image and wait until it finishes loading.
        try {
            tracker.waitForAll();
        }
        catch(InterruptedException e) {}

        ImagePanel imagePanel = new ImagePanel(image, tracker);
        getContentPane().add(imagePanel, BorderLayout.CENTER);
    }
}

class ImagePanel extends JPanel {
    Image image;
    MediaTracker tracker;

    public ImagePanel(Image image, MediaTracker tracker) {
        this.image = image;
        this.tracker = tracker;
    }

    public void paintComponent(Graphics g) {
        super.paintComponent(g);  // Paint background

        // Check that the image has loaded before trying to draw it.
        if (!tracker.checkAll()) {
            g.drawString("Please wait...", 0, 0);
            return;
        }

        // Draw image at its natural size first.
        g.drawImage(image, 0, 0, this); //85x62 image

        // Now draw the image scaled.
        g.drawImage(image, 90, 0, 300, 62, this);
    }
}
 

Image observers

Image observers provide a way to track image loading even more closely.  In order to track image loading, we must pass in an object that implements the ImageObserver interface as the last argument to the Graphics object's drawImage() method.  The ImageObserver interface has a method named

    imageUpdate(Image img, int flags, int x, int y, int width, int height)

which will be called whenever an interesting milestone in the image loading process is reached.  The flags argument can be examined to determine exactly what this milestone is.  The ImageObserver interface defines the following constants, against which the flags argument can be tested using the bitwise AND operator:

    public static final int WIDTH;
    public static final int HEIGHT;
    public static final int PROPERTIES;
    public static final int SOMEBITS;
    public static final int FRAMEBITS;
    public static final int ALLBITS;
    public static final int ERROR;
    public static final int ABORT;

The java.awt.Component class implements the ImageObserver interface and provides a default version of imageUpdate(), which calls repaint() when the image has finished loading.  The following example shows how we could modify the ImageDisplayer applet, so that the ImagePanel class provides its own version of imageUpdate() instead of using the one that it inherits from java.awt.Component.  Note that we pass this as the last argument to drawImage().
 

import java.awt.*;
import java.awt.event.*;
import java.awt.image.ImageObserver;
import javax.swing.*;

// This applet displays a single image twice,
// once at its normal size and once much wider.

public class ImageDisplayer extends JApplet {
    static String imageFile = "images/rocketship.gif";

    public void init() {
        Image image = getImage(getCodeBase(), imageFile);
        ImagePanel imagePanel = new ImagePanel(image);
        getContentPane().add(imagePanel, BorderLayout.CENTER);
    }
}

class ImagePanel extends JPanel implements ImageObserver {
    Image image;

    public ImagePanel(Image image) {
        this.image = image;
    }

    public void paintComponent(Graphics g) {
        super.paintComponent(g);  // Paint background

        // Draw image at its natural size first.
        g.drawImage(image, 0, 0, this); //85x62 image

        // Now draw the image scaled.
        g.drawImage(image, 90, 0, 300, 62, this);
    }

    public boolean imageUpdate(Image image, int flags, int x, int y,
          int width, int height) {
        // If the image has finished loading, repaint the window.
        if ((flags & ALLBITS) != 0) {
            repaint();
            return false;  // Return false to say we don't need further notification.
        }
        return true;       // Image has not finished loading, need further notification.
    }
}
 

3. Image Animations

(Ref. Java Tutorial: http://java.sun.com/docs/books/tutorial/uiswing/painting/movingImage.html and http://java.sun.com/docs/books/tutorial/uiswing/painting/imageSequence.html)
 

Moving an image across the screen

The simplest type of image animation involves moving a single frame image across the screen.  This is known as cutout animation, and it is accomplished by repeatedly updating the position of the image in an animation thread, in a similar fashion to the bouncing ball animation we saw earlier.  For an example, see

http://java.sun.com/docs/books/tutorial/uiswing/painting/movingImage.html

Note that this example makes use of a Timer class to control the delay between animation frames, instead of the Thread.sleep() method that we used in the bouncing ball example.  The Timer class is used to trigger an ActionListener event handler at regular intervals.  This event handler is used to update the position of the image (via the frame number) and then issue a repaint() request.  A more detailed discussion of the use of the Timer class can be found at

http://java.sun.com/docs/books/tutorial/uiswing/painting/animLoop.html
 

Displaying a sequence of images

Another type of image animation is cartoon style animation, in which a sequence of image frames is displayed in succession.  The following example does this by creating an array of ten Image objects and then incrementing the array index every time the paintComponent() method is called.

http://java.sun.com/docs/books/tutorial/uiswing/painting/imageSequence.html

The portion of code that is of main interest is:

    // In initialization code.
    Image[] images = new Image[10];
    for (int i = 1; i <= 10; i++) {
        images[i-1] = getImage(getCodeBase(), "images/duke/T"+i+".gif");
    }

    // In the paintComponent method.
    g.drawImage(images[ImageSequenceTimer.frameNumber % 10], 0, 0, this);

This is a good example of why a MediaTracker should be used to delay drawing until after all the images have loaded.  The following modified example does this.

http://java.sun.com/docs/books/tutorial/uiswing/painting/example-swing/MTImageSequenceTimer.java
 

4. Examples

Here are some more examples of image animation: Check out http://java.sun.com/applets/index.html for other interesting applets.