Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
0% found this document useful (0 votes)
69 views

2D Graphics & Java2D

This document discusses 2D graphics and the Java 2D API. It provides an overview of the java.awt.Graphics class for custom drawing and its limitations. The Java 2D API and Graphics2D class greatly extend the capabilities of Graphics by supporting geometric primitives, shapes, transformations, and images. The document also provides a template for custom drawing by extending JPanel and overriding its paintComponent method, and discusses how to run it as an applet or application.

Uploaded by

BB
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
69 views

2D Graphics & Java2D

This document discusses 2D graphics and the Java 2D API. It provides an overview of the java.awt.Graphics class for custom drawing and its limitations. The Java 2D API and Graphics2D class greatly extend the capabilities of Graphics by supporting geometric primitives, shapes, transformations, and images. The document also provides a template for custom drawing by extending JPanel and overriding its paintComponent method, and discusses how to run it as an applet or application.

Uploaded by

BB
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 20

2D Graphics & Java2D 7/14/2015

yet another insignificant programming notes... | HOME

TABLE OF CONTENTS (HIDE)


1. Revisit java.awt.Graphics for Custom Drawin
1.1 Template for Custom Drawing
Java Game Programming 2. Java 2D API & Graphics2D
2.1 java.awt.Graphics2D
2D Graphics, Java2D and Images 2.2 Affine Transform (java.awt.geom.AffineTr
2.3 Geometric Primitives and Shapes
2.4 Point2D and its Subclasses Point, Point2D.
2.5 Interface java.awt.Shape
The pre-requisite knowledge needed to for game programming includes: 2.6 Stroke, Paint and Composite Attributes
OOP, in particular the concepts of inheritance and polymorphism for designing classes. 3. Working with Bitmap Images
3.1 Loading Images
GUI and custom graphics programming (javax.swing). 3.2 drawImage()
Event-handling, in particular mouse-event and key-event handling (java.awt.event). 3.3 Image Affine Transforms
Graphics programming using Java 2D (java.awt.geom). 3.4 Image Filtering Operations
3.5 Animating Image Frames
Paying sounds (javax.sound).
4. High Performance Graphics
Basic knowledge on I/O, multi-threading for starting the game thread, and timing control. 4.1 Full-Screen Display Mode (JDK 1.4)
Transformation, collision detection and reaction algorithms. 4.2 Rendering to the Display & Double Buffering
Advanced Knowledge: 4.3 Splash Screen
JOGL (Java Bindings to OpenGL), or Java3D for 3D graphics.
JOAL (Java Bindings to OpenAL) for advanced sound.

1. Revisit java.awt.Graphics for Custom Drawing


Read: "Custom Graphics" chapter.
The java.awt.Graphics class is used for custom painting. It manages the graphics context (such as color, font and clip area) and provides
methods for rendering of three types of graphical objects:
1. Texts: via drawString().
2. Vector-graphic primitives and shapes: via drawXxx() and fillXxx() for Line, PolyLine, Oval, Rect, RoundRect, 3DRect, and
Arc.
3. Bitmap images: via drawImage().
The Graphics class also allows you to get/set the attributes of the graphics context:
Font (setFont(), getFont())
Color (setColor(), getColor())
Display clipping area (getClip(), getClipBounds(), setClip())
It is important to take note that the graphics co-ordinates system is "inverted", as illustrated.
T h e java.awt.Graphics class is, however, limited in its functions and capabilities. It supports mainly straight-line segments.
java.awt.Graphics2D (of the Java 2D API) greatly extends the functions and capabilities of the Graphics class.

1.1 Template for Custom Drawing


1 import java.awt.*;
2 import java.awt.event.*;
3 import javax.swing.*;
4
5 // Swing Program Template
6 @SuppressWarnings("serial")
7 public class SwingTemplateJPanel extends JPanel {
8 // Name-constants
9 public static final int CANVAS_WIDTH = 640;
10 public static final int CANVAS_HEIGHT = 480;
11 public static final String TITLE = "...Title...";
12 // ......
13
14 // private variables of GUI components

http://www.ntu.edu.sg/home/ehchua/programming/java/J8b_Game_2DGraphics.html 1 / 20
2D Graphics & Java2D 7/14/2015

15 // ......
16
17 /** Constructor to setup the GUI components */
18 public SwingTemplateJPanel() {
19 setPreferredSize(new Dimension(CANVAS_WIDTH, CANVAS_HEIGHT));
20 // "this" JPanel container sets layout
21 // setLayout(new ....Layout());
22
23 // Allocate the UI components
24 // .....
25
26 // "this" JPanel adds components
27 // add(....)
28
29 // Source object adds listener
30 // .....
31 }
32
33 /** Custom painting codes on this JPanel */
34 @Override
35 public void paintComponent(Graphics g) {
36 super.paintComponent(g); // paint background
37 setBackground(Color.BLACK);
38
39 // Your custom painting codes
40 // ......
41 }
42
43 /** The entry main() method */
44 public static void main(String[] args) {
45 // Run GUI codes in the Event-Dispatching thread for thread safety
46 SwingUtilities.invokeLater(new Runnable() {
47 public void run() {
48 JFrame frame = new JFrame(TITLE);
49 frame.setContentPane(new SwingTemplateJPanel());
50 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
51 frame.pack(); // "this" JFrame packs its components
52 frame.setLocationRelativeTo(null); // center the application window
53 frame.setVisible(true); // show it
54 }
55 });
56 }
57 }

Dissecting the Program


The custom drawing is done by extending a JPanel and overrides the paintComponent() method.
The size of the drawing panel is set via the setPreferredSize().
The JFrame does not set its size, but packs its components by invoking pack().
In the main(), the GUI construction is carried out in the event-dispatch thread to ensure thread-safety.
You can run the above class as an applet, by providing a main applet class:
1 import javax.swing.*;
2
3 /** Swing Program Template for running as Applet */
4 @SuppressWarnings("serial")
5 public class SwingTemplateApplet extends JApplet {
6
7 /** init() to setup the UI components */
8 @Override
9 public void init() {
10 // Run GUI codes in the Event-Dispatching thread for thread safety
11 try {
12 SwingUtilities.invokeAndWait(new Runnable() { // Applet uses invokeAndWait()
13 @Override
14 public void run() {
15 // Set the content-pane of the JApplet to an instance of main JPanel
16 setContentPane(new SwingTemplateJPanel());
17 }
18 });
19 } catch (Exception e) {
20 e.printStackTrace();
21 }
22 }

http://www.ntu.edu.sg/home/ehchua/programming/java/J8b_Game_2DGraphics.html 2 / 20
2D Graphics & Java2D 7/14/2015

23 }

2. Java 2D API & Graphics2D


Reference: Java Tutorial's "2D Graphics" @ http://docs.oracle.com/javase/tutorial/2d/TOC.html.
Java AWT API has been around since JDK 1.1. Java 2D API is part of Java Foundation Class (JFC), similar to Swing, and was introduced in JDK
1.2. It provides more capabilities for graphics programming. Java 2D spans many packages: java.awt, java.awt.image,
java.awt.color, java.awt.font, java.awt.geom, java.awt.print, and java.awt.image.renderable.

2.1 java.awt.Graphics2D
The core class in Java2D is the java.awt.Graphics2D. Graphics2D is a subclass of java.awt.Graphics, which extends the support
of the legacy Graphics class in rendering three groups of objects: text, vector-graphics and bitmap images. It also supports more attributes
that affect the rendering, e.g.,
Transform attribute (translation, rotation, scaling and shearing).
Pen attribute (outline of a shape) and Stroke attribute (point-size, dashing-pattern, end-cap and join decorations).
Fill attribute (interior of a shape) and Paint attribute (fill shapes with solid colors, gradients, and patterns).
Composite attribute (for overlapping shapes).
Clip attribute (display area).
Font attribute.
Graphics2D is designed as a subclass of Graphics. To use Graphcs2D context, downcast the Graphics handle g to Graphics2D in
painting methods such as paintComponent(). This works because the graphics subsystem in fact passes a Graphics2D object as the
argument when calling back the painting methods. For example,
@Override
public void paintComponent(Graphics g) { // graphics subsystem passes a Graphis2D subclass object as argument
super.paintComponent(g); // paint parent's background
Graphics2D g2d = (Graphics2D) g; // downcast the Graphics object back to Graphics2D
// Perform custom drawing using g2d handle
......
}

Besides the drawXxx() and fillXxx(), Graphics2D provides more general drawing methods which operates on Shape interface.
public abstract void draw(Shape s)
public abstract void fill(Shape s)
public abstract void clip(Shape s)

2.2 Affine Transform (java.awt.geom.AffineTransform)


Transform is key in game programming and animation!
An affine transform is a linear transform such as translation, rotation, scaling, or shearing in which a straight line remains straight and
parallel lines remain parallel after the transformation. It can be represented using the following 3x3 matrix operation:
[ x' ] [ m00 m01 m02 ] [ x ] [ m00x + m01y + m02 ]
[ y' ] = [ m10 m11 m12 ] [ y ] = [ m10x + m11y + m12 ]
[ 1 ] [ 0 0 1 ] [ 1 ] [ 1 ]

Affine transform is supported via the java.awt.geom.AffineTransform class. The Graphics2D context maintains an AffineTransform
attribute, and provides methods to change the transform attributes:
// in class java.awt.Graphics2D
public abstract void setTransform(AffineTransform at); // overwrites the current Transform in the Graphics2D context
public abstract void translate(double tx, double ty); // concatenates the current Transform with a translation transform
public abstract void rotate(double theta); // concatenates the current Transform with a rotation transform
public abstract void scale(double scaleX, double scaleY) // concatenates the current Transform with a scaling transform
public abstract void shear(double shearX, double shearY) // concatenates the current Transform with a shearing transform

Example
1 import java.awt.*;
2 import java.awt.geom.AffineTransform;
3 import javax.swing.*;
4
5 /** Test applying affine transform on vector graphics */
6 @SuppressWarnings("serial")
7 public class AffineTransformDemo extends JPanel {
8 // Named-constants for dimensions

http://www.ntu.edu.sg/home/ehchua/programming/java/J8b_Game_2DGraphics.html 3 / 20
2D Graphics & Java2D 7/14/2015

9 public static final int CANVAS_WIDTH = 640;


10 public static final int CANVAS_HEIGHT = 480;
11 public static final String TITLE = "Affine Transform Demo";
12
13 // Define an arrow shape using a polygon centered at (0, 0)
14 int[] polygonXs = { -20, 0, +20, 0};
15 int[] polygonYs = { 20, 10, 20, -20};
16 Shape shape = new Polygon(polygonXs, polygonYs, polygonXs.length);
17 double x = 50.0, y = 50.0; // (x, y) position of this Shape
18
19 /** Constructor to set up the GUI components */
20 public AffineTransformDemo() {
21 setPreferredSize(new Dimension(CANVAS_WIDTH, CANVAS_HEIGHT));
22 }
23
24 /** Custom painting codes on this JPanel */
25 @Override
26 public void paintComponent(Graphics g) {
27 super.paintComponent(g); // paint background
28 setBackground(Color.WHITE);
29 Graphics2D g2d = (Graphics2D)g;
30
31 // Save the current transform of the graphics contexts.
32 AffineTransform saveTransform = g2d.getTransform();
33 // Create a identity affine transform, and apply to the Graphics2D context
34 AffineTransform identity = new AffineTransform();
35 g2d.setTransform(identity);
36
37 // Paint Shape (with identity transform), centered at (0, 0) as defined.
38 g2d.setColor(Color.GREEN);
39 g2d.fill(shape);
40 // Translate to the initial (x, y) position, scale, and paint
41 g2d.translate(x, y);
42 g2d.scale(1.2, 1.2);
43 g2d.fill(shape);
44
45 // Try more transforms
46 for (int i = 0; i < 5; ++i) {
47 g2d.translate(50.0, 5.0); // translates by (50, 5)
48 g2d.setColor(Color.BLUE);
49 g2d.fill(shape);
50 g2d.rotate(Math.toRadians(15.0)); // rotates about transformed origin
51 g2d.setColor(Color.RED);
52 g2d.fill(shape);
53 }
54 // Restore original transform before returning
55 g2d.setTransform(saveTransform);
56 }
57
58 /** The Entry main method */
59 public static void main(String[] args) {
60 // Run the GUI codes on the Event-Dispatching thread for thread safety
61 SwingUtilities.invokeLater(new Runnable() {
62 @Override
63 public void run() {
64 JFrame frame = new JFrame(TITLE);
65 frame.setContentPane(new AffineTransformDemo());
66 frame.pack();
67 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
68 frame.setLocationRelativeTo(null); // center the application window
69 frame.setVisible(true);
70 }
71 });
72 }
73 }

These are the steps for carrying out affine transform with a Graphics2D context:
1. Override the paintComponent(Graphics) method of the custom drawing JPanel. Downcast the Graphics object into
Graphics2D.
@Override
public void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
......

http://www.ntu.edu.sg/home/ehchua/programming/java/J8b_Game_2DGraphics.html 4 / 20
2D Graphics & Java2D 7/14/2015

2. Prepare a Shape (such as Polygon, Rectangle2D, Rectangle). The original shape shall center at (0, 0) to simplify rotation.
3. Save the current transform associated with this Graphics2D context, and restore the saved transform before exiting the method.
AffineTransform saveTransform = g2d.getTransform(); // save
....
g2d.setTransform(saveTransform); // restore

4. Allocate a new AffineTransform. Initialize to the default identity transform. Apply it to the current Graphics2D context. You can
then use the Graphics2D context to perform translation, rotation, scaling and shearing.
AffineTransform identity = new AffineTransform(); // an identity transform
g2d.setTransform(identity); // overwrites the transform associated with this Graphics2D context
g2d.translate(x, y); // translates from (0, 0) to the current (x, y) position
g2d.scale(scaleX, scaleY); // scaling
g2d.rotate(angle); // rotation clockwise about (0, 0), by angle (in radians)
g2d.shear(shearX, shearY); // shearing

Take note that successive transforms are concatenated, until it is reset (to the identity transform) or overwritten.
In the above example, the Polygon, which is originally centered at (0, 0) (shown in green), is first translated to (50, 50) and scaled up by 1.2
(in green). A loop of 5-iterations is applied to translate by (50, 5) (in blue) and rotate by 15 degrees about (0, 0) (in red). Observe that after
each transform, the axes and origin are transformed accordingly. This is especially noticeable for rotation, as the axes are no longer parallel
and perpendicular to the screen and its origin is shifted as well. The origin of the axes is set to (0, 0) by the identity transform.

Rotation
The result of rotation depends on the angle rotated, as well as its rotation center. Two methods are provided:
public abstract void rotate(theta);
public abstract void rotate(theta, anchorX, anchorY)

The first method rotates about the origin (0, 0), while the second method rotate about (anchorX, anchorY), without affecting the origin after
the rotation. Take note that after each rotation, the coordinates is rotated as well. Rotation can be made simpler, by center the shape at (0, 0)
as shown in the above example.

2.3 Geometric Primitives and Shapes


Java 2D's primitives include:
point (Point2D)
line (Line2D)
rectangular shapes (Rectangle2D, RoundRectangle2D,, Ellipse2D, Arc2D, Dimension2D)
quadratic and cubic curves with control points (QuadCurve2D, CubicCurve2D)
arbitrary shapes (Path2D).
[TODO] Class diagram
The Xxx2D classes have two nested public static subclasses Xxx2D.Double and Xxx2D.Float to support double- and float-
precision. High-quality 2D rendering (e.g., rotation, shearing, curve segments) cannot be performed in int (even though eventually it will be
converted to integral screen-pixel values for display). Hence, double and float subclasses are introduced in Java 2D for better accuracy and
smoothness. The earlier int-precision classes, such as Point and Rectangle are retrofitted as a subclass of Point2D and Rectangle2D.
The Xxx2D, Xxx2D.Double and Xxx2D.Float are kept in package java.awt.geom package.

GeneralPath
The java.awt.geom.GeneralPath class represents a geometric path constructed from straight lines, quadratic and cubic curves. For
example,
int[] x = { -20, 0, 20, 0};
int[] y = { 20, 10, 20, -20};
GeneralPath p = new GeneralPath();
p.moveTo(x[0], y[0]);
for (int i = 1; i < x.length; ++i) {
p.lineTo(x[i], y[i]);
}
p.closePath();
g2d.translate(350, 250);
g2d.draw(p);

2.4 Point2D and its Subclasses Point, Point2D.Double and Point2D.Float (Advanced)

http://www.ntu.edu.sg/home/ehchua/programming/java/J8b_Game_2DGraphics.html 5 / 20
2D Graphics & Java2D 7/14/2015

Reference: Source codes of java.awt.geom.Point2D and java.awt.Point.


As an example, let's examine Point2D abstract superclass and its subclasses more closely.

java.awt.geom.Point2D
Point2D is an abstract class that cannot be instantiated. It declares the following abstract methods:
abstract public double getX();
abstract public double getY();
abstract public void setLocation(double x, double y);

It also defines and implemented some static methods and member methods. (Hence, it is designed as an abstract class, instead of
interface.)
// static methods
public static double distance(double x1, double y1, double x2, double y2)
public static double distanceSq(double x1, double y1, double x2, double y2)
// instance (non-static) methods
public double distance(double x, double y)
public double distanceSq(double x, double y)
public double distance(Point2D p)
public double distanceSq(Point2D p)
......

Point2D does not define any instance variable, in particular, the x and y location of the point. This is because it is not sure about the type of
x and y (which could be int, float or double). The instance variables, therefore, are left to the implementation subclasses. Three subclasses
were implemented for types of int, float and double, respectively.

Subclasses java.awt.Point, java.awt.geom.Point2D.Double and java.awt.geom.Point2D.Float


How to design these subclasses?
java.awt.Point is the subclass for int-precision. It declares instance variables x and y as int, provides implementation to the abstract
methods declared in the superclass, and provides some additional methods.
package java.awt;
public class Point extends Point2D {
// Instance variables (x, y) of type int
public int x;
public int y;
// Constructor
public Point(int x, int y) { this.x = x; this.y = y; }
// Provide implementation to the abstract methods declared in the superclass
public double getX() { return x; }
public double getY() { return y; }
public void setLocation(double x, double y) {
this.x = (int)Math.floor(x + 0.5);
this.y = (int)Math.floor(y + 0.5);
}
// Other methods
......
}

Point (of int-precision) is a straight-forward implementation of inheritance and polymorphism. Point is a legacy class (since JDK 1.1) and
retrofitted when Java 2D was introduced. For higher-quality and smoother graphics (e.g., rotation), int-precision is insufficient. Java 2D,
hence, introduced float-precision and double-precision points.
Point2D.Double (for double-precision point) and Point2D.Float (for float-precision point) are, however, implemented as nested
public static subclasses inside the Point2D outer class.
Recall that nested class (or inner class) can be used for:
1. Simplifying access control: inner class can access the private variables of the enclosing outer class, as they are at the same level.
2. Keeping codes together and namespace management.
In this case, it is used for namespace management, as there is no access-control involved.
package java.awt.geom;
abstract public class Point2D { // outer class

public static class Double extend Point2D { // public static nested class and subclass
public double x;
public double y;
// Constructor
public Double(double x, double y) { this.x = x; this.y = y; }
// Provides implementation to the abstract methods
public double getX() { return x; }
public double getY() { return y; }

http://www.ntu.edu.sg/home/ehchua/programming/java/J8b_Game_2DGraphics.html 6 / 20
2D Graphics & Java2D 7/14/2015

public void setLocation(double x, double y) {


this.x = x;
this.y = y;
}
// Other methods
......
}

public static class Float extend Point2D { // public static nested class and subclass
public float x;
public float y;
// Constructor
public Float(float x, float y) { this.x = x; this.y = y; }
// Provide implementation to the abstract methods
public double getX() { return x; }
public double getY() { return y; }
public void setLocation(double x, double y) {
this.x = (float)x;
this.y = (float)y;
}
// Other methods
......
}

// Definition for the outer class


abstract public double getX();
abstract public double getY();
abstract public void setLocation(double x, double y);
......
}

Double and Float are static. In other words, they can be referenced via the classname as Point2D.Double and Point2D.Float, and
used directly without instantiating the outer class, just like any static variable or method (e.g., Math.PI, Math.sqrt(),
Integer.parseInt()). They are subclasses of Point2D, and thus can be upcasted to Point2D.
Point2D.Double p1 = new Point2D.Double(1.1, 2.2);
Point2D.Float p2 = new Point2D.Float(3.3f, 4.4f);
Point p3 = new Point(5, 6);
// You can upcast subclasses Point2D.Double, Point2D.Float and Point to superclass Point2D.
Point2D p4 = new Point2D.Double(1.1, 2.2);
Point2D p5 = new Point2D.Float(3.3f, 4.4f);
Point2D p6 = new Point(5, 6);

In summary, you can treat Point2D.Double and Point2D.Float as ordinary classes with a slightly longer name. They were designed as
nested class for namespace management, instead of calling them Point2DDouble and Point2DFloat.
Note: These classes were designed before JDK 1.5, which introduces generic to support passing of type.

2.5 Interface java.awt.Shape


Almost all the Xxx2D classes (except Point2D) implements java.awt.Shape interface. It can be used as argument in Graphics2D's
draw(Shape) and fill(Shape) methods.
The Shape interface declares abstract methods contains() and intersects(), which are useful in game programming for collision
detection:
// Is this point within this Shape's bounds?
boolean contains(double x, double y)
boolean contains(Point2D p)
// Is this rectangle within this Shape's bounds?
boolean contains(double x, double y, double width, double height)
boolean contains(Rectangle2D rect)
// Does the interior of the given bounding rectangle intersect with this shape?
boolean intersects(double x, double y, double width, double height)
boolean intersects(Rectangle2D rect)

The Shape interface also declares methods to return the bounding rectangle of this shape (again, useful in collision detection).
Rectangle getBounds() // int-precision
Rectangle2D getBounds2D() // higher precision version

The Shape interface declares a method called getPathIterator() to retrieve a PathIterator object that can be used to iterate along
the Shape boundary.
PathIterator getPathIterator(AffineTransform at)
PathIterator getPathIterator(AffineTransform at, double flatness)

The popular legacy java.awt.Polygon class was retrofitted to implement Shape interface. However, there is no Polyon2D in Java 2D,

http://www.ntu.edu.sg/home/ehchua/programming/java/J8b_Game_2DGraphics.html 7 / 20
2D Graphics & Java2D 7/14/2015

which can be served by a more generic Path2D.


[TODO] Example

2.6 Stroke, Paint and Composite Attributes


Pen's Stroke
The Graphisc2D's stroke attribute control the pen-stroke used for the outline of a shape. It is set via the Graphis2D's setStroke(). A
Stroke object implements java.awt.Stroke interface. Java 2D provides a built-in java.awt.BasicStroke.
public BasicStroke(float width, int cap, int join, float miterlimit, float[] dash, float dash_phase)
// All parameters are optional
// width: width of the pen stroke
// cap: the decoration of the ends, CAP_ROUND, CAP_SQUARE or CAP_BUTT.
// join: the decoration where two segments meet, JOIN_ROUND, JOIN_MITER, or JOIN_BEVEL
// miterlimit: the limit to trim the miter join.
// dash: the array representing the dashing pattern.
// dash_phase: the offset to start the dashing pattern

For example,
g2d.setStroke(new BasicStroke(10, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
g2d.setColor(Color.CYAN);
g2d.draw(new Rectangle2D.Double(300, 50, 200, 100));

// Test dash-stroke
float[] dashPattern = {20, 5, 10, 5}; // dash, space, dash, space
g2d.setStroke(new BasicStroke(5, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND,
10, dashPattern, 0));
g2d.setColor(Color.CYAN);
g2d.draw(new Rectangle2D.Double(50, 200, 200, 100));

Paint
The Graphics2D's paint attribute determines the color used to render the shape. It is set via the Graphics2D's setPaint() method. A
Paint object implements the java.awt.Paint interface. Java 2D provides many built-in Paint objects such as GradientPaint,
LinearGradientPaint, RadialGradientPaint, MultipleGradientPaint, TexturePaint, and others.
For example,
g2d.setPaint(new GradientPaint(50, 80, Color.RED, 250, 180, Color.GREEN));
// set current paint context to a GradientPaint, from (x1, y1) with color1 to (x2, y2) with color2
g2d.fill(new Rectangle2D.Double(50, 80, 200, 100));
// fill the Shap with the current paint context

Composite
[TODO] How to compose the drawing of primitive with the underlying graphics area.

3. Working with Bitmap Images


Reference: Java Tutorial's "2D Graphics" Section "Working with Images" @ http://docs.oracle.com/javase/tutorial/2d/images/index.html"
A bitmap image is a 2D rectangular array of pixels. Each pixel has a color value (typically in RGB or RGBA). The dimension of the image is
represented by its width and length in pixels. In Java, the origin (0, 0) of an image is positioned at the top-left corner, like all other
components.
Most of the image display and processing methods work on java.awt.Image. Image is an abstract class that represent an image as a
rectangular array of pixels. The most commonly-used implementation subclass is java.awt.image.BufferedImage, which stores the
pixels in memory buffer so that they can be directly accessed. A BufferedImage comprises a ColorModel and a Raster of pixels. The
ColorModel provides a color interpretation of the image's pixel data.
An Image object (and subclass BufferedImage) can be rendered onto a JComponent (such as JPanel) via Graphics' (or Graphics2D')
drawImage() method.
Image is typically read in from an external image file into a BufferedImage (although you can create a BufferedImage based on
algorithm). Image file formats supported by JDK include:
GIF
PNG (Portable Network Graphics)
JPEG
BMP

http://www.ntu.edu.sg/home/ehchua/programming/java/J8b_Game_2DGraphics.html 8 / 20
2D Graphics & Java2D 7/14/2015

BufferedImage supports image filtering operations (such as convolution). The resultant image can be painted on the screen, sent to printer,
or save to an external file.

Transparent vs. Opaque Background


PNG and GIF supports transparent background. JPEG does not. PNG and GIF are palette-based. They maintain a list of palettes and map
each palette number to a RGB color value. Every image pixel is then labeled with a palette number, which is then mapped to the actual RGB
values. One of the palette number can be designated as transparent. Pixels with this transparent palette value will not be displayed. Instead,
the background of the image is displayed.

3.1 Loading Images


There are several ways to create an Image for use in your program.

Using ImageIO.read() (JDK 1.4)


The easiest way to load an image into your program is to use the static method ImageIO.read() of the javax.imageio.ImageIO
class, which returns a java.awt.image.BufferedImage. Similar, you can use ImageIO.write() to write an image.
public static BufferedImage read(URL imagePath) throws IOException
public static BufferedImage read(File imagePath) throws IOException
public static BufferedImage read(InputStream stream) throws IOException
public static BufferedImage read(ImageInputStream stream) throws IOException

Instead of using java.io.File class to handle a disk file, it is better to use java.net.URL. URL is more flexible and can handle files from
more sources, such as disk file and JAR file (used for distributing your program). It works on application as well as applet. For example,
1 import java.awt.*;
2 import java.io.IOException;
3 import java.net.URL;
4 import javax.imageio.ImageIO;
5 import javax.swing.*;
6
7 /** Test loading an external image into a BufferedImage using ImageIO.read() */
8 @SuppressWarnings("serial")
9 public class LoadImageDemo extends JPanel {
10 // Named-constants
11 public static final int CANVAS_WIDTH = 640;
12 public static final int CANVAS_HEIGHT = 480;
13 public static final String TITLE = "Load Image Demo";
14
15 private String imgFileName = "images/duke.gif"; // relative to project root (or bin)
16 private Image img; // a BufferedImage object
17
18 /** Constructor to set up the GUI components */
19 public LoadImageDemo() {
20 // Load an external image via URL
21 URL imgUrl = getClass().getClassLoader().getResource(imgFileName);
22 if (imgUrl == null) {
23 System.err.println("Couldn't find file: " + imgFileName);
24 } else {
25 try {
26 img = ImageIO.read(imgUrl);
27 } catch (IOException ex) {
28 ex.printStackTrace();
29 }
30 }
31
32 setPreferredSize(new Dimension(CANVAS_WIDTH, CANVAS_HEIGHT));
33 }
34
35 /** Custom painting codes on this JPanel */
36 @Override
37 public void paintComponent(Graphics g) {
38 super.paintComponent(g); // paint background
39 setBackground(Color.WHITE);
40 g.drawImage(img, 50, 50, null);
41 }
42
43 /** The Entry main method */
44 public static void main(String[] args) {
45 // Run the GUI codes on the Event-Dispatching thread for thread safety
46 SwingUtilities.invokeLater(new Runnable() {
47 @Override

http://www.ntu.edu.sg/home/ehchua/programming/java/J8b_Game_2DGraphics.html 9 / 20
2D Graphics & Java2D 7/14/2015

48 public void run() {


49 JFrame frame = new JFrame("Load Image Demo");
50 frame.setContentPane(new LoadImageDemo());
51 frame.pack();
52 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
53 frame.setVisible(true);
54 }
55 });
56 }
57 }

Using Toolkit's getImage()


// In java.awt.Toolkit
public abstract Image getImage(URL url)
public abstract Image getImage(String filename)

For example,
import java.awt.Toolkit;
......
Toolkit tk = Toolkit.getDefaultToolkit();
Image img = tk.getImage("images/duke.gif");

Via ImageIcon's getImage()


ImageIcon is used to decorate JComponents (such as JLabel and JButton). Construct an ImageIcon and get an Image via ImageIcon's
getImage(). For example,
ImageIcon icon = null;
String imgFilename = "images/duke.gif";
java.net.URL imgURL = getClass().getClassLoader().getResource(imgFilename);
if (imgURL != null) {
icon = new ImageIcon(imgURL);
} else {
System.err.println("Couldn't find file: " + imgFilename);
}
Image img = icon.getImage();

On the other hand, you can also construct an ImageIcon from an Image object via constructor:
public ImageIcon(Image image) // Construct an ImageIcon from the Image object

[TODO] Benchmark these methods for small and large images.

3.2 drawImage()
The java.awt.Graphics class declares 6 versions of drawImage() for drawing bitmap images. The subclass java.awt.Graphics2D
adds a few more.
// In class java.awt.Graphics
public abstract boolean drawImage(Image img, int x, int y, ImageObserver observer)
public abstract boolean drawImage(Image img, int x, int y, int width, int height, ImageObserver observer)
public abstract boolean drawImage(Image img, int x, int y, Color bgcolor, ImageObserver observer)
public abstract boolean drawImage(Image img, int x, int y, int width, int height, Color bgcolor, ImageObserver observer)
// The img is drawn with its top-left corner at (x, y) scaled to the specified width and height
// (default to the image's width and height).
// The bgColor (background color) is used for "transparent" pixels.

public abstract boolean drawImage(Image img, int destX1, int destY1, int destX2, int destY2,
int srcX1, int srcY1, int srcX2, int srcY2, ImageObserver observer)
public abstract boolean drawImage(Image img, int destX1, int destY1, int destX2, int destY2,
int srcX1, int srcY1, int srcX2, int srcY2, Color bgcolor, ImageObserver observer)
// The img "clip" bounded by (scrX1, scrY2) and (scrX2, srcY2) is scaled and drawn from
// (destX1, destY1) to (destX2, destY2).

The coordinates involved is shown in the above diagram. The ImageObserver receives notification about the Image as it is loaded. In most
purposes, you can set it to null, or the custom drawing JPanel (via this). The ImageObserver is not needed for BufferedImage, and
shall be set to null.

Graphics2D's drawImage()
Graphics2D supports affine transform and image filtering operations on images, as follows:
// In class java.awt.Graphics2D
public abstract boolean drawImage(Image img, AffineTransform xform, ImageObserver obs)

http://www.ntu.edu.sg/home/ehchua/programming/java/J8b_Game_2DGraphics.html 10 / 20
2D Graphics & Java2D 7/14/2015

// Apply the specified AffineTransform to the image


public abstract void drawImage(BufferedImage img, BufferedImageOp op, int x, int y)
// Apply the specified image filtering operation to the image

public abstract void drawRenderedImage(RenderedImage img, AffineTransform xform)


public abstract void drawRenderableImage(RenderableImage img, AffineTransform xform)

3.3 Image Affine Transforms


Java 2D's affine transform works on bitmap image as well as vector graphics. However, instead of manipulating the Graphics2D's current
transform context (which operates on vector-graphics only via rendering methods drawXxx() and fillXxx()), you need to allocate an
AffineTransform object to perform transformation on images.

Code Example
1 import java.awt.geom.AffineTransform;
2 import javax.imageio.ImageIO;
3 import java.net.URL;
4 import java.awt.*;
5 import javax.swing.*;
6 import java.io.*;
7
8 /** Test applying affine transform on images */
9 @SuppressWarnings("serial")
10 public class ImageTransformDemo extends JPanel {
11 // Named-constants for dimensions
12 public static final int CANVAS_WIDTH = 640;
13 public static final int CANVAS_HEIGHT = 480;
14 public static final String TITLE = "Image Transform Demo";
15
16 // Image
17 private String imgFileName = "images/duke.png"; // relative to project root or bin
18 private Image img;
19 private int imgWidth, imgHeight; // width and height of the image
20 private double x = 100.0, y = 80.0; // center (x, y), with initial value
21
22 /** Constructor to set up the GUI components */
23 public ImageTransformDemo() {
24 // URL can read from disk file and JAR file
25 URL url = getClass().getClassLoader().getResource(imgFileName);
26 if (url == null) {
27 System.err.println("Couldn't find file: " + imgFileName);
28 } else {
29 try {
30 img = ImageIO.read(url);
31 imgWidth = img.getWidth(this);
32 imgHeight = img.getHeight(this);
33 } catch (IOException ex) {
34 ex.printStackTrace();
35 }
36 }
37
38 this.setPreferredSize(new Dimension(CANVAS_WIDTH, CANVAS_HEIGHT));
39 }
40
41 /** Custom painting codes on this JPanel */
42 @Override
43 public void paintComponent(Graphics g) {
44 super.paintComponent(g); // paint background
45 setBackground(Color.WHITE);
46
47 Graphics2D g2d = (Graphics2D) g;
48 g2d.drawImage(img, 0, 0, this); // Display with top-left corner at (0, 0)
49
50 // drawImage() does not use the current transform of the Graphics2D context
51 // Need to create a AffineTransform and pass into drawImage()
52 AffineTransform transform = new AffineTransform(); // identity transform
53 // Display the image with its center at the initial (x, y)
54 transform.translate(x - imgWidth/2, y - imgHeight/2);
55 g2d.drawImage(img, transform, this);
56 // Try applying more transform to this image
57 for (int i = 0; i < 5; ++i) {
58 transform.translate(70.0, 5.0);
59 transform.rotate(Math.toRadians(15), imgWidth/2, imgHeight/2); // about its center

http://www.ntu.edu.sg/home/ehchua/programming/java/J8b_Game_2DGraphics.html 11 / 20
2D Graphics & Java2D 7/14/2015

60 transform.scale(0.9, 0.9);
61 g2d.drawImage(img, transform, this);
62 }
63 }
64
65 /** The Entry main method */
66 public static void main(String[] args) {
67 // Run the GUI codes on the Event-Dispatching thread for thread safety
68 SwingUtilities.invokeLater(new Runnable() {
69 @Override
70 public void run() {
71 JFrame frame = new JFrame(TITLE);
72 frame.setContentPane(new ImageTransformDemo());
73 frame.pack();
74 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
75 frame.setLocationRelativeTo(null); // center the application window
76 frame.setVisible(true);
77 }
78 });
79 }
80 }

3.4 Image Filtering Operations


Graphics2D supports image filtering operations via the following drawImage() method:
public abstract void drawImage(BufferedImage img, BufferedImageOp op, int x, int y)
// Apply the specified image filtering operation to the image

Many built-in image filtering operations are available in java.awt.image package.


[TODO] more and example

3.5 Animating Image Frames


There are two ways to organize animated image frames:
1. Keep each of the frames in its own file.
2. Keep all of the frames in a stripe (1D or 2D) in a single file for better organization and faster loading.

Code Example 1: Each Frame in its Own File


Three image frames (in its own file) was used in this example, as follow:

In a typical game, the actor has a (x, y) position, move at a certain speed (in pixels per move-step) and direction (in degrees), and may
rotate at a rotationSpeed (in degrees per move-step).
1 import java.awt.geom.AffineTransform;
2 import javax.imageio.ImageIO;
3 import java.net.URL;
4 import java.awt.*;
5 import javax.swing.*;
6 import java.io.*;
7
8 /** Animating image frames. Each frame has its own file */
9 @SuppressWarnings("serial")
10 public class AnimatedFramesDemo extends JPanel {
11 // Named-constants
12 static final int CANVAS_WIDTH = 640;
13 static final int CANVAS_HEIGHT = 480;
14 public static final String TITLE = "Animated Frame Demo";
15
16 private String[] imgFilenames = {
17 "images/pacman_1.png", "images/pacman_2.png", "images/pacman_3.png"};
18 private Image[] imgFrames; // array of Images to be animated
19 private int currentFrame = 0; // current frame number
20 private int frameRate = 5; // frame rate in frames per second
21 private int imgWidth, imgHeight; // width and height of the image
22 private double x = 100.0, y = 80.0; // (x,y) of the center of image
23 private double speed = 8; // displacement in pixels per move
24 private double direction = 0; // in degrees
25 private double rotationSpeed = 1; // in degrees per move
26
27 // Used to carry out the affine transform on images

http://www.ntu.edu.sg/home/ehchua/programming/java/J8b_Game_2DGraphics.html 12 / 20
2D Graphics & Java2D 7/14/2015

28 private AffineTransform transform = new AffineTransform();


29
30 /** Constructor to set up the GUI components */
31 public AnimatedFramesDemo() {
32 // Setup animation
33 loadImages(imgFilenames);
34 Thread animationThread = new Thread () {
35 @Override
36 public void run() {
37 while (true) {
38 update(); // update the position and image
39 repaint(); // Refresh the display
40 try {
41 Thread.sleep(1000 / frameRate); // delay and yield to other threads
42 } catch (InterruptedException ex) { }
43 }
44 }
45 };
46 animationThread.start(); // start the thread to run animation
47
48 // Setup GUI
49 setPreferredSize(new Dimension(CANVAS_WIDTH, CANVAS_HEIGHT));
50 }
51
52 /** Helper method to load all image frames, with the same height and width */
53 private void loadImages(String[] imgFileNames) {
54 int numFrames = imgFileNames.length;
55 imgFrames = new Image[numFrames]; // allocate the array
56 URL imgUrl = null;
57 for (int i = 0; i < numFrames; ++i) {
58 imgUrl = getClass().getClassLoader().getResource(imgFileNames[i]);
59 if (imgUrl == null) {
60 System.err.println("Couldn't find file: " + imgFileNames[i]);
61 } else {
62 try {
63 imgFrames[i] = ImageIO.read(imgUrl); // load image via URL
64 } catch (IOException ex) {
65 ex.printStackTrace();
66 }
67 }
68 }
69 imgWidth = imgFrames[0].getWidth(null);
70 imgHeight = imgFrames[0].getHeight(null);
71 }
72
73 /** Update the position based on speed and direction of the sprite */
74 public void update() {
75 x += speed * Math.cos(Math.toRadians(direction)); // x-position
76 if (x >= CANVAS_WIDTH) {
77 x -= CANVAS_WIDTH;
78 } else if (x < 0) {
79 x += CANVAS_WIDTH;
80 }
81 y += speed * Math.sin(Math.toRadians(direction)); // y-position
82 if (y >= CANVAS_HEIGHT) {
83 y -= CANVAS_HEIGHT;
84 } else if (y < 0) {
85 y += CANVAS_HEIGHT;
86 }
87 direction += rotationSpeed; // update direction based on rotational speed
88 if (direction >= 360) {
89 direction -= 360;
90 } else if (direction < 0) {
91 direction += 360;
92 }
93 ++currentFrame; // display next frame
94 if (currentFrame >= imgFrames.length) {
95 currentFrame = 0;
96 }
97 }
98
99 /** Custom painting codes on this JPanel */
100 @Override
101 public void paintComponent(Graphics g) {
102 super.paintComponent(g); // paint background

http://www.ntu.edu.sg/home/ehchua/programming/java/J8b_Game_2DGraphics.html 13 / 20
2D Graphics & Java2D 7/14/2015

103 setBackground(Color.WHITE);
104 Graphics2D g2d = (Graphics2D) g;
105
106 transform.setToIdentity();
107 // The origin is initially set at the top-left corner of the image.
108 // Move the center of the image to (x, y).
109 transform.translate(x - imgWidth / 2, y - imgHeight / 2);
110 // Rotate about the center of the image
111 transform.rotate(Math.toRadians(direction),
112 imgWidth / 2, imgHeight / 2);
113 // Apply the transform to the image and draw
114 g2d.drawImage(imgFrames[currentFrame], transform, null);
115 }
116
117 /** The Entry main method */
118 public static void main(String[] args) {
119 // Run the GUI codes on the Event-Dispatching thread for thread safety
120 SwingUtilities.invokeLater(new Runnable() {
121 @Override
122 public void run() {
123 JFrame frame = new JFrame(TITLE);
124 frame.setContentPane(new AnimatedFramesDemo());
125 frame.pack();
126 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
127 frame.setLocationRelativeTo(null); // center the application window
128 frame.setVisible(true);
129 }
130 });
131 }
132 }

Dissecting the Program


[TODO]

Code Example 2: Frames Organized in a Stripe


In this example, all the frames of an animated sequence are kept in a single file organized in rows and columns.

1 import javax.imageio.ImageIO;
2 import java.net.URL;
3 import java.awt.*;
4 import javax.swing.*;
5 import java.io.*;
6
7 /** Animating image frames. All frames kept in a stripe. */
8 @SuppressWarnings("serial")
9 public class AnimatedFramesInStripe extends JPanel {
10 // Named-constants
11 static final int CANVAS_WIDTH = 640;
12 static final int CANVAS_HEIGHT = 480;
13 public static final String TITLE = "Animated Frame Demo";
14
15 private String imgFilename = "images/GhostStripe.png";
16 private int numRows, numCols, numFrames;
17 private Image img; // for the entire image stripe
18 private int currentFrame; // current frame number
19 private int frameRate = 5; // frame rate in frames per second
20 private int imgWidth, imgHeight; // width and height of the image
21 private double x = 100.0, y = 80.0; // (x,y) of the center of image
22 private double speed = 8; // displacement in pixels per move
23 private double direction = 0; // in degrees
24 private double rotationSpeed = 1; // in degrees per move
25
26 /** Constructor to set up the GUI components */
27 public AnimatedFramesInStripe() {
28 // Setup animation
29 loadImage(imgFilename, 2, 4);
30 Thread animationThread = new Thread () {
31 @Override
32 public void run() {
33 while (true) {
34 update(); // update the position and image
35 repaint(); // Refresh the display
36 try {

http://www.ntu.edu.sg/home/ehchua/programming/java/J8b_Game_2DGraphics.html 14 / 20
2D Graphics & Java2D 7/14/2015

37 Thread.sleep(1000 / frameRate); // delay and yield to other threads


38 } catch (InterruptedException ex) { }
39 }
40 }
41 };
42 animationThread.start(); // start the thread to run animation
43
44 // Setup GUI
45 setPreferredSize(new Dimension(CANVAS_WIDTH, CANVAS_HEIGHT));
46 }
47
48 /** Helper method to load image. All frames have the same height and width */
49 private void loadImage(String imgFileName, int numRows, int numCols) {
50 URL imgUrl = getClass().getClassLoader().getResource(imgFileName);
51 if (imgUrl == null) {
52 System.err.println("Couldn't find file: " + imgFileName);
53 } else {
54 try {
55 img = ImageIO.read(imgUrl); // load image via URL
56 } catch (IOException ex) {
57 ex.printStackTrace();
58 }
59 }
60 numFrames = numRows * numCols;
61 this.imgHeight = img.getHeight(null) / numRows;
62 this.imgWidth = img.getWidth(null) / numCols;
63 this.numRows = numRows;
64 this.numCols = numCols;
65 currentFrame = 0;
66 }
67
68 /** Returns the top-left x-coordinate of the given frame number. */
69 private int getcurrentFrameX() {
70 return (currentFrame % numCols) * imgWidth;
71 }
72
73 /** Returns the top-left y-coordinate of the given frame number. */
74 private int getCurrentFrameY() {
75 return (currentFrame / numCols) * imgHeight;
76 }
77
78 /** Update the position based on speed and direction of the sprite */
79 public void update() {
80 x += speed * Math.cos(Math.toRadians(direction)); // x-position
81 if (x >= CANVAS_WIDTH) {
82 x -= CANVAS_WIDTH;
83 } else if (x < 0) {
84 x += CANVAS_WIDTH;
85 }
86 y += speed * Math.sin(Math.toRadians(direction)); // y-position
87 if (y >= CANVAS_HEIGHT) {
88 y -= CANVAS_HEIGHT;
89 } else if (y < 0) {
90 y += CANVAS_HEIGHT;
91 }
92 direction += rotationSpeed; // update direction based on rotational speed
93 if (direction >= 360) {
94 direction -= 360;
95 } else if (direction < 0) {
96 direction += 360;
97 }
98 ++currentFrame; // displays next frame
99 if (currentFrame >= numFrames) {
100 currentFrame = 0;
101 }
102 }
103
104 /** Custom painting codes on this JPanel */
105 @Override
106 public void paintComponent(Graphics g) {
107 super.paintComponent(g); // paint background
108 setBackground(Color.WHITE);
109 Graphics2D g2d = (Graphics2D) g;
110
111 int frameX = getcurrentFrameX();

http://www.ntu.edu.sg/home/ehchua/programming/java/J8b_Game_2DGraphics.html 15 / 20
2D Graphics & Java2D 7/14/2015

112 int frameY = getCurrentFrameY();


113 g2d.drawImage(img,
114 (int)x - imgWidth / 2, (int)y - imgHeight / 2,
115 (int)x + imgWidth / 2, (int)y + imgHeight / 2,
116 frameX, frameY, frameX + imgWidth, frameY + imgHeight, null);
117 }
118
119 /** The Entry main method */
120 public static void main(String[] args) {
121 // Run the GUI codes on the Event-Dispatching thread for thread safety
122 SwingUtilities.invokeLater(new Runnable() {
123 @Override
124 public void run() {
125 JFrame frame = new JFrame(TITLE);
126 frame.setContentPane(new AnimatedFramesInStripe());
127 frame.pack();
128 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
129 frame.setLocationRelativeTo(null); // center the application window
130 frame.setVisible(true);
131 }
132 });
133 }
134 }

Dissecting the Program


[TODO]

4. High Performance Graphics

4.1 Full-Screen Display Mode (JDK 1.4)


Reference: Java Tutorial's "Full-Screen Exclusive Mode API" @http://docs.oracle.com/javase/tutorial/extra/fullscreen/index.html.
You could check if full-screen mode is supported in your graphics environment by invoking isFullScreenSupported() of the screen
GraphicsDevice:
GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice defaultScreen = env.getDefaultScreenDevice();
System.out.println("isFullScreenSupported: " + defaultScreen.isFullScreenSupported());

// Enter fullscreen mode


setUndecorated(true);
setResizable(false);
defaultScreen.setFullScreenWindow(this); // "this" JFrame

To enter fullscreen mode, use GraphicsDevice's setFullScreenWindow(JFrame). To leave the fullscreen mode and return to windowed
mode, use setFullScreenWindow(null). You should not try to setSize() or resize the window in full screen mode.
You could run your program in fullscreen (without the window's title bar) by invoking JFrame's setUndecorated(true).
There are a few ways to find out the current screen size:
// via the default Toolkit
Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
int screenWidth = (int)dim.getWidth();
int screenHeight = (int)dim.getHeight();

Code Example 1: Running in Fullscreen Mode


1 import java.awt.*;
2 import java.awt.event.*;
3 import javax.swing.*;
4
5 /** Testing the full-screen mode */
6 @SuppressWarnings("serial")
7 public class FullScreenDemo extends JFrame {
8
9 /** Constructor to set up the GUI components */
10 public FullScreenDemo() {
11 // Check if full screen mode supported?
12 GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
13 GraphicsDevice defaultScreen = env.getDefaultScreenDevice();
14 if (!defaultScreen.isFullScreenSupported()) {
15 System.err.println("Full Screen mode is not supported!");

http://www.ntu.edu.sg/home/ehchua/programming/java/J8b_Game_2DGraphics.html 16 / 20
2D Graphics & Java2D 7/14/2015

16 System.exit(1);
17 }
18
19 // Use ESC key to quit
20 addKeyListener(new KeyAdapter() {
21 @Override
22 public void keyPressed(KeyEvent e) {
23 if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
24 System.exit(0);
25 }
26 }
27 });
28
29 setContentPane(new DrawCanvas());
30 setDefaultCloseOperation(EXIT_ON_CLOSE);
31 setUndecorated(true);
32 setResizable(false);
33 defaultScreen.setFullScreenWindow(this); // full-screen mode
34 setVisible(true);
35 }
36
37 /** DrawCanvas (inner class) is a JPanel used for custom drawing */
38 private class DrawCanvas extends JPanel {
39 @Override
40 public void paintComponent(Graphics g) {
41 super.paintComponent(g);
42 setBackground(Color.BLACK);
43
44 // Paint messages
45 g.setColor(Color.YELLOW);
46 g.setFont(new Font(Font.DIALOG, Font.BOLD, 30));
47 FontMetrics fm = g.getFontMetrics();
48 String msg = "In Full-Screen mode";
49 int msgWidth = fm.stringWidth(msg);
50 int msgAscent = fm.getAscent();
51 int msgX = getWidth() / 2 - msgWidth / 2;
52 int msgY = getHeight() / 2 + msgAscent / 2;
53 g.drawString(msg, msgX, msgY);
54
55 g.setColor(Color.WHITE);
56 g.setFont(new Font(Font.DIALOG, Font.PLAIN, 18));
57 fm = g.getFontMetrics();
58 msg = "Press ESC to quit";
59 msgWidth = fm.stringWidth(msg);
60 int msgHeight = fm.getHeight();
61 msgX = getWidth() / 2 - msgWidth / 2;
62 msgY += msgHeight * 1.5;
63 g.drawString(msg, msgX, msgY);
64 }
65 }
66
67 /** The Entry main method */
68 public static void main(String[] args) {
69 // Run the GUI codes on the Event-Dispatching thread for thread safety
70 SwingUtilities.invokeLater(new Runnable() {
71 @Override
72 public void run() {
73 new FullScreenDemo();
74 }
75 });
76 }
77 }

Code Example 2: Switching between Fullscreen and Windowed Mode


1 import java.awt.*;
2 import java.awt.event.*;
3 import javax.swing.*;
4
5 /** Testing the full-screen mode */
6 @SuppressWarnings("serial")
7 public class FullScreenEscDemo extends JFrame {
8 // Windowed mode settings
9 private static String winModeTitle =
10 "Switching between Full Screen Mode and Windowed Mode Demo";
11 private static int winModeX, winModeY; // top-left corner (x, y)

http://www.ntu.edu.sg/home/ehchua/programming/java/J8b_Game_2DGraphics.html 17 / 20
2D Graphics & Java2D 7/14/2015

12 private static int winModeWidth, winModeHeight; // width and height


13
14 private boolean inFullScreenMode; // in fullscreen or windowed mode?
15 private boolean fullScreenSupported; // is fullscreen supported?
16
17 /** Constructor to set up the GUI components */
18 public FullScreenEscDemo() {
19 // Get the screen width and height
20 Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
21 // Set the windowed mode initial width and height to about fullscreen
22 winModeWidth = (int)dim.getWidth();
23 winModeHeight = (int)dim.getHeight() - 35; // minus task bar
24 winModeX = 0;
25 winModeY = 0;
26
27 // Check if full screen mode supported?
28 GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
29 final GraphicsDevice defaultScreen = env.getDefaultScreenDevice();
30 fullScreenSupported = defaultScreen.isFullScreenSupported();
31
32 if (fullScreenSupported) {
33 setUndecorated(true);
34 setResizable(false);
35 defaultScreen.setFullScreenWindow(this); // full-screen mode
36 inFullScreenMode = true;
37 } else {
38 setUndecorated(false);
39 setResizable(true);
40 defaultScreen.setFullScreenWindow(null); // windowed mode
41 setBounds(winModeX, winModeY, winModeWidth, winModeHeight);
42 inFullScreenMode = false;
43 }
44
45 // Use ESC key to switch between Windowed and fullscreen modes
46 this.addKeyListener(new KeyAdapter() {
47 @Override
48 public void keyPressed(KeyEvent e) {
49 if (e.getKeyCode() == KeyEvent.VK_SPACE) {
50 if (fullScreenSupported) {
51 if (!inFullScreenMode) {
52 // Switch to fullscreen mode
53 setVisible(false);
54 setResizable(false);
55 dispose();
56 setUndecorated(true);
57 defaultScreen.setFullScreenWindow(FullScreenEscDemo.this);
58 setVisible(true);
59 } else {
60 // Switch to windowed mode
61 setVisible(false);
62 dispose();
63 setUndecorated(false);
64 setResizable(true);
65 defaultScreen.setFullScreenWindow(null);
66 setBounds(winModeX, winModeY, winModeWidth, winModeHeight);
67 setVisible(true);
68 }
69 inFullScreenMode = !inFullScreenMode;
70 repaint();
71 }
72 } else if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
73 System.exit(0);
74 }
75 }
76 });
77
78 // To save the window width and height if the window has been resized.
79 this.addComponentListener(new ComponentAdapter() {
80 @Override
81 public void componentMoved(ComponentEvent e) {
82 if (!inFullScreenMode) {
83 winModeX = getX();
84 winModeY = getY();
85 }
86 }

http://www.ntu.edu.sg/home/ehchua/programming/java/J8b_Game_2DGraphics.html 18 / 20
2D Graphics & Java2D 7/14/2015

87
88 @Override
89 public void componentResized(ComponentEvent e) {
90 if (!inFullScreenMode) {
91 winModeWidth = getWidth();
92 winModeHeight = getHeight();
93 }
94 }
95 });
96
97 setContentPane(new DrawCanvas());
98 setDefaultCloseOperation(EXIT_ON_CLOSE);
99 setTitle(winModeTitle);
100 setVisible(true);
101 }
102
103 /** DrawCanvas (inner class) is a JPanel used for custom drawing */
104 private class DrawCanvas extends JPanel {
105 @Override
106 public void paintComponent(Graphics g) {
107 super.paintComponent(g);
108 setBackground(Color.BLACK);
109
110 // Draw a box to indicate the borders
111 Graphics2D g2d = (Graphics2D)g;
112 g2d.setStroke(new BasicStroke(8));
113 g2d.setColor(Color.RED);
114 g2d.drawRect(0, 0, getWidth()-1, getHeight()-1);
115
116 // Paint messages
117 g.setColor(Color.YELLOW);
118 g.setFont(new Font(Font.DIALOG, Font.BOLD, 30));
119 FontMetrics fm = g.getFontMetrics();
120 String msg = inFullScreenMode ? "In Full-Screen mode" : "In Windowed mode";
121 int msgWidth = fm.stringWidth(msg);
122 int msgAscent = fm.getAscent();
123 int msgX = getWidth() / 2 - msgWidth / 2;
124 int msgY = getHeight() / 2 + msgAscent / 2;
125 g.drawString(msg, msgX, msgY);
126
127 g.setColor(Color.WHITE);
128 g.setFont(new Font(Font.DIALOG, Font.PLAIN, 18));
129 fm = g.getFontMetrics();
130 msg = "Press SPACE to toggle between Full-screen/windowed modes, ESC to quit.";
131 msgWidth = fm.stringWidth(msg);
132 int msgHeight = fm.getHeight();
133 msgX = getWidth() / 2 - msgWidth / 2;
134 msgY += msgHeight * 1.5;
135 g.drawString(msg, msgX, msgY);
136 }
137 }
138
139 /** The Entry main method */
140 public static void main(String[] args) {
141 // Run the GUI codes on the Event-Dispatching thread for thread safety
142 SwingUtilities.invokeLater(new Runnable() {
143 @Override
144 public void run() {
145 new FullScreenEscDemo();
146 }
147 });
148 }
149 }

4.2 Rendering to the Display & Double Buffering


The common problems in rendering a graphic object or image to the display are:
Flashing (or flickering): caused by clearing the display and then drawing the graphics.
Image Tearing: For a moving object, the user sees part of the new image and part of the old one.
The common way to resolve these display rendering problem is via so-called double buffering.
A few techniques are available in Java for double buffering:
BufferStrategy (JDK 1.4)

http://www.ntu.edu.sg/home/ehchua/programming/java/J8b_Game_2DGraphics.html 19 / 20
2D Graphics & Java2D 7/14/2015

BufferredImage
other??
[TODO]

4.3 Splash Screen


To show a splash screen before launching your application, include command-line VM argument " -splash:splashImagefilename" to
display the image.
To show a progress bar over the splash screen, need to write some codes to overlay the progress bar on top of the splash screen. The
following shows a simulation:
1 import java.awt.*;
2
3 /** Splash Screen Demo (with a Progress Bar)
4 Run with VM command-line option -splash:splashImageFilename */
5 public class SplashScreenDemo {
6
7 public static void main(String[] args) {
8 SplashScreen splash = SplashScreen.getSplashScreen();
9 if (splash == null) {
10 System.err.println("Splash Screen not available!");
11 } else {
12 // Okay, Splash screen created
13 Dimension splashBounds = splash.getSize();
14 Graphics2D g2d = splash.createGraphics();
15
16 // Simulate a progress bar
17 for (int i = 0; i < 100; i += 5) {
18 g2d.setColor(Color.RED);
19 g2d.fillRect(0, splashBounds.height / 2,
20 splashBounds.width * i / 100, 20);
21 splash.update();
22 try {
23 Thread.sleep(200); // Some delays
24 } catch (Exception e) {}
25 }
26 g2d.dispose();
27 splash.close();
28 }
29 }
30 }

[TODO] Can we use the JProgressBar class?

REFERENCES & RESOURCES


Java 2D Tutorial @ http://docs.oracle.com/javase/tutorial/2d/TOC.html.
Java Tutorial's "Full-Screen Exclusive Mode API" @ http://docs.oracle.com/javase/tutorial/extra/fullscreen/index.html.
Jonathan S. Harbour, "Beginning Java 5 Game Programming".

Latest version tested: JDK 1.7.2


Last modified: April, 2012

Feedback, comments, corrections, and errata can be sent to Chua Hock-Chuan (ehchua@ntu.edu.sg) | HOME

http://www.ntu.edu.sg/home/ehchua/programming/java/J8b_Game_2DGraphics.html 20 / 20

You might also like