| 1.124 Lecture 16 | 11/10/998 |
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.
// 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.
Toolkit toolkit = Toolkit.getDefaultToolkit();
Image image1 = toolkit.getImage("imageFile.gif");
Image image2 = toolkit.getImage(new URL("http://java.sun.com/graphics/people.gif"));
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);
}
}
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:
The idea behind double buffering is as follows:
// 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
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