Previous | Next | Trail Map | 2D Graphics | Displaying Graphics with Graphics2D

Stroking and Filling Graphics Primitives

By changing the stroke and paint attributes in the Graphics2D context before rendering, you can easily apply fancy line styles and fill patterns to graphics primitives. For example, you can draw a dashed line by creating an appropriate Stroke object and calling setStroke to add it to the Graphics2D context before you render the line. Similarly, you can apply a gradient fill to a Shape by creating a GradientPaint object and adding it to the Graphics2D context by calling setPaint before you render the Shape.

The ShapesDemo2D applet introduced in the lesson Overview of the Java 2D API demonstrates how you can render basic geometries by using the Graphics2D draw and fill methods. Each of the shapes is constructed from one of the geometries and is then rendered through Graphics2D. The rectHeight and rectWidth variables in this example define the dimensions of the space where each shape is drawn, in pixels. The x and y variables change for each shape so that they are drawn in a grid formation.

// draw Line2D.Double
g2.draw(new Line2D.Double(x, y+rectHeight-1,
                          x + rectWidth, y));
// draw Rectangle2D.Double
g2.setStroke(stroke);
g2.draw(new Rectangle2D.Double(x, y,
                               rectWidth,
                               rectHeight));
// draw RoundRectangle2D.Double
g2.setStroke(dashed);
g2.draw(new RoundRectangle2D.Double(x, y,
                                   rectWidth,
                                   rectHeight,
                                   10, 10));
// draw Arc2D.Double
g2.setStroke(wideStroke);
g2.draw(new Arc2D.Double(x, y,
                         rectWidth,
                         rectHeight,
                         90, 135,
                         Arc2D.OPEN));
// draw Ellipse2D.Double
g2.setStroke(stroke);
g2.draw(new Ellipse2D.Double(x, y,
                             rectWidth,
                             rectHeight));
// draw GeneralPath (polygon)
int x1Points[] = {x, x+rectWidth,
                  x, x+rectWidth};
int y1Points[] = {y, y+rectHeight,
                  y+rectHeight, y};
GeneralPath polygon = new
	 GeneralPath(GeneralPath.WIND_EVEN_ODD,
		     x1Points.length);
polygon.moveTo(x1Points[0], y1Points[0]);

for (int index = 1;
     index < x1Points.length;
     index++) {
        polygon.lineTo(x1Points[index],
                       y1Points[index]);
};

polygon.closePath();
g2.draw(polygon);
// draw GeneralPath (polyline)
int x2Points[] = {x, x+rectWidth, x,
                  x+rectWidth};
int y2Points[] = {y, y+rectHeight,
                  y+rectHeight, y};
GeneralPath polyline = new
         GeneralPath(GeneralPath.WIND_EVEN_ODD,
           	     x2Points.length);

polyline.moveTo (x2Points[0], y2Points[0]);

for (int index = 1;
     index < x2Points.length;
     index++) {
 	 polyline.lineTo(x2Points[index],
         y2Points[index]);
};

g2.draw(polyline);
// fill Rectangle2D.Double (red)
g2.setPaint(red);
g2.fill(new Rectangle2D.Double(x, y,
        rectWidth, rectHeight));
// fill RoundRectangle2D.Double
g2.setPaint(redtowhite);
g2.fill(new RoundRectangle2D.Double(x, y,
                                   rectWidth,
                                   rectHeight,
                                   10, 10));
// fill Arc2D 
g2.setPaint(red);
g2.fill(new Arc2D.Double(x, y, rectWidth,
                         rectHeight, 90,
                         135, Arc2D.OPEN));
// fill Ellipse2D.Double g2.setPaint(redtowhite); g2.fill (new Ellipse2D.Double(x, y, rectWidth, rectHeight));
// fill and stroke GeneralPath
int x3Points[] = {x, x+rectWidth, x,
                  x+rectWidth};
int y3Points[] = {y, y+rectHeight,
                  y+rectHeight, y};

GeneralPath filledPolygon = new
         GeneralPath(GeneralPath.WIND_EVEN_ODD,
		     x3Points.length);
filledPolygon.moveTo(x3Points[0],
                     y3Points[0]);

for (int index = 1;
     index < x3Points.length;
     index++) 	{
	filledPolygon.lineTo(x3Points[index],
                             y3Points[index]);

};
filledPolygon.closePath();
g2.setPaint(red);
g2.fill(filledPolygon);

Note that this example uses the double-precision implementations of the geometries classes. Where applicable, float and double-precision implementations of each of the geometries are provided as inner classes.

The complete code for is in ShapesDemo2D.java and an HTML file that includes the applet is ShapesDemo2D.html.

Defining Fancy Line Styles and Fill Patterns

You probably noticed that in the previous example some of the shapes have thicker outlines or are filled with a two-color gradient. Using the Java 2D Stroke and Paint classes, you can easily define fancy line styles and fill patterns.

Line Styles

Line styles are defined by the stroke attribute in the Graphics2D rendering context. To set the stroke attribute, you create a BasicStroke object and pass it into the Graphics2D setStroke method.

A BasicStroke object holds information about the line width, join style, end-cap style, and dash style. This information is used when a Shape is rendered with the draw method.

The line width is the thickness of the line measured perpendicular to its trajectory. The line width is specified as a float value in user coordinate units, which are roughly equivalent to 1/72 inch when the default transform is used.

The join style is the decoration that is applied where two line segments meet. BasicStroke supports three join styles:

JOIN_BEVEL

JOIN_MITER

JOIN_ROUND

The end-cap style is the decoration that is applied where a line segment ends. BasicStroke supports three end-cap styles:

CAP_BUTT

CAP_ROUND

CAP_SQUARE

The dash style defines the pattern of opaque and transparent sections applied along the length of the line. The dash style is defined by a dash array and a dash phase. The dash array defines the dash pattern. Alternating elements in the array represent the dash length and the length of the space between dashes in user coordinate units. Element 0 represents the first dash, element 1 the first space, and so on. The dash phase is an offset into the dash pattern, also specified in user coordinate units. The dash phase indicates what part of the dash pattern is applied to the beginning of the line.

Fill Patterns

Fill patterns are defined by the paint attribute in the Graphics2D rendering context. To set the paint attribute, you create an instance of an object that implements the Paint interface and pass it into the Graphics2D setPaint method.

Three classes implement the Paint interface: Color, GradientPaint, and TexturePaint. GradientPaint and TexturePaint are new in JDK 1.2.

To create a GradientPaint, you specify a beginning position and color and an ending position and color. The gradient changes proportionally from one color to the other along the line connecting the two positions.

The pattern for a TexturePaint is defined by a BufferedImage. To create a TexturePaint, you specify the image that contains the pattern and a rectangle that is used to replicate and anchor the pattern.

Example: StrokeAndFill

The StrokeAndFill program allows the user to select a graphics primitive, a line style, and a paint style and to either stroke the object's outline, fill it with the selected paint, or stroke the object in black and then fill it with the selected paint. You can see the applet in action on the CD-ROM that accompanies this book and online here:
http://java.sun.com/docs/books/tutorial/2d/display/example-1dot2/StrokeAndFill.html

How rendering is performed depends on which rendering option is chosen.

  • When the user chooses stroke, Graphics2D.draw is called to perform the rendering. If text is chosen as the primitive, the glyph outlines are retrieved and then rendered with the draw method.
  • When the user chooses fill, Graphics2D.fill or Graphics2D.drawString is called to perform the rendering.
  • When the user chooses stroke and fill, fill or drawString is called to fill the Shape, and then draw is called to stroke its outline.

Note: To both fill and stroke a graphics primitive, you need to make two separate method calls: fill or drawString to fill its interior and draw to stroke its outline.

The three line styles used in this example--thin, thick, and dashed--are instances of BasicStroke.

// Sets the Stroke.
...
case 0 : g2.setStroke(new BasicStroke(3.0f)); break;
case 1 : g2.setStroke(new BasicStroke(8.0f)); break;
case 2 : float dash[] = {10.0f};
         	g2.setStroke(new BasicStroke(3.0f, BasicStroke.CAP_BUTT,
                BasicStroke.JOIN_MITER, 10.0f, dash, 0.0f)); break;

The dash style in this example has 10 unit dashes alternating with 10 unit spaces. The beginning of the dash pattern is applied to the beginning of the line--the dash phase is set to 0.0.

Three paint styles are used in this example--solid, gradient, and polka. The solid-color paint style is an instance of Color, the gradient an instance of GradientPaint, and the pattern an instance of TexturePaint.

// Sets the Paint.
...
case 0 : g2.setPaint(Color.blue); break;
case 1 : g2.setPaint(new GradientPaint(0, 0, Color.lightGray,
                          w-250, h, Color.blue, false));
         break;
case 2 : BufferedImage bi = new BufferedImage(5, 5,
                                BufferedImage.TYPE_INT_RGB);
         Graphics2D big = bi.createGraphics();
         big.setColor(Color.blue);
         big.fillRect(0, 0, 5, 5);
         big.setColor(Color.lightGray);
         big.fillOval(0, 0, 5, 5);
         Rectangle r = new Rectangle(0,0,5,5);
         g2.setPaint(new TexturePaint(bi, r));
         break;

You can find the full code for In StrokeAndFill.java and an HTML file that includes the applet in StrokeAndFill.html.


Previous | Next | Trail Map | 2D Graphics | Displaying Graphics with Graphics2D