Sei sulla pagina 1di 82

Trail: 2D Graphics ................................................................................................................................................

4
Lesson: Overview of the Java 2D API Concepts ................................................................................................... 6
Coordinates ..................................................................................................................................................... 7
Java 2D Rendering ........................................................................................................................................... 8
Geometric Primitives ..................................................................................................................................... 10
Points ......................................................................................................................................................... 10
Lines ........................................................................................................................................................... 10
Rectangular Shapes ................................................................................................................................... 10
Quadratic and Cubic Curves ...................................................................................................................... 11
Arbitrary Shapes ........................................................................................................................................ 11
Areas .......................................................................................................................................................... 11
Text ................................................................................................................................................................ 12
Fonts .......................................................................................................................................................... 12
Text Layout ................................................................................................................................................ 12
Rendering Hints for Text............................................................................................................................ 13
Images ........................................................................................................................................................... 14
Printing .......................................................................................................................................................... 15
Lesson: Getting Started with Graphics .............................................................................................................. 16
Lesson: Working with Geometry ....................................................................................................................... 20
Drawing Geometric Primitives....................................................................................................................... 21
Point .......................................................................................................................................................... 21
Line ............................................................................................................................................................ 21
Curves ........................................................................................................................................................ 22
Quadratic Curve Segment ........................................................................................................ 22
Cubic Curve Segment .............................................................................................................. 22
Rectangle ................................................................................................................................................... 23
Ellipse ......................................................................................................................................................... 23
Arc.............................................................................................................................................................. 24
Drawing Arbitrary Shapes.............................................................................................................................. 25
Stroking and Filling Graphics Primitives ........................................................................................................ 27
Defining Fancy Line Styles and Fill Patterns .............................................................................................. 27
Line Styles ............................................................................................................................... 28
Fill Patterns .............................................................................................................................. 28
Lesson: Working with Text APIs ........................................................................................................................ 30

1
Selecting a Font ............................................................................................................................................. 31
Measuring Text .............................................................................................................................................. 34
Advanced Text Display................................................................................................................................... 36
Displaying Antialiased Text by Using Rendering Hints .............................................................................. 37
Using Text Attributes to Style Text ............................................................................................................ 39
Drawing Multiple Lines of Text .................................................................................................................. 40
Lesson: Working with Images ............................................................................................................................ 42
Reading/Loading an Image ............................................................................................................................ 44
Drawing an Image .......................................................................................................................................... 46
Filtering Images ......................................................................................................................................... 47
Creating and Drawing to an Image ................................................................................................................ 50
Writing/Saving an Image ............................................................................................................................... 52
Lesson: Printing ................................................................................................................................................. 54
A Basic Printing Program ............................................................................................................................... 55
Using Print Setup Dialogs .............................................................................................................................. 58
Printing a Multiple Page Document .............................................................................................................. 60
Working with Print Services and Attributes .................................................................................................. 62
Document Type Specification .................................................................................................................... 62
Attribute Definitions .................................................................................................................................. 63
Print Service Discovery .............................................................................................................................. 63
Common Use of the API ............................................................................................................................ 63
Printing the Contents of a User Interface...................................................................................................... 64
Printing Support in Swing Components ........................................................................................................ 66
Lesson: Advanced Topics in Java2D ................................................................................................................... 67
Transforming Shapes, Text, and Images........................................................................................................ 68
Example: Transform .................................................................................................................................. 68
Clipping the Drawing Region ......................................................................................................................... 71
Example: ClipImage ................................................................................................................................... 71
Example: Starry.......................................................................................................................................... 72
Compositing Graphics.................................................................................................................................... 73
Example: Composite .................................................................................................................................. 74
Controlling Rendering Quality ....................................................................................................................... 76
Constructing Complex Shapes from Geometry Primitives ............................................................................ 78
Example: Areas .......................................................................................................................................... 78

2
Supporting User Interaction .......................................................................................................................... 80
Example: ShapeMover ............................................................................................................................... 80
Example: HitTestSample ............................................................................................................................ 81

3
Trail: 2D Graphics

This trail introduces you to the Java 2D™ API and shows you how to display and
print 2D graphics in your Java programs. The trial is intended for developers who
want to enrich their knowledge of the Java 2D API, as well as for beginners in
computer graphics. Almost every section contains relevant examples to illustrate
specific capabilities. The Java 2D API enables you to easily perform the following
tasks:

 Draw lines, rectangles and any other geometric shape.


 Fill those shapes with solid colors or gradients and textures.
 Draw text with options for fine control over the font and rendering process.
 Draw images, optionally applying filtering operations.
 Apply operations such as compositing and transforming during any of the
above rendering operations.

This chapter also explains less familiar concepts such as compositing.

Using 2D Graphics API to display complex charts

Using image-filtering operations

This chapter describes the concept of drawing on-screen and off-screen images, as
well as surfaces and printer devices. This trail covers the most common uses of the
Java 2D APIs and briefly describes some of the more advanced features.

Overview of the Java 2D Graphics API introduces the key Java 2D concepts and
describes the Java 2D rendering model. This lesson is more conceptual than other
lessons of this trail; it enables you to get deep into basic notions and classes
descriptions.

Getting started with Graphics uses a developed example to show you how to
obtain a Graphics object and use it for common graphics rendering tasks.

Working with Geometry teaches you how to use APIs to draw graphic primitives
and arbitrary shapes, and how to apply fancy strokes and fill styles.

Working with Text APIs shows you how to effectively use text APIs, including
how to create a Font object with desired attributes, measure text, and determine
the names of the fonts available on your system.

4
Working with Images explains how to create a BufferedImage object, perform
image-filtering operations, and draw on an image.

Printing teaches you how to render 2D graphics to a printer, print complex


documents, and use Print Services.

Advanced topics in Java 2D explains how to perform transformations, clip the


drawing region, composite overlapping graphics, specify rendering preferences,
and control rendering quality.

5
Lesson: Overview of the Java 2D API Concepts
The Java 2D™ API provides two-dimensional graphics, text, and imaging
capabilities for Java™ programs through extensions to the Abstract Windowing
Toolkit (AWT). This comprehensive rendering package supports line art, text, and
images in a flexible, full-featured framework for developing richer user interfaces,
sophisticated drawing programs, and image editors. Java 2D objects exist on a
plane called user coordinate space, or just user space. When objects are rendered
on a screen or a printer, user space coordinates are transformed to device space
coordinates. The following links are useful to start learning about the Java 2D
API:

 Graphics class
 Graphics2D class

The Java 2D API provides following capabilities:

 A uniform rendering model for display devices and printers


 A wide range of geometric primitives, such as curves, rectangles, and
ellipses, as well as a mechanism for rendering virtually any geometric
shape
 Mechanisms for performing hit detection on shapes, text, and images
 A compositing model that provides control over how overlapping objects
are rendered
 Enhanced color support that facilitates color management
 Support for printing complex documents
 Control of the quality of the rendering through the use of rendering hints

These topics are discussed in the following sections:

 Java 2D Rendering
 Geometric Primitives
 Text
 Images
 Printing

6
Coordinates
The Java 2D™ API maintains two coordinate spaces.

 User space – The space in which graphics primitives are specified


 Device space – The coordinate system of an output device such as a screen,
window, or a printer

User space is a device-independent logical coordinate system, the coordinate space


that your program uses. All geometries passed into Java 2D rendering routines are
specified in user-space coordinates.

When the default transformation from user space to device space is used, the origin
of user space is the upper-left corner of the component’s drawing area. The x
coordinate increases to the right and the y coordinate increases downward, as
shown in the following figure. The top-left corner of a window is 0,0. All
coordinates are specified using integers, which is usually sufficient. However,
some cases require floating point or even double precision which are also
supported.

Device space is a device-dependent coordinate system that varies according to the


target rendering device. Although the coordinate system for a window or screen
might be very different from the coordinate system of a printer, these differences
are invisible to Java programs. The necessary conversions between user space and
device space are performed automatically during rendering.

7
Java 2D Rendering
The Java 2D™ API provides a uniform rendering model across different types of
devices. At the application level, the rendering process is the same whether the
target rendering device is a screen or a printer. When a component needs to be
displayed, its paint or update method is automatically invoked with the
appropriate Graphics context.

The Java 2D API includes the java.awt.Graphics2D class, which extends the
Graphics class to provide access to the enhanced graphics and rendering features
of the Java 2D API. These features include:

 Rendering the outline of any geometric primitive, using the stroke and
paint attributes (draw method).
 Rendering any geometric primitive by filling its interior with the color or
pattern specified by the paint attributes (fill method).
 Rendering any text string (the drawString method). The font attribute is
used to convert the string to glyphs, which are then filled with the color or
pattern specified by the paint attributes.
 Rendering the specified image (the drawImage method).

In addition, the Graphics2D class supports the Graphics rendering methods for
particular shapes, such as drawOval and fillRect. All methods that are
represented above can be divided into two groups:

1. Methods to draw a shape


2. Methods that affect rendering

The second group of the methods uses the state attributes that form the
Graphics2D context for following purposes:

 To vary the stroke width


 To change how strokes are joined together
 To set a clipping path to limit the area that is rendered
 To translate, rotate, scale, or shear objects when they are rendered
 To define colors and patterns to fill shapes with
 To specify how to compose multiple graphics objects

To employ Java 2D API features in the application, cast the Graphics object
passed into a component’s rendering method to a Graphics2D object. For
example:

public void paint (Graphics g) {


Graphics2D g2 = (Graphics2D) g;
...
}

As the following figure shows, the Graphics2D class rendering context contains
several attributes.

8
The pen attribute is applied to the outline of a shape. This stroke attribute
enables you to draw lines with any point size and dashing pattern and apply
end-cap and join decorations to a line.

The fill attribute is applied to a shape's interior. This paint attribute enables you
to fill shapes with solid colors, gradients, and patterns.

The compositing attribute is used when rendered objects overlap existing


objects.

The transform attribute is applied during rendering to convert the rendered


object from user space to device-space coordinates. Optional translation,
rotation, scaling, or shearing transforms can also be applied through this
attribute.

The clip, type restricts rendering to the area within the outline of the Shape
object used to define the clipping path. Any Shape object that is used to define
the clip.

The font attribute is used to convert text strings to glyphs.

Rendering hints specify preferences in the trade-offs between speed and


quality. For example, you can specify whether antialiasing should be used, if
this feature available. See also Controlling Rendering Quality.

To learn more about transforming and compositing see the Advanced Topics in
Java2D lesson.

When an attribute is set, the appropriate attribute object is passed. As the following
example shows, to change the paint attribute to a blue-green gradient fill, you
construct a GradientPaint object and then call the setPaint method.

gp = new GradientPaint(0f,0f,blue,0f,30f,green);
g2.setPaint(gp);

9
Geometric Primitives
The Java 2D™ API provides a useful set of standard shapes such as points, lines,
rectangles, arcs, ellipses, and curves. The most important package to define
common geometric primitives is the java.awt.geom package. Arbitrary shapes
can be represented by combinations of straight geometric primitives.

The Shape interface represents a geometric shape, which has an outline and an
interior. This interface provides a common set of methods for describing and
inspecting two-dimensional geometric objects and supports curved line segments
and multiple sub-shapes. The Graphics class supports only straight line segments.
The Shape interface can support curves segments.

For more details about how to draw and fill shapes, see the Working with
Geometry lesson.

Points

The Point2D class defines a point representing a location in (x, y) coordinate


space. The term ―point‖ in the Java 2D API is not the same as a pixel. A point has
no area, does not contain a color, and cannot be rendered.

Points are used to create other shapes. ThePoint2D class also includes a method
for calculating the distance between two points.

Lines

The Line2D class is an abstract class that represents a line. A line’s coordinates can
be retrieved as double. The Line2D class includes several methods for setting a
line’s endpoints.

Also, you can create a straight line segment by using the GeneralPath class
described below.

Rectangular Shapes

The Rectangle2D, RoundRectangle2D, Arc2D, and Ellipse2D primitives are all


derived from the RectangularShape class. This class defines methods for Shape
objects that can be described by a rectangular bounding box. The geometry of a
RectangularShape object can be extrapolated from a rectangle that completely
encloses the outline of the Shape.

10
Quadratic and Cubic Curves

The QuadCurve2D enables you to create quadratic parametric curve segments. A


quadratic curve is defined by two endpoints and one control point.

The CubicCurve2D class enables you to create cubic parametric curve segments. A
cubic curve is defined by two endpoints and two control points. The following are
examples of quadratic and cubic curves. See Stroking and Filling for
implementations of cubic and quadratic curves.

This figure represents a quadratic curve.

This figure represents a cubic curve.

Arbitrary Shapes

The GeneralPath class enables you to construct an arbitrary shape by specifying a


series of positions along the shape’s boundary. These positions can be connected
by line segments, quadratic curves, or cubic (Bézier) curves. The following shape
can be created with three line segments and a cubic curve. See Stroking and Filling
for more information about the implementation of this shape.

Areas

With the Area class, you can perform boolean operations, such as union,
intersection, and subtraction, on any two Shape objects. This technique, often
referred to as constructive area geometry, enables you to quickly create complex
Shape objects without having to describe each line segment or curve.

11
Text
The Java 2D™ API has various text rendering capabilities including methods for
rendering strings and entire classes for setting font attributes and performing text
layout.

If you just want to draw a static text string, the most direct way to render it directly
through the Graphics class by using the drawString method. To specify the font,
you use the setFont method of the Graphics class.

If you want to implement your own text-editing routines or need more control over
the layout of the text than the text components provide, you can use the Java 2D
text layout classes in the java.awt.font package.

Fonts

The shapes that a font uses to represent the characters in a string are called glyphs.
A particular character or combination of characters might be represented as one or
more glyphs. For example, á might be represented by two glyphs, whereas the
ligature fi might be represented by a single glyph.

A font can be thought of as a collection of glyphs. A single font might have many
faces, such as italic and regular. All of the faces in a font have similar typographic
features and can be recognized as members of the same family. In other words, a
collection of glyphs with a particular style form a font face. A collection of font
faces forms a font family. The collection of font families forms the set of fonts that
are available on the system.

When you are using the Java 2D API, you specify fonts by using an instance of
Font. You can determine what fonts are available by calling the static method
GraphicsEnvironment.getLocalGraphicsEnvironment and then querying the
returned GraphicsEnvironment. The getAllFonts method returns an array that
contains Font instances for all of the fonts available on the system. The
getAvailableFontFamilyNames method returns a list of the available font
families.

Text Layout

Before text can be displayed, it must be laid out so that the characters are
represented by the appropriate glyphs in the proper positions. The following are
two Java 2D mechanisms for managing text layout:

 The TextLayout class manages text layout, highlighting, and hit detection.
The facilities provided by TextLayout handle the most common cases,
including strings with mixed fonts, mixed languages, and bidirectional text.
.
 You can create the own GlyphVector objects by using the Font class and
then rendering each GlyphVector object through the Graphics2D class.
Thus, you can completely control how text is shaped and positioned.

12
Rendering Hints for Text

The Java 2D API enables you to control the quality of shapes and text rendering by
using rendering hints. Rendering hints are encapsulated by the
java.awt.RenderingHints class.

As applied to text, this capability is used for antialiasing (which is also known as
an smooth edges). For example, the KEY_TEXT_ANTIALIASING hint enables you to
control the antialiasing of text separately from the antialiasing of other shapes. To
learn more about rendering hints see the Controlling Rendering Quality lesson.

13
Images
In the Java 2D™ API an image is typically a rectangular two-dimensional array of
pixels, where each pixel represents the color at that position of the image and
where the dimensions represent the horizontal extent (width) and vertical extent
(height) of the image as it is displayed.

The most important image class for representing such images is the
java.awt.image.BufferedImage class. The Java 2D API stores the contents of
such images in memory so that they can be directly accessed.

Applications can directly create a BufferedImage object or obtain an image from


an external image format such as PNG or GIF.

In either case, the application can then draw on to image by using Java 2D API
graphics calls. So, images are not limited to displaying photographic type images.
Different objects such as line art, text, and other graphics and even other images
can be drawn onto an image (as shown on the following images).

The Java 2D API enables you to apply image filtering operations to


BufferedImage and includes several built-in filters. For example, the ConvolveOp
filter can be used to blur or sharpen images.

The resulting image can then be drawn to a screen, sent to a printer, or saved in a
graphics format such as PNG, GIF etc. To learn more about images see the
Working with Images lesson.

14
Printing
All of the Swing and Java 2D™ graphics, including composited graphics and
images, can be rendered to a printer by using the Java 2D Printing API. This API
also provides document composition features that enable you to perform such
operations as changing the order in which pages are printed.

Rendering to a printer is like rendering to a screen. The printing system controls


when pages are rendered, just like the drawing system controls when a component
is painted on the screen.

The Java 2D Printing API is based on a callback model in which the printing
system, not the application, controls when pages are printed. The application
provides the printing system with information about the document to be printed,
and the printing system determines when each page needs to be imaged.

The following two features are important to support printing:

 Job control – Initiating and managing the print job including displaying
the standard print and setup dialog boxes
 Pagination – Rendering each page when the printing system requests it

When pages need to be imaged, the printing system calls the application’s print
method with an appropriate Graphics context. To use Java 2D API features when
you print, you cast the Graphics object to a Graphics2D class, just like you do
when you are rendering to the screen.

15
Lesson: Getting Started with Graphics
The Java 2D™ API is powerful and complex. However, the vast majority of uses
for the Java 2D API utilize a small subset of its capabilities encapsulated in the
java.awt.Graphics class. This lesson covers the most common needs of
applications developers. Less common needs are described later in the Advanced
topics in the Java 2D API lesson.

Most methods of the Graphics class can be divided into two basic groups:

 Draw and fill methods, enabling you to render basic shapes, text, and
images
 Attributes setting methods, which affect how that drawing and filling
appears

Methods such as setFont and setColor define how draw and fill methods render.

This figure illustrates how these methods relate to graphic objects:

Drawing methods include:

 drawString – For drawing text

g.drawString("Hello", 10, 10);

 drawImage – For drawing images

g.drawImage(img,
0, 0, width, height,
0, 0, imageWidth, imageHeight,
null);

 drawLine, drawArc, drawRect, drawOval, drawPolygon – For drawing


geometric shapes

16
g2.draw(new Line2D.Double(0, 0, 30, 40));

Depending on your current need, you can choose one of several methods in the
Graphics class based on the following criteria:

 Whether you want to render the image at the specified location in its
original size or scale it to fit inside the given rectangle
 Whether you prefer to fill the transparent areas of the image with color or
keep them transparent

Fill methods apply to geometric shapes and include fillArc, fillRect,


fillOval, fillPolygon.

Whether you draw a line of text or an image, remember that in 2D graphics every
point is determined by its x and y coordinates. All of the draw and fill methods
need this information which determines where the text or image should be
rendered.

For example, to draw a line, an application calls the following:

java.awt.Graphics.drawLine(int x1, int y1, int x2, int y2)

In this code (x1, y1) is the start point of the line, and (x2, y2) is the end point of the
line.

So the code to draw a horizontal line is as follows:

Graphics.drawLine(20, 100, 120, 100);

The demo below accumulates all mentioned techniques. Move the slider to display
various weather types.

Note: If you don't see the applet running, you need to install release 6 of the Java
SE Development Kit (JDK).

The WeatherWizard demo uses the JSlider component as well as various


graphics capabilities to generate and display a specified weather type. For more
information about the JSlider class see the How to Use Sliders section of the
Swing Tutorial.

The paint method of the WeatherPainter class implements graphics features.


The following code draws an image determined by using the
setupWeatherReport() method.

...
origComposite = g2.getComposite();

17
if (alpha0 != null) g2.setComposite(alpha0);
g2.drawImage(img0,
0, 0, size.width, size.height,
0, 0, img0.getWidth(null), img0.getHeight(null),
null);
if (img1 != null) {
if (alpha1 != null) g2.setComposite(alpha1);
g2.drawImage(img1,
0, 0, size.width, size.height,
0, 0, img1.getWidth(null),
img1.getHeight(null),
null);
}
...

The setFont and drawString methods render the temperature and the weather
condition.

...
// Freezing, Cold, Cool, Warm, Hot,
// Blue, Green, Yellow, Orange, Red
Font font = new Font("Serif", Font.PLAIN, 36);
g.setFont(font);

String tempString = feels + " " + temperature+"F";


FontRenderContext frc =
((Graphics2D)g).getFontRenderContext();
...
g.setColor(textColor);
int xTextTemp = rX-(int)boundsTemp.getX();
int yTextTemp = rY-(int)boundsTemp.getY();
g.drawString(tempString, xTextTemp, yTextTemp);

int xTextCond = rX-(int)boundsCond.getX();


int yTextCond = rY-(int)boundsCond.getY() +
(int)boundsTemp.getHeight();
g.drawString(condStr, xTextCond, yTextCond);

The fillRect method allows you to draw a rectangle filled with the specified
color.

...
Rectangle2D boundsTemp = font.getStringBounds(tempString,
frc);
Rectangle2D boundsCond = font.getStringBounds(condStr,
frc);
int wText = Math.max((int)boundsTemp.getWidth(),
(int)boundsCond.getWidth());
int hText = (int)boundsTemp.getHeight() +
(int)boundsCond.getHeight();
int rX = (size.width-wText)/2;
int rY = (size.height-hText)/2;

g.setColor(Color.LIGHT_GRAY);
g2.fillRect(rX, rY, wText, hText);
...

18
Try to modify the WeatherWizard demo to alter the graphical content. For
example, use the fillRoundRect method instead of fillRect or apply another
font size in the setFont method. Find the complete code for this applet in the
WeatherWizard.java file. The demo also requires the following images:
weather-cloud.png, weather-rain.png, weather-snow.png, and weather-
sun.png located in the images directory.

19
Lesson: Working with Geometry
In prior lessons, you have learned the graphics concept, including basic
information about the coordinate system and graphic object creation. Now, you
will progress to more detailed lessons about the 2D graphics classes. This lesson
shows you how to use the Graphics2D class to draw graphic primitives as well as
arbitrary shapes, and how to display graphics with fancy outline and fill styles.
These topics are discussed in the following sections.
Drawing Geometric Primitives
This section explains how to create standard shapes such as points, lines, curves,
arcs, rectangles, and ellipses.
Drawing Arbitrary Shapes
This section explains how to draw shapes represented by combinations of straight
geometric primitives by using the GeneralPath class.
Filling and Stroking
This section explains how to set the stroke and paint attributes to control the
outline and fill styles applied to Shape objects and text.

20
Drawing Geometric Primitives
The Java 2D™ API provides several classes that define common geometric objects
such as points, lines, curves, and rectangles. These geometry classes are part of the
java.awt.geom package.
The PathIterator interface defines methods for retrieving elements from a path.
The Shape interface provides a set of methods for describing and inspecting
geometric path objects. This interface is implemented by the GeneralPath class
and other geometry classes.

All examples represented in this section create geometries by using


java.awt.geom and then render them by using the Graphics2D class. To begin
you obtain a Graphics2D object, for example by casting the Graphics parameter
of the paint() method.

public void paint (Graphics g) {


Graphics2D g2 = (Graphics2D) g;
...
}

Point

The Point class creates a point representing a location in (x,y) coordinate space.
The subclasses Point2D.Float and Point2D.Double provide correspondingly
float and double precision for storing the coordinates of the point.
//Create Point2D.Double
Point2D.Double point = new Point2D.Double(x, y);
To create a point with the coordinates 0,0 you use the default constructor,
Point2D.Double().
You can use the setLocation method to set the position of the point as follows:

 setLocation(double x, double y) – To set the location of the point-


defining coordinates as double values
 setLocation(Point2D p) – To set the location of the point using the
coordinates of another point.

Also, the Point2D class has methods to calculate the distance between the current
point and a point with given coordinates, or the distance between two points.

Line

The Line2D class represents a line segment in (x, y) coordinate space. The
Line2D.Float and Line2D.Double subclasses specify lines in float and double
precision. For example:

// draw Line2D.Double
g2.draw(new Line2D.Double(x1, y1, x2, y2));

21
This class includes several setLine() methods to define the endpoints of the line.
Aternatively, the endpoints of the line could be specified by using the constructor
for the Line2D.Float class as follows:

 Line2D.Float(float X1, float Y1, float X2, float Y2)


 Line2D.Float(Point2D p1, Point2D p2)

Use the Stroke object in the Graphics2D class to define the stroke for the line
path.

Curves

The java.awt.geom package enables you to create a quadratic or cubic curve


segment.

Quadratic Curve Segment

The QuadCurve2D class implements the Shape interface. This class represents a
quadratic parametric curve segment in (x, y) coordinate space. The
QuadCurve2D.Float and QuadCurve2D.Double subclasses specify a quadratic
curve in float and double precision.

Several setCurve methods are used to specify two endpoints and a control point of
the curve, whose coordinates can be defined directly, by the coordinates of other
points and by using a given array.
A very useful method, setCurve(QuadCurve2D c), sets the quadratic curve with
the same endpoints and the control point as a supplied curve. For example:

// create new QuadCurve2D.Float


QuadCurve2D q = new QuadCurve2D.Float();
// draw QuadCurve2D.Float with set coordinates
q.setCurve(x1, y1, ctrlx, ctrly, x2, y2);
g2.draw(q);

Cubic Curve Segment

The CubicCurve2D class also implements the Shape interface. This class
represents a cubic parametric curve segment in (x, y) coordinate space.
CubicCurve2D.Float and CubicCurve2D.Double subclasses specify a cubic
curve in float and double precision.

The CubicCurve2D class has similar methods for setting the curve as the
QuadraticCurve2Dclass, except with a second control point. For example:

// create new CubicCurve2D.Double


CubicCurve2D c = new CubicCurve2D.Double();
// draw CubicCurve2D.Double with set coordinates
c.setCurve(x1, y1, ctrlx1,
ctrly1, ctrlx2, ctrly2, x2, y2);

22
g2.draw(c);

Rectangle

Classes that specify primitives represented in the following example extend the
RectangularShape class, which implements the Shape interface and adds a few
methods of its own.

These methods enables you to get information about a shape’s location and size, to
examine the center point of a rectangle, and to set the bounds of the shape.

The Rectangle2D class represents a rectangle defined by a location (x, y) and


dimension (w x h). The Rectangle2D.Float and Rectangle2D.Double
subclasses specify a rectangle in float and double precision. For example:

// draw Rectangle2D.Double
g2.draw(new Rectangle2D.Double(x, y,
rectwidth,
rectheight));

The RoundRectangle2D class represents a rectangle with rounded corners defined


by a location (x, y), a dimension (w x h), and the width and height of the corner
arc. The RoundRectangle2D.Float and RoundRectangle2D.Double subclasses
specify a round rectangle in float and double precision.

The rounded rectangle is specified with following parameters:

 Location
 Width
 Height
 Width of the corner arc
 Height of the corner acr

To set the location, size, and arcs of a RoundRectangle2D object, use the method
setRoundRect(double a, double y, double w, double h, double
arcWidth, double arcHeight). For example:

// draw RoundRectangle2D.Double
g2.draw(new RoundRectangle2D.Double(x, y,
rectwidth,
rectheight,
10, 10));

Ellipse

The Ellipse2D class represents an ellipse defined by a bounding rectangle. The


Ellipse2D.Float and Ellipse2D.Double subclasses specify an ellipse in float
and double precision.

23
Ellipse is fully defined by a location, a width and a height. For example:

// draw Ellipse2D.Double
g2.draw(new Ellipse2D.Double(x, y,
rectwidth,
rectheight));

Arc

To draw a piece of an ellipse, you use the Arc2D class. This class represents an arc
defined by a bounding rectangle, a start angle, an angular extent, and a closure
type. The Arc2D.Float and Arc2D.Double subclasses specify an ellipse in float
and double precision.

The Arc2D class defines the following three types of arcs, represented by
corresponding constants in this class: OPEN, PIE and CHORD.

Several methods set the size and parameters of the arc:

 Directly, by coordinates
 By supplied Point2D and Dimension2D
 By copying an existing Arc2D

Also, you can use the setArcByCenter method to specify an arc from a center
point, given by its coordinates and a radius.

// draw Arc2D.Double
g2.draw(new Arc2D.Double(x, y,
rectwidth,
rectheight,
90, 135,
Arc2D.OPEN));

The ShapesDemo2D.java code example contains implementations off all described


geometric primitives. For more information about classes and methods represented
in this section see the java.awt.geom specification.

24
Drawing Arbitrary Shapes
You have already learned how to draw most of shapes represented in the
java.awt.geom package. To create more complicated geometry, such as polygons,
polylines, or stars you use another class from this package, GeneralPath.

This class implements the Shape interface and represents a geometric path
constructed from lines, and quadratic and cubic curves. The three constructors in
this class can create the GeneralPath object with the default winding rule
(WIND_NON_ZERO), the given winding rule (WIND_NON_ZERO or WIND_EVEN_ODD), or
the specified initial coordinate capacity. The winding rule specifies how the
interior of a path is determined.

public void Paint (Graphics g) {


Graphics2D g2 = (Graphics2D) g;
...
}

To create an empty GeneralPath instance call new GeneralPath() and then add
segments to the shape by using the following methods:

 moveTo(float x, float y) – Moves the current point of the path to the


given point
 lineTo(float x, float y) – Adds a line segment to the current path
 quadTo(float ctrlx, float ctrly, float x2, floaty2) – Adds a
quadratic curve segment to the current path
 curveTo(float ctrlx1, float ctrly1, float ctrlx2, float
ctrly2, float x3, floaty3) – Adds a cubic curve segment to the
current path
 closePath() – Closes the current path

The following example illustrates how to draw a polyline by using GeneralPath:

// draw GeneralPath (polyline)


int x2Points[] = {0, 100, 0, 100};
int y2Points[] = {0, 50, 50, 0};
GeneralPath polyline =
new GeneralPath(GeneralPath.WIND_EVEN_ODD,
x2Points.length);

polyline.moveTo (x2Points[0], y2Points[0]);

for (int index = 1; index < x2Points.length; index++) {


polyline.lineTo(x2Points[index],
y2Points[index]);
};

g2.draw(polyline);

This example illustrates how to draw a polygon by using GeneralPath:

25
// draw GeneralPath (polygon)
int x1Points[] = {0, 100, 0, 100};
int y1Points[] = {0, 50, 50, 0};
GeneralPath polygon =
new GeneralPath(GeneralPath.WIND_EVEN_ODD,
x1Points.length);
polygon.moveTo(x1Points[0], y1Points[0]);

for (int index = 1; index < x1Points.length; index++) {


polygon.lineTo(x1Points[index], y1Points[index]);
};

polygon.closePath();
g2.draw(polygon);

Note that the only difference between two last code examples is the closePath()
method. This method makes a polygon from a polyline by drawing a straight line
back to the coordinates of the last moveTo.

To add a specific path to the end of your GeneralPath object you use one of the
append() methods. The ShapesDemo2D.java code example contains additional
implementations of arbitrary shapes.

26
Stroking and Filling Graphics Primitives

You already know how to create different geometric primitives and more
complicated shapes. This lesson teaches how to add some color and fancy outlines
to your graphics and represents filling and stroking:

 Filling – is a process of painting the shape’s interior with solid color or a


color gradient, or a texture pattern
 Stroking – is a process of drawing a shape’s outline applying stroke width,
line style, and color attribute

To apply fancy line styles and fill patterns to geometric primitives change the
stroke and paint attributes in the Graphics2D context before rendering. For
example, draw a dashed line by creating an appropriate Stroke object. To add this
stroke to the Graphics2D context before you render the line call the setStroke
method . Similarly, you apply a gradient fill to a Shape object by creating a
GradientPaint object and adding it to the Graphics2D context.

The following code lines enrich geometric primitives with filling and stroking
context:

// draw RoundRectangle2D.Double

final static float dash1[] = {10.0f};


final static BasicStroke dashed = new
BasicStroke(1.0f,

BasicStroke.CAP_BUTT,

BasicStroke.JOIN_MITER,
10.0f, dash1,
0.0f);
g2.setStroke(dashed);
g2.draw(new RoundRectangle2D.Double(x, y,
rectWidth,
rectHeight,
10, 10));

// fill Ellipse2D.Double
redtowhite = new GradientPaint(0,0,color.RED,100,
0,color.WHITE);
g2.setPaint(redtowhite);
g2.fill (new Ellipse2D.Double(0, 0, 100, 50));

The ShapesDemo2D.java code example represents additional implementations of


stoking and filling.

Defining Fancy Line Styles and Fill Patterns

Using the Java 2D™ Stroke and Paint classes, you can define fancy line styles
and fill patterns.

27
Line Styles

Line styles are defined by the stroke attribute in the Graphics2D rendering context.
To set the stroke attribute, you create a BasicStroke object and pass it into the
Graphics2D setStroke method.

A BasicStroke object holds information about the line width, join style, end-cap
style, and dash style. This information is used when a Shape is rendered with the
draw method.

The line width is the thickness of the line measured perpendicular to its trajectory.
The line width is specified as a float value in user coordinate units, which are
roughly equivalent to 1/72 of an inch when the default transform is used.

The join style is the decoration that is applied where two line segments meet.
BasicStroke supports the following three join styles:

JOIN_BEVEL

JOIN_MITER

JOIN_ROUND

The end-cap style is the decoration that is applied where a line segment ends.
BasicStroke supports the following three end-cap styles:

CAP_BUTT

CAP_ROUND

CAP_SQUARE

The dash style defines the pattern of opaque and transparent sections applied along
the length of the line. The dash style is defined by a dash array and a dash phase.
The dash array defines the dash pattern. Alternating elements in the array
represent the dash length and the length of the space between dashes in user
coordinate units. Element 0 represents the first dash, element 1 the first space, and
so on. The dash phase is an offset into the dash pattern, also specified in user
coordinate units. The dash phase indicates what part of the dash pattern is applied
to the beginning of the line.

Fill Patterns

Fill patterns are defined by the paint attribute in the Graphics2D rendering context.
To set the paint attribute, you create an instance of an object that implements the
Paint interface and pass it into the Graphics2D setPaint method.

28
The following three classes implement the Paint interface: Color,
GradientPaint, and TexturePaint.

To create a GradientPaint, you specify a beginning position and color and an


ending position and color. The gradient changes proportionally from one color to
the other color along the line connecting the two positions. For example:

The pattern for a TexturePaint class is defined by a BufferedImage class. To


create a TexturePaint object, you specify the image that contains the pattern and
a rectangle that is used to replicate and anchor the pattern. The following image
represents this feature:

29
Lesson: Working with Text APIs

This lesson introduces you to the concept of working with text API’s to apply text
rendering capabilities. Already in this trail, you have used basic Java 2D™ text
APIs and know how to set a font and position, and how to draw text.

This lesson expands on that material to help you develop an understanding of how
to use those APIs and moves further into the capabilities of Java 2D text display.

These topics are discussed in the following sections.

Selecting a font
This section explains how to use the methods of the Font class to determine which
fonts are available on your system, to create a Font object, and obtain information
about a font family.
Measuring Text
This section explains how to properly measure text by using an instance of the
FontMetrics class.
Advanced Text Display
This section explains how to position and render a paragraph of styled text, how to
display antialiased text, and how to use text attributes to style text.

30
Selecting a Font

Java 2D™ defines the following five logical font families:

 Dialog
 DialogInput
 Monospaced
 Serif
 SansSerif

These fonts are available on any Java platform and can be thought of as aliases for
some underlying font that has the properties implied by its name. A Serif font is a
font similar to Times New Roman, which is commonly used in print. A Sans Serif
font is more typical for onscreen use.

These fonts can be customized for the locale of the user. In addition these fonts
support the widest range of code points (unicode characters).

Apart from the family, fonts have other attributes, the most important of which are
style and size. Styles are Bold and Italic.

The default Java 2D font is 12 pt Dialog. This font is a typical point size for
reading text on a normal 72–120 DPI display device. An application can create an
instance of this font directly by specifying the following:

Font font = new Font("Dialog", Font.PLAIN, 12);

In addition to the logical fonts, Java software provides access to other fonts that are
installed on your system. The names of all available font families can be found by
calling the following:

GraphicsEnvironment ge =
GraphicsEnvironment.getLocalGraphicsEnvironment();
String []fontFamilies = ge.getAvailableFontFamilyNames();

The FontSelector sample program (available in FontSelector.java) illustrates


how to locate and select these fonts. You can use this example to view how Sans
Serif appears on your system. These other fonts are called physical fonts.

Note: Applications should not assume that any particular physical font is present.
However, the logical fonts are a safe choice because they are always present.

Note: If you don't see the applet running, you need to install release 6 of the Java
SE Development Kit (JDK).

31
Sometimes, an application cannot depend on a font being installed on the system,
usually because the font is a custom font that is not otherwise available. In this
case, the application must include the font. This lesson shows how to obtain a
TrueType font, the most commonly used font type on modern operating systems,
to a java.awt.Font object.

You can use either of these methods:

Font java.awt.Font.createFont(int fontFormat, InputStream in);


Font java.awt.Font.createFont(int fontFormat, File fontFile);
To identify a TrueType font, fontFormat must be the constant
Font.TRUETYPE_FONT.

Font font = Font.createFont(Font.TRUETYPE_FONT, new


File("A.ttf"));

Accessing the font directly from a file must be more convenient for some cases.
However, an InputStream might be needed if your code is unable to access file
system resources, or if the font is packaged in a Java Archive (JAR) file along with
the rest of the application or applet.

The returned Font instance can then be used with the Font.deriveFont(..)
methods to derive a version that is the required size. For example:

try {
/* Returned font is of pt size 1 */
Font font = Font.createFont(Font.TRUETYPE_FONT, new
File("A.ttf"));

/* derive and return a 12 pt version : need to use float


otherwise
* it would be interpreted as style
*/
return font.deriveFont(12f);

} catch (IOException ioe);


} catch (FontFormatException ffe);
}

It is important to use deriveFont() because fonts which are created by


application are not part of the set of fonts known to the underlying font system.
Because deriveFont works from the original created font it does not have this
limitation.

The solution for this problem is to register the created font with the graphics
environment. For example:

try {

32
GraphicsEnvironment ge =
GraphicsEnvironment.getLocalGraphicsEnvironment();
ge.registerFont(Font.createFont(Font.TRUETYPE_FONT, new
File("A.ttf"));
} catch (IOException ioe);
} catch (FontFormatException ffe);
}
After this step is done, the font is available in calls to
getAvailableFontFamilyNames() and can be used in font constructors.

33
Measuring Text

To properly measure text, you need to learn a few methods and some mistakes to
avoid. Font metrics are measurements of text rendered by a Font object such as the
height of a line of text in the font. The most common way to measure text is to use
a FontMetrics instance which encapsulates this metrics information. For
example:

// get metrics from the graphics


FontMetrics metrics = graphics.getFontMetrics(font);
// get the height of a line of text in this font and render
context
int hgt = metrics.getHeight();
// get the advance of my text in this font and render context
int adv = metrics.stringWidth(text);
// calculate the size of a box to hold the text with some
padding.
Dimension size = new Dimension(adv+2, hgt+2);
This way is sufficient for many applications to evenly space lines of text or to size
Swing components.

Note the following:

 The metrics are obtained from the Graphics class, because this class
encapsulates the FontRenderContext, which is needed to accurately
measure text. At screen resolutions, fonts are adjusted for ease of reading.
As text size increases, this adjustment does not scale linearly. So, at 20 pt, a
font will not display text exactly twice the length as it would at 10 pt.
Besides the text itself and the font, the other important piece of information
needed to measure text is the FontRenderContext. This method includes
the transform from user space to device pixels that is used in measuring
text.
 The height is reported without reference to any particular string of text. It is
useful, for example in, a text editor where you want the same line spacing
between each line of text.
 stringWidth() returns the advance width of the text. Advance width is the
distance from the origin of the text to the position of a subsequently
rendered string.

When using these methods to measure text, note that the text can extend in
any direction outside of a rectangle, defined by the font height and the
advance of the string.

34
Typically, the simplest solution is to ensure that the text is not clipped, for
example, by components that surround the text. Add padding in cases
where the text might otherwise be clipped.

If this solution is insufficient, other text measurement APIs in the Java


2D™ software can return rectangular bounding boxes. These boxes
account for the height of the specific text to be measured and for
pixelization effects.

35
Advanced Text Display

The Java 2D™ API provides mechanisms to support sophisticated text layout. This
section describes following features of advanced text display.

Displaying Antialiased Text by Using Rendering Hints


This section explains how to control the rendering quality through the use of
rendering hints.
Using Text Attributes to Style Text
This section explains how to use the TextAttribute class to underline or
strikethough text.
Drawing Multiple Lines of Text
This section explains how to position and render a paragraph of styled text by
using the TextLayout and LineBreakMeasurer classes.

36
Displaying Antialiased Text by Using Rendering Hints

Java 2D™ text rendering can be affected by rendering hints.

Recall that the most important text drawing method is the following:

Graphics.drawString(String s, int x, int y);


Usually, this method draws each glyph in a string of text with a solid color and
each pixel that is ―on‖ in that glyph is set to that colour. This type of drawing
produces the highest contrast text, but sometimes with jagged (aliased) edges.

Text antialiasing is a technique used to smooth the edges of text on a screen. The
Java 2D API enables applications to specify whether this technique should be used
and what algorithm to use by applying a text rendering hint to the Graphics.

The most common rendering hint blends the foreground (text) color with the
onscreen background pixels at the edges of the text. To request this hint an
application must invoke the following:

graphics2D.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,

RenderingHints.VALUE_TEXT_ANTIALIAS_ON);

The following figure illustrates the antialiasing capability.

If used inappropriately this method can make the text appear overly fuzzy. In such
cases, a better hint to use is the following:

graphics2D.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,

RenderingHints.VALUE_TEXT_ANTIALIAS_GASP);

This method automatically uses information in the font itself to decide whether to
use antialiasing or to use solid colors.

LCD displays have a property that the Java 2D API can use to produce text that
isn't as fuzzy as typical antialiasing but is more legible at small sizes. To request
that text be drawn using the sub-pixel LCD text mode for a typical LCD display,
an application must invoke the following:

graphics2D.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,

RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB);

37
The code example represented below illustrates the antialiasing capability in the
following order:

1. Antialiasing is off.
2. Antialiasing is on.
3. Antialiasing using the TEXT_ANTIALIAS_GASP hint.

Note: Consequently the GASP table specifies to use only hinting at those
sizes and not "smoothing". So in many cases the resulting text display is
equivalent to VALUE_TEXT_ANTIALIAS_OFF.

4. Antialiasing using the TEXT_ANTIALIAS_LCD_HRGB hint.

Note: If you don't see the applet running, you need to install release 6 of the Java
SE Development Kit (JDK).

The complete code for this applet is in AntialiasedText.java.

38
Using Text Attributes to Style Text

Applications typically need the capability to apply the following text attributes:

 Underline – A line that is drawn underneath text


 Strikethrough – A horizontal line that is drawn through the text
 Superscript or Subscript – A text or a letter that appears slightly above a
line or correspondingly below a line
 Kerning – The adjustment of the space between characters

These and other text attributes can be applied by using the Java 2D™
TextAttribute class.

To apply these text attributes by add them to a Font object. For example:

Map<TextAttribute, Object> map = new Hashtable<TextAttribute,


Object>();
map.put(TextAttribute.KERNING, TextAttribute.KERNING_ON);
font = font.deriveFont( map );
graphics.setFont( font );

The code example represented below shows the application of text attributes in the
following order:

1. Sample string (no text attributes applied)


2. Kerning
3. Kerning and Underlining
4. Kerning,Underlining and Strikethrough
5. Kerning,Underlining, Strikethrough and Color

Note: If you don't see the applet running, you need to install release 6 of the Java
SE Development Kit (JDK).

The complete code for this applet is in AttributedText.java.

39
Drawing Multiple Lines of Text

If you have a paragraph of styled text that you would like to fit within a specific
width, you can use the LineBreakMeasurer class. This class enables styled text to
be broken into lines so that they fit within a particular visual advance. Each line is
returned as a TextLayout object, which represents unchangeable, styled character
data. However, this class also enables access to layout information. The
getAscent and getDescent methods of TextLayout return information about the
font that is used to position the lines in the component. The text is stored as an
AttributedCharacterIterator object so that the font and point size attributes
can be stored with the text.

The following applet positions a paragraph of styled text within a component,


using LineBreakMeasurer, TextLayout and AttributedCharacterIterator.

Note: If you don't see the applet running, you need to install release 6 of the Java
SE Development Kit (JDK).

The complete code for this applet is in LineBreakSample.java.

The following code creates an iterator with the string vanGogh. The start and end
of the iterator is retrieved and a new LineBreakMeasurer is created from the
iterator.

AttributedCharacterIterator paragraph = vanGogh.getIterator();


paragraphStart = paragraph.getBeginIndex();
paragraphEnd = paragraph.getEndIndex();
FontRenderContext frc = g2d.getFontRenderContext();
lineMeasurer = new LineBreakMeasurer(paragraph, frc);

The size of the window is used to determine where the line should break. Also a
TextLayout object is created for each line in the paragraph.

// Set break width to width of Component.


float breakWidth = (float)getSize().width;
float drawPosY = 0;
// Set position to the index of the first character in the
paragraph.
lineMeasurer.setPosition(paragraphStart);

40
// Get lines from until the entire paragraph has been
displayed.
while (lineMeasurer.getPosition() < paragraphEnd) {

TextLayout layout =
lineMeasurer.nextLayout(breakWidth);

// Compute pen x position. If the paragraph is right-


to-left we
// will align the TextLayouts to the right edge of the
panel.
float drawPosX = layout.isLeftToRight()
? 0 : breakWidth - layout.getAdvance();

// Move y-coordinate by the ascent of the layout.


drawPosY += layout.getAscent();

// Draw the TextLayout at (drawPosX, drawPosY).


layout.draw(g2d, drawPosX, drawPosY);

// Move y-coordinate in preparation for next layout.


drawPosY += layout.getDescent() + layout.getLeading();
}

The TextLayout class is not frequently created directly by applications. However,


this class is useful when applications need to work directly with text that has had
styles (text attributes) applied at specific positions in text. For example, to draw a
single word italicized in a paragraph, an application would need to perform
measurements and set the font for each substring. If the text is bidirectional, this
task is not so easy to do correctly. Creating a TextLayout object from an
AttributedString object handles this problem for you. Consult the Java SE
specification for more information about TextLayout.

41
Lesson: Working with Images

As you have already learned from the Overview lesson, Images are described by a
width and a height, measured in pixels, and have a coordinate system that is
independent of the drawing surface.

There are a number of common tasks when working with images.

 Loading an external GIF, PNG JPEG image format file into Java 2D™'s internal image
representation.
 Directly creating a Java 2D image and rendering to it.
 Drawing the contents of a Java 2D image on to a drawing surface.
 Saving the contents of a Java 2D image to an external GIF, PNG, or JPEG image file.

This lesson teaches you the basics of loading, displaying, and saving images.

The are two main classes that you must learn about to work with images:

 The java.awt.Image class is the superclass that represents graphical images as rectangular
arrays of pixels.
 The java.awt.image.BufferedImage class, which extends the Image class to allow the
application to operate directly with image data (for example, retrieving or setting up the pixel
color). Applications can directly construct instances of this class.

The BufferedImage class is a cornerstone of the Java 2D immediate-mode


imaging API. It manages the image in memory and provides methods for storing,
interpreting, and obtaining pixel data. Since BufferedImage is a subclass of Image
it can be rendered by the Graphics and Graphics2D methods that accept an Image
parameter.

A BufferedImage is essentially an Image with an accessible data buffer. It is


therefore more efficient to work directly with BufferedImage. A BufferedImage
has a ColorModel and a Raster of image data. The ColorModel provides a color
interpretation of the image's pixel data.

The Raster performs the following functions:

 Represents the rectangular coordinates of the image


 Maintains image data in memory
 Provides a mechanism for creating multiple subimages from a single image data buffer
 Provides methods for accessing specific pixels within the image

The basic operations with images are represented in the following sections:

Reading/Loading an image

This section explains how to load an image from an external image format into a
Java application using the Image I/O API

42
Drawing an image

This section teaches how to display images using the drawImage method of the
Graphics and Graphics2D classes.

Creating and drawing To an image

This section describes how to create an image and how to use the image itself as a
drawing surface.

Writing/saving an image

This section explains how to save created images in an appropriate format.

43
Reading/Loading an Image

When you think of digital images, you probably think of sampled image formats
such as the JPEG image format used in digital photography, or GIF images
commonly used on web pages. All programs that can use these images must first
convert them from that external format into an internal format.

Java 2D™ supports loading these external image formats into its BufferedImage
format using its Image I/O API which is in the javax.imageio package. Image
I/O has built-in support for GIF, PNG, JPEG, BMP, and WBMP. Image I/O is also
extensible so that developers or administrators can "plug-in" support for additional
formats. For example, plug-ins for TIFF and JPEG 2000 are separately available.

To load an image from a specific file use the following code:

BufferedImage img = null;


try {
img = ImageIO.read(new File("strawberry.jpg"));
} catch (IOException e) {
}

Image I/O recognises the contents of the file as a JPEG format image, and decodes
it into a BufferedImage which can be directly used by Java 2D.

LoadImageApp.java shows how to display this image.

If the code is running in an applet, then its just as easy to obtain the image from the
applet codebase :

try {
URL url = new URL(getCodeBase(), "strawberry.jpg");
img = ImageIO.read(url);
} catch (IOException e) {
}

The getCodeBase method used in this example returns the URL of the directory
containing this applet.

The following example shows how to use the getCodeBase method to load the
strawberry.jpg file.

Note: If you don't see the applet running, you need to install release 6 of the Java SE
Development Kit (JDK).

LoadImageApp.java contains the complete code for this example and this applet requires
the strawberry.jpg image file. In addition to reading from files or URLS, Image I/O can
read from other sources, such as an InputStream.

44
ImageIO.read() is the most straightforward convenience API for most applications, but
the javax.imageio.ImageIO provides many more static methods for more advanced
usages of the Image I/O API. The collection of methods on this class represent just a subset
of the rich set of APIs for discovering information about the images and for controlling the
image decoding (reading) process.

We will explore some of the other capabilities of Image I/O later in the Writing/saving an
image section. More information can be found in the Image I/O guide.

45
Drawing an Image

As you have already learned, the Graphics.drawImage method draws an image at a


specific location:

boolean Graphics.drawImage(Image img,


int x, int y,
ImageObserver observer);

The x,y location specifies the position for the top-left of the image. The observer
parameter notifies the application of updates to an image that is loaded asynchronously. The
observer parameter is not frequently used directly and is not needed for the
BufferedImage class, so it usually is null.

The described method addresses only the case where the entire image is to be drawn,
mapping image pixels to user space coordinates 1:1. Sometimes applications require to
draw a part of the image (a sub-image), or scale the image to cover a particular area of the
drawing surface, or transform or filter the image before drawing.

The overloads of the drawImage() method perform these operations. For example, the
following overload of the drawImage() method enables you to draw as much of a specified
area of the specified image as is currently available, scaling it to fit inside the specified area
of the destination drawable surface:

boolean Graphics.drawImage(Image img,


int dstx1, int dsty1, int dstx2, int dsty2,
int srcx1, int srcy1, int srcx2, int srcy2,
ImageObserver observer);

The src parameters represent the area of the image to copy and draw. The dst parameters
display the area of the destination to cover by the the source area. The dstx1, dsty1
coordinates define the location to draw the image. The width and height dimensions on the
destination area are calculated by the following expressions: (dstx2-dstx1), (dsty2-
dsty1). If the dimensions of the source and destinations areas are different, the Java 2D™
API will scale up or scale down, as needed.

The following code example divides an image into four quadrants and randomly draws each
quadrant of the source image into a different quadrant of the destination.

46
Note: If you don't see the applet running, you need to install release 6 of the Java SE
Development Kit (JDK).

The complete code for this applet is in JumbledImageApplet.java. This example uses the
following code to paint the jumbled duke_skateboard.jpg image. It iterates over the four
sub-images of the source, drawing each in turn into a randomly selected destination
quadrant.
/* divide the image 'bi' into four rectangular areas and draw each
* of these areas in to a different part of the image, so as to
* jumble up the image.
* 'cells' is an array which has been populated with values
* which redirect drawing of one subarea to another subarea.
*/
int cellWidth = bi.getWidth(null)/2;
int cellHeight = bi.getHeight(null)/2;
for (int x=0; x<2; x++) {
int sx = x*cellWidth;
for (int y=0; y<2; y++) {
int sy = y*cellHeight;
int cell = cells[x*2+y];
int dx = (cell / 2) * cellWidth;
int dy = (cell % 2) * cellHeight;
g.drawImage(bi,
dx, dy, dx+cellWidth, dy+cellHeight,
sx, sy, sx+cellWidth, sy+cellHeight,
null);
}
}

Filtering Images

In addition to copying and scaling images, the Java 2D API also filter an image. Filtering is
drawing or producing a new image by applying an algorithm to the pixels of the source
image. Image filters can be applied by using the following method:

void Graphics2D.drawImage(BufferedImage img,


BufferedImageOp op,
int x, int y)

47
The BufferedImageOp parameter implements the filter. The following applet represents an
image drawn on top of text. Drag the slider to show more or less of the text through the
image and make the image more or less transparent.

Note: If you don't see the applet running, you need to install release 6 of the Java SE
Development Kit (JDK).

The following code shows how the filter action is done by operating on a BufferedImage
object with an alpha channel and rescales that alpha channel by using the RescaleOp
object. The alpha channel determines the translucency of each pixel. It also specifies the
degree to which this image overwrites.

/* Create an ARGB BufferedImage */


BufferedImage img = ImageIO.read(imageSrc);
int w = img.getWidth(null);
int h = img.getHeight(null);
BufferedImage bi = new BufferedImage(w, h,
BufferedImage.TYPE_INT_ARGB);
Graphics g = bi.getGraphics();
g.drawImage(img, 0, 0, null);

/* Create a rescale filter op that makes the image 50% opaque */


float[] scales = { 1f, 1f, 1f, 0.5f };
float[] offsets = new float[4];
RescaleOp rop = new RescaleOp(scales, offsets, null);

/* Draw the image, applying the filter */


g2d.drawImage(bi, rop, 0, 0);

The complete example represented in SeeThroughImageApplet.java includes the code


that uses the slider to adjust the transparency from the initial 50%. This example also
requires the duke_skateboard.jpg image.

The RescaleOp object is just one of many filters that can be created. The Java 2D API has
several built in filters including the following:

48
 ConvolveOp. Each output pixel is computed from surrounding pixels in the source image. It
may be used to blur or sharpen images.
 AffineTransformOp. This filter maps pixels in the source to a different position in the
destination by applying a transformation on the pixel location.
 LookupOp. This filter uses an application supplied lookup table to remap pixel colors.
 RescaleOp. This filter multiplies the colors by some factor. Can be used to lighten or darken
the image, to increase or reduce its opacity, etc.

The following example uses each of the described filters as well as scaling:

Note: If you don't see the applet running, you need to install release 6 of the Java SE
Development Kit (JDK).

The complete code for this applet is in ImageDrawingApplet.java and this applet requires
the bld.jpg image.

Use the drop-down menu to select an image scaling or filtering operation.

49
Creating and Drawing to an Image

We already know how to load an existing image, which was created and stored in your
system or in any network location. But, you probably would like also to create an new
image as a pixel data buffer.

In this case, you can create a BufferedImage object manually, using three constructors of
this class:

 new BufferedImage(width, height, type) - constructs a BufferedImage of one of the


predefined image types.
 new BufferedImage(width, height, type, colorModel) - constructs a BufferedImage of one of
the predefined image types: TYPE_BYTE_BINARY or TYPE_BYTE_INDEXED.
 new BufferedImage(colorModel, raster, premultiplied, properties) - constructs a
new BufferedImage with a specified ColorModel and Raster.

On the other hand, we can use methods of the Component class. These methods can analyze
the display resolution for the given Component or GraphicsConfiguration and create an
image of an appropriate type.

 Component.createImage(width, height)
 GraphicsConfiguration.createCompatibleImage(width, height)
 GraphicsConfiguration.createCompatibleImage(width, height, transparency)

GraphicsConfiguration returns an object of BufferedImage type, but the Component returns


an object of Image type, if you need a BufferedImage object instead then you can perform
an instanceof and cast to a BufferedImage in your code.

As was already mentioned in the previous lessons, we can render images not only on screen.
An images itself can be considered as a drawing surface. You can use a createGraphics()
method of the BufferedImage class for this purpose:

...

BufferedImage off_Image =
new BufferedImage(100, 50, BufferedImage.TYPE_INT_ARGB);

Graphics2D g2 = off_Image.createGraphics();

Another interesting use of offscreen images is an automatic double buffering. This feature
allows to avoid flicker in animated images by drawing an image to a back buffer and then
copying that buffer onto the screen instead of drawing directly to the screen.

Java 2D™ also allows access to hardware acceleration for offscreen images, which can
provide the better performance of rendering to and copying from these images. You can get
the benefit of this functionality by using the following methods of the Image class:

 The getCapabilities method allows you to determine whether the image is currently
accelerated.

50
 The setAccelerationPriority method lets you set a hint about how important acceleration
is for the image.
 The getAccelerationPriority method gets a hint about the acceleration importance.

51
Writing/Saving an Image

This lesson started with an explanation for using the javax.imageio package, to load
images from an external image format into Java 2D™'s internal BufferedImage format.
Then it explains how to use the Graphics.drawImage() to draw that image, with optional
filtering.

The final stage is saving a BufferedImage object into an external image format. This may
be an image that was originally loaded by the Image I/O class from an external image
format and perhaps modified using the Java 2D APIs, or it may be one that was created by
Java 2D.

The Image I/O class provides a simple way to save images in a variety of image formats in
the following example:

static boolean ImageIO.write(RenderedImage im,


String formatName,
File output) throws IOException

Note: the BufferedImage class implements the RenderedImage interface.

The formatName parameter selects the image format in which to save the BufferedImage.

try {
BufferedImage bi = getMyImage(); // retrieve image
File outputfile = new File("saved.png");
ImageIO.write(bi, "png", outputfile);
} catch (IOException e) {
...
}

The ImageIO.write method calls the code that implements PNG writing a ―PNG writer
plug-in‖. The term plug-in is used since Image I/O is extensible and can support a wide
range of formats.

But the following standard image format plugins : JPEG, PNG, GIF, BMP and WBMP are
always be present.

Each image format has its advantages and disadvantages:

Plus Minus
Supports animation, and transparent
GIF Supports only 256 colors and no translucency
pixels
Better alternative than GIF or JPG for
PNG high colour lossless images, supports Doesn't support animation
translucency

52
Loss of compression, not good for text, screenshots,
JPG Great for photographic images or any application where the original image must be
preserved exactly

For most applications it is sufficient to use one of these standard plugins. They have the
advantage of being readily available. The Image I/O class provides a way to plug in
support for additional formats which can be used, and many such plug-ins exist. If you are
interested in what file formats are available to load or save in your system, you may use the
getReaderFormatNames and getWriterFormatNames methods of the ImageIO class. These
methods return an array of strings listing all of the formats supported in this JRE.

String writerNames[] = ImageIO.getWriterFormatNames();

The returned array of names will include any additional plug-ins that are installed and any
of these names may be used as a format name to select an image writer. The following code
example is a simple version of a complete image edit/touch up program which uses a
revised version of the ImageDrawingApplet.java sample program which can be used as
follows :

 An image is first loaded via Image I/O


 The user selects a filter from the drop down list and a new updated image is drawn
 The user selects a save format from the drop down list
 Next a file chooser appears and the user selects where to save the image
 The modified image can now be viewed by other desktop applications

The complete code of this example is represented in SaveImage.java.

In this lesson you have learned just the basics of Image I/O, which provides extensive
support for writing images, including working directly with an ImageWriter plug-in to
achieve finer control over the encoding process. ImageIO can write multiple images, image
metadata, and determine quality vs. size tradeoffs. For more information see Java Image I/O
API Guide.

53
Lesson: Printing

Since the Java 2D™ API enables you to draw on any surface, a natural extension of that is
the ability to print Java 2D graphics. A printer can be considered a graphics device just like
a display.

The Java 2D printing API is not limited to printing graphics. It enables you to print the
content of an application's user interface as well. Content can be printed by sending raw
data to the printer under the formatting control of the Java 2D printing API, or by using the
Java 2D Graphics API.

In this lesson you will explore the printer and job control functions of the Java 2D printing
API which are complements to the rendering elements. You will learn how to look up
printers configured on the system or network and discover information about these printers
such as the paper sizes it supports, and selecting these attributes for printing, and user
dialogs.

The main classes and interfaces involved in printing are represented in the java.awt.print
and javax.print packages (the last package allows you to get access to the printing
services).

The basic printing operations are represented in the following sections:

A Basic Printing Program


This section describes the Printable interface and presents a basic printing program.
Using Print Setup Dialogs
This sections explains how to display the Print Setup Dialog.
Printing a Multiple Page Document
This section explains how to use pagination for printing a multiple page document.
Working with Print Services and Attributes
This section teaches you about print services, how to specify the print data format, and how
to create print job using the javax.print package.
Printing the Contents of a User Interface
This section explains how to print the contents of a window or a frame.
Printing Support in Swing Components
This section provides a brief description of the related printing functionality in Swing and
refers to specific Swing classes and interfaces.

54
A Basic Printing Program

This section explains how to create a basic printing program that displays a print dialog and
prints the text "Hello World" to the selected printer.

Printing task usually consists of two parts:

 Job control — Creating a print job, associating it with a printer, specifying the number of
copies, and user print dialog interaction.
 Page Imaging — Drawing content to a page, and managing content that spans pages
(pagination).

First create the printer job. The class representing a printer job and most other related
classes is located in the java.awt.print package.

import java.awt.print.*;

PrinterJob job = PrinterJob.getPrinterJob();

Next provide code that renders the content to the page by implementing the Printable
interface.

class HelloWorldPrinter implements Printable { ... }


..
job.setPrintable(new HelloWorldPrinter());

An application typically displays a print dialog so that the user can adjust various options
such as number of copies, page orientation, or the destination printer.

boolean doPrint = job.printDialog();

This dialog appears until the user either approves or cancels printing. The doPrint variable
will be true if the user gave a command to go ahead and print. If the doPrint variable is
false, the user cancelled the print job. Since displaying the dialog at all is optional, the
returned value is purely informational.

If the doPrint variable is true, then the application will request that the job be printed by
calling the PrinterJob.print method.

if (doPrint) {
try {
job.print();
} catch (PrinterException e) {
/* The job did not successfully complete */
}
}

The PrinterException will be thrown if there is problem sending the job to the printer.
However, since the PrinterJob.print method returns as soon as the job is sent to the

55
printer, the user application cannot detect paper jams or paper out problems. This job
control boilerplate is sufficient for basic printing uses.

The Printable interface has only one method:

public int print(Graphics graphics, PageFormat pf, int page)


throws PrinterException;

The PageFormat class describes the page orientation (portrait or landscape) and its size and
imageable area in units of 1/72nd of an inch. Imageable area accounts for the margin limits
of most printers (hardware margin). The imageable area is the space inside these margins,
and in practice if is often further limited to leave space for headers or footers.

A page parameter is the zero-based page number that will be rendered.

The following code represents the full Printable implementation:

import java.awt.print.*;
import java.awt.*;

public class HelloWorldPrinter implements Printable {

public int print(Graphics g, PageFormat pf, int page) throws


PrinterException
{

if (page > 0) { /* We have only one page, and 'page' is zero-based */


return NO_SUCH_PAGE;
}

/* User (0,0) is typically outside the imageable area, so we must


* translate by the X and Y values in the PageFormat to avoid
clipping
*/
Graphics2D g2d = (Graphics2D)g;
g2d.translate(pf.getImageableX(), pf.getImageableY());

/* Now we perform our rendering */


g.drawString("Hello world!", 100, 100);

/* tell the caller that this page is part of the printed document */
return PAGE_EXISTS;
}
}
The complete code for this example is in HelloWorldPrinter.java.

Sending a Graphics instance to the printer is essentially the same as rendering it to the
screen. In both cases you need to perform the following steps:

 To draw a test string is as easy as the other operations that were described for drawing to a
Graphics2D.
 Printer graphics have a higher resolution, which should be transparent to most code.

56
 The Printable.print() method is called by the printing system, just as the
Component.paint() method is called to paint a Component on the display. The printing
system will call the Printable.print() method for page 0, 1,.. etc until the print()
method returns NO_SUCH_PAGE.
 The print() method may be called with the same page index multiple times until the
document is completed. This feature is applied when the user specifies attributes such as
multiple copies with collate option.
 The PageFormat's imageable area determines the clip area. Imageable area is also important
in calculating pagination, or how to span content across printed pages, since page breaks are
determined by how much can fit on each page.

Note: A call to the print() method may be skipped for certain page indices if the user has
specified a different page range that does not involve a particular page index.

57
Using Print Setup Dialogs

Traditionally, the user wants to see the page setup and print dialog boxes. From the print
dialog you can select a printer, specify pages to print, and set the number of copies.

An application displays a print dialog when the user presses a button related to the print
command, or chooses an item from the print menu. To display this dialog, call the
printDialog method of the PrinterJob class:

PrinterJob pj = PrinterJob.getPrinterJob();
...
if (pj.printDialog()) {
try {pj.print();}
catch (PrinterException exc) {
System.out.println(exc);
}
}
...

This method returns true if the user clicked OK to leave the dialog, and false otherwise.
The user's choices in the dialog are constrained based on the number and format of the
pages that have been set to the PrinterJob.

The printDialog method in the above code snippet opens a native print dialog. The
PrintDialogExample.java code example shows how to display a cross-platform print
dialog.

You can change the page setup information contained in the PageFormat object by using
the page setup dialog.

58
To display the page setup dialog, call the pageDialog method of the PrinterJob class.
PrinterJob pj = PrinterJob.getPrinterJob();
PageFormat pf = pj.pageDialog(pj.defaultPage());

The page setup dialog is initialized using the parameter passed to pageDialog. If the user
clicks the OK button in the dialog, the PageFormat instance will be created in accordance
with the user’s selections, and then returned. If the user cancels the dialog, pageDialog
returns the original unchanged PageFormat.

Usually the Java 2D™ Printing API requires an application to display a print dialog, but in
sometimes it's possible to print without showing any dialog at all. This type of printing is
called silent printing. It may be useful in specific cases, such as, when you need to print a
particular database weekly report. In the other cases it is always recommended to inform the
user when a print process is starting.

59
Printing a Multiple Page Document

You have already learned how to use the Printable interface to print a single page
document. However, documents are usually more than one physical page in length.
Pagination is the process of identifying the location in a document where page breaks and
printing accordingly.

In case of printing several graphics images, one per page, use the page index to iterate
through these pages and print one on each page. For example, if several images are
represented in the following array:

BufferedImage[] images = new BufferedImage[10];


then use the print() method as shown in the following code fragment:
public int print(Graphics graphics, PageFormat pageFormat, int pageIndex)
throws PrinterException {

if (pageIndex < images.length) {


graphics.drawImage(images[pageIndex], 100, 100, null);
return PAGE_EXISTS;
} else {
return NO_SUCH_PAGE:
}
}
If the document is continuous, the application must calculate how much content can fit on
each page, and break the page at that point. If text document consists of many lines, then an
application must calculate how many of these lines can fit entirely on a page. The Point
class creates a point representing a location in (x,y)

To calculate the height of a single line of text, use the FontMetrics class.

Font font = new Font("Serif", Font.PLAIN, 10);


FontMetrics metrics = graphics.getFontMetrics(font);
int lineHeight = metrics.getHeight();

The PageFormat parameter describes the printable area of the page. In particular, to find the
vertical span of the page use the following code fragment:

double pageHeight = pageFormat.getImageableHeight();


Use the following code fragment to calculate the number of lines that fit on a page and the
number of page breaks:
int linesPerPage = ((int)pageHeight)/lineHeight);
int numBreaks = (textLines.length-1)/linesPerPage;
int[] pageBreaks = new int[numBreaks];
for (int b=0; b < numBreaks; b++) {
pageBreaks[b] = (b+1)*linesPerPage;
}

Use the print() method to calculate the printable area for the following reasons:

60
 Text measurement depends on the FontRenderContext and this is implicit in the
FontMetrics object returned by the printer graphics which is not available except inside the
print() method.
 The page format may not be disclosured until printing occurs. Since if the user selected a
landscape mode in the print dialog, then this setting needs to be accounted for. The
PageFormat object passed into the print() method provides this information.

The page break positions are used as represented in the following code fragment:
/* Draw each line that is on this page.
* Increment 'y' position by lineHeight for each line.
*/
int y = 0;
int start = (pageIndex == 0) ? 0 : pageBreaks[pageIndex-1];
int end = (pageIndex == pageBreaks.length)
? textLines.length : pageBreaks[pageIndex];
for (int line=start; line<end; line++) {
y += lineHeight;
g.drawString(textLines[line], 0, y);
}
If a document contains 100 lines and only 48 lines fit on a page, then an application prints 3
pages with page breaks after 48 and 96 lines of text. The remaining 4 lines are printed on
the last page. The complete code for this example is in PaginationExample.java.

The following simplifying factors are used in the PaginationExample code:

 Each page has the same height.


 The same font is used.

61
Working with Print Services and Attributes

From the previous lessons you have learned that the Java 2D ™ printing API supports page
imaging, displays print and page setup dialogs, and specifies printing attributes. Printing
services is another key component of any printing subsystem.

The Java™ Print Service (JPS) API extends the current Java 2D printing features to offer
the following functionality:

 Application discovers printers that cater to its needs by dynamically querying the printer
capabilities.
 Application extends the attributes included with the JPS API.
 Third parties can plug in their own print services with the Service Provider Interface, which
print different formats, including Postscript, PDF, and SVG.

The Java Print Service API consists of four packages:

The javax.print package provides the principal classes and interfaces for the Java™ Print
Service API. It enables client and server applications to:

 Discover and select print services based on their capabilities.


 Specify the format of print data.
 Submit print jobs to services that support the document type to be printed.

Document Type Specification

The DocFlavor class represents format of the print data, such as JPEG or PostScript. The
DocFlavor format consists of two parts: a MIME type and a representation class name. A
MIME type describes the format, and a document representation class name indicates how
the document is delivered to the printer or output stream. An application uses the
DocFlavor and an attribute set to find printers with the capabilities specified by the attribute
set. This code sample demonstrates obtaining an array of StreamPrintServiceFactory
objects that can return StreamPrintService objects able to convert a GIF image into
PostScript:

DocFlavor flavor = DocFlavor.INPUT_STREAM.GIF;


String psMimeType = DocFlavor.BYTE_ARRAY.POSTSCRIPT.getMimeType();
StreamPrintServiceFactory[] psfactories =
StreamPrintServiceFactory.lookupStreamPrintServiceFactories(

62
flavor, psMimeType);

Attribute Definitions

The javax.print.attribute and javax.print.attribute.standard packages define


print attributes which describe the capabilities of a print service, specify the requirements of
a print job, and track the progress of the print job.

For example, if you would like to use A4 paper format and print three copies of your
document you will have to create a set of the following attributes implementing the
PrintRequestAttributeSet interface:

PrintRequestAttributeSet attr_set = new


HashPrintRequestAttributeSet();
attr_set.add(MediaSize.ISO_A4);
attr_set.add(new Copies(3));

Then you must pass the attribute set to the print job's print method, along with the
DocFlavor.

Print Service Discovery

An application invokes the static methods of the abstract class PrintServiceLookup to


locate print services that have the capabilities to satisfy the application's print request. For
example, in order to print two copies of a double-sided document, the application first needs
to find printers that have double-sided printing capability:

DocFlavor doc_flavor = DocFlavor.INPUT_STREAM.PDF;


PrintRequestAttributeSet attr_set = new
HashPrintRequestAttributeSet();
attr_set.add(new Copies(2));
attr_set.add(Sides.DUPLEX);
PrintService[] service =
PrintServiceLookup.lookupPrintServices(doc_flavor, attr_set);

Common Use of the API

In conclusion, the Java Print Service API performs the following steps to process a print
request:

1. Chooses a DocFlavor.
2. Creates a set of attributes.
3. Locates a print service that can handle the print request as specified by the DocFlavor
and the attribute set.
4. Creates a Doc object encapsulating the DocFlavor and the actual print data.
5. Gets a print job, represented by DocPrintJob, from the print service.
6. Calls the print method of the print job.

For more information about Java Print Service, see Java 2D™ Print Service API User
Guide.

63
Printing the Contents of a User Interface

Another common printing task is to print the contents of a window or a frame, either in
whole, or in part. The window may contain the following components: toolbars, buttons
sliders, text labels, scrollable text areas, images, and other graphical content. All of these
components are printed using the following methods of the Java 2D™ printing API:

java.awt.Component.print(Graphics g);
java.awt.Component.printAll(Graphics g);

The following figure represents a simple user interface.

The code to create this UI is located in the sample program PrintUIWindow.java.

To print this window, modify the code in the earlier examples which printed text or images.
The resulting code should appear as follows:

public int print(Graphics g, PageFormat pf, int page) throws


PrinterException {
if (page > 0) {
return NO_SUCH_PAGE;
}

Graphics2D g2d = (Graphics2D)g;


g2d.translate(pf.getImageableX(), pf.getImageableY());

/* Print the entire visible contents of a java.awt.Frame */


frame.printAll(g);

return PAGE_EXISTS;
}

Note: The call to the printAll method is the only difference between
this example and examples to print text or image.
The print(Graphics g) method mirrors the
java.awt.Component.paint(Graphics g) method used

64
for on-screen rendering. Use the print() method rather
than the paint() method as the Components class may have overridden the
print() method to handle the printing case differently.

The printAll(Graphics g)method prints the component and

all its subcomponents. This method is usually used to print

object such as a complete window, rather than a single component.

65
Printing Support in Swing Components

The PrintUIWindow.java example represented in the previous section demonstrates that


the printout is exactly the same you saw on the screen. This appearance seems reasonable.
However, if a window is scrollable, then the contents that are currently scrolled out of view
are not included in the printout. This creates a dump effect on the printer. This becomes a
particular problem when printing large components such as a Swing table or text
components. Components may contain many lines of text which cannot all be entirely
visible on the screen. In this case, print the contents displayed by the component in a
manner consistent with the screen display.

To solve this problem, the Swing table and all text components are printing aware. The
following methods directly provide the use of the Java 2D™ printing:

 javax.swing.JTable.print();
 javax.swing.text.JTextComponent.print();

These methods provide a full implementation of printing for their contents. An application
doesn't need directly create a PrinterJob object and implement the Printable interface.
The call of these methods displays a print dialog and prints the component's data in
accordance with the user's selections. There are also additional methods which provide
more options.

66
Lesson: Advanced Topics in Java2D
This lesson shows you how to use Graphics2D to display graphics with fancy outline and
fill styles, transform graphics when they are rendered, constrain rendering to a particular
area, and generally control the way graphics look when they are rendered. You'll also learn
how to create complex Shape objects by combining simple ones and how to detect when the
user clicks on a displayed graphics primitive. These topics are discussed in the following
sections:
Transforming Shapes, Text, and Images
This section shows you how to modify the default transformation so that objects are
translated, rotated, scaled, or sheared when they are rendered.
Clipping the Drawing Region
You can use any shape as a clipping path--the area within which rendering takes place.
Compositing Graphics
This section illustrates the various compositing styles supported by AlphaComposite and
shows you how to set the compositing style in the Graphics2D rendering context.
Controlling Rendering Quality
This section describes the rendering hints that Graphics2D supports and shows you how to
specify your preference in the trade-off between rendering quality and speed.
Constructing Complex Shapes from Geometry Primitives
This section shows you how to perform boolean operations on Shape objects using the Area
class.
Supporting User Interaction
This section shows you how to perform hit detection on graphics primitives.

67
Transforming Shapes, Text, and Images
You can modify the transform attribute in the Graphics2D context to move, rotate, scale,
and shear graphics primitives when they are rendered. The transform attribute is defined by
an instance of the AffineTransform class. An affine transform is a transformation such as
translate, rotate, scale, or shear in which parallel lines remain parallel even after being
transformed.

The Graphics2D class provides several methods for changing the transform attribute. You
can construct a new AffineTransform and change the Graphics2D transform attribute by
calling transform.

AffineTransform defines the following factory methods to make it easier to construct new
transforms:

 getRotateInstance
 getScaleInstance
 getShearInstance
 getTranslateInstance

Alternatively you can use one of the Graphics2D transformation methods to modify the
current transform. When you call one of these convenience methods, the resulting transform
is concatenated with the current transform and is applied during rendering:

 rotate--to specify an angle of rotation in radians


 scale--to specify a scaling factor in the x and y directions
 shear--to specify a shearing factor in the x and y directions
 translate--to specify a translation offset in the x and y directions

You can also construct an AffineTransform object directly and concatenate it with the
current transform by calling the transform method.

The drawImage method is also overloaded to allow you to specify an AffineTransform


that is applied to the image as it is rendered. Specifying a transform when you call
drawImage does not affect the Graphics2D transform attribute.

Example: Transform

The following program is the same as StrokeandFill, but also allows the user to choose a
transformation to apply to the selected object when it is rendered.

68
Note: If you don't see the applet running, you need to install release 6 of the Java SE
Development Kit (JDK).

Transform.java contains the complete code for this applet.

When a transform is chosen from the Transform menu, the transform is concatenated onto
the AffineTransform at:

public void setTrans(int transIndex) {


// Sets the AffineTransform.
switch ( transIndex ) {
case 0 : at.setToIdentity();
at.translate(w/2, h/2); break;
case 1 : at.rotate(Math.toRadians(45)); break;
case 2 : at.scale(0.5, 0.5); break;
case 3 : at.shear(0.5, 0.0); break;
}
}
Before displaying the shape corresponding to the menu choices, the application first
retrieves the current transform from the Graphics2D object:
AffineTransform saveXform = g2.getTransform();
This transform will be restored to the Graphics2D after rendering.

After retrieving the current transform, another AffineTransform, toCenterAt, is created


that causes shapes to be rendered in the center of the panel. The at AffineTransform is
concatenated onto toCenterAt:

AffineTransform toCenterAt = new AffineTransform();


toCenterAt.concatenate(at);
toCenterAt.translate(-(r.width/2), -(r.height/2));
The toCenterAt transform is concatenated onto the Graphics2D transform with the
transform method:

69
g2.transform(toCenterAt);
After rendering is completed, the original transform is restored using the setTransform
method:
g2.setTransform(saveXform);

Note: Never use the setTransform method to concatenate a coordinate transform onto an
existing transform. The setTransform method overwrites the Graphics2D object's current
transform, which might be needed for other reasons, such as positioning Swing and
lightweight components in a window. Use these steps to perform transformations:

1. Use the getTransform method to get the current transform.


2. Use transform, translate, scale, shear, or rotate to concatenate a transform.
3. Perform the rendering.
4. Restore the original transform using the setTransform method.

70
Clipping the Drawing Region
Any Shape object can be used as a clipping path that restricts the portion of the drawing
area that will be rendered. The clipping path is part of the Graphics2D context; to set the
clip attribute, you call Graphics2D.setClip and pass in the Shape that defines the clipping
path you want to use. You can shrink the clipping path by calling the clip method and
passing in another Shape; the clip is set to the intersection of the current clip and the
specified Shape.

Example: ClipImage

This example animates a clipping path to reveal different portions of an image.

Note: If you don't see the applet running, you need to install release 6 of the Java SE
Development Kit (JDK).

ClipImage.java contains the complete code for this applet. The applet requires the
clouds.jpg image file.

The clipping path is defined by the intersection of an ellipse and a rectangle whose
dimensions are set randomly. The ellipse is passed to the setClip method, and then clip is
called to set the clipping path to the intersection of the ellipse and the rectangle.

private Ellipse2D ellipse = new Ellipse2D.Float();


private Rectangle2D rect = new Rectangle2D.Float();
...
ellipse.setFrame(x, y, ew, eh);
g2.setClip(ellipse);
rect.setRect(x+5, y+5, ew-10, eh-10);
g2.clip(rect);

71
Example: Starry

A clipping area can also be created from a text string. The following example creates a
TextLayout with the string The Starry Night. Then, it gets the outline of the TextLayout.
The TextLayout.getOutline method returns a Shape object and a Rectangle is created
from the bounds of this Shape object. The bounds contain all the pixels the layout can draw.
The color in the graphics context is set to blue and the outline shape is drawn, as illustrated
by the following image and code snippet.

FontRenderContext frc = g2.getFontRenderContext();


Font f = new Font("Helvetica", 1, w/10);
String s = new String("The Starry Night");
TextLayout textTl = new TextLayout(s, f, frc);
AffineTransform transform = new AffineTransform();
Shape outline = textTl.getOutline(null);
Rectangle r = outline.getBounds();
transform = g2.getTransform();
transform.translate(w/2-(r.width/2), h/2+(r.height/2));
g2.transform(transform);
g2.setColor(Color.blue);
g2.draw(outline);

Next, a clipping area is set on the graphics context using the Shape object created from
getOutline. The starry.gif image, which is Van Gogh's famous painting, The Starry
Night, is drawn into this clipping area starting at the lower left corner of the Rectangle
object.

g2.setClip(outline);
g2.drawImage(img, r.x, r.y, r.width, r.height, this);

Note: If you don't see the applet running, you need to install release 6 of the Java SE
Development Kit (JDK).

Starry.java contains the complete code for this program. This applet requires the
Starry.gif image file.

72
Compositing Graphics
The AlphaComposite class encapsulates various compositing styles, which determine how
overlapping objects are rendered. An AlphaComposite can also have an alpha value that
specifies the degree of transparency: alpha = 1.0 is totally opaque, alpha = 0.0 totally
transparent (clear). AlphaComposite supports most of the standard Porter-Duff compositing
rules shown in the following table.
Source-over (SRC_OVER)
If pixels in the object being rendered (the source) have the same
location as previously rendered pixels (the destination), the source
pixels are rendered over the destination pixels.

Source-in (SRC_IN)
If pixels in the source and the destination overlap, only the source pixels
in the overlapping area are rendered.

Source-out (SRC_OUT)
If pixels in the source and the destination overlap, only the source pixels
outside of the overlapping area are rendered. The pixels in the
overlapping area are cleared.

Destination-over
(DST_OVER)
If pixels in the source and the destination overlap, only the source pixels
outside of the overlapping area are rendered. The pixels in the
overlapping area are not changed.

Destination-in (DST_IN)
If pixels in the source and the destination overlap, the alpha from the
source is applied to the destination pixels in the overlapping area. If the
alpha = 1.0, the pixels in the overlapping area are unchanged; if the
alpha is 0.0, pixels in the overlapping area are cleared.

Destination-out (DST_OUT)
If pixels in the source and the destination overlap, the alpha from the
source is applied to the destination pixels in the overlapping area. If the
alpha = 1.0, the pixels in the overlapping area are cleared; if the alpha is
0.0, the pixels in the overlapping area are unchanged.

Clear (CLEAR)

If the pixels in the source and the destination overlap, the pixels in the
overlapping area are cleared.

73
To change the compositing style used by the Graphics2D class, create an AlphaComposite
object and pass it into the setComposite method.

Example: Composite

This program illustrates the effects of various compositing style and alpha combinations.

Note: If you don't see the applet running, you need to install release 6 of the Java SE
Development Kit (JDK).

Composite.java. contains the full code for this applet.

A new AlphaComposite object ac is constructed by calling


AlphaComposite.getInstance and specifying the desired compositing rule.

AlphaComposite ac =
AlphaComposite.getInstance(AlphaComposite.SRC);

When a different compositing rule or alpha value is selected,


AlphaComposite.getInstance is called again, and the new AlphaComposite is assigned
to ac. The selected alpha is applied in addition to the per-pixel alpha value and is passed as
a second parameter to AlphaComposite.getInstance.

ac = AlphaComposite.getInstance(getRule(rule), alpha);

The composite attribute is modified by passing the AlphaComposite object to Graphics 2D


setComposite. The objects are rendered into a BufferedImage and are later copied to the
screen, so the composite attribute is set on the Graphics2D context for the BufferedImage:

BufferedImage buffImg = new BufferedImage(w, h,


BufferedImage.TYPE_INT_ARGB);

74
Graphics2D gbi = buffImg.createGraphics();
...
gbi.setComposite(ac);

75
Controlling Rendering Quality

Use the Graphics2D class rendering hints attribute to specify whether you want objects to
be rendered as quickly as possible or whether you prefer that the rendering quality be as
high as possible.

To set or change the rendering hints attribute in the Graphics2D context, construct a
RenderingHints object and pass it into Graphics2D by using the setRenderingHints
method. If you just want to set one hint, you can call Graphics2D setRenderingHint and
specify the key-value pair for the hint you want to set. (The key-value pairs are defined in
the RenderingHints class.)

For example, to set a preference for antialiasing to be used if possible, you could use
setRenderingHint:

public void paint (graphics g){


Graphics2D g2 = (Graphics2D)g;
RenderingHints rh = new RenderingHints(
RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g2.setRenderingHints(rh);
...
}

Note: Not all platforms support modification of the rendering mode, so specifying
rendering hints does not guarantee that they will be used.

RenderingHints supports the following types of hints:

Hint Key Values


Antialiasing KEY_ANTIALIASING VALUE_ANTIALIAS_ON
VALUE_ANTIALIAS_OFF
VALUE_ANTIALIAS_DEFAULT

Alpha KEY_ALPHA_INTERPOLATION VALUE_ALPHA_INTERPOLATION_QUALITY


Interpolation VALUE_ALPHA_INTERPOLATION_SPEED
VALUE_ALPHA_INTERPOLATION_DEFAULT

Color KEY_COLOR_RENDERING VALUE_COLOR_RENDER_QUALITY


Rendering VALUE_COLOR_RENDER_SPEED
VALUE_COLOR_RENDER_DEFAULT

Dithering KEY_DITHERING VALUE_DITHER_DISABLE


VALUE_DITHER_ENABLE
VALUE_DITHER_DEFAULT

Fractional KEY_FRACTIONALMETRICS VALUE_FRACTIONALMETRICS_ON


VALUE_FRACTIONALMETRICS_OFF
Text Metrics
VALUE_FRACTIONALMETRICS_DEFAULT

Image KEY_INTERPOLATION VALUE_INTERPOLATION_BICUBIC


VALUE_INTERPOLATION_BILINEAR

76
Interpolation VALUE_INTERPOLATION_NEAREST_NEIGHBOR

Rendering KEY_RENDERING VALUE_RENDER_QUALITY


VALUE_RENDER_SPEED
VALUE_RENDER_DEFAULT

Stroke KEY_STROKE_CONTROL VALUE_STROKE_NORMALIZE


Normalization VALUE_STROKE_DEFAULT
VALUE_STROKE_PURE
Control
Text KEY_TEXT_ANTIALIASING VALUE_TEXT_ANTIALIAS_ON
VALUE_TEXT_ANTIALIAS_OFF
Antialiasing
VALUE_TEXT_ANTIALIAS_DEFAULT
VALUE_TEXT_ANTIALIAS_GASP
VALUE_TEXT_ANTIALIAS_LCD_HRGB
VALUE_TEXT_ANTIALIAS_LCD_HBGR
VALUE_TEXT_ANTIALIAS_LCD_VRGB
VALUE_TEXT_ANTIALIAS_LCD_VBGR

LCD Text KEY_TEXT_LCD_CONTRAST Values should be a positive integer in the range 100
Contrast to 250. A lower value (eg 100) corresponds to higher
contrast text when displaying dark text on a light
background. A higher value (eg 200) corresponds to
lower contrast text when displaying dark text on a
light background. A typical useful value is in the
narrow range 140-180. If no value is specified, a
system or implementation default value will be
applied.

When a hint is set to default, the platform rendering default is used.

77
Constructing Complex Shapes from Geometry Primitives

Constructive area geometry (CAG) is the process of creating new geometric shapes
by performing boolean operations on existing ones. In the Java 2D™ API the Area
class implements the Shape interface and supports the following boolean
operations.

Union Subtraction

Intersection Exclusive-or (XOR)

Example: Areas

In this example Area objects construct a pear shape from several ellipses.

Note: If you don't see the applet running, you need to install release 6 of the Java SE
Development Kit (JDK).

Pear.java contains the complete code for this applet.

The leaves are each created by performing an intersection on two overlapping


circles.

leaf = new Ellipse2D.Double();


...
leaf1 = new Area(leaf);
leaf2 = new Area(leaf);
...
leaf.setFrame(ew-16, eh-29, 15.0, 15.0);
leaf1 = new Area(leaf);
leaf.setFrame(ew-14, eh-47, 30.0, 30.0);
leaf2 = new Area(leaf);

78
leaf1.intersect(leaf2);
g2.fill(leaf1);
...
leaf.setFrame(ew+1, eh-29, 15.0, 15.0);
leaf1 = new Area(leaf);
leaf2.intersect(leaf1);
g2.fill(leaf2);

Overlapping circles are also used to construct the stem through a subtraction
operation.

stem = new Ellipse2D.Double();


...
stem.setFrame(ew, eh-42, 40.0, 40.0);
st1 = new Area(stem);
stem.setFrame(ew+3, eh-47, 50.0, 50.0);
st2 = new Area(stem);
st1.subtract(st2);
g2.fill(st1);

The body of the pear is constructed by performing a union operation on a circle


and an oval.

circle = new Ellipse2D.Double();


oval = new Ellipse2D.Double();
circ = new Area(circle);
ov = new Area(oval);
...
circle.setFrame(ew-25, eh, 50.0, 50.0);
oval.setFrame(ew-19, eh-20, 40.0, 70.0);
circ = new Area(circle);
ov = new Area(oval);
circ.add(ov);
g2.fill(circ);

79
Supporting User Interaction

To enable the user to interact with the graphics you display, you need to be able to
determine when the user clicks on one of them. The hit method of the
Graphics2D class provides a way to easily determine whether a mouse click
occurred over a particular Shape object. Alternatively you can get the location of
the mouse click and call contains on the Shape to determine whether the click
was within the bounds of the Shape.

If you are using primitive text, you can perform simple hit testing by getting the
outline Shape that corresponds to the text and then calling hit or contains with
that Shape. Supporting text editing requires much more sophisticated hit testing. If
you want to allow the user to edit text, you should generally use one of the Swing
editable text components. If you are working with primitive text and are using the
TextLayout class to manage the shaping and positioning of the text, you can also
use TextLayout to perform hit testing for text editing. For more information see
the chapter Text and Fonts in the Java 2D™ Programmer's Guide or see the
HitTestSample example below, which uses a TextLayout to perform simple hit-
testing.

Example: ShapeMover

This applet allows the user to drag a Shape around within the applet window. The
Shape is redrawn at every mouse location to provide feedback as the user drags it.

Note: If you don't see the applet running, you need to install release 6 of the Java SE
Development Kit (JDK).

ShapeMover.java contains the complete code for this applet.

The contains method is called to determine whether the cursor is within the
bounds of the rectangle when the mouse is pressed. If it is, the location of the
rectangle is updated.

public void mousePressed(MouseEvent e){


last_x = rect.x - e.getX();
last_y = rect.y - e.getY();
if(rect.contains(e.getX(), e.getY())) updateLocation(e);
...

public void updateLocation(MouseEvent e){


rect.setLocation(last_x + e.getX(), last_y + e.getY());
...
repaint();

80
You might notice that redrawing the Shape at every mouse location is slow,
because the filled rectangle is rerendered every time it is moved. Using double
buffering can eliminate this problem. If you use Swing, the drawing will be double
buffered automatically; you don't have to change the rendering code at all. The
code for a Swing version of this program is SwingShapeMover.java.

Example: HitTestSample

This application illustrates hit testing by drawing the default caret wherever the
user clicks on the TextLayout, as shown in the following figure.

Note: If you don't see the applet running, you need to install release 6 of the Java SE
Development Kit (JDK).

HitTestSample.java contains the complete code for this applet.

The mouseClicked method uses TextLayout.hitTestChar to return a


java.awt.font.TextHitInfo object that contains the mouse click location (the insertion
index) in the TextLayout object.

Information returned by the TextLayout getAscent, getDescent, and getAdvance


methods is used to compute the location of the origin for the TextLayout object so it is
horizontally and vertically centered.

...

private Point2D computeLayoutOrigin() {


Dimension size = getPreferredSize();
Point2D.Float origin = new Point2D.Float();

origin.x = (float) (size.width - textLayout.getAdvance()) / 2;


origin.y =
(float) (size.height - textLayout.getDescent()
+ textLayout.getAscent())/2;
return origin;
}

...

public void paintComponent(Graphics g) {


super.paintComponent(g);
setBackground(Color.white);
Graphics2D graphics2D = (Graphics2D) g;
Point2D origin = computeLayoutOrigin();
graphics2D.translate(origin.getX(), origin.getY());

81
// Draw textLayout.
textLayout.draw(graphics2D, 0, 0);

// Retrieve caret Shapes for insertionIndex.


Shape[] carets = textLayout.getCaretShapes(insertionIndex);

// Draw the carets. carets[0] is the strong caret and


// carets[1] is the weak caret.
graphics2D.setColor(STRONG_CARET_COLOR);
graphics2D.draw(carets[0]);
if (carets[1] != null) {
graphics2D.setColor(WEAK_CARET_COLOR);
graphics2D.draw(carets[1]);
}
}

...

private class HitTestMouseListener extends MouseAdapter {

/**
* Compute the character position of the mouse click.
*/
public void mouseClicked(MouseEvent e) {

Point2D origin = computeLayoutOrigin();

// Compute the mouse click location relative to


// textLayout's origin.
float clickX = (float) (e.getX() - origin.getX());
float clickY = (float) (e.getY() - origin.getY());

// Get the character position of the mouse click.


TextHitInfo currentHit = textLayout.hitTestChar(clickX, clickY);
insertionIndex = currentHit.getInsertionIndex();

// Repaint the Component so the new caret(s) will be displayed.


hitPane.repaint();
}

82

Potrebbero piacerti anche