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.

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 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.

Drawing strings

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 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.

public abstract Font getFont ()

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).

public abstract void setFont (Font font)

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));

public FontMetrics getFontMetrics ()

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.

public abstract FontMetrics getFontMetrics (Font font)

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.

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.

public abstract void setColor (Color color)

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:

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 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.

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.

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 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.

public abstract void setClip(Shape clip)

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.

public abstract Shape getClip ()

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.

Painting mode

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.

public abstract void setXORMode (Color xorColor)

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.

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. Call setPaintMode()
to return to normal painting when finished with XOR mode.

Drawing shapes

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.

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.

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.
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.

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.

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.

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.
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:

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:

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.

public void fillPolygon (Polygon p)

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():

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. 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.

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.

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.

public abstract void dispose ()

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().

public void finalize ()

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.

public String toString ()

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: