Java2D & Piccolo

skatechildrenSoftware and s/w Development

Nov 3, 2013 (3 years and 7 months ago)


Java2D & Piccolo

The Origins of the Java®
Graphics 2D API

The original Java® GUI toolkit, the AWT, was a
quick and dirty solution. It used native peer
components to draw all widgets.

Swing draws all components except the top level
containers using Java® methods rather than
relying on platform specific widgets.

To do this, Swing required improved graphics.

The Java® 2D API was born as enabling
technology for Swing.

There is now a Java® 3D API also.

See tutorial at

Java Graphics Architecture

JFrame : Heavyweight

JPanel : Lightweight

Event Handling

One of the key concepts in GUI programming is
Event Handling.

Something (Mouse click / Key press, Menu
Selection, Timer expiring, Network message
received) happens. These would be all

Some GUI components might care about one of
these things happening and need to react to it.
These components would register themselves
with the Event Source, so the source would tell
them when something happens. These are the
Event Listeners

Event Basics

All Events are objects of Event

All Event Classes derive from

When an Event occurs, Java sends a
message to all registered Event
Listeners from the Event source (this
actually was a change, the old AWT
event model would send it out to

Event Handling in Java

Action that results in the event

Listener type

User clicks a button, presses Return while typing
in a text field, or chooses a menu item


User closes a frame (main window)


User presses a mouse button while the cursor is
over a component


User moves the mouse over a component


Component becomes visible


Component gets the keyboard focus


Table of list selection changes


Registering Event Listeners

To register a listener object with a source
object, you use lines of code that follow the


So, from here we have 2 ways to create the
listener object.

Create a class that implements that interface
(ActionListener, WindowListener, etc.)

Use an anonymous Inner Class and attach it

Registering Event Listeners

import javax.swing.*;

import java.awt.*;

import java.awt.event.*;

class MyListener
implements ActionListener


public void actionPerformed(ActionEvent e)


//code to run



ActionListener listener = new MyListener();

JButton button = new JButton(“Ok”);


Event Listeners

When the user clicks the button, the JButton
object generates an ActionEvent object.

It this calls the listener object’s
actionPerformed() method and passes that
method the event object it just generated.

Note that a single event source can have
multiple listeners listening for its events. In
this case, the source calls the
actionPerformed() method of each of its
listeners when the event is generated.

Or… (the way everyone
does it.. with a Inner Class)

JButton button=new JButton(“OK”);

button.addActionListener( new ActionListener()
//Anon Inner class


public void actionPerformed(ActionEvent e)


//handling code here



Things to realize

If we’re doing Event handling, we probably
need to import javaw.swing.* (for the Swing
components), java.awt.* and
java.awt.event.* (for the Event handling
stuff). Another example of how Swing and
AWT comingle.

the ActionListener interface defines only 1
method, actionPerformed. Others, like
WindowListener and the MouseListeners,
define more than 1. You have to implement
them all to use it. More on this shortly

So, How Do I Draw in

You might think that if you want to do
your own drawing in Java®, you do it in
the main thread just as you create a GUI
out of components in the main thread.

In fact, you have to create components
on which you draw, like all your other
GUI components in the main thread.

But the drawing has to be done in the
event thread.

And to understand why, we need to start
by considering when drawing happens.

When Does a GUI Get

On initial display (but not
necessarily when the program gets

When the display gets damaged.
For example when it gets hidden
and then exposed.

When the content changes, and
Swing or the programmer calls for
a refresh (repaint()).

How does a GUI Get

Swing schedules the drawing. It may
combine multiple redraw requests that
occur in rapid succession.

Swing calls the following three methods
in order (on the event thread):

paintComponent() paintBorder()


The last recursively

paints a container's children.

How to Do Custom

Standard Swing components like JLabel and
JComboBox use paintComponent() to draw

If you want to do custom drawing, extend a
container class, usually JPanel, and override

Use calls from the 2D API in paintComponent() to
draw what you want on the JPanel background.

Override getPreferredSize() or call
setPreferredSize() to size JPanel to your drawing.

The Graphics Class

paintComponent() is called with argumentGraphics
g, that serves as a
drawing toolkit
initialized to the
component's defaults.

In more recent versions of the JDK, the argument
is really a Graphics2D object, which extends
Graphics for the 2D API . So cast it. Graphics was
the original AWT class.

Using the 2D API usually starts off like this:

public void paintComponent( Graphics g ) {

super.paintComponent( g );

Graphics2D g2 = (Graphics2D) g;

Where Does the Graphics
Argument Come From?

The Graphics argument to the paintComponent()
method is a snapshot of your component's default
graphics values like font and drawing color at the
moment the paint methods are called.

It is only a copy of these values. Each time the
paint methods are called, you get a new version of

No changes you make to a Graphics
instance in one call to paintComponent() are
remembered the next time you enter the method.
And no changes, like setFont() are propagated
back to the component itself.

Basic 2D API Operations

You can use the Graphics2D argument to

1. draw outline figures using the method
public void draw( Shape s )

2. draw filled figures using the method
public void fill( Shape s )

3. draw an image using one of the
methods: public void drawImage( . . . )

4. draw a text string using the methods:
public void drawString( . . . )

Graphics 2D Rendering

Much of the power of the 2D API comes
from the user’s ability to set attributes of
the Graphics2D object known
collectively as the rendering context:

public void setStroke(Stroke s)

public void setPaint(Paint p)

public void setFont(Font f)

public void setComposite(Composite c)

public void setTransform(Transform t)

public void setRenderingHints(Map m)

Custom Drawing Template

import java.awt.*; // for Graphics2D, Paint, Shape, …

import java.awt.geom.*; // for concrete Shape classes

import javax.swing.*; // for JPanel, etc

public class MyPanel extends JPanel { . . .

public void paintComponent( Graphics g ) {
super.paintComponent( g ); Graphics2D g2 =
(Graphics2D) g; . . .


Shape s = new
( ... );
( s );

2D Shapes

Shape is an interface defined in java.awt,
but the classes that implement Shape
are all defined in java.awt.geom.

Shapes come in two versions, one with
high precision coordinates and one with
low, e.g.: Ellipse2D.Double // high
precision Ellipse2D.Float // low precision

Each shape has a different constructor
arguments, doubles or floats depending
on whether they are high precision or

Creating an Ellipse

Graphics2D Coordinate

The Graphics2D object uses a variety of
(as opposed to
device coordinates
that Java® calls
user space

Graphics2D shapes and operations are defined in
floating point (float or double) but the Y coordinate
increases downwards.

Some Graphics2D calls only take floats.

The floating point coordinates are designed to
make your graphics independent of the output

The 2D API is designed for printing as well as
output to screens at different resolutions.

The Graphics2D Default

The 2D rendering pipeline applies a
to map user
space to device space.

The default transformation maps 1.0 user
space units to ~1/72 of an inch, which
happens to be the average pixel size on
a screen or a printer’s point size on a

So unless you do something special, 2D
drawing defaults to pixel coordinates.

import java.awt.*;

import java.awt.geom.*;

import javax.swing.*;

public class Ellipse extends JPanel {

private Shape ellipse;

public static void main( String[] args ) {

JFrame frame = new JFrame( "Ellipse" );


frame.getContentPane().add( new Ellipse(), "Center" );

frame.setSize( 650, 300 ); frame.setVisible( true );

}, 2

public Ellipse() {

ellipse = new Ellipse2D.Double( 100.0, 50.0, 300.0, 150.0 );

setBackground( Color.white ); setOpaque( true );


public void paintComponent( Graphics g ) {

super.paintComponent( g );

Graphics2D g2 = (Graphics2D) g;


g2.fill( ellipse );



Piccolo Framework

Piccolo is a direct
graphics framework that supports
constructing zoomable interfaces.

Four main classes that define
the framework's core:

PNode (anything that is visible and gets events)

PCamera (a node that looks at other layer nodes, and
applies a view transform)

PLayer (a node that can be looked at by a camera)

PRoot (the root of the Piccolo display tree)

PCanvas (the host component that lets PNodes exist in a
Java Swing application. Each PCanvas is associated with
a PCamera. But all cameras are not necessarily directly
associated with a PCanvas, internal cameras for example
are not.)

Piccolo at Runtime

Piccolo at Runtime

At runtime these classes form a tree
structure with the PRoot situated at the top.

Each PCamera is normally linked with at
least one PLayer that it looks at through it's
view transform.

If a camera is associated with a canvas
then that camera's view is displayed on the
canvas, and input events from the canvas
enter the Piccolo scene graph at that
camera's point in the hierarchy.


Nodes are the central design concept
in Piccolo. Any object that wants to
paint itself on the screen should
inherit from the node class. In addition
to painting on the screen all nodes
may have other "child" nodes added
to them. Visual structures are build up
by grouping and sub grouping
collections of nodes.

PNode (continued)

Each node also has its own
affine transform

that is applied before the node is drawn to
the screen. This transform can be modified
to scale and translate the node. The
transform exits directly above the node, but
below the node's parent.

Thus, translating it
will translate the node (and all its
descendents) but will not translate the
node's parent.


Cameras are nodes that have an additional
view transform and a collection of layers in
addition to the collection of children that
they inherit from PNode.

The view transform is applied before
drawing or picking the layers, but not when
drawing or picking the camera's children.
Cameras may (an internal camera might
not) also reference a PCanvas, and forward
repaint events to that canvas. The canvas
would then later ask the camera to draw the
damaged region on its surface.


Layer nodes are nodes that can be
viewed by a one or more cameras.
They maintain a list of the cameras
that are viewing them, and notify
these cameras when they are


The PRoot serves as the topmost
node in the Piccolo runtime structure;
all other nodes are its direct children
or descendents of its children. The
PCanvas communicates with the root
node to manage screen updates and
to dispatch events to its children.


The PCanvas is a JComponent in Piccolo.
Thus, it is used to view a Piccolo scene
graph in Java Swing. The PCanvas views
the scene graph through a PCamera. It
forwards input events to that camera, and
uses that camera to draw itself. Translating
and scaling that camera's view transform is
how panning and zooming are


Piccolo uses the term "full" to mean a node
and its descendants with the node's
transform applied. This helps distinguish
between methods that work on a single
node and those that work on a node
together with all of its descendants with the
node's transform applied. When traversing
the node scene graph to paint or calculate
bounds parent nodes generally call the "full"
methods of their direct child nodes.

First Application

All Piccolo interfaces need to be
placed in a PCanvas so that they may
be viewed and interacted with by the
user. PCanvas extends the
javax.swing.JComponent class in

See Example