We mentioned an Chapter
6 : Java : Images that an image is not actually loaded when
getImage()is
invoked. Only when an attempt is made to access the image, such
as an invocation of drawImage()
or getWidth()
will the loading of the image begin.
Once the loading begins, it is, of course, not instantaneous. It
can in fact take considerable time if the loading takes place over
a slow network link. The loading is controlled by an internal thread
so that other processing can continue. We look here a three approaches
to loading images.
- ImageObserver
- override the method imageUpdate
() and monitor the loading "manually"
- MediaTracker
- tool to monitor the loading of one or more images
- ImageIcon
- a useful class to load a single image.
See also the discussion in Chapter
11: Supplements: Creating & Saving Images about using classes
in the javax.imageio
package for reading/writing instances of BufferedImage.
Monitoring
with an ImageObserver
If no other steps are taken, a call to drawImage()
will return immediately while the image continues to be periodically
updated as more pixels arrive. The last argument to drawImage()
is a reference to an object that implements the ImageObserver
interface:
drawImage
(Image img, int x, int y, ImageObserver iob)
The image loading thread periodically calls
imageUpdate() in the ImageObserver
object to provide status reports on the loading:
public
boolean imageUpdate (Image img, int info_flags,
int x, int y, int w, int h)
This method receives information via the bit settings in the info_flags
argument and from the dimension values. For the situation of multiple
images loading, you can identify which image is being updated from
the reference value in the first argument.
The method imageUpdate()
returns true
when the loading is not yet finished. See the ImageObserver
class description in the API
Specifications and the Constant
Field Values for more details about the info_flags
values passed in the method's arguments.
You could create an ImageObserver
class just for monitoring the image loading but usually you just
put "this"
in the last argument, as in
g.drawImage
(img, x, y, this)
and rely on the default imageUpdate()
of the Applet
class, which inherits this method from Component.
The Component
class implements this interface and provides a default imageUpdate()
method..
You can also override imageUpdate()
and monitor the loading yourself. The program ImageObsApplet
below shows one way to implement imageUpdate().
The applet's start()
method starts a thread that will periodically check a flag - fDoneLoadingImage
- that indicates whether the image has finished loading.
In the run()
method, the invocation of getWidth(ImageObserver)
for the image initiates the loading process. The image loading machinery
in the AWT will periodically call back to the ImageObserver
object passed in the argument to getWidth().
It invokes the imageUpdate()
method and passes the image information described above.
As long as info_flags
!= ALLBITS then the method returns true.
When info_flags
== ALLBITS then the flag fDoneLoadingImage
is set to true,
which will cause the loop in the run()
method to finish and the thread to die. The imageUpdate()
will return false and it will no longer be invoked.
ImageObsApplet.java
Resources: 07-JG-01-pan-A074R1_br2.jpg
|
import
javax.swing.*;
import java.awt.*;
/** Illustrate use of ImageObserver for image loading. **/
public class ImageObsApplet extends JApplet
implements Runnable
{
// Need a reference to the panel for the
// thread loop.
DrawingPanel fDrawingPanel;
Image fImg;
int fImageNum = 0;
String fMessage ="Loading...";
boolean fDoneLoadingImage = false;
/** Use a ImageObserver to load an image.**/
public void init () {
Container content_pane = getContentPane
();
// Create an instance of DrawingPanel
fDrawingPanel = new DrawingPanel
(this);
// Add the DrawingPanel to the contentPane.
content_pane.add (fDrawingPanel);
// Get image and monitor its loading.
fImg = getImage (getCodeBase (),
"07-JG-01-pan-A074R1_br2.jpg" );
} // init
/**
* Start the thread to monitor the
image loading.
**/
public void start () {
Thread thread = new Thread (this);
thread.start ();
}
/** Use a thread to wait for the image to load
* before painting it.
**/
public void run () {
// Checking the image width will
initiate the loading.
int width = fImg.getWidth (this);
// If the width is not equal to
-1 then the file has already
// been loaded. This can happen
if the applet page was loaded
// once before and then loaded again
into the browser.
if (width >= 0) {
fDoneLoadingImage
= true;
repaint
();
return;
}
// Wait if the image is not loaded
yet
while (!fDoneLoadingImage) {
try {
Thread.sleep
(500);
}
catch (InterruptedException
ie) {
}
// Repaint with either
the image or the text message
repaint ();
}
} // run
/** Override the ImageObserver imageUpdate method
and monitor
* the loading of the image. Set
a flag when it is loaded.
**/
public boolean imageUpdate (Image img, int info_flags,
int x, int y, int w, int h) {
if (info_flags != ALLBITS) {
// Indicates
image has not finished loading
// Returning
true will tell the image loading
// thread
to keep drawing until image fully
// drawn
loaded.
return true;
} else {
fDoneLoadingImage
= true;
return false;
}
} // imageUpdate
}// class ImageObsApplet
/** This JPanel subclass draws an image on the panel if
* the image is loaded. Otherwise, it draws a
text message.
**/
class DrawingPanel extends JPanel {
ImageObsApplet fParent = null;
DrawingPanel (ImageObsApplet parent) {
fParent = parent;
}// ctor
public void paintComponent (Graphics g) {
super.paintComponent (g);
// Add your drawing instructions
here
if (fParent.fDoneLoadingImage)
g.drawImage
(fParent.fImg,10,10,this);
else
g.drawString
(fParent.fMessage, 10,10);
} // paintComponent
} // class DrawingPanel
|
MediaTracker
A more powerful and elegant way to monitor image loading is to
use the MediaTracker
class. Below we see that an instance of the MediaTracker
is created and the image added to it. A thread is initiated to load
the image. The run()
waits for the tracker to signal that the image is loaded and then
the paint()
switches from drawing a string message to drawing the image.
MediaTracker
provides the method
int
status (int ID, boolean load)
which returns a status indicator in an integer value that is an
OR of four flags:
- ABORTED
- COMPLETE
- ERRORED
- LOADING
The MediaTracker
is especially useful when one needs to load many images. The statusAll()
returns a similar value as above except that it is an OR of the
status of all the images currently loading.
MediaTrackApplet.java
Resources: m20.gif
|
import
javax.swing.*;
import java.awt.*;
/** Illustrate use of MediaTracker for image loading. **/
public class MediaTrackApplet extends JApplet
implements Runnable
{
// Need a reference to the panel for the
// thread loop.
DrawingPanel fDrawingPanel;
// Parameters to track the image
MediaTracker fTracker;
Image fImg;
int fImageNum = 0;
boolean fShow = false;
String fMessage ="Loading...";
/** Use a MediaTracker to load an image.**/
public void init () {
Container content_pane = getContentPane
();
// Create an instance of DrawingPanel
fDrawingPanel = new DrawingPanel
(this);
// Add the DrawingPanel to the contentPane.
content_pane.add (fDrawingPanel);
// Get image and monitor its loading.
fImg = getImage (getCodeBase (),
"m20.gif.jpg" );
fTracker = new MediaTracker (this);
// Pass the image reference and
an ID number.
fTracker.addImage (fImg, fImageNum);
} // init
/** If the image not yet loaded, run the thread
* so the run() will monitor the
image loading.
**/
public void start () {
if (!fTracker.checkID (fImageNum)
) {
Thread thread
= new Thread (this);
thread.start
();
} else
// Unloading/reloading
web page can will leave
// checkID
true but fShow will be false.
fShow = true;
} // start
/** Use a thread to wait for the image to load
* before painting it.
**/
public void run () {
// Paint the loading message
repaint ();
// The wait for the image to finish
loading
try {
fTracker.waitForID (fImageNum
);
} catch (InterruptedException e)
{}
// Check if there was a loading
error
if (fTracker.isErrorID (fImageNum
))
fMessage=
"Error";
else
fShow =
true;
// Repaint with the image now if
it loaded OK
repaint ();
} // run
}// class MediaTrackApplet
/** This JPanel subclass draws an image on the panel if
* the image is loaded. Otherwise, it draws a
text message.
**/
class DrawingPanel extends JPanel {
MediaTrackApplet parent = null;
DrawingPanel (MediaTrackApplet parent) {
this.parent = parent;
}// ctor
public void paintComponent (Graphics g) {
super.paintComponent (g);
// Add your drawing instructions
here
if (parent.fShow)
g.drawImage
(parent.fImg,10,10,this);
else
g.drawString
(parent.fMessage, 10,10);
} // paintComponent
} // class DrawingPanel
|
See the API
Specifications for details of the MediaTracker
class.
Using
ImageIcon
to an Load Image
The ImageIcon
class offers a shortcut technque for loading an image. This class,
which came with Java 1.2 in the
java.swing package, was primarily intended
for obtaining small icon images on buttons and other components.
However, it can also be used to load any image file, regardless
of the size.
ImageIcon
uses a MediaTracker
internally so it provides a convenient way to load a single image
as illustrated by this snippet:.
...
ImageIcon img = new ImageIcon (url);
Image image = img.getImage ();
...
The ImageIcon
constructor will block (i.e. not return) until either the image
loads or the address is deemed invalid. In the later case an ImageIcon
object is still created but it will have zero dimensions. The method
getImageLoadStatus()
returns an integer with the status flags (see above) from the internal
MediaTracker.
References & Web Resources
Latest update: March 8, 2006
|