1.124 Lecture 16 11/10/998

Working with Images

Contents

1. Loading and Displaying Images

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

Images provide a way to augment the aethetic appeal of a Java program.  The Java AWT provides support for two common image formats: GIF and JPEG.   An image that is in one of these formats can be loaded by either using 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 either the getImage() methods in the Applet class or the getImage() methods in the Toolkit class.
 

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 Graphics object's drawImage() methods.  We usually do this inside the paint() method of an Applet subclass or a Canvas subclass that we have written.

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)
 

Note on ImageObservers

The ImageObserver argument is only important if we are interested in tracking the loading of an image.  For small images, we usually do not bother to track image loading.  However, for a large background image we would probably want to track image loading, so that we can ensure that we only draw the image once it has finished loading.

If we are not interested in tracking image loading, we can pass in a null argument to drawImage().

The ImageObserver argument is an object of a class that implements the ImageObserver interface.  This interface requires the object's class to implement a method named

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

which will be called whenever an interesting milestone in the image loading process is reached.  The ImageObserver interface is implemented by all AWT components, and we can override it if we wish.
 

An example

To view this example, visit http://web.mit.edu/1.124/Lecture-code/L16/Image/rocket.html .  In this example, we are not concerned with tracking the loading of the image.

import java.awt.*;
import java.applet.Applet;

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

public class ImageDisplayer extends Applet {
    Image image;

    public void init() {
        image = getImage(getCodeBase(), "rocketship.gif");
    }

    public void paint(Graphics g) {
        //Draw image at its natural size first.
        g.drawImage(image, 0, 0, null); // 85x62 image.

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

2. Double Buffering

(Ref. Java Tutorial: http://java.sun.com/docs/books/tutorial/ui/drawing/noFlashing.html)

One of the problems with animations is how to make them appear smooth.  The approach that we have used up to now is likely to produce undesirable flashing, similar to that shown in the checkerboard example below:

http://java.sun.com/docs/books/tutorial/ui/drawing/animGraphics.html

The main cause of the flashing is that every call to the paint() method is being preceded by a call to clear the entire component with the background color.  This is the default behavior of the AWT.  Every time we call the repaint() method of a component, the AWT responds by calling the component's update() method.  The default update() method clears the background and then calls the paint() method.
 

Overriding the update() method

The first step toward producing a smooth animation is to provide our own implementation of update().  By doing this, we prevent the flashing that was caused by the automatic clearing of the background.  The responsibility is now on us to draw everything (including any portion of the background) that needs to be drawn.   The trick is not to do any unnecessary drawing of the background.

It is still necessary to provide a paint() method, since paint() will be called directly whenever an area of the window that was previously hidden is exposed.  Thus, we restructure our code as follows:
 

    public void update( Graphics g) {
        // Code that is necessary to draw any portion of the
        // background that needs to be redrawn goes here.

        // All code that used to be in the paint() method goes here.
    }
 

    public void paint(Graphics g) {
         update(g);
    }
 

This eliminates the flashing in the checkerboard example:

http://java.sun.com/docs/books/tutorial/ui/drawing/update.html

However, there are still two problems:

Double buffering

The idea behind double buffering is as follows:

The way to create an offscreen buffer using the AWT is to first create an Image that is a member variable of our component subclass.  We also provide another member variable to hold a Graphics object for the image.  This Graphics object is the one that we use to do all the drawing in the offscreen buffer.  We then use the Graphics object that was passed into the update() method to draw the offscreen image on to the screen.
 

    // Where the instance variables are declared:
    Dimension mOffDimension;
    Image mOffImage;
    Graphics mOffGraphics;

    public void update(Graphics g) {
        // Create the offscreen buffer, if necessary.  d is assumed to
        // hold the size of the onscreen drawing area:
        if ((mOffGraphics == null) ||
            (d.width != mOffDimension.width) ||
            (d.height != mOffDimension.height)) {
                mOffDimension = d;
                mOffImage = createImage(d.width, d.height);
                mOffGraphics = mOffImage.getGraphics();
            }

        // Code that clears all or part of the offscreen buffer, as necessary.
        // The following code clears the whole offscreen buffer.
        mOffGraphics.setColor(getBackground());
        mOffGraphics.fillRect(0, 0, d.width, d.height);
        mOffGraphics.setColor(Color.black);

        // All code that used to be in the original paint method goes here, except
        // that we use mOffGraphics instead of g.  For example:
        mOffGraphics.drawOval(50, 50, 10, 10);

        // Finally, copy the offscreen buffer on to the screen.
        g.drawImage(mOffImage, 0, 0, this);
    }
 

    public void paint(Graphics g) {
         update(g);
    }
 

Here is the checkerboard example modified to use double buffering.

http://java.sun.com/docs/books/tutorial/ui/drawing/doubleBuffer.html
 
 

3. Cutout Animations and Cartoon Style Animations

(Ref. Java Tutorial: http://java.sun.com/docs/books/tutorial/ui/drawing/movingImage.html and http://java.sun.com/docs/books/tutorial/ui/drawing/imageSequence.html)
 
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.  The following example illustrates this, and it also does double buffering.

http://web.mit.edu/1.124/Lecture-code/L16/CutoutAnimation/rocket.html
 

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

    // Where instance variables are declared:
    Image duke[10];

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

    // In the update() method:
    offGraphics.drawImage(images[frameNumber % 10], 0, 0, this);
 

http://web.mit.edu/1.124/Lecture-code/L16/CartoonAnimation/duke.html
 
 

4. Examples

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