Recipe 13.8 Drawing an Image


Problem

You want to display an image, a preformatted bitmap, or raster file.

Solution

Use the Graphics drawImage( ) method in your paint routine. Image objects represent bitmaps. They are normally loaded from a file via getImage( ) but can also be synthesized using createImage( ) . You can't construct them yourself, however: the Image class is abstract. Once you have an image, displaying it is trivial:

// File graphics/DrawImageDemo.java  public void paint(Graphics g) {      g.drawImage(0, 0, myImage, this);  }

Discussion

You can get an image by using a routine named, naturally, getImage( ) . If your code is used only in an applet, you can use the Applet method getImage( ), but if you want it to run in an application as well, you need to use the Toolkit version, which takes either a filename or a URL. The filename, of course, when it turns up in an applet, fails with a security exception unless the user installs a policy file. Program GetImage shows the code for doing this both ways:

/*  * For Applet, invoke as:  * <applet code="GetImage" width="100" height="100">  * </applet>  * For Application, just run it (has own main).  */ import java.awt.Graphics; import java.awt.Image; import java.net.URL; import javax.swing.JApplet; import javax.swing.JFrame; /** This program, which can be an Applet or an Application,  * shows a form of Toolkit.getImage( ) which works the same  * in either Applet or Application!  */ public class GetImage extends JApplet {         Image image;         public void init( ) {                 loadImage( );         }         public void loadImage( ) {                 // Applet-only version:                 // Image = getImage(getCodeBase( ), "Duke.gif");                                  // Portable version: getClass( ).getResource( ) works in either                 // applet or application, 1.1 or 1.3, returns URL for file name.                 URL url = getClass( ).getResource("Duke.gif");                 image = getToolkit( ).getImage(url);                 // Shorter portable version: same but avoids temporary variables                 // image = getToolkit( ).getImage(getClass( ).getResource("Duke.gif"));         }         public void paint(Graphics g) {                 g.drawImage(image, 20, 20, this);         }         public static void main(String[] args) {                 JFrame f = new JFrame("GetImage");                 f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);                 GetImage myApplet = new GetImage( );                 f.getContentPane( ).add(myApplet);                 myApplet.init( );                 f.setSize(100, 100);                 f.setVisible(true);                 myApplet.start( );         } }

You may sometimes want to display an image more than once in the same panel. Example 13-4 is a program that paints its background with the same image over and over. We use the image's getWidth( ) and getHeight( ) methods to find the image's size and the more regular getSize( ) method on the component itself. As usual, we don't hardcode the window size in the paint( ) method, since the user has the option of resizing with the mouse.

Example 13-4. TiledImageComponent.java
import com.darwinsys.util.WindowCloser; import java.awt.*; import java.awt.event.*; import java.net.*; /**  * Demo of Tiled Image  */ public class TiledImageComponent extends Container {     TextField nameTF, passTF, domainTF;     Image im;     String IMAGE_NAME = "background.gif";     /** Set things up nicely. */     public TiledImageComponent( ) {         Label l;         setLayout(new FlowLayout( ));         add(l = new Label("Name:", Label.CENTER));         add(nameTF=new TextField(10));         add(l = new Label("Password:", Label.CENTER));         add(passTF=new TextField(10));         passTF.setEchoChar('*');         add(l = new Label("Domain:", Label.CENTER));         add(domainTF=new TextField(10));         im = getToolkit( ).getImage(IMAGE_NAME);     }     /** paint( )  - just tile the background.  */     public void paint(Graphics g) {         // System.out.println("In paint( )");         if (im == null)             return;         int iw = im.getWidth(this), ih=im.getHeight(this);         if (iw < 0 || ih < 0)    // image not ready             return;                // live to try again later.         int w = getSize( ).width, h = getSize( ).height;         // System.out.println(iw + "," + ih + "; " + w + ", " + h);         for (int i = 0; i<=w; i+=iw) {             for (int j = 0; j<=h; j+=ih) {                 // System.out.println("drawImage(im,"+i+","+j+")");                 g.drawImage(im, i, j, this);             }         }     }     public static void main(String[] av) {         Frame f = new Frame("TiledImageComponent Demo");         f.add(new TiledImageComponent( ));         f.setSize(200, 200);         f.setVisible(true);         f.addWindowListener(new WindowCloser(f, true));     } }

In the paint( ) method, we must check that the image is not null and has a nonnegative width and height we are more careful than we were in the previous, somewhat cavalier, example. The image is null only if something went very wrong in the constructor, but it can have a negative size. How? In certain creation myths, time ran backward before the beginning of time; therefore, before an image is fully created, its size is backward, that is, it has a width and height of -1. The getImage( ) method doesn't actually get the image, you see. It creates the Image object, true, but it doesn't necessarily load all the bits: it starts a background thread to do the reading and returns. This dates from the days when the Web was slower and took a long time to fully load an image. In particular, with some image file formats (some kinds of TIFF files, perhaps), you don't know the actual image size until you've read the entire file. Thus, when getImage( ) returns, the Image object is created, but its size is set to -1, -1. Since two threads are now running (see Chapter 24), two outcomes are possible. Either the image-reading thread reads enough to know the width and height before you need them, or you need them before the thread reads enough to know them. The curious-looking code in paint( ) is defensive about this. You should be, too.

But what if you really need the size of the image, for example to lay out a larger panel? If you read a bit of the Image documentation, you might think you can use the prepareImage( ) method to ensure that the object has been loaded. Unfortunately, this method can get you stuck in a loop if the image file is missing because prepareImage never returns true! If you need to be sure, you must construct a MediaTracker object to ensure that the image has been loaded successfully. That looks something like this:

/**  * This CODE FRAGMENT shows using a MediaTracker to ensure  * that an Image has been loaded successfully, then obtaining  * its Width and Height. The MediaTracker can track an arbitrary  * number of Images; the "0" is an arbitrary number used to track  * this particular image.  */ Image im; int imWidth, imHeight; public void setImage(Image i) {     im = i;     MediaTracker mt = new MediaTracker(this);     // use of "this" assumes we're in a Component subclass.     mt.addImage(im, 0);     try {         mt.waitForID(0);     } catch(InterruptedException e) {         throw new IllegalArgumentException(             "InterruptedException while loading Image");     }     if (mt.isErrorID(0)) {         throw new IllegalArgumentException(                             "Couldn't load image");     }     imWidth  = im.getWidth(this);     imHeight = im.getHeight(this); }

You can ask the MediaTracker for its status at any time using the method status(int ID, boolean load), which returns an integer made by or-ing together the values shown in Table 13-1. The Boolean load flag, if true, tells the MediaTracker to start loading any images that haven't yet been started. A related method, statusAll( ) , returns the inclusive or of any flags applying to images that have started loading.

Table 13-1. MediaTracker status values

Flag

Meaning

ABORTED

Downloading of at least one item was aborted.

COMPLETE

Downloading of all items completed without error.

ERRORED

Something went wrong while downloading at least one item.

LOADING

Downloading is ongoing.


You can shorten the previous code by using the Swing ImageIcon class, which includes this functionality. The ImageIcon class has several constructor forms, one of which takes just a filename argument. ImageIcon uses a MediaTracker internally; you can ask for its status using the ImageIcon 's getImageLoadStatus( ) method, which returns the same values as MediaTracker 's statusAll( )/statusID( ).



Java Cookbook
Java Cookbook, Second Edition
ISBN: 0596007019
EAN: 2147483647
Year: 2003
Pages: 409
Authors: Ian F Darwin

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net