You are on page 1of 68

Lesson: Performing Custom Painting

This lesson describes custom painting in Swing. Many programs will get by just fine without writing
their own painting code; they will simply use the standard GUI components that are already available
in the Swing API. But if you need specific control over how your graphics are drawn, then this
lesson is for you. We will explore custom painting by creating a simple GUI application that draws a
shape in response to the user's mouse activity. By intentionally keeping its design simple, we can
focus on the underlying painting concepts, which in turn will relate to other GUI applications that
you develop in the future.

This lesson explains each concept in steps as you construct the demo application. It presents the code
as soon as possible with a minimum amount of background reading. Custom painting in Swing is
similar to custom painting in AWT, but since we do not recommend writing your applications
entirely with the AWT, its painting mechanism is not specifically discussed here. You may find it
useful to read this lesson followed by the in-depth discussion of Painting in AWT and Swing on the
Sun Developer Network (SDN).

Creating the Demo Application (Step 1)


All Graphical User Interfaces require some kind of main application frame in which to display. In
Swing, this is an instance of javax.swing.JFrame. Therefore, our first step is to instantiate this
class and make sure that everything works as expected. Note that when programming in Swing, your
GUI creation code should be placed on the Event Dispatch Thread (EDT). This will prevent potential
race conditions that could lead to deadlock. The following code listing shows how this is done.

An Instance of javax.swing.JFrame

Click the Launch button to run SwingPaintDemo1 using Java™ Web Start (download JDK 6).
Alternatively, to compile and run the example yourself, consult the example index.

package painting;

import javax.swing.SwingUtilities;
import javax.swing.JFrame;

1039
public class SwingPaintDemo1 {

public static void main(String[] args) {


SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}

private static void createAndShowGUI() {


System.out.println("Created GUI on EDT? "+
SwingUtilities.isEventDispatchThread());
JFrame f = new JFrame("Swing Paint Demo");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(250,250);
f.setVisible(true);
}
}

This creates the frame, sets its title, and makes everything visible. We have used the
SwingUtilities helper class to construct this GUI on the Event Dispatch Thread. Note that by
default, a JFrame does not exit the application when the user clicks its "close" button. We provide
this behavior by invoking the setDefaultCloseOperation method, passing in the appropriate
argument. Also, we are explicity setting the frame's size to 250 x 250 pixels. This step will not be
necessary once we start adding components to the frame.

Exercises:

1. Compile and run the application.


2. Test the minimize and maximize buttons.
3. Click the close button (the application should exit.)

Creating the Demo Application (Step 2)


Next, we will add a custom drawing surface to the frame. For this we will create a subclass of
javax.swing.JPanel (a generic lightweight container) which will supply the code for rendering our
custom painting.

1040
A javax.swing.JPanel Subclass

Click the Launch button to run SwingPaintDemo2 using Java™ Web Start (download JDK 6).
Alternatively, to compile and run the example yourself, consult the example index.

package painting;

import javax.swing.SwingUtilities;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.BorderFactory;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;

public class SwingPaintDemo2 {

public static void main(String[] args) {


SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}

private static void createAndShowGUI() {


System.out.println("Created GUI on EDT? "+
SwingUtilities.isEventDispatchThread());
JFrame f = new JFrame("Swing Paint Demo");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(new MyPanel());
f.pack();
f.setVisible(true);
}
}

class MyPanel extends JPanel {

public MyPanel() {
setBorder(BorderFactory.createLineBorder(Color.black));
}

public Dimension getPreferredSize() {


return new Dimension(250,200);
}

public void paintComponent(Graphics g) {


super.paintComponent(g);

// Draw Text
g.drawString("This is my custom Panel!",10,20);
}
}

The first change you will notice is that we are now importing a number of additional classes, such as
JPanel, Color, and Graphics. Since some of the older AWT classes are still used in modern Swing
applications, it is normal to see the java.awt package in a few of the import statements. We have
also defined a custom JPanel subclass, called MyPanel, which comprises the majority of the new
code.
1041
The MyPanel class definition has a constructor that sets a black border around its edges. This is a
subtle detail that might be difficult to see at first (if it is, just comment out the invocation of
setBorder and then recompile.) MyPanel also overrides getPreferredSize, which returns the
desired width and height of the panel (in this case 250 is the width, 200 is the height.) Because of
this, the SwingPaintDemo class no longer needs to specify the size of the frame in pixels. It simply
adds the panel to the frame and then invokes pack.

The paintComponent method is where all of your custom painting takes place. This method is
defined by javax.swing.JComponent and then overridden by your subclasses to provide their
custom behavior. Its sole parameter, a java.awt.Graphics object, exposes a number of methods for
drawing 2D shapes and obtaining information about the application's graphics environment. In most
cases the object that is actually received by this method will be an instance of
java.awt.Graphics2D (a Graphics subclass), which provides support for sophisticated 2D graphics
rendering.

Most of the standard Swing components have their look and feel implemented by separate "UI
Delegate" objects. The invocation of super.paintComponent(g) passes the graphics context off to
the component's UI delegate, which paints the panel's background. For a closer look at this process,
see the section entitled "Painting and the UI Delegate" in the aforementioned SDN article.

Exercises:

1. Now that you have drawn some custom text to the screen, try minimizing and restoring the
application as you did before.
2. Obscure a part of the text with another window, then move that window out of the way to re-
expose the custom text. In both cases, the painting subsystem will determine that the
component is damaged and will ensure that your paintComponent method is invoked.

Creating the Demo Application (Step 3)


Finally, we will add the event-handling code to programatically repaint the component whenever the
user clicks or drags the mouse. To keep our custom painting as efficient as possible, we will track the
mouse coordinates and repaint only the areas of the screen that have changed. This is a
recommended best practice that will keep your application running as efficiently as possible.

The Completed Application

1042
Figure 3: The Completed Application

Click the Launch button to run SwingPaintDemo3 using Java™ Web Start (download JDK 6).
Alternatively, to compile and run the example yourself, consult the example index.

package painting;

import javax.swing.SwingUtilities;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.BorderFactory;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseMotionAdapter;

public class SwingPaintDemo3 {

public static void main(String[] args) {


SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}

private static void createAndShowGUI() {


System.out.println("Created GUI on EDT? "+
SwingUtilities.isEventDispatchThread());
JFrame f = new JFrame("Swing Paint Demo");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(new MyPanel());
f.pack();
f.setVisible(true);
}
}

class MyPanel extends JPanel {

private int squareX = 50;


private int squareY = 50;
private int squareW = 20;
private int squareH = 20;

public MyPanel() {

setBorder(BorderFactory.createLineBorder(Color.black));

addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
moveSquare(e.getX(),e.getY());
}
});

addMouseMotionListener(new MouseAdapter() {
public void mouseDragged(MouseEvent e) {
moveSquare(e.getX(),e.getY());

1043
}
});

private void moveSquare(int x, int y) {


int OFFSET = 1;
if ((squareX!=x) || (squareY!=y)) {
repaint(squareX,squareY,squareW+OFFSET,squareH+OFFSET);
squareX=x;
squareY=y;
repaint(squareX,squareY,squareW+OFFSET,squareH+OFFSET);
}
}

public Dimension getPreferredSize() {


return new Dimension(250,200);
}

protected void paintComponent(Graphics g) {


super.paintComponent(g);
g.drawString("This is my custom Panel!",10,20);
g.setColor(Color.RED);
g.fillRect(squareX,squareY,squareW,squareH);
g.setColor(Color.BLACK);
g.drawRect(squareX,squareY,squareW,squareH);
}
}

This change first imports the various mouse classes from the java.awt.event package, making the
application capable of responding to the user's mouse activity. The constructor has been updated to
register event listeners for mouse presses and drags. Whenever a MouseEvent received, it is
forwarded to the moveSquare method, which updates the square's coordinates and repaints the
component in an intelligent manner. Note that by default, any code that is placed within these event
handlers will be executed on the Event Dispatch Thread.

But the most important change is the invocation of the repaint method. This method is defined by
java.awt.Component and is the mechanism that allows you to programatically repaint the surface of
any given component. It has a no-arg version (which repaints the entire component) and a multi-arg
version (which repaints only the specified area.) This area is also known as the clip. Invoking the
multi-arg version of repaint takes a little extra effort, but guarantees that your painting code will
not waste cycles repainting areas of the screen that have not changed.

Because we are manually setting the clip, our moveSquare method invokes the repaint method not
once, but twice. The first invocation tells Swing to repaint the area of the component where the
square previously was (the inherited behavior uses the UI Delegate to fill that area with the current
background color.) The second invocation paints the area of the component where the square
currently is. An important point worth noting is that although we have invoked repaint twice in a row
in the same event handler, Swing is smart enough to take that information and repaint those sections
of the screen all in one single paint operation. In other words, Swing will not repaint the component
twice in a row, even if that is what the code appears to be doing.

Exercises:

1044
1. Comment out the first invocation of repaint and note what happens when you click or drag
the mouse. Because that line is responsible for filling in the background, you should notice
that all squares remain on screen after they are painted.
2. With multiple squares on screen, minimize and restore the application frame. What happens?
You should notice that the act of maximizing the screen causes the system to entirely repaint
the components surface, which will erase all squares except the current one.
3. Comment out both invocations of repaint, and add a line at the end of the paintComponent
method to invoke the zero-arg version of repaint instead. The application will appear to be
restored to its original behavior, but painting will now be less efficient since the entire surface
area of the component is now being painted. You may notice slower performace, especially if
the application is maximized.

Refining the Design


For demonstration purposes it makes sense to keep the painting logic entirely contained within the
MyPanel class. But if your application will need to track multiple instances, one pattern that you
could use is to factor that code out into a separate class so that each square can be treated as an
individual object. This technique is common in 2D game programming and is sometimes referred to
as "sprite animation."

Click the Launch button to run SwingPaintDemo4 using Java™ Web Start (download JDK 6).
Alternatively, to compile and run the example yourself, consult the example index.

package painting;

import javax.swing.SwingUtilities;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.BorderFactory;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseMotionAdapter;

public class SwingPaintDemo4 {

public static void main(String[] args) {

SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}

private static void createAndShowGUI() {


System.out.println("Created GUI on EDT? "+
SwingUtilities.isEventDispatchThread());
JFrame f = new JFrame("Swing Paint Demo");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(new MyPanel());
f.setSize(250,250);

1045
f.setVisible(true);
}

class MyPanel extends JPanel {

RedSquare redSquare = new RedSquare();

public MyPanel() {

setBorder(BorderFactory.createLineBorder(Color.black));

addMouseListener(new MouseAdapter(){
public void mousePressed(MouseEvent e){
moveSquare(e.getX(),e.getY());
}
});

addMouseMotionListener(new MouseAdapter(){
public void mouseDragged(MouseEvent e){
moveSquare(e.getX(),e.getY());
}
});

private void moveSquare(int x, int y){

// Current square state, stored as final variables


// to avoid repeat invocations of the same methods.
final int CURR_X = redSquare.getX();
final int CURR_Y = redSquare.getY();
final int CURR_W = redSquare.getWidth();
final int CURR_H = redSquare.getHeight();
final int OFFSET = 1;

if ((CURR_X!=x) || (CURR_Y!=y)) {

// The square is moving, repaint background


// over the old square location.
repaint(CURR_X,CURR_Y,CURR_W+OFFSET,CURR_H+OFFSET);

// Update coordinates.
redSquare.setX(x);
redSquare.setY(y);

// Repaint the square at the new location.


repaint(redSquare.getX(), redSquare.getY(),
redSquare.getWidth()+OFFSET,
redSquare.getHeight()+OFFSET);
}
}

public Dimension getPreferredSize() {


return new Dimension(250,200);
}

public void paintComponent(Graphics g) {


super.paintComponent(g);
g.drawString("This is my custom Panel!",10,20);

redSquare.paintSquare(g);
}

1046
}

class RedSquare{

private int xPos = 50;


private int yPos = 50;
private int width = 20;
private int height = 20;

public void setX(int xPos){


this.xPos = xPos;
}

public int getX(){


return xPos;
}

public void setY(int yPos){


this.yPos = yPos;
}

public int getY(){


return yPos;
}

public int getWidth(){


return width;
}

public int getHeight(){


return height;
}

public void paintSquare(Graphics g){


g.setColor(Color.RED);
g.fillRect(xPos,yPos,width,height);
g.setColor(Color.BLACK);
g.drawRect(xPos,yPos,width,height);
}
}

In this particular implementation we have created a RedSquare class entirely from scratch. Another
approach would be to resuse the functionlity of java.awt.Rectangle by making the RedSquare a
subclass of it. Regardless of how RedSquare is implemented, the important point is that we have
given the class a method that accepts a Graphics object, and that method is invoked from the panel's
paintComponent method. This separation keeps your code clean because it essentially tells each red
square to paint itself.

A Closer Look at the Paint Mechanism


By now you know that the paintComponent method is where all of your painting code should be
placed. It is true that this method will be invoked when it is time to paint, but painting actually
begins higher up the class heirarchy, with the paint method (defined by java.awt.Component.)
This method will be executed by the painting subsystem whenever you component needs to be
rendered. Its signature is:

 public void paint(Graphics g)

1047
javax.swing.JComponent extends this class and further factors the paint method into three
separate methods, which are invoked in the following order:

 protected void paintComponent(Graphics g)


 protected void paintBorder(Graphics g)
 protected void paintChildren(Graphics g)

The API does nothing to prevent your code from overriding paintBorder and paintChildren, but
generally speaking, there is no reason for you to do so. For all practical purposes paintComponent
will be the only method that you will ever need to override.

As previously mentioned, most of the standard Swing components have their look and feel
implemented by separate UI Delegates. This means that most (or all) of the painting for the standard
Swing components proceeds as follows.

1. paint() invokes paintComponent().


2. If the ui property is non-null, paintComponent() invokes ui.update().
3. If the component's opaque property is true, ui.udpate() fills the component's background
with the background color and invokes ui.paint().
4. ui.paint() renders the content of the component.

This is why our SwingPaintDemo code invokes super.paintComponent(g). We could add an


additional comment to make this more clear:
public void paintComponent(Graphics g) {
// Let UI Delegate paint first, which
// includes background filling since
// this component is opaque.

super.paintComponent(g);
g.drawString("This is my custom Panel!",10,20);
redSquare.paintSquare(g);
}
If you have understood all of the demo code provided in this lesson, congratulations! You have
enough practical knowledge to write efficient painting code in your own applications. If however
you want a closer look "under the hood", please refer to the SDN article linked to from the first page
of this lesson.

Summary

 In Swing, painting begins with the paint method, which then invokes
paintComponent, paintBorder, and paintChildren. The system will invoke this
automatically when a component is first painted, is resized, or becomes exposed after
being hidden by another window.

 Programatic repaints are accomplished by invoking a component's repaint method;


do not invoke its paintComponent directly. Invoking repaint causes the painting
subsystem to take the necessary steps to ensure that your paintComponent method is
invoked at an appropriate time.
 The multi-arg version of repaint allows you to shrink the component's clip rectangle
(the section of the screen that is affected by painting operations) so that painting can

1048
become more efficient. We utilized this technique in the moveSquare method to avoid
repainting sections of the screen that have not changed. There is also a no-arg version
of this method that will repaint the component's entire surface area.
 Because we have shrunk the clip rectangle, our moveSquare method invokes repaint
not once, but twice. The first invocation repaints the area of the component where the
square previously was (the inherited behavior is to fill the area with the current
background color.) The second invocation paints the area of the component where the
square currently is.
 You can invoke repaint multiple times from within the same event handler, but
Swing will take that information and repaint the component in just one operation.
 For components with a UI Delegate, you should pass the Graphics paramater with
the line super.paintComponent(g) as the first line of code in your paintComponent
override. If you do not, then your component will be responsible for manually
painting its background. You can experiment with this by commenting out that line
and recompiling to see that the background is no longer painted.
 By factoring out our new code into a separate RedSquare class, the application
maintains an object-oriented design, which keeps the paintComponent method of the
MyPanel class free of clutter. Painting still works because we have passed the
Graphics object off to the red square by invoking its paintSquare(Graphics g)
method. Keep in mind that the name of this method is one that we have created from
scratch; we are not overriding paintSquare from anywhere higher up in the Swing
API.

Solving Common Painting Problems


Problem: I don't know where to put my painting code.

 Painting code belongs in the paintComponent method of any component descended


from JComponent.

Problem: The stuff I paint doesn't show up.

 Check whether your component is showing up at all. Solving Common Component


Problems should help you with this.
 Check whether repaint is invoked on your component whenever its appearance
needs to be updated.

Problem: My component's foreground shows up, but its background is invisible. The result is that
one or more components directly behind my component are unexpectedly visible.

 Make sure your component is opaque. JPanels, for example, are opaque by default in
many but not all look and feels. To make components such as JLabels and GTK+
JPanels opaque, you must invoke setOpaque(true) on them.
 If your custom component extends JPanel or a more specialized JComponent
descendant, then you can paint the background by invoking super.paintComponent
before painting the contents of your component.
 You can paint the background yourself using this code at the top of a custom
component's paintComponent method:
 g.setColor(getBackground());
 g.fillRect(0, 0, getWidth(), getHeight());

1049
 g.setColor(getForeground());

Problem: I used setBackground to set my component's background color, but it seemed to have no
effect.

 Most likely, your component isn't painting its background, either because it's not
opaque or your custom painting code doesn't paint the background. If you set the
background color for a JLabel, for example, you must also invoke setOpaque(true)
on the label to make the label's background be painted.

Problem: I'm using the exact same code as a tutorial example, but it doesn't work. Why?

 Is the code executed in the exact same method as the tutorial example? For example,
if the tutorial example has the code in the example's paintComponent method, then
this method might be the only place where the code is guaranteed to work.

Problem: How do I paint thick lines? patterns?

 The JavaTM 2D API provides extensive support for implementing line widths and
styles, as well as patterns for use in filling and stroking shapes. See the 2D Graphics
trail for more information on using the Java 2D API.

Problem: The edges of a particular component look odd.

 Because components often update their borders to reflect component state, you
generally should avoid invoking setBorder except on JPanels and custom
subclasses of JComponent.
 Is the component painted by a look and feel such as GTK+ or Windows XP that uses
UI-painted borders instead of Border objects? If so, don't invoke setBorder on the
component.
 Does the component have custom painting code? If so, does the painting code take the
component's insets into account?

Problem: Visual artifacts appear in my GUI.

 If you set the background color of a component, be sure the color has no transparency
if the component is supposed to be opaque.
 Use the setOpaque method to set component opacity if necessary. For example, the
content pane must be opaque, but components with transparent backgrounds must not
be opaque.
 Make sure your custom component fills its painting area completely if it's opaque.

Problem: The performance of my custom painting code is poor.

 If you can paint part of your component, use the getClip or getClipBounds method
of Graphics to determine which area you need to paint. The less you paint, the faster
it will be.
 If only part of your component needs to be updated, make paint requests using a
version of repaint that specifies the painting region.
 For help on choosing efficient painting techniques, look for the string "performance"
in the Java 2D API home page.

1050
Problem: The same transforms applied to seemingly identical Graphics objects sometimes have
slightly different effects.

 Because the Swing painting code sets the transform (using the Graphics method
translate) before invoking paintComponent, any transforms that you apply have a
cumulative effect. This doesn't matter when doing a simple translation, but a more
complex AffineTransform, for example, might have unexpected results.

If you don't see your problem in this list, see Solving Common Component Problems and Solving
Common Layout Problems.

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

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

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.

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

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

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

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

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

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.

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

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

1056
rectangular bounding box. The geometry of a RectangularShape object can be extrapolated from a
rectangle that completely encloses the outline of the Shape.

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.

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

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

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

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.

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

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

1060
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

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:

1061
 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 above, you need to install release 6 of the 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();
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.

...

1062
// 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);
...
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.

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.

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

Drawing Geometric Primitives


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

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

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

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.

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

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:

1067
// 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:

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

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

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

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

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

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:

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

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.

1071
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 above, you need to install release 6 of the JDK.

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

1072
} 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 {
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.

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.

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

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.

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.

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:

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

1075
Note: If you don't see the applet running above, you need to install release 6 of the JDK. The
complete code for this applet is in AntialiasedText.java.

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 above, you need to install release 6 of the JDK. The
complete code for this applet is in AttributedText.java.

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.

1076
Note: If you don't see the applet running above, you need to install release 6 of the 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);

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

1077
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

Drawing an image

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

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

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.

1079
Note: If you don't see the applet running above, you need to install release 6 of the 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.

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.

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.

1080
Note: If you don't see the applet running above, you need to install release 6 of the 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)

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 above, you need to install release 6 of the 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);

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

 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 above, you need to install release 6 of the 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.

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:

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

Writing/Saving an Image

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

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

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.

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

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

1086
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 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;
}
}

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

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.

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

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

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:

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

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

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:

1091
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(
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

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

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.

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

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

For more information see related articles at java.sun.com.

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

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:

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

Note: If you don't see the applet running above, you need to install release 6 of the 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:

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

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 above, you need to install release 6 of the 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);

1098
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 above, you need to install release 6 of the JDK.

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

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.

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

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

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 above, you need to install release 6 of the 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.

1100
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);
Graphics2D gbi = buffImg.createGraphics();
...
gbi.setComposite(ac);

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:

1101
Hint Key Values
Antialiasing KEY_ANTIALIASING VALUE_ANTIALIAS_ON
VALUE_ANTIALIAS_OFF
VALUE_ANTIALIAS_DEFAULT

Alpha KEY_ALPHA_INTERPOLATION VALUE_ALPHA_INTERPOLATION_QUALITY


VALUE_ALPHA_INTERPOLATION_SPEED
Interpolation VALUE_ALPHA_INTERPOLATION_DEFAULT

Color KEY_COLOR_RENDERING VALUE_COLOR_RENDER_QUALITY


VALUE_COLOR_RENDER_SPEED
Rendering 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
Interpolation
VALUE_INTERPOLATION_NEAREST_NEIGHBOR

Rendering KEY_RENDERING VALUE_RENDER_QUALITY


VALUE_RENDER_SPEED
VALUE_RENDER_DEFAULT

Stroke KEY_STROKE_CONTROL VALUE_STROKE_NORMALIZE


VALUE_STROKE_DEFAULT
Normalization VALUE_STROKE_PURE
Control
Stroke KEY_STROKE_CONTROL VALUE_STROKE_NORMALIZE
VALUE_STROKE_DEFAULT
Normalization 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.

1102
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 above, you need to install release 6 of the 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);
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);

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

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 above, you need to install release 6 of the 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);
...

1104
public void updateLocation(MouseEvent e){
rect.setLocation(last_x + e.getX(), last_y + e.getY());
...
repaint();

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 above, you need to install release 6 of the 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());

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

// Retrieve caret Shapes for insertionIndex.


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

1105
// 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();

1106

You might also like