The AWT Imaging Model


JDK 1.0 introduced the AWT imaging model for downloading and drawing images. Back then, it was thought that the most common use of imaging would involve applets pulling graphics from the Web. A standard '90s example (with the exception of using JApplet) is shown in Example 5-1.

Example 5-1. ShowImage applet (Version 1) using Image
 import javax.swing.*; import java.awt.*; public class ShowImage extends JApplet {   private Image im;   public void init( )   {  im = getImage( getDocumentBase( ), "ball.gif");  }   public void paint(Graphics g)   {  g.drawImage(im, 0, 0, this);  } } 

The getdocumentBase( ) method returns the URL of the directory holding the original web document, and this is prepended to the image's filename to get a URL suitable for getImage( ).

The central problem with networked image retrieval is speed. Consequently, the Java designers considered it a bad idea to have an applet stop while an image crawled over from the server side. As a result, we have confusing behavior for getImage( ) and drawImage( ). Neither of these do what their name implies. The getImage( ) method is poorly named since it doesn't get (or download) the image at all; instead it prepares an empty Image object (im) for holding the image, returning immediately after that. The downloading is triggered by drawImage( ) in paint( ), which is called as the applet is loaded into the browser after init( ) has finished.

The fourth argument supplied to drawImage( ) is an ImageObserver (usually the applet or JFrame in an application), which will monitor the gradual downloading of the image. As data arrives, the Component's imageUpdate( ) is repeatedly called. imageUpdate( )'s default behavior is to call repaint( ), to redraw the image since more data are available, and return TRue. However, if an error has occurred with the image retrieval then imageUpdate( ) will return false. imageUpdate( ) can be overridden and modified by the programmer.

The overall effect is that paint( ) will be called repeatedly as the image is downloaded, causing the image to appear gradually on-screen. This effect is only noticeable if the image is coming over the network; if the file is stored locally, then it will be drawn in full almost instantaneously.

The result of this coding style means that the Image (im) contains no data until paint( ) is called and even then may not contain complete information for several seconds or minutes. This makes programming difficult: for instance, a GUI cannot easily allocate an on-screen space to the image since it has no known width or height until painting has started.

Since the introduction of JDK 1.0, experience has shown that most programs do not want graphics to be drawn incrementally during execution. For example, game sprites should be fully realized from the start.

The getImage( ) method is only for applets; there is a separate getImage( ) method for applications, accessible from Toolkit. For example:

     Image im = Toolkit.getDefaultToolkit( ).getImage("http://....");

As with the getImage( ) method for applets, it doesn't download anything. That task is done by paint( ).

The MediaTracker Class

Most programs (and most games) want to preload images before drawing them. In other words, we do not want to tie downloading to painting.

One solution is the java.awt.MediaTracker class: a MediaTracker object can start the download of an image and suspend execution until it has fully arrived or an error occurs. The init( ) method in the ShowImage class can be modified to do this:

     public void init( )     {       im = getImage( getDocumentBase( ), "ball.gif");       MediaTracker tracker = new MediaTracker(this);       tracker.addImage(im, 0);       try {         tracker.waitForID(0);       }       catch (InterruptedException e)       {  System.out.println("Download Error"); }     }

waitForID( ) starts the separate download thread, and suspends until it finishes. The ID used in the MediaTracker object can be any positive integer.

This approach means that the applet will be slower to start since init( )'s execution will be suspended while the image is retrieved.


In paint( ), drawImage( ) will only draw the image since a download is unnecessary. Consequently, drawImage( ) can be supplied with a null (empty) ImageObserver:

     drawImage(im, 0, 0, null);

A common way of accelerating the downloading of multiple images is to spawn a pool of threads, each one assigned to the retrieval of a single image. Only when every thread has completed will init( ) return.

ImageIcon

Writing MediaTracker code in every applet/application can be boring, so an ImageIcon class was introduced, which sets up a MediaTracker by itself. The ImageIcon name is a bit misleading: any size of image can be downloaded, not just an icon.

Using ImageIcon, the init( ) method becomes:

     public void init( )     { im = new ImageIcon( getDocumentBase( )+"ball.gif").getImage( );  }

The ImageIcon object can be converted to an Image (as here) or can be painted with ImageIcon's paintIcon( ) method.

The Rise of JARs

A JAR file is a way of packaging code and resources together into a single, compressed file. Resources can be almost anything, including images and sounds.

If an applet (or application) is going to utilize a lot of images, repeated network connections to download them will severely reduce execution speed. It's better to create a single JAR file containing the applet (or application) and all the images and to have the browser (or user) download it. Then, when an image comes to be loaded, it's a fast, local load from the JAR file. From a user's point of view, the download of the code takes a little longer, but it executes without any annoying delays caused by image loading.

At the end of Chapter 6, I'll explain how to package the ImagesTests code, and the large number of images it uses, as a JAR file. The only coding change occurs in specifying the location of an image file. Going back to a simpler example, the ImageIcon example from above would need to be rewritten this way:

     im = new ImageIcon( getClass( ).getResource("ball.gif") ).getImage( );

getClass( ) gets the Class reference for the object (e.g., ShowImage), and getresource( ) specifies the resource is stored in the same place as that class.

AWT Image Processing

It can be difficult to access the various elements of an Image object, such as pixel data or the color model. For instance, the image manipulation features in AWT are primarily aimed at modifying individual pixels as they pass through a filter. A stream of pixel data is sent out by a ImageProducer, passes through an ImageFilter, and on to an ImageConsumer (see Figure 5-1). This is known as the push model since stream data are "pushed" out by the producer.

Figure 5-1. Image processing in AWT


The two predefined ImageFilter subclasses are CropImageFilter for cropping regions of pixels and RGBImageFilter for processing individual pixels.

Chaining filters together is possible by making a consumer of one filter the producer for another.


This stream-view of filtering makes it difficult to process groups of pixels, especially ones that are noncontiguous. For example, a convolution operation for image smoothing would require a new subclass of ImageFilter and a new ImageConsumer to deal with the disruption to the pixels stream.

An alternative approach is to use the PixelGrabber class to collect all the pixel data from an image into an array, where it can then be conveniently processed in its entirety. The MemoryImageSource class is necessary to output the changed array's data as a stream to a specified ImageConsumer. The additional steps in the push model are shown in Figure 5-2.

Figure 5-2. Processing the image as an array


Modern Java code (since J2SE 1.2) can utilize the image processing capabilities of Java 2D, with its many predefined operations, so you're unlikely to meet the push model except in legacy code. If Java 2D is insufficient, then JAI should be considered.



Killer Game Programming in Java
Killer Game Programming in Java
ISBN: 0596007302
EAN: 2147483647
Year: 2006
Pages: 340

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