Simple Graphics
Contents:
Graphics
Point
Dimension
Shape
Rectangle
Polygon
Image
MediaTracker
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 Image Processing.
Graphics
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.
Graphics Methods
Constructors- protected Graphics ()
- Because
Graphics
is an abstract class, it doesn't have a visible constructor. The way to get aGraphics
object is to ask for one by callinggetGraphics()
or to use the one given to you by theComponent.paint()
orComponent.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 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.
- public abstract Graphics create ()
- This method creates a second reference to the graphics context. It is useful for clipping (reducing the drawable area).
- public Graphics create (int x, int y, int width, int height)
- 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 newGraphics
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.
- public abstract void drawString (String text, int x, int y)
- The
drawString()
method drawstext
on the screen in the current font and color, starting at position (x
,y
). The starting coordinates specify the left end of theString
's baseline. - public void drawChars (char text[], int offset, int length, int x, int y)
- The
drawChars()
method creates aString
from the char arraytext
starting attext[offset]
and continuing forlength
characters. The newly createdString
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 theString
's baseline. - public void drawBytes (byte text[], int offset, int length, int x, int y)
- The
drawBytes()
method creates aString
from the byte arraytext
starting attext[offset]
and continuing forlength
characters. ThisString
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 theString
's baseline. - public abstract Font getFont ()
- The
getFont()
method returns the currentFont
of the graphics context. See Fonts and Colors, for more on what you can do with fonts. You cannot get meaningful results withgetFont()
until the applet or application is displayed on the screen (generally, not ininit()
of an applet ormain()
of an application). - public abstract void setFont (Font font)
- The
setFont()
method changes the currentFont
tofont
. Iffont
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));
- public FontMetrics getFontMetrics ()
- The
getFontMetrics()
method returns the currentFontMetrics
object of the graphics context. You useFontMetrics
to reveal sizing properties of the currentFont
--for example, how wide the "Hello World" string will be in pixels when displayed on the screen. - public abstract FontMetrics getFontMetrics (Font font)
- This version of
getFontMetrics()
returns theFontMetrics
for theFont
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 Fonts and Colors. Painting
- public abstract Color getColor ()
- The
getColor()
method returns the current foregroundColor
of theGraphics
object. All future drawing operations will use this color. Fonts and Colors describes theColor
class. - public abstract void setColor (Color color)
- The
setColor()
method changes the current drawing color tocolor
. As you will see in the next chapter, theColor
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));
- public abstract void clearRect (int x, int y, int width, int height)
- 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. - public abstract void clipRect (int x, int y, int width, int height)
- 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 withclipRect()
; the drawing area can only get smaller. (However, if theclipRect()
call is inpaint()
, the size of the drawing area will be reset to its original size on subsequent calls topaint()
.) If you want the ability to draw to the entire area, you must create a secondGraphics
object that contains a copy of the drawing area before callingclipRect()
or usesetClip()
. 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); } }
Figure 2.1: Clipping restricts the drawing area
The
paint()
method for this applet starts by setting the foreground color to red. It then creates a copy of theGraphics
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 clippedGraphics
object and draws an unclipped line from lower left to upper right using the original objectg
.
- public abstract void setClip(int x, int y, int width, int height)
- This
setClip()
method allows you to change the current clipping area based on the parameters provided.setClip()
is similar toclipRect()
, 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. - public abstract void setClip(Shape clip)
- This
setClip()
method allows you to change the current clipping area based on theclip
parameter, which may be any object that implements theShape
interface. Unfortunately, practice is not as good as theory, and in practice,clip
must be aRectangle
; if you passsetClip()
aPolygon
, it throws anIllegalArgumentException
.[1] (TheShape
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 thePolygon
's bounding rectangle. - public abstract Rectangle getClipBounds ()
public abstract Rectangle getClipRect () - The
getClipBounds()
methods returns aRectangle
that describes the clipping area of aGraphics
object. TheRectangle
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. - public abstract Shape getClip ()
- The
getClip()
method returns aShape
that describes the clipping area of aGraphics
object. That is, it returns the same thing asgetClipBounds()
but as aShape
, instead of as aRectangle
. By callingShape.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 thatgetClip()
returns being anything other than aRectangle
. - public abstract void copyArea (int x, int y, int width, int height, int delta_x, int delta_y)
- 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
). Thedelta_x
anddelta_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 developers have learned to expect.
The behavior of XOR mode is rather strange, at least to people accustomed to modern developing 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.
- public abstract void setXORMode (Color xorColor)
- The
setXORMode()
method changes the drawing mode to XOR mode. In XOR mode, the system uses thexorColor
color to determine an alternate color for anything drawn such that drawing the same item twice restores the screen to its original condition. ThexorColor
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 thexorColor
.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); } }
Figure 2.2: Drawing in XOR mode
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.
- public abstract void setPaintMode ()
- The
setPaintMode()
method puts the system into paint mode. When in paint mode, any drawing operation replaces whatever is underneath it. CallsetPaintMode()
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.
- public abstract void drawLine (int x1, int y1, int x2, int y2)
- 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
Figure 2.3: Drawing lines and points with drawLine()
- public void drawRect (int x, int y, int width, int height)
- The
drawRect()
method draws a rectangle on the drawing area in the current color from (x
,y
) to (x+width
,y+height
). Ifwidth
orheight
is negative, nothing is drawn. - public abstract void fillRect (int x, int y, int width, int height)
- 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. Ifwidth
orheight
is negative, nothing is drawn. - public abstract void drawRoundRect (int x, int y, int width, int height, int arcWidth, int arcHeight)
- 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 ofarcWidth
and a vertical diameter ofarcHeight
. Ifwidth
orheight
is a negative number, nothing is drawn. Ifwidth
,height
,arcWidth
, andarcHeight
are all equal, you get a circle.To help you visualize the
arcWidth
andarcHeight
of a rounded rectangle, Figure 2.4 shows one corner of a rectangle drawn with anarcWidth
of 20 and aarcHeight
of 40.Figure 2.4: Drawing rounded corners
- public abstract void fillRoundRect (int x, int y, int width, int height, int arcWidth, int arcHeight)
- 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 ofarcWidth
and a vertical diameter ofarcHeight
for the four corners. Notice that the filled rectangle is one pixel smaller to the right and bottom than requested. Ifwidth
orheight
is a negative number, nothing is filled. Ifwidth
,height
,arcWidth
, andarcHeight
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);
Figure 2.5: Varieties of rectangles
- public void draw3DRect (int x, int y, int width, int height, boolean raised)
- 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. Theraised
parameter has an effect only if the current color is not black. Ifraised
istrue
, the rectangle looks like a button waiting to be pushed. Ifraised
isfalse
, the rectangle looks like a depressed button. Ifwidth
orheight
is negative, the shadow appears from another direction. - public void fill3DRect (int x, int y, int width, int height, boolean raised)
- 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. Theraised
parameter has an effect only if the current color is not black. Ifraised
istrue
, the rectangle looks like a button waiting to be pushed. Ifraised
isfalse
, 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. Ifwidth
orheight
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);
Figure 2.6: Filled and unfilled 3D rectangles
- public abstract void drawOval (int x, int y, int width, int height)
- 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. Ifwidth
andheight
are equal, you get a circle. Ifwidth
orheight
is negative, nothing is drawn. - public abstract void fillOval (int x, int y, int width, int height)
- 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. Ifwidth
orheight
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);
Figure 2.7: Filled and unfilled ovals
- public abstract void drawArc (int x, int y, int width, int height, int startAngle, int arcAngle)
- 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 atstartAngle
degrees and goes tostartAngle
+arcAngle
degrees. An angle of 0 degrees is at the 3 o'clock position; angles increase counter-clockwise. IfarcAngle
is negative, drawing is in a clockwise direction. Ifwidth
andheight
are equal andarcAngle
is 360 degrees,drawArc()
draws a circle. Ifwidth
orheight
is negative, nothing is drawn. - public abstract void fillArc (int x, int y, int width, int height, int startAngle, int arcAngle)
- 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 atstartAngle
degrees and goes tostartAngle
+arcAngle
degrees. An angle of 0 degrees is at the 3 o'clock position; angles increase counter-clockwise. IfarcAngle
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. Ifwidth
andheight
are equal andarcAngle
is 360 degrees,fillArc()
draws a filled circle. Ifwidth
orheight
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);
Figure 2.8: Filled and unfilled arcs
- public void drawPolygon (Polygon p)
- The
drawPolygon()
method draws a path for the points in polygonp
in the current color. Polygon discusses thePolygon
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 aPolygon
are not the same, a call todrawPolygon()
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. - public abstract void drawPolygon (int xPoints[], int yPoints[], int numPoints)
- The
drawPolygon()
method draws a path ofnumPoints
nodes by plucking one element at a time out ofxPoints
andyPoints
to make each point. The path is drawn in the current color. If eitherxPoints
oryPoints
does not havenumPoints
elements,drawPolygon()
throws a run-time exception. In 1.0.2, this exception is anIllegalArgumentException
; in 1.1, it is anArrayIndexOutOfBoundsException
. This change shouldn't break older programs, since you are not required to catch run-time exceptions. - public abstract void drawPolyline (int xPoints[], int yPoints[], int numPoints)
- The
drawPolyline()
method functions like the 1.0 version ofdrawPolygon()
. It plays connect the dots with the points in thexPoints
andyPoints
arrays and does not connect the endpoints. If eitherxPoints
oryPoints
does not havenumPoints
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.
Figure 2.9: Polygon fill algorithm
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.
- public void fillPolygon (Polygon p)
- The
fillPolygon()
method draws a filled polygon for the points inPolygon p
in the current color. If the polygon is not closed,fillPolygon()
adds a segment connecting the endpoints. Polygon discusses thePolygon
class in detail. - public abstract void fillPolygon (int xPoints[], int yPoints[], int nPoints)
- The
fillPolygon()
method draws a polygon ofnumPoints
nodes by plucking one element at a time out ofxPoints
andyPoints
to make each point. The polygon is drawn in the current color. If eitherxPoints
oryPoints
does not havenumPoints
elements,fillPolygon()
throws the run-time exceptionIllegalArgumentException
. If the polygon is not closed,fillPolygon()
adds a segment connecting the endpoints.[3][3] In Java 1.1, this method throws
ArrayIndexOutOfBoundsException
, notIllegalArgumentException
.
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]));
Figure 2.10: Filled and unfilled polygons
Drawing images
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 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.
- public abstract boolean drawImage (Image image, int x, int y, ImageObserver observer)
- The
drawImage()
method drawsimage
onto the screen with its upper left corner at (x
,y
), usingobserver
as itsImageObserver
. Returnstrue
if the object is fully drawn,false
otherwise. - public abstract boolean drawImage (Image image, int x, int y, int width, int height, ImageObserver observer)
- The
drawImage()
method drawsimage
onto the screen with its upper left corner at (x
,y
), usingobserver
as itsImageObserver
. The system scalesimage
to fit into awidth
xheight
area. The scaling may take time. This method returnstrue
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 theImage.getScaledInstance()
method, then use the previous version ofdrawImage()
. - public abstract boolean drawImage (Image image, int x, int y, Color backgroundColor, ImageObserver observer)
- The
drawImage()
method drawsimage
onto the screen with its upper left corner at (x
,y
), usingobserver
as itsImageObserver
.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 seebackgroundColor
. Returnstrue
if the object is fully drawn,false
otherwise. - public abstract boolean drawImage (Image image, int x, int y, int width, int height, Color backgroundColor, ImageObserver observer)
- The
drawImage()
method drawsimage
onto the screen with its upper left corner at (x
,y
), usingobserver
as itsImageObserver
.backgroundColor
is the color of the background seen through the transparent parts of the image. The system scalesimage
to fit into awidth
xheight
area. The scaling may take time. This method returnstrue
if the image is fully drawn,false
otherwise.With Java 1.1, you can prescale the image with the
AreaAveragingScaleFilter
orReplicateScaleFilter
described in Image Processing, then use the previous version ofdrawImage()
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); } }
Figure 2.11: Scaled and unscaled images
- public abstract boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, ImageObserver observer)
- The
drawImage()
method draws a portion ofimage
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
), usingobserver
as itsImageObserver
. (Think ofd
for destination location ands
for source image.) This method returnstrue
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). - public abstract boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, Color backgroundColor, ImageObserver observer)
- The
drawImage()
method draws a portion ofimage
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
), usingobserver
as itsImageObserver
. (Think ofd
for destination location ands
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 seebackgroundColor
. This method returnstrue
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); } }
Figure 2.12: Flipped, mirrored, and cropped images
Miscellaneous methods
- public abstract void translate (int x, int y)
- 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 newGraphics
context with its origin in the upper left corner. Therefore, don't expect translations to persist from one call topaint()
to the next. - public abstract void dispose ()
- The
dispose()
method frees any system resources used by theGraphics
context. It's a good idea to calldispose()
whenever you are finished with aGraphics
object, rather than waiting for the garbage collector to call it automatically (throughfinalize()
). Disposing of theGraphics
object yourself will help your programs on systems with limited resources. However, you should not dispose theGraphics
parameter toComponent.paint()
orComponent.update()
. - public void finalize ()
- The garbage collector calls
finalize()
when it determines that theGraphics
object is no longer needed.finalize()
callsdispose()
, which frees any resources that theGraphics
object has used. - public String toString ()
- The
toString()
method ofGraphics
returns a string showing the current font and color. However,Graphics
is an abstract class, and classes that extendGraphics
usually overridetoString()
. For example, on a Windows machine,sun.awt.win32.Win32Graphics
is the concrete class that extendsGraphics
. The class'stoString()
method displays the current origin of theGraphics
object, relative to the original coordinate system:sun.awt.win32.Win32Graphics[0,0]