This chapter digs into the meat of the AWT classes. After completing this chapter, you will be able to draw strings, images, and shapes via the Graphics class in your Java programs. We discuss geometry-related classes--Polygon, Rectangle, Point, and Dimension, and the Shape interface--you will see these throughout the remaining AWT objects. You will also learn several ways to do smooth animation by using double buffering and the MediaTracker.
After reading this chapter, you should be able to do simple animation and image manipulation with AWT. For most applications, this should be sufficient. If you want to look at AWT's more advanced graphics capabilities, be sure to take a look at Chapter 12, Image Processing.
The Graphics class is an abstract class that provides the means to access different graphics devices. It is the class that lets you draw on the screen, display images, and so forth. Graphics is an abstract class because working with graphics requires detailed knowledge of the platform on which the program runs. The actual work is done by concrete classes that are closely tied to a particular platform. Your Java Virtual Machine vendor provides the necessary concrete classes for your environment. You never need to worry about the platform-specific classes; once you have a Graphics object, you can call all the methods of the Graphics class, confident that the platform-specific classes will work correctly wherever your program runs.
You rarely need to create a Graphics object yourself; its constructor is protected and is only called by the subclasses that extend Graphics. How then do you get a Graphics object to work with? The sole parameter of the Component.paint() and Component.update() methods is the current graphics context. Therefore, a Graphics object is always available when you override a component's paint() and update() methods. You can ask for the graphics context of a Component by calling Component.getGraphics(). However, many components do not have a drawable graphics context. Canvas and Container objects return a valid Graphics object; whether or not any other component has a drawable graphics context depends on the run-time environment. (The latest versions of Netscape Navigator provide a drawable graphics context for any component, but you shouldn't get used to writing platform-specific code.) This restriction isn't as harsh as it sounds. For most components, a drawable graphics context doesn't make much sense; for example, why would you want to draw on a List? If you want to draw on a component, you probably can't. The notable exception is Button, and that may be fixed in future versions of AWT.
Because Graphics is an abstract class, it doesn't have a visible constructor. The way to get a Graphics object is to ask for one by calling getGraphics() or to use the one given to you by the Component.paint() or Component.update() method.
The abstract methods of the Graphics class are implemented by some windowing system-specific class. You rarely need to know which subclass of Graphics you are using, but the classes you actually get (if you are using the JDK) are sun.awt.win32.Win32Graphics ( JDK1.0), sun.awt.window.WGraphics ( JDK1.1), sun.awt.motif.X11Graphics, or sun.awt.macos.MacGraphics. Pseudo-constructors
In addition to using the graphics contexts given to you by getGraphics() or in Component.paint(), you can get a Graphics object by creating a copy of another Graphics object. Creating new graphics contexts has resource implications. Certain platforms have a limited number of graphics contexts that can be active. For instance, on Windows 95 you cannot have more than four in use at one time. Therefore, it's a good idea to call dispose() as soon as you are done with a Graphics object. Do not rely on the garbage collector to clean up for you.
This method creates a second reference to the graphics context. It is useful for clipping (reducing the drawable area).
This method creates a second reference to a subset of the drawing area of the graphics context. The new Graphics object covers the rectangle from (x, y) through (x+width-1, y+height-1) in the original object. The coordinate space of the new Graphics context is translated so that the upper left corner is (0, 0) and the lower right corner is (width, height). Shifting the coordinate system of the new object makes it easier to work within a portion of the drawing area without using offsets.
These methods let you draw text strings on the screen. The coordinates refer to the left end of the text's baseline.
The drawString() method draws text on the screen in the current font and color, starting at position (x, y). The starting coordinates specify the left end of the String's baseline.
The drawChars() method creates a String from the char array text starting at text[offset] and continuing for length characters. The newly created String is then drawn on the screen in the current font and color, starting at position (x, y). The starting coordinates specify the left end of the String's baseline.
The drawBytes() method creates a String from the byte array text starting at text[offset] and continuing for length characters. This String is then drawn on the screen in the current font and color, starting at position (x, y). The starting coordinates specify the left end of the String's baseline.
The getFont() method returns the current Font of the graphics context. See Chapter 3, Fonts and Colors, for more on what you can do with fonts. You cannot get meaningful results with getFont() until the applet or application is displayed on the screen (generally, not in init() of an applet or main() of an application).
The setFont() method changes the current Font to font. If font is not available on the current platform, the system chooses a default. To change the current font to 12 point bold TimesRoman:
setFont (new Font ("TimesRoman", Font.BOLD, 12));
The getFontMetrics() method returns the current FontMetrics object of the graphics context. You use FontMetrics to reveal sizing properties of the current Font--for example, how wide the "Hello World" string will be in pixels when displayed on the screen.
This version of getFontMetrics() returns the FontMetrics for the Font font instead of the current font. You might use this method to see how much space a new font requires to draw text.
For more information about Font and FontMetrics, see Chapter 3, Fonts and Colors. Painting
The getColor() method returns the current foreground Color of the Graphics object. All future drawing operations will use this color. Chapter 3, Fonts and Colors describes the Color class.
The setColor() method changes the current drawing color to color. As you will see in the next chapter, the Color class defines some common colors for you. If you can't use one of the predefined colors, you can create a color from its RGB values. To change the current color to red, use any of the following:
setColor (Color.red); setColor (new Color (255, 0, 0)); setColor (new Color (0xff0000));
The clearRect() method sets the rectangular drawing area from (x, y) to (x+width-1, y+height-1) to the current background color. Keep in mind that the second pair of parameters is not the opposite corner of the rectangle, but the width and height of the area to clear.
The clipRect() method reduces the drawing area to the intersection of the current drawing area and the rectangular area from (x, y) to (x+width-1, y+height-1). Any future drawing operations outside this clipped area will have no effect. Once you clip a drawing area, you cannot increase its size with clipRect(); the drawing area can only get smaller. (However, if the clipRect() call is in paint(), the size of the drawing area will be reset to its original size on subsequent calls to paint().) If you want the ability to draw to the entire area, you must create a second Graphics object that contains a copy of the drawing area before calling clipRect() or use setClip(). The following code is a simple applet that demonstrates clipping; Figure 2.1 shows the result.
import java.awt.*; public class clipping extends java.applet.Applet { public void paint (Graphics g) { g.setColor (Color.red); Graphics clippedGraphics = g.create(); clippedGraphics.drawRect (0,0,100,100); clippedGraphics.clipRect (25, 25, 50, 50); clippedGraphics.drawLine (0,0,100,100); clippedGraphics.dispose(); clippedGraphics=null; g.drawLine (0,100,100,0); } }
The paint() method for this applet starts by setting the foreground color to red. It then creates a copy of the Graphics context for clipping, saving the original object so it can draw on the entire screen later. The applet then draws a rectangle, sets the clipping area to a smaller region, and draws a diagonal line across the rectangle from upper left to lower right. Because clipping is in effect, only part of the line is displayed. The applet then discards the clipped Graphics object and draws an unclipped line from lower left to upper right using the original object g.
This setClip() method allows you to change the current clipping area based on the parameters provided. setClip() is similar to clipRect(), except that it is not limited to shrinking the clipping area. The current drawing area becomes the rectangular area from (x, y) to (x+width-1, y+height-1); this area may be larger than the previous drawing area.
This setClip() method allows you to change the current clipping area based on the clip parameter, which may be any object that implements the Shape interface. Unfortunately, practice is not as good as theory, and in practice, clip must be a Rectangle; if you pass setClip() a Polygon, it throws an IllegalArgumentException.[1] (The Shape interface is discussed later in this chapter.)
[1] It should be simple for Sun to fix this bug; one would expect clipping to a Polygon to be the same as clipping to the Polygon's bounding rectangle.
The getClipBounds() methods returns a Rectangle that describes the clipping area of a Graphics object. The Rectangle gives you the (x, y) coordinates of the top left corner of the clipping area along with its width and height. (Rectangle objects are discussed later in this chapter.)
getClipRect() is the Java 1.0 name for this method.
The getClip() method returns a Shape that describes the clipping area of a Graphics object. That is, it returns the same thing as getClipBounds() but as a Shape, instead of as a Rectangle. By calling Shape.getBounds(), you can get the (x, y) coordinates of the top left corner of the clipping area along with its width and height. In the near future, it is hard to imagine the actual object that getClip() returns being anything other than a Rectangle.
The copyArea() method copies the rectangular area from (x, y) to (x+width, y+height) to the area with an upper left corner of (x+delta_x, y+delta_y). The delta_x and delta_y parameters are not the coordinates of the second point but an offset from the first coordinate pair (x, y). The area copied may fall outside of the clipping region. This method is often used to tile an area of the graphics context. copyArea() does not save the contents of the area copied.
There are two painting or drawing modes for the Graphics class: paint (the default) and XOR mode. In paint mode, anything you draw replaces whatever is already on the screen. If you draw a red square, you get a red square, no matter what was underneath; this is what most programmers have learned to expect.
The behavior of XOR mode is rather strange, at least to people accustomed to modern programming environments. XOR mode is short for eXclusive-OR mode. The idea behind XOR mode is that drawing the same object twice returns the screen to its original state. This technique was commonly used for simple animations prior to the development of more sophisticated methods and cheaper hardware.
The side effect of XOR mode is that painting operations don't necessarily get the color you request. Instead of replacing the original pixel with the new value, XOR mode merges the original color, the painting color, and an XOR color (usually the background color) to form a new color. The new color is chosen so that if you repaint the pixel with the same color, you get the original pixel back. For example, if you paint a red square in XOR mode, you get a square of some other color on the screen. Painting the same red square again returns the screen to its original state.
The setXORMode() method changes the drawing mode to XOR mode. In XOR mode, the system uses the xorColor color to determine an alternate color for anything drawn such that drawing the same item twice restores the screen to its original condition. The xorColor is usually the current background color but can be any color. For each pixel, the new color is determined by an exclusive-or of the old pixel color, the painting color, and the xorColor.
For example, if the old pixel is red, the XOR color is blue, and the drawing color is green, the end result would be white. To see why, it is necessary to look at the RGB values of the three colors. Red is (255, 0, 0). Blue is (0, 0, 255). Green is (0, 255, 0). The exclusive-or of these three values is (255, 255, 255), which is white. Drawing another green pixel with a blue XOR color yields red, the pixel's original color, since (255, 255, 255) ^ (0, 0, 255) ^ (0, 255, 0) yields (255, 0, 0).[2] The following code generates the display shown in Figure 2.2.
[2] ^ is the Java XOR operator.
import java.awt.*; public class xor extends java.applet.Applet { public void init () { setBackground (Color.red); } public void paint (Graphics g) { g.setColor (Color.green); g.setXORMode (Color.blue); g.fillRect (10, 10, 100, 100); g.fillRect (10, 60, 100, 100); } }
Although it's hard to visualize what color XOR mode will pick, there is one important special case. Let's say that there are only two colors: a background color (the XOR color) and a foreground color (the painting color). Each pixel must be in one color or the other. Painting "flips" each pixel to the other color. Foreground pixels become background, and vice versa.
The setPaintMode() method puts the system into paint mode. When in paint mode, any drawing operation replaces whatever is underneath it. Call setPaintMode() to return to normal painting when finished with XOR mode.
Most of the drawing methods require you to specify a bounding rectangle for the object you want to draw: the location of the object's upper left corner, plus its width and height. The two exceptions are lines and polygons. For lines, you supply two endpoints; for polygons, you provide a set of points.
Versions 1.0.2 and 1.1 of AWT always draw solid lines that are one pixel wide; there is no support for line width or fill patterns. A future version should support lines with variable widths and patterns.
The drawLine() method draws a line on the graphics context in the current color from (x1, y1) to (x2, y2). If (x1, y1) and (x2, y2) are the same point, you will draw a point. There is no method specific to drawing a point. The following code generates the display shown in Figure 2.3.
g.drawLine (5, 5, 50, 75); // line g.drawLine (5, 75, 5, 75); // point g.drawLine (50, 5, 50, 5); // point
The drawRect() method draws a rectangle on the drawing area in the current color from (x, y) to (x+width, y+height). If width or height is negative, nothing is drawn.
The fillRect() method draws a filled rectangle on the drawing area in the current color from (x, y) to (x+width-1, y+height-1). Notice that the filled rectangle is one pixel smaller to the right and bottom than requested. If width or height is negative, nothing is drawn.
The drawRoundRect() method draws a rectangle on the drawing area in the current color from (x, y) to (x+width, y+height). However, instead of perpendicular corners, the corners are rounded with a horizontal diameter of arcWidth and a vertical diameter of arcHeight. If width or height is a negative number, nothing is drawn. If width, height, arcWidth, and arcHeight are all equal, you get a circle.
To help you visualize the arcWidth and arcHeight of a rounded rectangle, Figure 2.4 shows one corner of a rectangle drawn with an arcWidth of 20 and a arcHeight of 40.
The fillRoundRect() method draws a filled rectangle on the drawing area in the current color from (x, y) to (x+width-1, y+height-1). However, instead of having perpendicular corners, the corners are rounded with a horizontal diameter of arcWidth and a vertical diameter of arcHeight for the four corners. Notice that the filled rectangle is one pixel smaller to the right and bottom than requested. If width or height is a negative number, nothing is filled. If width, height, arcWidth, and arcHeight are all equal, you get a filled circle.
Figure 2.4 shows how AWT generates rounded corners. Figure 2.5 shows the collection of rectangles created by the following code. The rectangles in Figure 2.5 are filled and unfilled, with rounded and square corners.
g.drawRect (25, 10, 50, 75); g.fillRect (25, 110, 50, 75); g.drawRoundRect (100, 10, 50, 75, 60, 50); g.fillRoundRect (100, 110, 50, 75, 60, 50);
The draw3DRect() method draws a rectangle in the current color from (x, y) to (x+width, y+height); a shadow effect makes the rectangle appear to float slightly above or below the screen. The raised parameter has an effect only if the current color is not black. If raised is true, the rectangle looks like a button waiting to be pushed. If raised is false, the rectangle looks like a depressed button. If width or height is negative, the shadow appears from another direction.
The fill3DRect() method draws a filled rectangle in the current color from (x, y) to (x+width, y+height); a shadow effect makes the rectangle appear to float slightly above or below the screen. The raised parameter has an effect only if the current color is not black. If raised is true, the rectangle looks like a button waiting to be pushed. If raised is false, the rectangle looks like a depressed button. To enhance the shadow effect, the depressed area is given a slightly deeper shade of the drawing color. If width or height is negative, the shadow appears from another direction, and the rectangle isn't filled. (Different platforms could deal with this differently. Try to ensure the parameters have positive values.)
Figure 2.6 shows the collection of three-dimensional rectangles created by the following code. The rectangles in the figure are raised and depressed, filled and unfilled.
g.setColor (Color.gray); g.draw3DRect (25, 10, 50, 75, true); g.draw3DRect (25, 110, 50, 75, false); g.fill3DRect (100, 10, 50, 75, true); g.fill3DRect (100, 110, 50, 75, false);
The drawOval() method draws an oval in the current color within an invisible bounding rectangle from (x, y) to (x+width, y+height). You cannot specify the oval's center point and radii. If width and height are equal, you get a circle. If width or height is negative, nothing is drawn.
The fillOval() method draws a filled oval in the current color within an invisible bounding rectangle from (x, y) to (x+width-1, y+height-1). You cannot specify the oval's center point and radii. Notice that the filled oval is one pixel smaller to the right and bottom than requested. If width or height is negative, nothing is drawn.
Figure 2.7 shows the collection of ovals, filled and unfilled, that were generated by the following code:
g.drawOval (25, 10, 50, 75); g.fillOval (25, 110, 50, 75); g.drawOval (100, 10, 50, 50); g.fillOval (100, 110, 50, 50);
The drawArc() method draws an arc in the current color within an invisible bounding rectangle from (x, y) to (x+width, y+height). The arc starts at startAngle degrees and goes to startAngle + arcAngle degrees. An angle of 0 degrees is at the 3 o'clock position; angles increase counter-clockwise. If arcAngle is negative, drawing is in a clockwise direction. If width and height are equal and arcAngle is 360 degrees, drawArc() draws a circle. If width or height is negative, nothing is drawn.
The fillArc() method draws a filled arc in the current color within an invisible bounding rectangle from (x, y) to (x+width-1, y+height-1). The arc starts at startAngle degrees and goes to startAngle + arcAngle degrees. An angle of 0 degrees is at the 3 o'clock position; angles increase counter-clockwise. If arcAngle is negative, drawing is in a clockwise direction. The arc fills like a pie (to the origin), not from arc endpoint to arc endpoint. This makes creating pie charts easier. If width and height are equal and arcAngle is 360 degrees, fillArc() draws a filled circle. If width or height is negative, nothing is drawn.
Figure 2.8 shows a collection of filled and unfilled arcs that were generated by the following code:
g.drawArc (25, 10, 50, 75, 0, 360); g.fillArc (25, 110, 50, 75, 0, 360); g.drawArc (100, 10, 50, 75, 45, 215); g.fillArc (100, 110, 50, 75, 45, 215);
The drawPolygon() method draws a path for the points in polygon p in the current color. Polygon discusses the Polygon class in detail.
The behavior of drawPolygon() changes slightly between Java 1.0.2 and 1.1. With version 1.0.2, if the first and last points of a Polygon are not the same, a call to drawPolygon() results in an open polygon, since the endpoints are not connected for you. Starting with version 1.1, if the first and last points are not the same, the endpoints are connected for you.
The drawPolygon() method draws a path of numPoints nodes by plucking one element at a time out of xPoints and yPoints to make each point. The path is drawn in the current color. If either xPoints or yPoints does not have numPoints elements, drawPolygon() throws a run-time exception. In 1.0.2, this exception is an IllegalArgumentException; in 1.1, it is an ArrayIndexOutOfBoundsException. This change shouldn't break older programs, since you are not required to catch run-time exceptions.
The drawPolyline() method functions like the 1.0 version of drawPolygon(). It plays connect the dots with the points in the xPoints and yPoints arrays and does not connect the endpoints. If either xPoints or yPoints does not have numPoints elements, drawPolygon() throws the run-time exception, ArrayIndexOutOfBoundsException.
Filling polygons is a complex topic. It is not as easy as filling rectangles or ovals because a polygon may not be closed and its edges may cross. AWT uses an even-odd rule to fill polygons. This algorithm works by counting the number of times each scan line crosses an edge of the polygon. If the total number of crossings to the left of the current point is odd, the point is colored. If it is even, the point is left alone. Figure 2.9 demonstrates this algorithm for a single scan line that intersects the polygon at x values of 25, 75, 125, 175, 225, and 275.
The scan line starts at the left edge of the screen; at this point there haven't been any crossings, so the pixels are left untouched. The scan line reaches the first crossing when x equals 25. Here, the total number of crossings to the left is one, so the scan line is inside the polygon, and the pixels are colored. At 75, the scan line crosses again; the total number of crossings is two, so coloring stops.
The fillPolygon() method draws a filled polygon for the points in Polygon p in the current color. If the polygon is not closed, fillPolygon() adds a segment connecting the endpoints. Polygon discusses the Polygon class in detail.
The fillPolygon() method draws a polygon of numPoints nodes by plucking one element at a time out of xPoints and yPoints to make each point. The polygon is drawn in the current color. If either xPoints or yPoints does not have numPoints elements, fillPolygon() throws the run-time exception IllegalArgumentException. If the polygon is not closed, fillPolygon() adds a segment connecting the endpoints.[3]
[3] In Java 1.1, this method throws ArrayIndexOutOfBoundsException, not IllegalArgumentException.
Figure 2.10 shows several polygons created by the following code, containing different versions of drawPolygon() and fillPolygon():
int[] xPoints[] = {{50, 25, 25, 75, 75}, {50, 25, 25, 75, 75}, {100, 100, 150, 100, 150, 150, 125, 100, 150}, {100, 100, 150, 100, 150, 150, 125, 100, 150}}; int[] yPoints[] = {{10, 35, 85, 85, 35, 10}, {110, 135, 185, 185, 135}, {85, 35, 35, 85, 85, 35, 10, 35, 85}, {185, 135, 135, 185, 185, 135, 110, 135, 185}}; int nPoints[] = {5, 5, 9, 9}; g.drawPolygon (xPoints[0], yPoints[0], nPoints[0]); g.fillPolygon (xPoints[1], yPoints[1], nPoints[1]); g.drawPolygon (new Polygon(xPoints[2], yPoints[2], nPoints[2])); g.fillPolygon (new Polygon(xPoints[3], yPoints[3], nPoints[3]));
An Image is a displayable object maintained in memory. To get an image on the screen, you must draw it onto a graphics context, using the drawImage() method of the Graphics class. For example, within a paint() method, you would call g.drawImage(image, ... , this) to display some image on the screen. In other situations, you might use the createImage() method to generate an offscreen Graphics object, then use drawImage() to draw an image onto this object, for display later.
This begs the question: where do images come from? We will have more to say about the Image class later in this chapter. For now, it's enough to say that you can call getImage() to load an image from disk or across the Net. There are versions of getImage() in the Applet and Toolkit classes; the latter is for use in applications. You can also call createImage(), a method of the Component class, to generate an image in memory.
What about the last argument to drawImage()? What is this for? The last argument of drawImage() is always an image observer--that is, an object that implements the ImageObserver interface. This interface is discussed in detail in Chapter 12, Image Processing. For the time being, it's enough to say that the call to drawImage() starts a new thread that loads the requested image. An image observer monitors the process of loading an image; the thread that is loading the image notifies the image observer whenever new data has arrived. The Component class implements the ImageObserver interface; when you're writing a paint() method, you're almost certainly overriding some component's paint() method; therefore, it's safe to use this as the image observer in a call to drawImage(). More simply, we could say that any component can serve as an image observer for images that are drawn on it.
The drawImage() method draws image onto the screen with its upper left corner at (x, y), using observer as its ImageObserver. Returns true if the object is fully drawn, false otherwise.
The drawImage() method draws image onto the screen with its upper left corner at (x, y), using observer as its ImageObserver. The system scales image to fit into a width x height area. The scaling may take time. This method returns true if the object is fully drawn, false otherwise.
With Java 1.1, you don't need to use drawImage() for scaling; you can prescale the image with the Image.getScaledInstance() method, then use the previous version of drawImage().
The drawImage() method draws image onto the screen with its upper left corner at (x, y), using observer as its ImageObserver. backgroundColor is the color of the background seen through the transparent parts of the image. If no part of the image is transparent, you will not see backgroundColor. Returns true if the object is fully drawn, false otherwise.
The drawImage() method draws image onto the screen with its upper left corner at (x, y), using observer as its ImageObserver. backgroundColor is the color of the background seen through the transparent parts of the image. The system scales image to fit into a width x height area. The scaling may take time. This method returns true if the image is fully drawn, false otherwise.
With Java 1.1, you can prescale the image with the AreaAveragingScaleFilter or ReplicateScaleFilter described in Chapter 12, Image Processing, then use the previous version of drawImage() to display it.
The following code generated the images in Figure 2.11. The images on the left come from a standard JPEG file. The images on the right come from a file in GIF89a format, in which the white pixel is "transparent." Therefore, the gray background shows through this pair of images.
import java.awt.*; import java.applet.*; public class drawingImages extends Applet { Image i, j; public void init () { i = getImage (getDocumentBase(), "rosey.jpg"); j = getImage (getDocumentBase(), "rosey.gif"); } public void paint (Graphics g) { g.drawImage (i, 10, 10, this); g.drawImage (i, 10, 85, 150, 200, this); g.drawImage (j, 270, 10, Color.lightGray, this); g.drawImage (j, 270, 85, 150, 200, Color.lightGray, this); } }
The drawImage() method draws a portion of image onto the screen. It takes the part of the image with corners at (sx1, sy1) and (sx2, sy2); it places this rectangular snippet on the screen with one corner at (dx1, dy1) and another at (dx2, dy2), using observer as its ImageObserver. (Think of d for destination location and s for source image.) This method returns true if the object is fully drawn, false otherwise.
drawImage() flips the image if source and destination endpoints are not the same corners, crops the image if the destination is smaller than the original size, and scales the image if the destination is larger than the original size. It does not do rotations, only flips (i.e., it can produce a mirror image or an image rotated 180 degrees but not an image rotated 90 or 270 degrees).
The drawImage() method draws a portion of image onto the screen. It takes the part of the image with corners at (sx1, sy1) and (sx2, sy2); it places this rectangular snippet on the screen with one corner at (dx1, dy1) and another at (dx2, dy2), using observer as its ImageObserver. (Think of d for destination location and s for source image.) backgroundColor is the color of the background seen through the transparent parts of the image. If no part of the image is transparent, you will not see backgroundColor. This method returns true if the object is fully drawn, false otherwise.
Like the previous version of drawImage(), this method flips the image if source and destination endpoints are not the same corners, crops the image if the destination is smaller than the original size, and scales the image if the destination is larger than the original size. It does not do rotations, only flips (i.e., it can produce a mirror image or an image rotated 180 degrees but not an image rotated 90 or 270 degrees).
The following code demonstrates the new drawImage() methods in Java 1.1. They allow you to scale, flip, and crop images without the use of image filters. The results are shown in Figure 2.12.
// Java 1.1 only import java.awt.*; import java.applet.*; public class drawingImages11 extends Applet { Image i, j; public void init () { i = getImage (getDocumentBase(), "rosey.gif"); } public void paint (Graphics g) { g.drawImage (i, 10, 10, this); g.drawImage (i, 10, 85, i.getWidth(this)+10, i.getHeight(this)+85, i.getWidth(this), i.getHeight(this), 0, 0, this); g.drawImage (i, 270, 10, i.getWidth(this)+270, i.getHeight(this)*2+10, 0, 0, i.getWidth(this), i.getHeight(this), Color.gray, this); g.drawImage (i, 10, 170, i.getWidth(this)*2+10, i.getHeight(this)+170, 0, i.getHeight(this)/2, i.getWidth(this)/2, 0, this); } }
The translate() method sets how the system translates the coordinate space for you. The point at the (x, y) coordinates becomes the origin of this graphics context. Any future drawing will be relative to this location. Multiple translations are cumulative. The following code leaves the coordinate system translated by (100, 50).
translate (100, 0); translate (0, 50);
Note that each call to paint() provides an entirely new Graphics context with its origin in the upper left corner. Therefore, don't expect translations to persist from one call to paint() to the next.
The dispose() method frees any system resources used by the Graphics context. It's a good idea to call dispose() whenever you are finished with a Graphics object, rather than waiting for the garbage collector to call it automatically (through finalize()). Disposing of the Graphics object yourself will help your programs on systems with limited resources. However, you should not dispose the Graphics parameter to Component.paint() or Component.update().
The garbage collector calls finalize() when it determines that the Graphics object is no longer needed. finalize() calls dispose(), which frees any resources that the Graphics object has used.
The toString() method of Graphics returns a string showing the current font and color. However, Graphics is an abstract class, and classes that extend Graphics usually override toString(). For example, on a Windows 95 machine, sun.awt.win32.Win32Graphics is the concrete class that extends Graphics. The class's toString() method displays the current origin of the Graphics object, relative to the original coordinate system:
sun.awt.win32.Win32Graphics[0,0]