An Introduction to the Java 2D API
This paper will focus on the Java 2D API. Rather than jump straight into that API, this paper will begin with a few basics on Java GUI programming and applet coding, and then go into further details about the Java 2D API. In Java, graphics began with AWT. The Abstract Windows Toolkit (AWT) has been, and still is, used in the Java language to create graphical user interface programs which function as either stand-alone applications or applets that load when the browser loads. After the introduction of AWT, Sun, and Netscape jointly contrived and developed the Swing toolkit. Swing has proved to be a more powerful GUI mechanism for applications that use "widgets", or components. Here is some sample code:
import javax.swing.*;
public class CheckGrade {
public static void main(String[] args) {
String strScore = JOptionPane.showInputDialog(null, "Enter your grade");
int score = Integer.parseInt(strScore);
String comment;
if ( score >= 60) {
comment = "You have passed";
}else{
comment = "You have failed";
}
JOptionPane.showMessageDialog(null, comment);
}
}
Compiling it:
C:\Sun\SDK\JDK\bin>javac.exe CheckGrade.java
C:\Sun\SDK\JDK\bin>java CheckGrade
Almost all uses of the JOptionPane
class are one-line calls to one of the static showXxxDialog
methods shown below:
showConfirmDialog
asks a confirmation question, for yes/no/cancel.showInputDialog
prompts for some input.showMessageDialog
tells the user about something that has happened.showOptionDialog
is the grand unification of the above three.
Code explanation: The JVM reads each data type in String
format, so if we are to enter a primitive integer type, then we have to use a format like this: int x = Integer.parseInt(“Some string”)
because the entered value is an integer, but will be interpreted as a string. We thus change the string to an integer. Here, Integer
is the name of a class defined in the built-in java.lang package and parseInt()
is a method of the Integer
class that converts a String
to an Integer
. The purpose of the if
statement is to make a decision, and execute parts of your program depending on a boolean true
/false
.
The Basics of Applets
Applets do not have a main()
method as a starting point of code execution. Nor are they interpreted by the Java compiler. They are initialized by the init()
method. As with any Java program, the name of the file is the name of the class that you are declaring, except that this declared class extends the Applet
class that is defined in the java.awt package. Below is some basic (and perhaps outdated) applet code. Note that the system calls the paint()
method. Also note that we are drawing lines, and to draw those lines, we need to iterate the variable i
that is part of the dimensions of the applet:
import java.applet.*;
import java.awt.*;
public class CreateLines extends Applet {
int width, height;
public void init() {
width = getSize().width;
height = getSize().height;
setBackground( Color.black);
}
public void paint( Graphics g ) {
g.setColor( Color.red );
for ( int i = 0; i < 10; ++i ) {
g.drawLine( width, height, i * width / 10, 0 );
}
}
}
This code must be compiled by the javac.exe compiler in order to create a class file:
C:\Sun\SDK\JDK\bin>javac.exe CreateLines.java
C:\Sun\SDK\JDK\bin> type con > somefile.html
The output:
<html>
<body>
<applet code="CreateLines.class" width = 400 height = 400></applet>
</body>
<html>
Now, type somefile.html.
To see the strength of the applet, the writer of this paper refers you to http://www.echoecho.com. This web page has a stunning applet with its corresponding class file for download. Just make sure that you save the image file, the HTML file, and the class file in the same working directory.
An Overview of the Java 2D API
In Java 1.2 and up, the paintComponent
method is supplied with a Graphics2D
object (a subclass of Graphics
), which contains a much richer set of drawing operations. It includes pen widths, dashed lines, image and gradient color fill patterns, the use of arbitrary local fonts, a floating point coordinate system, and a number of coordinate transformation operations. The Java 2D API provides advanced two-dimensional graphics capabilities for programmers. The API includes features for processing line art, text, and images in the packages java.awt, java.awt.image, java.awt.color, java.awt.font, java.awt.geom, java.awt.print, and java.awt.image.renderable. Drawing with the Java 2D API requires a Graphics2D
reference (package java.awt). Graphics2D
is an abstract subclass of the Graphics
class, so it has all the graphics capabilities mentioned. In fact, the actual object used to draw in every paintComponent
method is an instance of a subclass of Graphics2D
that is passed to the method paintComponent
and accessed via the super class Graphics
. To access Graphics2D
capabilities, we must cast the Graphics
reference (g
) passed to paintComponent
into a Graphics2D
reference, with a statement such as:
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D)g;
g2d.doSomework(...);
...
}
Create a Shape
object
Rectangle2D.Double rect = ...;
Ellipse2D.Double ellipse = ...;
Polygon poly = ...;
GeneralPath path = ...;
- ...
Draw an outlined or solid version of the Shape
:
g2d.draw(someShape);
g2d.fill(someShape)
;
Modify the drawing parameters using some of these methods.
g2d.setPaint(fillColorOrPattern);
g2d.setStroke(penThicknessOrPattern);
g2d.setComposite(someAlphaComposite);
g2d.setFont(someFont);
g2d.translate(...);
g2d.rotate(...);
g2d.scale(...);
g2d.shear(...);
g2d.setTransform(someAffineTransform);Rectangle2D.Double rect = ...;
Ellipse2D.Double ellipse = ...;
Polygon poly = ...;
GeneralPath path = ...;
Draw an outlined or solid version of the Shape
:
g2d.draw(someShape);
g2d.fill(someShape);
Modify the drawing parameters:
g2d.setPaint(fillColorOrPattern);
g2d.setStroke(penThicknessOrPattern);
g2d.setComposite(someAlphaComposite);
g2d.setFont(someFont);
g2d.translate(...);
g2d.rotate(...);
g2d.scale(...);
g2d.shear(...);
g2d.setTransform(someAffineTransform);
This example is going to draw an ellipse inside a window. The darkened ellipse is contained inside a window, or a container, the top-most of which is called JFrame
. Below is the main file, and notice that contained in the main()
method is an undefined class called OSTools
. The Example.java file is therefore dependent on the class file OSTools.java. Looking at OSTools.java tells us that OSTools is dependent on the ExitListener.java file. OSTools.java and ExitListener.java are only compiled into byte code for their class files to function as a “blueprint” for the application. Here is the main file, Example.java:
import javax.swing.*;
import java.awt.*;
import java.awt.geom.*;
public class Example extends JPanel {
private Ellipse2D.Double circle =
new Ellipse2D.Double(10, 10, 350, 350);
private Rectangle2D.Double square =
new Rectangle2D.Double(10, 10, 350, 350);
public void paintComponent(Graphics g) {
clear(g);
Graphics2D g2d = (Graphics2D)g;
g2d.fill(circle);
g2d.draw(square);
}
protected void clear(Graphics g) {
super.paintComponent(g);
}
protected Ellipse2D.Double getCircle() {
return(circle);
}
public static void main(String[] args) {
OSTools.openInJFrame(new Example(), 380, 400);
}
}
We see the OSTools
class and know that it is not defined in any imported package. This is a class file that must have its source code compiled into a class file. Recall that the top-most level container (or window) in Swing is the JFrame
. The JFrame
, however, would be just a blank user interface if we did not add components onto it. JPanel
is called the content pane where we put the components onto our window.
import java.awt.*;
public class OSTools {
public static JFrame openInJFrame(Container content,
int width,
int height,
String title,
Color bgColor) {
JFrame frame = new JFrame(title);
frame.setBackground(bgColor);
content.setBackground(bgColor);
frame.setSize(width, height);
frame.setContentPane(content);
frame.addWindowListener(new ExitListener());
frame.setVisible(true);
return(frame);
}
public static JFrame openInJFrame(Container content,
int width,
int height,
String title) {
return(openInJFrame(content, width, height, title, Color.white));
}
public static JFrame openInJFrame(Container content,
int width,
int height) {
return(openInJFrame(content, width, height,
content.getClass().getName(),
Color.white));
}
}
Notice that a new ExitListener
object was created, which means we have to define the class:
import java.awt.*;
import java.awt.event.*;
public class ExitListener extends WindowAdapter {
public void windowClosing(WindowEvent event) {
System.exit(0);
}
}
The compilation of the class files and the main file produces this image:
Transparent drawing
Java 2D permits you to assign transparency (alpha) values to drawing operations so that the underlying graphics partially shows through when you draw shapes or images. You set a transparency by creating an AlphaComposite
object and then passing the AlphaComposite
object to the setComposite
method of the Graphics2D
object. You create an AlphaComposite
by calling AlphaComposite.getInstance
. The AlphaComposite
API provides eight built-in mixing rules, but the one normally used for drawing with transparency settings is AlphaComposite.SRC_OVER
, a source over destination mixing rule that places the source (shape) over the destination (background). Alpha values can range from 0.0f (completely transparent) to 1.0f (completely opaque). Partially overlapping is a red square drawn with an initial alpha value of 0.0f at the far left. The red square is repeatedly drawn at new locations across the panel with alpha values that gradually increase by a step size of 0.1f until, finally, total opaqueness is reached at the far right with an alpha value of 1.0f.
Here is some code that demonstrates this example. Again, this main file is dependent on the OSTools.java class file:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Transparent extends JPanel {
private static int gap=10, width=60, offset=20,
deltaX=gap+width+offset;
private Rectangle
blueSquare = new Rectangle(gap+offset, gap+offset, width,
width),
redSquare = new Rectangle(gap, gap, width, width);
private AlphaComposite makeComposite(float alpha) {
int type = AlphaComposite.SRC_OVER;
return(AlphaComposite.getInstance(type, alpha));
}
private void drawSquares(Graphics2D g2d, float alpha) {
Composite originalComposite = g2d.getComposite();
g2d.setPaint(Color.blue);
g2d.fill(blueSquare);
g2d.setComposite(makeComposite(alpha));
g2d.setPaint(Color.red);
g2d.fill(redSquare);
g2d.setComposite(originalComposite);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D)g;
for(int i=0; i<11; i++) {
drawSquares(g2d, i*0.1F);
g2d.translate(deltaX, 0);
}
}
public static void main(String[] args) {
String title = "Transparency example: alpha of the top " +
"(red) square ranges from 0.0 at the left " +
"to 1.0 at the right. Bottom (blue) square " +
"is opaque.";
OSTools.openInJFrame(new Transparent(),
11*deltaX + 2*gap,
deltaX + 3*gap,
title, Color.lightGray);
}
}
Suggested reading
- Introduction to Programming using Java, by David J. Eck
- Thinking in Java, by Bruce Eckel