Java 2D
Java 2D
1 Overview 2.2 Shape Classes 2.3 Drawing Shapes: Example Code 2.4 Drawing Shapes: Example Output 3. Paint Styles 3.1 Overview 3.2 Paint Classes 3.3 Transparency 3.4 Gradient Fills: Example Code 3.5 Gradient Fills: Example Output 3.6 Tiled Images: Overview 3.7 Tiled Images: Example Code 3.8 Tiled Images: Example Output 4. Transparency 4.1 Overview 4.2 Transparency: Example Code 4.3 Transparency: Example Output 5. Using Local Fonts 5.1 Overview 5.2 Listing Available Fonts: Example Code 5.3 Drawing with Local Fonts: Example Code 5.4 Drawing with Local Fonts: Example Output 6. Stroke Styles 6.1 Overview 6.2 Stroke Attributes 6.3 Stroke Thickness: Example Code 6.4 Stroke Thickness: Example Output 6.5 Dashed Lines: Example Code 6.6 Dashed Lines: Example Output 6.7 Line Cap and Join Styles: Example Code 6.8 Line Cap and Join Styles: Example Output 7. Coordinate Transformations 7.1 Overview 7.2 Translations and Rotations: Example Code 7.3 Translations and Rotations: Example Output 7.4 Shear Transformations 7.5 Shear Transformations: Example Code 7.6 Shear Transformations: Example Output 8. Conclusions 8.1 Requesting More Accurate Drawing: Rendering Hints 8.2 Summary of Using Java2D 8.3 Other Java2D Capabilities
8.4 Learning More About Java2D 1. Introduction to Java2D 1.1 Overview In Java 1.2, 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. However, to maintain compatibility with Swing as used in Java 1.1, the declared type of the paintComponent argument is Graphics, so you have to cast it to Graphics2D before using it. Java 1.1 Java 1.2 public void paint(Graphics g) { // Set pen parameters g.setColor(someColor); g.setFont(someLimitedFont); // Draw a shape g.drawString(...); g.drawLine(...) g.drawRect(...); // outline g.fillRect(...); // solid g.drawPolygon(...); // outline g.fillPolygon(...); // solid g.drawOval(...); // outline g.fillOval(...); // solid ... } public void paintComponent(Graphics g) { // Clear off-screen bitmap super.paintComponent(g); // Cast Graphics to Graphics2D Graphics2D g2d = (Graphics2D)g; // Set pen parameters g2d.setPaint(fillColorOrPattern); g2d.setStroke(penThicknessOrPattern); g2d.setComposite(someAlphaComposite); g2d.setFont(anyFont); g2d.translate(...); g2d.rotate(...); g2d.scale(...); g2d.shear(...); g2d.setTransform(someAffineTransform); // Allocate a shape
SomeShape s = new SomeShape(...); // Draw shape g2d.draw(s); // outline g2d.fill(s); // solid } 1.2 Main New Features Colors and patterns: gradient fills, fill patterns from tiled images, transparency Local fonts Pen thicknesses, dashing patterns, and segment connection styles Coordinate transformations 1.3 General Approach Cast the Graphics object to a Graphics2D object public void paintComponent(Graphics g) { super.paintComponent(g); // Typical Swing approach Graphics2D g2d = (Graphics2D)g; g2d.doSomeStuff(...); ... } Create a Shape object Rectangle2D.Double rect = ...; Ellipse2D.Double ellipse = ...; Polygon poly = ...; GeneralPath path = ...; SomeShapeYouDefined shape = ...; // Satisfies Shape interface ... Optional: modify 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); Draw an outlined or solid version of the Shape g2d.draw(someShape); g2d.fill(someShape); 2. Drawing Shapes in Java2D 2.1 Drawing Shapes: Overview With the AWT, you generally drew a shape by calling the drawXxx or fillXxx method of the Graphics object. In Java2D, you generally create a Shape object, then call either the draw or fill method of the Graphics2D object, supplying the Shape object as an argument. For example: public void paintComponent(Graphics g) {
super.paintComponent(g); Graphics2D g2d = (Graphics2D)g; // Assume x, y, and diameter are instance variables Ellipse2D.Double circle = new Ellipse2D.double(x, y, diameter, diameter); g2d.fill(circle); ... } You can still call the drawXxx methods if you like, however. This is necessary for drawString and drawImage, and possibly convenient for draw3DRect. Several classes have similar versions that store coordinates as either double precision numbers (Xxx.Double) or single precision numbers (Xxx.Float). The idea is that single precision coordinates might be slightly faster to manipulate on some platforms. 2.2 Shape Classes Arguments to the Graphics2D draw and fill methods must implement the Shape interface. You can create your own shapes, of course, but following are the major built-in ones. Except for Rectangle and Polygon, which are Java 1.1 holdovers, these appear in the java.awt.geom package. Arc2D.Double, Arc2D.Float Area (a shape built by adding/subtracting other shapes) CubicCurve2D.Double, CubicCurve2D.Float Ellipse2D.Double, Ellipse2D.Float GeneralPath (a series of connected shapes) Line2D.Double, Line2D.Float Polygon QuadCurve2D.Double, QuadCurve2D.Float Rectangle2D.Double, Rectangle2D.Float, Rectangle RoundRectangle2D.Double, RoundRectangle2D.Float 2.3 Drawing Shapes: Example Code Download the source: ShapeExample.java, WindowUtilities.java, and ExitListener.java. ShapeExample.java import javax.swing.*; // For JPanel, etc. import java.awt.*; // For Graphics, etc. import java.awt.geom.*; // For Ellipse2D, etc. /** An example of drawing/filling shapes with Java2D in Java 1.2. * * From tutorial on learning Java2D at * http://www.apl.jhu.edu/~hall/java/Java2D-Tutorial.html * * 1998 Marty Hall, http://www.apl.jhu.edu/~hall/java/ */ public class ShapeExample 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); } // super.paintComponent clears offscreen pixmap, // since we're using double buffering by default. protected void clear(Graphics g) { super.paintComponent(g); } protected Ellipse2D.Double getCircle() { return(circle); } public static void main(String[] args) { WindowUtilities.openInJFrame(new ShapeExample(), 380, 400); } } WindowUtilities.java import javax.swing.*; import java.awt.*; /** A few utilities that simplify testing of windows in Swing. * 1998 Marty Hall, http://www.apl.jhu.edu/~hall/java/ */ public class WindowUtilities { /** Tell system to use native look and feel, as in previous * releases. Metal (Java) LAF is the default otherwise. */ public static void setNativeLookAndFeel() { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch(Exception e) { System.out.println("Error setting native LAF: " + e); } } /** A simplified way to see a JPanel or other Container.
* Pops up a JFrame with specified Container as the content pane. */ 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); } /** Uses Color.white as the background color. */ public static JFrame openInJFrame(Container content, int width, int height, String title) { return(openInJFrame(content, width, height, title, Color.white)); } /** Uses Color.white as the background color, and the * name of the Container's class as the JFrame title. */ public static JFrame openInJFrame(Container content, int width, int height) { return(openInJFrame(content, width, height, content.getClass().getName(), Color.white)); } } ExitListener.java import java.awt.*; import java.awt.event.*; /** A listener that you attach to the top-level Frame or JFrame of * your application, so quitting the frame exits the application. * 1998 Marty Hall, http://www.apl.jhu.edu/~hall/java/ */
public class ExitListener extends WindowAdapter { public void windowClosing(WindowEvent event) { System.exit(0); } } 2.4 Drawing Shapes: Example Output 3. Paint Styles in Java2D 3.1 Paint Styles: Overview When you fill a Shape, the current Paint attribute of the Graphics2D object is used. This can be a Color (solid color), a GradientPaint (gradient fill gradually combining two colors), a TexturePaint (tiled image), or a new version of Paint that you write yourself. Use setPaint and getPaint to change and retrieve the Paint settings. Note that setPaint and getPaint supersede the setColor and getColor methods that were used in Graphics. 3.2 Paint Classes Arguments to the Graphics2D setPaint method (and return values of getPaint) must implement the Paint interface. Here are the major built-in Paint classes: Color Has the same constants (Color.red, Color.yellow, etc.) as the AWT version, plus some extra constructors. GradientPaint Constructors takes two points, two colors, and optionally a boolean flag that indicates that the color pattern should cycle. The first color is used at the first point, the second color at the second point, and points in between are colored based on how close they are to each of the points. TexturePaint Constructor takes a BufferedImage and a Rectangle2D, maps the image to the rectangle, then tiles the rectangle. Creating a BufferedImage from a GIF or JPEG file is a pain. First load an Image normally, get its size, create a BufferedImage that size with BufferedImage.TYPE_INT_ARGB as the image type, get the BufferedImage's Graphics object via createGraphics, then draw the Image into the BufferedImage using drawImage. An example of this process is shown later. 3.3 Transparency Transparency is not set in the Paint object, rather separately via an AlphaComposite object that is applied via setComposite. See Section 4 for details. 3.4 Gradient Fills: Example Code Download the source: GradientPaintExample.java (plus ShapeExample.java, WindowUtilities.java, and ExitListener.java if you don't have them from the previous examples). import java.awt.*; /** An example of gradient fills with Java2D in Java 1.2. * * From tutorial on learning Java2D at * http://www.apl.jhu.edu/~hall/java/Java2D-Tutorial.html *
* 1998 Marty Hall, http://www.apl.jhu.edu/~hall/java/ */ public class GradientPaintExample extends ShapeExample { // Red at (0,0), yellow at (175,175), changes gradually between. private GradientPaint gradient = new GradientPaint(0, 0, Color.red, 175, 175, Color.yellow, true); // true means to repeat pattern public void paintComponent(Graphics g) { clear(g); Graphics2D g2d = (Graphics2D)g; drawGradientCircle(g2d); } protected void drawGradientCircle(Graphics2D g2d) { g2d.setPaint(gradient); g2d.fill(getCircle()); g2d.setPaint(Color.black); g2d.draw(getCircle()); } public static void main(String[] args) { WindowUtilities.openInJFrame(new GradientPaintExample(), 380, 400); } } 3.5 Gradient Fills: Example Output 3.6 Tiled Images as Fill Patterns -- Overview To use tiled images, you create a TexturePaint object and specify its use via the setPaint method of Graphics2D, just as with solid colors and gradient fills. The TexturePaint constructor takes a BufferedImage and a Rectangle2D as arguments. The BufferedImage specifies what to draw, and the Rectangle2D specifies where the tiling starts. Creating a BufferedImage to hold custom drawing is relatively straightforward: call the BufferedImage constructor with a width, a height, and a type of BufferedImage.TYPE_INT_RGB, then call createGraphics on that to get a Graphics2D with which to draw. It is a bit harder to create one from an image file. First load an Image from an image file, then use MediaTracker to be sure it is done loading, then create an empty BufferedImage using the Image width and height, then get the Graphics2D via createGraphics, then draw the Image onto the BufferedImage. This process has been wrapped up in the getBufferedImage method of my ImageUtilities class. Note, however, that as of JDK1.2beta4, tiled images fail when used in conjunction with rotation transformations. 3.7 Tiled Images as Fill Patterns: Example Code Download the source: TiledImages.java and ImageUtilities.java (plus WindowUtilities.java and ExitListener.java if you don't have them from the previous
examples). TiledImages.java import javax.swing.*; import java.awt.*; import java.awt.geom.*; import java.awt.image.*; /** An example of using TexturePaint to fill objects with tiled * images. Uses the getBufferedImage method of ImageUtilities to * load an Image from a file and turn that into a BufferedImage. * * From tutorial on learning Java2D at * http://www.apl.jhu.edu/~hall/java/Java2D-Tutorial.html * * 1998 Marty Hall, http://www.apl.jhu.edu/~hall/java/ */ public class TiledImages extends JPanel { private String dir = System.getProperty("user.dir"); private String imageFile1 = dir + "/images/marty.jpg"; private TexturePaint imagePaint1; private Rectangle imageRect; private String imageFile2 = dir + "/images/bluedrop.gif"; private TexturePaint imagePaint2; private int[] xPoints = { 30, 700, 400 }; private int[] yPoints = { 30, 30, 600 }; private Polygon imageTriangle = new Polygon(xPoints, yPoints, 3); public TiledImages() { BufferedImage image = ImageUtilities.getBufferedImage(imageFile1, this); imageRect = new Rectangle(235, 70, image.getWidth(), image.getHeight()); imagePaint1 = new TexturePaint(image, imageRect); image = ImageUtilities.getBufferedImage(imageFile2, this); imagePaint2 = new TexturePaint(image, new Rectangle(0, 0, 32, 32)); } public void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2d = (Graphics2D)g; g2d.setPaint(imagePaint2); g2d.fill(imageTriangle); g2d.setPaint(Color.blue); g2d.setStroke(new BasicStroke(5)); g2d.draw(imageTriangle);
g2d.setPaint(imagePaint1); g2d.fill(imageRect); g2d.setPaint(Color.black); g2d.draw(imageRect); } public static void main(String[] args) { WindowUtilities.openInJFrame(new TiledImages(), 750, 650); } } ImageUtilities.java import java.awt.*; import java.awt.image.*; /** A class that simplifies a few common image operations, in * particular creating a BufferedImage from an image file, and * using MediaTracker to wait until an image or several images are * done loading. * * From tutorial on learning Java2D at * http://www.apl.jhu.edu/~hall/java/Java2D-Tutorial.html * * 1998 Marty Hall, http://www.apl.jhu.edu/~hall/java/ */ public class ImageUtilities { /** Create Image from a file, then turn that into a BufferedImage. */ public static BufferedImage getBufferedImage(String imageFile, Component c) { Image image = c.getToolkit().getImage(imageFile); waitForImage(image, c); BufferedImage bufferedImage = new BufferedImage(image.getWidth(c), image.getHeight(c), BufferedImage.TYPE_INT_RGB); Graphics2D g2d = bufferedImage.createGraphics(); g2d.drawImage(image, 0, 0, c); return(bufferedImage); } /** Take an Image associated with a file, and wait until it is * done loading. Just a simple application of MediaTracker. * If you are loading multiple images, don't use this * consecutive times; instead use the version that takes * an array of images. */
public static boolean waitForImage(Image image, Component c) { MediaTracker tracker = new MediaTracker(c); tracker.addImage(image, 0); try { tracker.waitForAll(); } catch(InterruptedException ie) {} return(!tracker.isErrorAny()); } /** Take some Images associated with files, and wait until they * are done loading. Just a simple application of MediaTracker. */ public static boolean waitForImages(Image[] images, Component c) { MediaTracker tracker = new MediaTracker(c); for(int i=0; i<images.length; i++) tracker.addImage(images[i], 0); try { tracker.waitForAll(); } catch(InterruptedException ie) {} return(!tracker.isErrorAny()); } } 3.8 Tiled Images as Fill Patterns: Example Output 4. Transparency in Java2D 4.1 Transparency: Overview Java2D 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 transparency by creating an AlphaComposite object then passing it to the setComposite method of the Graphics2D object. You create an AlphaComposite by calling AlphaComposite.getInstance with a mixing rule designator and a transparency (or "alpha") value. There are 8 built-in mixing rules (see the AlphaComposite API for details), but the one normally used for drawing with transparency settings is AlphaComposite.SRC_OVER. Alpha values range from 0.0F (completely transparent) to 1.0F (completely opaque). 4.2 Transparency: Example Code Download the source: TransparencyExample.java (plus WindowUtilities.java and ExitListener.java if you don't have them from the previous examples). import javax.swing.*; import java.awt.*; import java.awt.geom.*; /** An illustration of the use of AlphaComposite to make partially * transparent drawings. *
* From tutorial on learning Java2D at * http://www.apl.jhu.edu/~hall/java/Java2D-Tutorial.html * * 1998 Marty Hall, http://www.apl.jhu.edu/~hall/java/ */ public class TransparencyExample 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."; WindowUtilities.openInJFrame(new TransparencyExample(), 11*deltaX + 2*gap, deltaX + 3*gap, title, Color.lightGray); } } 4.3 Transparency: Example Output
5. Using Local Fonts in Java2D 5.1 Local Fonts: Overview You can use the same logical font names as in Java 1.1, namely Serif (e.g. Times), SansSerif (e.g. Helvetica or Arial), Monospaced (e.g. Courier), Dialog, and DialogInput. You can also use arbitrary local fonts if you first look up the entire list, which may take a few seconds. Lookup the fonts via the getAvailableFontFamilyNames or getAllFonts methods of GraphicsEnvironment. E.g.: GraphicsEnvironment env = GrapicsEnvironment.getLocalGraphicsEnvironment(); Then env.getAvailableFontFamilyNames(); or env.getAllFonts(); // Much slower! Despite a misleading description in the API, trying to use an available local font in JDK 1.2 without first looking up the fonts as above gives the same result as asking for an unavailable font: a default font instead of the actual one. Note that getAllFonts returns an array of real Font objects that you can use like any other Font, but is much slower. If all you need to do is tell Java to make all local fonts available, always use getAvailableFontFamilyNames. The best approach would be to loop down getAvailableFontFamilyNames, checking for your name, having several backup names to use if the first choice is not available. If you pass an unavailable family name to the Font constructor, a default font (SansSerif) will be used. 5.2 Example 1 -- Printing Out All Local Font Names Download the source: ListFonts.java. import java.awt.*; /** Lists the names of all available fonts with Java2D in Java 1.2. * * From tutorial on learning Java2D at * http://www.apl.jhu.edu/~hall/java/Java2D-Tutorial.html * * 1998 Marty Hall, http://www.apl.jhu.edu/~hall/java/ */ public class ListFonts { public static void main(String[] args) { GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment(); String[] fontNames = env.getAvailableFontFamilyNames(); System.out.println("Available Fonts:"); for(int i=0; i<fontNames.length; i++) System.out.println(" " + fontNames[i]); } } 5.3 Example 2 -- Drawing with Local Fonts
Download the source: FontExample.java (plus GradientPaintExample.java, ShapeExample.java, WindowUtilities.java, and ExitListener.java if you don't have them from the previous examples). import java.awt.*; /** An example of using local fonts with Java2D in Java 1.2. * * From tutorial on learning Java2D at * http://www.apl.jhu.edu/~hall/java/Java2D-Tutorial.html * * 1998 Marty Hall, http://www.apl.jhu.edu/~hall/java/ */ public class FontExample extends GradientPaintExample { public FontExample() { GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment(); env.getAvailableFontFamilyNames(); setFont(new Font("Goudy Handtooled BT", Font.PLAIN, 100)); } protected void drawBigString(Graphics2D g2d) { g2d.setPaint(Color.black); g2d.drawString("Java 2D", 25, 215); } public void paintComponent(Graphics g) { clear(g); Graphics2D g2d = (Graphics2D)g; drawGradientCircle(g2d); drawBigString(g2d); } public static void main(String[] args) { WindowUtilities.openInJFrame(new FontExample(), 380, 400); } } 5.4 Drawing with Local Fonts: Example Output 6. Stroke Styles in Java2D 6.1 Stroke Styles: Overview In the AWT, the drawXxx methods of Graphics resulted in solid, 1-pixel wide lines. Furthermore, drawing commands that consisted of multiple line segments (e.g. drawRect and drawPolygon) had a predefined way of joining the line segments together and terminating segments that do not join to others. Java2D gives you much more flexibility. In addition to setting the pen color or pattern (via setPaint, as discussed in the previous section), Java2D permits you to set the pen thickness and
dashing pattern, and to specify the way line segments end and are joined together. You do this by creating a BasicStroke object, then telling the Graphics2D object to use it via the setStroke method. 6.2 Stroke Attributes Arguments to setStroke must implement the Stroke interface, and the BasicStroke class is the sole builtin class that implements Stroke. Here are the BasicStroke constructors: BasicStroke() Creates a BasicStroke with a pen width of 1.0, the default cap style of CAP_SQUARE, and the default join style of JOIN_MITER. See the following examples of pen widths and cap/join styles. BasicStroke(float penWidth) Uses the specified pen width and the default cap/join styles (CAP_SQUARE and JOIN_MITER). BasicStroke(float penWidth, int capStyle, int joinStyle) Uses the specified pen width, cap style, and join style. The cap style can be one of CAP_SQUARE (make a square cap that extends past the end point by half the pen width -- this is the default), CAP_BUTT (cut off segment exactly at end point -- use this one for dashed lines), or CAP_ROUND (make a circular cap centered on the end point, with a diameter of the pen width). The join style can be one of JOIN_MITER (extend outside edges of lines until they meet -- this is the default), JOIN_BEVEL (connect outside corners of outlines with straight line), or JOIN_ROUND (round off corner with circle with diameter equal to the pen width). BasicStroke(float penWidth, int capStyle, int joinStyle, float miterLimit) Same as above but you can limit how far up the miter join can go (default is 10.0). Stay away from this. BasicStroke(float penWidth, int capStyle, int joinStyle, float miterLimit, float[] dashPattern, float dashOffset) Lets you make dashed lines by specifying an array of opaque (entries at even array indices) and transparent (odd indices) segments. The offset, which is often 0.0, specifies where to start in the dashing pattern. 6.3 Stroke Thickness: Example Code Download the source: StrokeThicknessExample.java (plus FontExample.java, GradientPaintExample.java, ShapeExample.java, WindowUtilities.java, and ExitListener.java if you don't have them from the previous examples). import java.awt.*; /** An example of Stroke (pen) widths with Java2D in Java 1.2. * * From tutorial on learning Java2D at * http://www.apl.jhu.edu/~hall/java/Java2D-Tutorial.html * * 1998 Marty Hall, http://www.apl.jhu.edu/~hall/java/ */ public class StrokeThicknessExample extends FontExample { public void paintComponent(Graphics g) { clear(g);
Graphics2D g2d = (Graphics2D)g; drawGradientCircle(g2d); drawBigString(g2d); drawThickCircleOutline(g2d); } protected void drawThickCircleOutline(Graphics2D g2d) { g2d.setPaint(Color.blue); g2d.setStroke(new BasicStroke(8)); // 8-pixel wide pen g2d.draw(getCircle()); } public static void main(String[] args) { WindowUtilities.openInJFrame(new StrokeThicknessExample(), 380, 400); } } 6.4 Stroke Thickness: Example Output 6.5 Dashed Lines: Example Code Download the source: DashedStrokeExample.java (plus FontExample.java, GradientPaintExample.java, ShapeExample.java, WindowUtilities.java, and ExitListener.java if you don't have them from the previous examples). import java.awt.*; /** An example of dashed lines with Java2D in Java 1.2. * * From tutorial on learning Java2D at * http://www.apl.jhu.edu/~hall/java/Java2D-Tutorial.html * * 1998 Marty Hall, http://www.apl.jhu.edu/~hall/java/ */ public class DashedStrokeExample extends FontExample { public void paintComponent(Graphics g) { clear(g); Graphics2D g2d = (Graphics2D)g; drawGradientCircle(g2d); drawBigString(g2d); drawDashedCircleOutline(g2d); } protected void drawDashedCircleOutline(Graphics2D g2d) { g2d.setPaint(Color.blue); // 30 pixel line, 10 pixel gap, 10 pixel line, 10 pixel gap float[] dashPattern = { 30, 10, 10, 10 }; g2d.setStroke(new BasicStroke(8, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10,
dashPattern, 0)); g2d.draw(getCircle()); } public static void main(String[] args) { WindowUtilities.openInJFrame(new DashedStrokeExample(), 380, 400); } } 6.6 Dashed Lines: Example Output 6.7 Line Cap and Join Styles: Example Code Download the source: LineStyles.java (plus WindowUtilities.java and ExitListener.java if you don't have them from the previous examples). import javax.swing.*; import java.awt.*; import java.awt.geom.*; /** An example of line cap and join styles with Java2D in Java 1.2. * * From tutorial on learning Java2D at * http://www.apl.jhu.edu/~hall/java/Java2D-Tutorial.html * * 1998 Marty Hall, http://www.apl.jhu.edu/~hall/java/ */ public class LineStyles extends JPanel { private GeneralPath path; private static int x = 30, deltaX = 150, y = 300, deltaY = 250, thickness = 40; private Circle p1Large, p1Small, p2Large, p2Small, p3Large, p3Small; private int compositeType = AlphaComposite.SRC_OVER; private AlphaComposite transparentComposite = AlphaComposite.getInstance(compositeType, 0.4F); private int[] caps = { BasicStroke.CAP_SQUARE, BasicStroke.CAP_BUTT, BasicStroke.CAP_ROUND }; private String[] capNames = { "CAP_SQUARE", "CAP_BUTT", "CAP_ROUND" }; private int[] joins = { BasicStroke.JOIN_MITER, BasicStroke.JOIN_BEVEL, BasicStroke.JOIN_ROUND }; private String[] joinNames = { "JOIN_MITER", "JOIN_BEVEL", "JOIN_ROUND" }; public LineStyles() { path = new GeneralPath(); path.moveTo(x, y);
p1Large = new Circle(x, y, thickness/2); p1Small = new Circle(x, y, 2); path.lineTo(x + deltaX, y - deltaY); p2Large = new Circle(x + deltaX, y - deltaY, thickness/2); p2Small = new Circle(x + deltaX, y - deltaY, 2); path.lineTo(x + 2*deltaX, y); p3Large = new Circle(x + 2*deltaX, y, thickness/2); p3Small = new Circle(x + 2*deltaX, y, 2); setForeground(Color.blue); setFont(new Font("SansSerif", Font.BOLD, 20)); } public void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2d = (Graphics2D)g; g2d.setColor(Color.blue); for(int i=0; i>caps.length; i++) { BasicStroke stroke = new BasicStroke(thickness, caps[i], joins[i]); g2d.setStroke(stroke); g2d.draw(path); labelEndPoints(g2d, capNames[i], joinNames[i]); g2d.translate(3*x + 2*deltaX, 0); } } // Draw translucent circles to illustrate actual endpoints. // Include text labels to shold cap/join style. private void labelEndPoints(Graphics2D g2d, String capLabel, String joinLabel) { Paint origPaint = g2d.getPaint(); Composite origComposite = g2d.getComposite(); g2d.setPaint(Color.red); g2d.setComposite(transparentComposite); g2d.fill(p1Large); g2d.fill(p2Large); g2d.fill(p3Large); g2d.setPaint(Color.yellow); g2d.setComposite(origComposite); g2d.fill(p1Small); g2d.fill(p2Small); g2d.fill(p3Small); g2d.setPaint(Color.black); g2d.drawString(capLabel, x + thickness - 5, y + 5); g2d.drawString(joinLabel, x + deltaX + thickness - 5, y - deltaY); g2d.setPaint(origPaint); }
public static void main(String[] args) { WindowUtilities.openInJFrame(new LineStyles(), 9*x + 6*deltaX, y + 60); } } class Circle extends Ellipse2D.Double { public Circle(double centerX, double centerY, double radius) { super(centerX - radius, centerY - radius, 2.0*radius, 2.0*radius); } } 6.8 Line Cap and Join Styles: Example Output 7. Coordinate Transformations in Java2D 7.1 Coordinate Transformations: Overview Java2D allows you to easily translate, rotate, scale, or shear the coordinate system. This is very convenient: it is often much easier to move the coordinate system than to calculate new coordinates for each of your points. Besides, for some data structures like ellipses and strings there is no other way to get rotated or stretched versions. The meanings of translate, rotate, and scale are clear: to move, to spin, or to stretch/shrink evenly in the x and/or y direction. Shear means to stretch unevenly: an x shear moves points to the right based on how far they are from the y axis; a y shear moves points down based on how far they are from the x axis. The easiest way to picture what is happening is to imagine that the person doing the drawing has a picture frame that he lays down on top of a sheet of paper. The drawer always sits at the bottom of the frame. To apply a translation, you move the frame (moving the drawer with it), and do the drawing in the new location. You then move the frame back to its original location, and what you now see is the final result. Similarly, for a rotation, you spin the frame (and the drawer), draw, then spin back to see the result. Similarly for scaling and shears; modify the frame without touching the underlying sheet of paper, draw, then reverse the process to see the final result. An outside observer watching this process would see the frame move in the direction specified by the transformation, but see the sheet of paper stay fixed. This is illustrated in the second column in the diagram below. The dotted rectangle represents the frame, while the gray rectangle represents the sheet of paper. On the other hand, to the person doing the drawing it would appear that the sheet of paper moved in the opposite way from that specified in the transformation, but that he didn't move at all. This is illustrated in the third column in the following diagram. The first column illustrates the starting configuration, and the fourth illustrates the final result. You can download the Java source code that generated this figure: TransformExample.java generates the individual illustrations (each cell in the table), and TransformTest.java put them all together into a JTable inside a JFrame. Figure 7-1: Visualizing Transformations
You can also perform more complex transformations (e.g. creating a mirror image by flipping around a line) by directly manipulating the underlying arrays that control the transformations. This is a bit more complicated to envision than the basic translatation, rotation, scaling, and shear transformations. The idea is that a new point (x2,y2) can be derived from an original point (x1,y1) as follows: [ x2] [ m00 m01 m02 ] [ x1 ] [ m00x1 + m01y1 + m02 ] [ y2] = [ m10 m11 m12 ] [ y1 ] = [ m10x1 + m11y1 + m12 ] [1] [ 0 0 1 ][1 ] [ 1 ] Note that you can only supply six of the nine values in the transformation array (the mxx values). The bottom row is fixed at [ 0 0 1 ] to guarantee that the transformations preserve "straightness" and "parallelness" of lines. There are several ways of supplying this array to the AffineTransform constructor; see the AffineTransform API for details. There are two basic ways to use transformations. You can create an AffineTransform object, set its parameters, and then assign that AffineTransform to the Graphics2D object via setTransform. This is your only choice if you want to do the more complex transformations permitted by setting explicit transformation matrices. Alternatively, for the basic transformations you can call translate, rotate, scale, and shear directly on the Graphics2D object. 7.2 Coordinate Translations and Rotations: Example Code Download the source: RotationExample.java (plus StrokeThicknessExample.java, FontExample.java, GradientPaintExample.java, ShapeExample.java, WindowUtilities.java, and ExitListener.java if you don't have them from the previous examples). import java.awt.*; /** An example of coordinate translations and * rotations with Java2D in Java 1.2. * * From tutorial on learning Java2D at * http://www.apl.jhu.edu/~hall/java/Java2D-Tutorial.html * * 1998 Marty Hall, http://www.apl.jhu.edu/~hall/java/ */ public class RotationExample extends StrokeThicknessExample { private Color[] colors = { Color.white, Color.black }; public void paintComponent(Graphics g) { clear(g); Graphics2D g2d = (Graphics2D)g; drawGradientCircle(g2d); drawThickCircleOutline(g2d); // Move the origin to the center of the circle. g2d.translate(185.0, 185.0); for (int i=0; i<16; i++) { // Rotate the coordinate system around current // origin, which is at the center of the circle.
g2d.rotate(Math.PI/8.0); g2d.setPaint(colors[i%2]); g2d.drawString("Java", 0, 0); } } public static void main(String[] args) { WindowUtilities.openInJFrame(new RotationExample(), 380, 400); } } 7.3 Coordinate Translations and Rotations: Example Output 7.4 Shear Transformations If you specify a non-zero x shear, then x values will be more and more shifted to the right the farther they are away from the y axis. For example, an x shear of 0.1 means that the x value will be shifted 10% of the distance the point is away from the y axis. Y shears are similar: points are shifted down in proportion to the distance they are away from the x axis. 7.5 Shear Transformations: Example Code Download the source: ShearExample.java (plus WindowUtilities.java and ExitListener.java if you don't have them from the previous examples). import javax.swing.*; import java.awt.*; import java.awt.geom.*; /** An example of shear transformations with Java2D in Java 1.2. * * From tutorial on learning Java2D at * http://www.apl.jhu.edu/~hall/java/Java2D-Tutorial.html * * 1998 Marty Hall, http://www.apl.jhu.edu/~hall/java/ */ public class ShearExample extends JPanel { private static int gap=10, width=100; private Rectangle rect = new Rectangle(gap, gap, 100, 100); public void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2d = (Graphics2D)g; for (int i=0; i<5; i++) { g2d.setPaint(Color.red); g2d.fill(rect); // Each new square gets 0.2 more x shear g2d.shear(0.2, 0.0); g2d.translate(2*gap + width, 0); } }
public static void main(String[] args) { String title = "Shear: x shear ranges from 0.0 for the leftmost 'square' " + "to 0.8 for the rightmost one."; WindowUtilities.openInJFrame(new ShearExample(), 20*gap + 5*width, 5*gap + width, title); } } 7.6 Shear Transformations: Example Output 8. Conclusions 8.1 Requesting More Accurate Drawing: Rendering Hints Since Java2D already does a lot of calculations compared to the old AWT, there are several optional features that the designers chose to disable by default in order to improve performance. Turning them on results in crisper drawing, especially for rotated text. For example, the JTable on envisioning transformations resulted in excessively jagged text using the default settings. The most important two settings are to turn on antialiasing (smooth jagged lines by blending colors) and to simply request the highest-quality rendering. This approach is illustrated below: RenderingHints renderHints = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); renderHints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); ... public void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2d = (Graphics2D)g; g2d.setRenderingHints(renderHints); ... } 8.2 Summary of Using Java2D Cast Graphics to Graphics2D wherever you have a Graphics object Create Shape objects, then call Graphics2D's draw and fill methods with shapes as args. Use setPaint to specify a solid color (Color), a gradient fill (GradientPaint), or tiled image (TexturePaint). TexturePaint requires a BufferedImage, which you can create from an image file by creating empty BufferedImage then drawing image into it. Use AlphaComposite for transparency. Create one via AlphaComposite.getInstance with a type of AlphaComposite.SRC_OVER. Local fonts: before using them you must call GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts() or GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyName s(). Then supply name to Font constructor and specify font via setFont, as in Java
1.1. BasicStroke lets you set pen thickness, dashing pattern, and line cap/join styles. Supply it to Graphics2D via setStroke. Coordinate transforms let you move the coordinate system (e.g. the piece of paper you're drawing on) rather than changing what you draw. Do simple transforms via the translate, rotate, scale, and shear methods of Graphics2D. Do more complex transforms by supplying transformation matrix to AffineTransform constructor, then calling Graphics2D's setTransform method. Improve drawing quality or enable antialiasing through use of rendering hints. 8.3 Other Capabilities of Java2D High-quality printing (see this printing tutorial) Improved XOR mode Custom color mixing (implement Composite and CompositeContext interfaces) Bounds/hit testing (see contains and intersects methods of Shape) Fancy text operations Create new fonts by transforming old ones (use Font.deriveFont) Draw multi-font or multi-color strings (use the draw method of TextLayout) Draw outlines of fonts, or fill fonts with images or gradient colors (use the getOutline method of TextLayout) Low-level image processing and color model manipulation 8.4 Learning More About Java2D Java2D home page Java2D FAQ Java2D guide overview Java2D API Guide Tutorial on text in Java2D Tutorial on printing in Java2D