You have already seen how to build up simple images by drawing lines and shapes. Complex images, such as photographs, are usually generated externally, for example, with a scanner or special image-manipulation software. (As you will see in Volume 2, it is also possible to produce an image, pixel by pixel, and store the result in an array. This procedure is common for fractal images, for example.) Once images are stored in local files or someplace on the Internet, you can read them into a Java app and display them on Graphics objects. As of SDK 1.4, reading an image is very simple. If the image is stored in a local file, call

String filename = "...";
Image image = ImageIO.read(new File(filename));

Otherwise, you can supply a URL:

String urlname = "...";
Image image = ImageIO.read(new URL(urlname));

The read method throws an IOException if the image is not available. We will discuss the general topic of exception handling in . For now, our sample program just catches that exception and prints a stack trace if it occurs. Now the variable image contains a reference to an object that encapsulates the image data. You can display the image with the drawImage method of the Graphics class.

public void paintComponent(Graphics g)
{
 . . .
 g.drawImage(image, x, y, null);
}

takes this a little bit further and tiles the window with the graphics image. The result looks like the screen shown in . We do the tiling in the paintComponent method. We first draw one copy of the image in the top-left corner and then use the copyArea call to copy it into the entire window:

for (int i = 0; i * imageWidth <= getWidth(); i++)
 for (int j = 0; j * imageHeight <= getHeight(); j++)
 if (i + j > 0)
 g.copyArea(0, 0, imageWidth, imageHeight,
 i * imageWidth, j * imageHeight);

Window with tiled graphics image

Java graphics 07fig15Java graphics notes_icon

When coding with the basic AWT, you need to work harder to produce flicker-free images: you need to use buffering. With Swing, buffering is automatically done for you by default. (You can turn it off but there is rarely a reason to do so.) If you need to acquire an image for an AWT component, first assemble your drawing in a background image, then draw the background image.

Image buffered_image = createImage(width, height);
Graphics bg = buffered_image.getGraphics();
// all drawing commands that use bg fill the buffered_image
// . . .
// finally, draw the buffer g.drawImage(buffered_image, 0, 0, null);
bg.dispose();
buffered_image.flush();
Java graphics notes_icon

Earlier in this chapter, we used the getImage method of the Toolkit class to read an image file. However, that method was written with the assumption that an image may arrive slowly over a network connection. Instead of loading the file and returning to the caller when the image is actually loaded, the method spawns a new thread of execution to load the image and then returns to the caller without actually having completed that task. (See the Multithreading chapter of Volume 2 for more information on threads.) This is-to say the least-surprising to anyone who expects that a method won't return until it has done its job. But here, the multithreaded aspect of Java works against your assumptions. The code in our program runs in parallel with the code to load the image. Eventually, the image will be loaded and available. Of course, in the meantime, our code has tiled the entire buffer with copies of a blank image. There is a mechanism for tracking the image acquisition process. Using this mechanism, you can be notified when the image size is known, each time that a chunk of the image is ready, and finally, when the entire image is complete. When you use an Internet browser and look at a web page that contains an image, you can see how these notifications are translated into actions. An Internet browser lays out a web page as soon as it knows the sizes of the images in the page. Then it gradually fills in the images, as more detailed information becomes available. The fourth parameter of the drawImage call, which we set to null, can optionally point to an ImageObserver object that receives these notifications. However, we are not interested in incremental rendering. We just want to find out when the GIF image is completely loaded and then tile the buffer. One option is to use the ImageIO class, but that class is only available with SDK 1.4. If you use an earlier version of the SDK, you should use the MediaTracker class instead. A media tracker can track the acquisition of one or more images. (The name "media" suggests that the class should also be able to track audio files or other media. While such an extension may be available in the future, the current implementation tracks images only.) You add an image to a tracker object with the following command:

MediaTracker tracker = new MediaTracker();
Image img = Toolkit.getDefaultToolkit().getImage(name);
int id = 1; // the ID used to track the image loading process tracker.addImage(img, id);

You can add as many images as you like to a single media tracker. Each of the images should have a different ID number, but you can choose any numbering that is convenient. To wait for an image to be loaded completely, you use code like this:

try { tracker.waitForID(id); }
catch (InterruptedException e) {}

If you want to acquire multiple images, then you can add them all to the media tracker object and wait until they are all loaded. You can achieve this with the following code:

try { tracker.waitForAll(); }
catch (InterruptedException e) {}
shows the full source code of the image display program. This concludes our introduction to Java graphics programming. For more advanced techniques, you will want to turn to the discussion about 2D graphics and image manipulation in Volume 2.

Example ImageTest.java

 1. import java.awt.*;
 2. import java.awt.event.*;
 3. import java.io.*;
 4. import javax.imageio.*;
 5. import javax.swing.*;
 6.
 7. public class ImageTest
 8. {
 9. public static void main(String[] args)
10. {
11. ImageFrame frame = new ImageFrame();
12. frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
13. frame.show();
14. }
15. }
16.
17. /**
18. A frame with an image panel
19. */
20. class ImageFrame extends JFrame
21. {
22. public ImageFrame()
23. {
24. setTitle("ImageTest");
25. setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
26.
27. // add panel to frame
28.
29. ImagePanel panel = new ImagePanel();
30. Container contentPane = getContentPane();
31. contentPane.add(panel);
32. }
33.
34. public static final int DEFAULT_WIDTH = 300;
35. public static final int DEFAULT_HEIGHT = 200;
36. }
37.
38. /**
39. A panel that displays a tiled image
40. */
41. class ImagePanel extends JPanel
42. {
43. public ImagePanel()
44. {
45. // acquire the image
46.
47. /*
48. image = Toolkit.getDefaultToolkit().getImage
49. ("blue-ball-java-image.gif");
50. MediaTracker tracker = new MediaTracker(this);
51. tracker.addImage(image, 0);
52. try { tracker.waitForID(0); }
53. catch (InterruptedException exception) {}
54.
55. */
56. try
57. {
58. image = ImageIO.read(new File("blue-ball-java-image.gif"));
59. }
60. catch (IOException exception)
61. {
62. exception.printStackTrace();
63. }
64. }
65.
66. public void paintComponent(Graphics g)
67. {
68. super.paintComponent(g);
69.
70. if (image == null) return;
71.
72. int imageWidth = image.getWidth(this);
73. int imageHeight = image.getHeight(this);
74.
75. // draw the image in the upper-left corner
76.
77. g.drawImage(image, 0, 0, null);
78.
79. // tile the image across the panel
80.
81. for (int i = 0; i * imageWidth <= getWidth(); i++)
82. for (int j = 0; j * imageHeight <= getHeight(); j++)
83. if (i + j > 0)
84. g.copyArea(0, 0, imageWidth, imageHeight,
85. i * imageWidth, j * imageHeight);
86. }
87.
88. private Image image;
89. }

javax.swing.ImageIO 1.4

Java graphics api_icon

java.awt.Image 1.0

Java graphics api_icon