Areas
An Area object stores and manipulates a resolution-independent description of an enclosed area of 2-dimensional space. Area objects can be transformed and can perform various Constructive Area Geometry (CAG) operations when combined with other Area objects. The CAG operations include area additionsubtractionintersection, and exclusive or. See the linked method documentation for examples of the various operations.
The Area class implements the Shape interface and provides full support for all of its hit-testing and path iteration facilities, but an Area is more specific than a generalized path in a number of ways..

With the Area class, you can perform boolean operations, such as union, intersection, and subtraction, on any two Shape objects. This technique, often referred to as constructive area geometry, enables you to quickly create complex Shape objects without having to describe each line segment or curve.

Area - Core Java™ 2 Volume II - Advanced Features, Seventh Edition
In the preceding section, you saw how you can specify complex shapes by constructing general paths that are composed of lines and curves. By using a sufficient number of lines and curves, you can draw essentially any shape. For example, the shapes of characters in the fonts that you see on the screen and on your printouts are all made up of lines and cubic curves.
Occasionally, it is easier to describe a shape by composing it from areas, such as rectangles, polygons, or ellipses. The Java 2D API supports four constructive area geometry operations that combine two areas to a new area:
  • add The combined area contains all points that are in the first or the second area.
  • subtract The combined area contains all points that are in the first but not the second area.
  • intersect The combined area contains all points that are in the first and the second area.
  • exclusiveOr The combined area contains all points that are in either the first or the second area, but not in both.
Figure 7-10 shows these operations.


Figure 7-10. Constructive area geometry operations


To construct a complex area, you start out with a default area object.
Area a = new Area();

Then, you combine the area with any shape:
a.add(new Rectangle2D.Double(. . .));
a.subtract(path);
. . .

The Area class implements the Shape interface. You can stroke the boundary of the area with the draw method or paint the interior with the fill method of the Graphics2D class.
The program in Example 7-2 shows the constructive area geometry operations. Select one of the four operations, and see the result of combining an ellipse and a rectangle with the operation that you selected (see Figure 7-11).

Example 7-2. AreaTest.java
1. import java.awt.*;
  2. import java.awt.event.*;
  3. import java.awt.geom.*;
  4. import java.util.*;
  5. import javax.swing.*;
  6.
  7. /**
  8.    This program demonstrates constructive area geometry
  9.    operations.
 10. */
 11. public class AreaTest
 12. {
 13.    public static void main(String[] args)
 14.    {
 15.       JFrame frame = new AreaTestFrame();
 16.       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
 17.       frame.setVisible(true);
 18.    }
 19. }
 20.
 21. /**
 22.    This frame contains a set of radio buttons to define
 23.    area operations and a panel to show their result.
 24. */
 25. class AreaTestFrame extends JFrame
 26. {
 27.    public AreaTestFrame()
 28.    {
 29.       setTitle("AreaTest");
 30.       setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
 31.
 32.       area1 = new Area(new Ellipse2D.Double(100, 100, 150, 100));
 33.       area2 = new Area(new Rectangle2D.Double(150, 150, 150, 100));
 34.
 35.       panel = new
 36.          JPanel()
 37.          {
 38.             public void paintComponent(Graphics g)
 39.             {
 40.                super.paintComponent(g);
 41.                Graphics2D g2 = (Graphics2D)g;
 42.                g2.draw(area1);
 43.                g2.draw(area2);
 44.                if (area != null) g2.fill(area);
 45.             }
 46.          };
 47.
 48.       add(panel, BorderLayout.CENTER);
 49.
 50.       JPanel buttonPanel = new JPanel();
 51.       ButtonGroup group = new ButtonGroup();
 52.
 53.       JRadioButton addButton = new JRadioButton("Add", false);
 54.       buttonPanel.add(addButton);
 55.       group.add(addButton);
 56.       addButton.addActionListener(new
 57.          ActionListener()
 58.          {
 59.             public void actionPerformed(ActionEvent event)
 60.             {
 61.                area = new Area();
 62.                area.add(area1);
 63.                area.add(area2);
 64.                panel.repaint();
 65.             }
 66.          });
 67.
 68.       JRadioButton subtractButton = new JRadioButton("Subtract", false);
 69.       buttonPanel.add(subtractButton);
 70.       group.add(subtractButton);
 71.       subtractButton.addActionListener(new
 72.          ActionListener()
 73.          {
 74.             public void actionPerformed(ActionEvent event)
 75.             {
 76.                area = new Area();
 77.                area.add(area1);
 78.                area.subtract(area2);
 79.                panel.repaint();
 80.             }
 81.          });
 82.
 83.       JRadioButton intersectButton = new JRadioButton("Intersect", false);
 84.       buttonPanel.add(intersectButton);
 85.       group.add(intersectButton);
 86.       intersectButton.addActionListener(new
 87.          ActionListener()
 88.          {
 89.             public void actionPerformed(ActionEvent event)
 90.             {
 91.                area = new Area();
 92.                area.add(area1);
 93.                area.intersect(area2);
 94.                panel.repaint();
 95.             }
 96.          });
 97.
 98.       JRadioButton exclusiveOrButton = new JRadioButton("Exclusive Or", false);
 99.       buttonPanel.add(exclusiveOrButton);
100.       group.add(exclusiveOrButton);
101.       exclusiveOrButton.addActionListener(new
102.          ActionListener()
103.          {
104.             public void actionPerformed(ActionEvent event)
105.             {
106.                area = new Area();
107.                area.add(area1);
108.                area.exclusiveOr(area2);
109.                panel.repaint();
110.             }
111.          });
112.
113.       add(buttonPanel, BorderLayout.NORTH);
114.    }
115.
116.    private JPanel panel;
117.    private Area area;
118.    private Area area1;
119.    private Area area2;
120.
121.    private static final int DEFAULT_WIDTH = 400;
122.    private static final int DEFAULT_HEIGHT = 400;
123. }
Figure 7-11. The AreaTest program



java.awt.geom.Area
  • void add(Area other)
  • void subtract(Area other)
  • void intersect(Area other)
  • void exclusiveOr(Area other)
    carry out the constructive area geometry operation with this area and the other area and set this area to the result.


Strokes

The draw operation of the Graphics2D class draws the boundary of a shape by using the currently selected stroke. By default, the stroke is a solid line that is one pixel wide. You can select a different stroke by calling the setStroke method. You supply an object of a class that implements the Stroke interface. The Java 2D API defines only one such class, called BasicStroke. In this section, we look at the capabilities of the BasicStroke class.
You can construct strokes of arbitrary thickness. For example, here is how you draw lines that are 10 pixels wide.
g2.setStroke(new BasicStroke(10.0F));
g2.draw(new Line2D.Double(. . .));

When a stroke is more than a pixel thick, then the end of the stroke can have different styles. Figure 7-12 shows these so-called end cap styles. You have three choices:
  • A butt cap simply ends the stroke at its end point.
  • A round cap adds a half-circle to the end of the stroke.
  • A square cap adds a half-square to the end of the stroke.


Figure 7-12. End cap styles


When two thick strokes meet, there are three choices for the join style (see Figure 7-13).
  • A bevel join joins the strokes with a straight line that is perpendicular to the bisector of the angle between the two strokes.
  • A round join extends each stroke to have a round cap.
  • A miter join extends both strokes by adding a "spike."


Figure 7-13. Join styles


The miter join is not suitable for lines that meet at small angles. If two lines join with an angle that is less than the miter limit, then a bevel join is used instead. That usage prevents extremely long spikes. By default, the miter limit is 10 degrees.
You specify these choices in the BasicStroke constructor, for example:
g2.setStroke(new BasicStroke(10.0F, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
g2.setStroke(new BasicStroke(10.0F, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER,
   15.0F /* miter limit */));

Finally, you can specify dashed lines by setting a dash pattern. In the program in Example 7-3, you can select a dash pattern that spells out SOS in Morse code. The dash pattern is a float[] array of numbers that contains the lengths of the "on" and "off" strokes (see Figure 7-14).


Figure 7-14. A dash pattern


You specify the dash pattern and a dash phase when constructing the BasicStroke. The dash phase indicates where in the dash pattern each line should start. Normally, you set this value to 0.
float[] dashPattern = { 10, 10, 10, 10, 10, 10, 30, 10, 30, ... };
g2.setStroke(new BasicStroke(10.0F, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER,
   10.0F /* miter limit */, dashPattern, 0 /* dash phase */));


NOTE
End cap styles are applied to the ends of each dash in a dash pattern.
The program in Example 7-3 lets you specify end cap styles, join styles, and dashed lines (see Figure 7-15). You can move the ends of the line segments to test the miter limit: Select the miter join, then move the line segment to form a very acute angle. You will see the miter join turn into a bevel join.


Figure 7-15. The StrokeTest program


The program is similar to the program in Example 7-1. The mouse listener remembers if you click on the end point of a line segment, and the mouse motion listener monitors the dragging of the end point. A set of radio buttons signal the user choices for the end cap style, join style, and solid or dashed line. The paintComponent method of the StrokePanel class constructs a GeneralPath consisting of the two line segments that join the three points that the user can move with the mouse. It then constructs a BasicStroke, according to the selections that the user made, and finally draws the path.

Example 7-3. StrokeTest.java
1. import java.awt.*;
  2. import java.awt.event.*;
  3. import java.awt.geom.*;
  4. import java.util.*;
  5. import javax.swing.*;
  6.
  7. /**
  8.    This program demonstrates different stroke types.
  9. */
 10. public class StrokeTest
 11. {
 12.    public static void main(String[] args)
 13.    {
 14.       JFrame frame = new StrokeTestFrame();
 15.       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
 16.       frame.setVisible(true);
 17.    }
 18. }
 19.
 20. /**
 21.    This frame lets the user choose the cap, join, and
 22.    line style, and shows the resulting stroke.
 23. */
 24. class StrokeTestFrame extends JFrame
 25. {
 26.    public StrokeTestFrame()
 27.    {
 28.       setTitle("StrokeTest");
 29.       setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
 30.
 31.       canvas = new StrokePanel();
 32.       add(canvas, BorderLayout.CENTER);
 33.
 34.       buttonPanel = new JPanel();
 35.       buttonPanel.setLayout(new GridLayout(3, 3));
 36.       add(buttonPanel, BorderLayout.NORTH);
 37.
 38.       ButtonGroup group1 = new ButtonGroup();
 39.       makeCapButton("Butt Cap", BasicStroke.CAP_BUTT, group1);
 40.       makeCapButton("Round Cap", BasicStroke.CAP_ROUND, group1);
 41.       makeCapButton("Square Cap", BasicStroke.CAP_SQUARE, group1);
 42.
 43.       ButtonGroup group2 = new ButtonGroup();
 44.       makeJoinButton("Miter Join", BasicStroke.JOIN_MITER, group2);
 45.       makeJoinButton("Bevel Join", BasicStroke.JOIN_BEVEL, group2);
 46.       makeJoinButton("Round Join", BasicStroke.JOIN_ROUND, group2);
 47.
 48.       ButtonGroup group3 = new ButtonGroup();
 49.       makeDashButton("Solid Line", false, group3);
 50.       makeDashButton("Dashed Line", true, group3);
 51.    }
 52.
 53.    /**
 54.       Makes a radio button to change the cap style.
 55.       @param label the button label
 56.       @param style the cap style
 57.       @param group the radio button group
 58.    */
 59.    private void makeCapButton(String label, final int style, ButtonGroup group)
 60.    {
 61.       // select first button in group
 62.       boolean selected = group.getButtonCount() == 0;
 63.       JRadioButton button = new JRadioButton(label, selected);
 64.       buttonPanel.add(button);
 65.       group.add(button);
 66.       button.addActionListener(new
 67.          ActionListener()
 68.          {
 69.             public void actionPerformed(ActionEvent event)
 70.             {
 71.                canvas.setCap(style);
 72.             }
 73.          });
 74.    }
 75.
 76.    /**
 77.       Makes a radio button to change the join style.
 78.       @param label the button label
 79.       @param style the join style
 80.       @param group the radio button group
 81.    */
 82.    private void makeJoinButton(String label, final int style,
 83.       ButtonGroup group)
 84.    {
 85.       // select first button in group
 86.       boolean selected = group.getButtonCount() == 0;
 87.       JRadioButton button = new JRadioButton(label, selected);
 88.       buttonPanel.add(button);
 89.       group.add(button);
 90.       button.addActionListener(new
 91.          ActionListener()
 92.          {
 93.             public void actionPerformed(ActionEvent event)
 94.             {
 95.                canvas.setJoin(style);
 96.             }
 97.          });
 98.    }
 99.
100.    /**
101.       Makes a radio button to set solid or dashed lines
102.       @param label the button label
103.       @param style false for solid, true for dashed lines
104.       @param group the radio button group
105.    */
106.    private void makeDashButton(String label, final boolean style,
107.       ButtonGroup group)
108.    {
109.       // select first button in group
110.       boolean selected = group.getButtonCount() == 0;
111.       JRadioButton button = new JRadioButton(label, selected);
112.       buttonPanel.add(button);
113.       group.add(button);
114.       button.addActionListener(new
115.          ActionListener()
116.          {
117.             public void actionPerformed(ActionEvent event)
118.             {
119.                canvas.setDash(style);
120.             }
121.          });
122.    }
123.
124.    private StrokePanel canvas;
125.    private JPanel buttonPanel;
126.
127.    private static final int DEFAULT_WIDTH = 400;
128.    private static final int DEFAULT_HEIGHT = 400;
129. }
130.
131. /**
132.    This panel draws two joined lines, using different
133.    stroke objects, and allows the user to drag the three
134.    points defining the lines.
135. */
136. class StrokePanel extends JPanel
137. {
138.    public StrokePanel()
139.    {
140.       addMouseListener(new
141.          MouseAdapter()
142.          {
143.             public void mousePressed(MouseEvent event)
144.             {
145.                Point p = event.getPoint();
146.                for (int i = 0; i < points.length; i++)
147.                {
148.                   double x = points[i].getX() - SIZE / 2;
149.                   double y = points[i].getY() - SIZE / 2;
150.                   Rectangle2D r = new Rectangle2D.Double(x, y, SIZE, SIZE);
151.                   if (r.contains(p))
152.                   {
153.                      current = i;
154.                      return;
155.                   }
156.                }
157.             }
158.
159.             public void mouseReleased(MouseEvent event)
160.             {
161.                current = -1;
162.             }
163.          });
164.
165.       addMouseMotionListener(new
166.          MouseMotionAdapter()
167.          {
168.             public void mouseDragged(MouseEvent event)
169.             {
170.                if (current == -1) return;
171.                points[current] = event.getPoint();
172.                repaint();
173.             }
174.          });
175.
176.       points = new Point2D[3];
177.       points[0] = new Point2D.Double(200, 100);
178.       points[1] = new Point2D.Double(100, 200);
179.       points[2] = new Point2D.Double(200, 200);
180.       current = -1;
181.       width = 8.0F;
182.    }
183.
184.    public void paintComponent(Graphics g)
185.    {
186.       super.paintComponent(g);
187.       Graphics2D g2 = (Graphics2D) g;
188.       GeneralPath path = new GeneralPath();
189.       path.moveTo((float) points[0].getX(), (float) points[0].getY());
190.       for (int i = 1; i < points.length; i++)
191.          path.lineTo((float) points[i].getX(), (float) points[i].getY());
192.       BasicStroke stroke;
193.       if (dash)
194.       {
195.          float miterLimit = 10.0F;
196.          float[] dashPattern = { 10F, 10F, 10F, 10F, 10F, 10F,
197.             30F, 10F, 30F, 10F, 30F, 10F, 10F, 10F, 10F, 10F, 10F, 30F };
198.          float dashPhase = 0;
199.          stroke = new BasicStroke(width, cap, join, miterLimit, dashPattern, dashPhase);
200.       }
201.       else
202.          stroke = new BasicStroke(width, cap, join);
203.       g2.setStroke(stroke);
204.       g2.draw(path);
205.    }
206.
207.    /**
208.       Sets the join style.
209.       @param j the join style
210.    */
211.    public void setJoin(int j)
212.    {
213.       join = j;
214.       repaint();
215.    }
216.
217.    /**
218.       Sets the cap style.
219.       @param c the cap style
220.    */
221.    public void setCap(int c)
222.    {
223.       cap = c;
224.       repaint();
225.    }
226.
227.    /**
228.       Sets solid or dashed lines
229.       @param d false for solid, true for dashed lines
230.    */
231.    public void setDash(boolean d)
232.    {
233.       dash = d;
234.       repaint();
235.    }
236.
237.    private Point2D[] points;
238.    private static int SIZE = 10;
239.    private int current;
240.    private float width;
241.    private int cap;
242.    private int join;
243.    private boolean dash;
244. }



java.awt.Graphics2D 1.2
  • void setStroke(Stroke s)
    sets the stroke of this graphics context to the given object that implements the Stroke interface.


java.awt.BasicStroke 1.2
  • BasicStroke(float width)
  • BasicStroke(float width, int cap, int join)
  • BasicStroke(float width, int cap, int join, float miterlimit)
  • BasicStroke(float width, int cap, int join, float miterlimit, float[] dash, float dashPhase)
    construct a stroke object with the given attributes.

    Parameters:
    width
    The width of the pen
    cap
    The end cap style, one of CAP_BUTT, CAP_ROUND, and CAP_SQUARE
    join
    The join style, one of JOIN_BEVEL, JOIN_MITER, and JOIN_ROUND
    miterlimit
    The angle, in degrees, below which a miter join is rendered as a bevel join
    dash
    An array of the lengths of the alternating filled and blank portions of a dashed stroke
    dashPhase
    The "phase" of the dash pattern; a segment of this length, preceding the starting point of the stroke, is assumed to have the dash pattern already applied

Paint

When you fill a shape, its inside is covered with paint. You use the setPaint method to set the paint style to an object with a class that implements the Paint interface. The Java 2D API provides three such classes:
  • The Color class implements the Paint interface. To fill shapes with a solid color, simply call setPaint with a Color object, such as
    g2.setPaint(Color.red);
  • The GradientPaint class varies colors by interpolating between two given color values (see Figure 7-16).
    Figure 7-16. Gradient paint

  • The TexturePaint class fills an area with repetitions of an image (see Figure 7-17).
    Figure 7-17. Texture paint

You construct a GradientPaint object by specifying two points and the colors that you want at these two points.
g2.setPaint(new GradientPaint(p1, Color.red, p2, Color.blue));

Colors are interpolated along the line joining the two points. Colors are constant along lines that are perpendicular to that joining line. Points beyond an end point of the line are given the color at the end point.
Alternatively, if you call the GradientPaint constructor with true for the cyclic parameter,
g2.setPaint(new GradientPaint(p1, Color.red, p2, Color.blue, true));

then the color variation cycles and keeps varying beyond the end points.
To construct a TexturePaint object, you specify a BufferedImage and an anchor rectangle. The anchor rectangle is extended indefinitely in x- and y-directions to tile the entire coordinate plane. The image is scaled to fit into the anchor and then replicated into each tile.
We introduce the BufferedImage class later in this chapter when we discuss images in detail. You create a BufferedImage object by giving the image size and the image type. The most common image type is TYPE_INT_ARGB, in which each pixel is specified by an integer that describes the alpha, or transparency, red, green, and blue values. For example,
BufferedImage bufferedImage = new BufferedImage(width,height, BufferedImage.TYPE_INT_ARGB);

You then obtain a graphics context to draw into the buffered image.
Graphics2D g2 = bufferedImage.createGraphics();

Any drawing operations on g2 now fill the buffered image with pixels. When you are done, you can create your TexturePaint object:
g2.setPaint(new TexturePaint(bufferedImage, anchorRectangle));

The program in Example 7-4 lets the user choose between a solid color paint, a gradient paint, and a texture paint. Then, an ellipse is filled with the specified paint.
The texture paint uses an image that is read from a GIF file. As you will see later in this chapter, the ImageIO class makes it simple to read a graphics file into a buffered image by calling
bufferedImage = ImageIO.read(new File("blue-ball.gif"));


NOTE
The ImageIO class was added in JDK version 1.4. If you have an older version of the JDK, use the following code instead:
Image image = Toolkit.getDefaultToolkit().getImage("blue-ball.gif");
MediaTracker tracker = new MediaTracker(this);
tracker.addImage(image, 0);
try { tracker.waitForID(0); }
catch (InterruptedException e) {}
bufferedImage = new BufferedImage(image.getWidth(null), image.getHeight(null),
   BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = bufferedImage.createGraphics();
g2.drawImage(image, 0, 0, null);

To show the significance of the anchor rectangle, we specify the anchor to have twice the size of the image:
Rectangle2D anchor = new Rectangle2D.Double(0, 0,
   2 * bufferedImage.getWidth(),
   2 * bufferedImage.getHeight());
paint = new TexturePaint(bufferedImage, anchor);

As you can see when you select Texture Paint, the image is scaled to fit the anchor, and it is then replicated to fill the shape. Tiles that meet the boundary of the shape are clipped.

Example 7-4. PaintTest.java
1. import java.awt.*;
  2. import java.awt.event.*;
  3. import java.awt.geom.*;
  4. import java.awt.image.*;
  5. import java.io.*;
  6. import java.util.*;
  7. import javax.imageio.*;
  8. import javax.swing.*;
  9.
 10. /**
 11.    This program demonstrates the various paint modes.
 12. */
 13. public class PaintTest
 14. {
 15.    public static void main(String[] args)
 16.    {
 17.       JFrame frame = new PaintTestFrame();
 18.       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
 19.       frame.setVisible(true);
 20.    }
 21. }
 22.
 23. /**
 24.    This frame contains radio buttons to choose the paint mode
 25.    and a panel that draws a circle in the selected paint mode.
 26. */
 27. class PaintTestFrame extends JFrame
 28. {
 29.    public PaintTestFrame()
 30.    {
 31.       setTitle("PaintTest");
 32.       setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
 33.
 34.       canvas = new PaintPanel();
 35.       add(canvas, BorderLayout.CENTER);
 36.
 37.       JPanel buttonPanel = new JPanel();
 38.       ButtonGroup group = new ButtonGroup();
 39.
 40.       JRadioButton colorButton = new JRadioButton("Color", true);
 41.       buttonPanel.add(colorButton);
 42.       group.add(colorButton);
 43.       colorButton.addActionListener(new
 44.          ActionListener()
 45.          {
 46.             public void actionPerformed(ActionEvent event)
 47.             {
 48.                canvas.setColor();
 49.             }
 50.          });
 51.
 52.       JRadioButton gradientPaintButton = new JRadioButton("Gradient Paint", false);
 53.       buttonPanel.add(gradientPaintButton);
 54.       group.add(gradientPaintButton);
 55.       gradientPaintButton.addActionListener(new
 56.          ActionListener()
 57.          {
 58.             public void actionPerformed(ActionEvent event)
 59.             {
 60.                canvas.setGradientPaint();
 61.             }
 62.          });
 63.
 64.       JRadioButton texturePaintButton = new JRadioButton("Texture Paint", false);
 65.       buttonPanel.add(texturePaintButton);
 66.       group.add(texturePaintButton);
 67.       texturePaintButton.addActionListener(new
 68.          ActionListener()
 69.          {
 70.             public void actionPerformed(ActionEvent event)
 71.             {
 72.                canvas.setTexturePaint();
 73.             }
 74.          });
 75.
 76.       add(buttonPanel, BorderLayout.NORTH);
 77.    }
 78.
 79.    private PaintPanel canvas;
 80.    private static final int DEFAULT_WIDTH = 400;
 81.    private static final int DEFAULT_HEIGHT = 400;
 82. }
 83.
 84. /**
 85.    This panel paints a circle in various paint modes.
 86. */
 87. class PaintPanel extends JPanel
 88. {
 89.    public PaintPanel()
 90.    {
 91.       try
 92.       {
 93.          bufferedImage = ImageIO.read(new File("blue-ball.gif"));
 94.       }
 95.       catch (IOException e)
 96.       {
 97.          e.printStackTrace();
 98.       }
 99.       setColor();
100.    }
101.
102.    public void paintComponent(Graphics g)
103.    {
104.       super.paintComponent(g);
105.       Graphics2D g2 = (Graphics2D) g;
106.       g2.setPaint(paint);
107.       Ellipse2D circle = new Ellipse2D.Double(0, 0, getWidth(), getHeight());
108.       g2.fill(circle);
109.    }
110.
111.    /**
112.       Paints in a plain color.
113.    */
114.    public void setColor()
115.    {
116.       paint = Color.red; // Color implements Paint
117.       repaint();
118.    }
119.
120.    /**
121.       Sets the paint mode to gradient paint.
122.    */
123.    public void setGradientPaint()
124.    {
125.       paint = new GradientPaint(0, 0, Color.red,
126.          (float) getWidth(), (float) getHeight(), Color.blue);
127.       repaint();
128.    }
129.
130.    /**
131.       Sets the paint mode to texture paint.
132.    */
133.    public void setTexturePaint()
134.    {
135.       Rectangle2D anchor = new Rectangle2D.Double(0, 0,
136.          2 * bufferedImage.getWidth(),
137.          2 * bufferedImage.getHeight());
138.       paint = new TexturePaint(bufferedImage, anchor);
139.       repaint();
140.    }
141.
142.    private Paint paint;
143.    private BufferedImage bufferedImage;
144. }




java.awt.Graphics2D 1.2
  • void setPaint(Paint s)
    sets the paint of this graphics context to the given object that implements the Paint interface.


java.awt.GradientPaint 1.2
  • GradientPaint(float x1, float y1, Color color1, float x2, float y2, Color color2)
  • GradientPaint(float x1, float y1, Color color1, float x2, float y2, Color color2, boolean cyclic)
  • GradientPaint(Point2D p1, Color color1, Point2D p2, Color color2)
  • GradientPaint(Point2D p1, Color color1, Point2D p2, Color color2, boolean cyclic)
    construct a gradient paint object that fills shapes with color such that the start point is colored with color1, the end point is colored with color2, and the colors in between are linearly interpolated. Colors are constant along lines that are perpendicular to the line joining the start and the end point. By default, the gradient paint is not cyclic; that is, points beyond the start and end points are colored with the same color as the start and end point. If the gradient paint is cyclic, then colors continue to be interpolated, first returning to the starting point color and then repeating indefinitely in both directions.

    Parameters:
    x1, y1, or p1
    The start point
    color1
    The color to use for the start point
    x2, y2, or p2
    The end point
    color2
    The color to use for the end point
    cyclic
    true if the color change pattern repeats, false if the colors beyond the start and end point are constant


java.awt.TexturePaint 1.2
  • TexturePaint(BufferedImage texture, Rectangle2D anchor)
    creates a texture paint object.

    Parameters:
    texture
    The texture to use for filling shapes
    anchor
    The anchor rectangle that defines the tiling of the space to be painted; it is repeated indefinitely in x- and y-directions, and the texture image is scaled to fill each tile


Transform.java 
/*
 * Copyright (c) 1995, 2008, Oracle and/or its affiliates. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *   - Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *
 *   - Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 *   - Neither the name of Oracle or the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */ 

import java.lang.Integer;
import java.awt.*;
import java.awt.event.*;
import java.awt.font.*;
import java.awt.geom.*;
import java.awt.image.*;
import java.awt.event.ItemListener;
import java.awt.event.ItemEvent;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import javax.swing.*;

/*
 * This applet renders a shape, selected by the user, with a paint,stroke, and rendering method,
 * also selected by the user.
*/

public class Transform extends JApplet implements ItemListener,
ActionListener {
    JLabel primLabel, lineLabel, paintLabel, transLabel, strokeLabel;
    TransPanel display;
    static JComboBox primitive, line, paint, trans, stroke;
    JButton redraw;
    public static boolean no2D = false;

    public void init() {
        GridBagLayout layOut = new GridBagLayout();
        getContentPane().setLayout(layOut);
        GridBagConstraints c = new GridBagConstraints();

        c.weightx = 1.0;
        c.fill = GridBagConstraints.BOTH;
        primLabel = new JLabel();
        primLabel.setText("Primitive");
        Font newFont = getFont().deriveFont(1);
        primLabel.setFont(newFont);
        primLabel.setHorizontalAlignment(JLabel.CENTER);
        layOut.setConstraints(primLabel, c);
        getContentPane().add(primLabel);

        lineLabel = new JLabel();
        lineLabel.setText("Lines");
        lineLabel.setFont(newFont);
        lineLabel.setHorizontalAlignment(JLabel.CENTER);
        layOut.setConstraints(lineLabel, c);
        getContentPane().add(lineLabel);

        paintLabel = new JLabel();
        paintLabel.setText("Paints");
        paintLabel.setFont(newFont);
        paintLabel.setHorizontalAlignment(JLabel.CENTER);
        layOut.setConstraints(paintLabel, c);
        getContentPane().add(paintLabel);

        c.gridwidth = GridBagConstraints.RELATIVE;
        transLabel = new JLabel();  
        transLabel.setText("Transforms");
        transLabel.setFont(newFont);
        transLabel.setHorizontalAlignment(JLabel.CENTER);
        layOut.setConstraints(transLabel, c);
        getContentPane().add(transLabel);

        c.gridwidth = GridBagConstraints.REMAINDER;
        strokeLabel = new JLabel();
        strokeLabel.setText("Rendering");
        strokeLabel.setFont(newFont);
        strokeLabel.setHorizontalAlignment(JLabel.CENTER); 
        layOut.setConstraints(strokeLabel, c);
        getContentPane().add(strokeLabel);

        GridBagConstraints ls = new GridBagConstraints();
        ls.weightx = 1.0;
        ls.fill = GridBagConstraints.BOTH;
        primitive = new JComboBox( new Object []{
                                   "rectangle",
                                   "ellipse",
                                   "text"});
        primitive.addItemListener(this);
        newFont = newFont.deriveFont(0, 14.0f);
        primitive.setFont(newFont);
        layOut.setConstraints(primitive, ls);
        getContentPane().add(primitive);

        line = new JComboBox( new Object []{  
                              "thin",
                              "thick",
                              "dashed"});
        line.addItemListener(this);
        line.setFont(newFont);
        layOut.setConstraints(line, ls);
        getContentPane().add(line);

        paint = new JComboBox( new Object[]{
                               "solid",
                               "gradient",
                               "polka"});
        paint.addItemListener(this);
        paint.setFont(newFont);
        layOut.setConstraints(paint, ls);
        getContentPane().add(paint);

        ls.gridwidth = GridBagConstraints.RELATIVE;

        trans = new JComboBox( new Object[]{
    "Identity",
    "rotate",
    "scale",
    "shear"});
 trans.addItemListener(this);
 trans.setFont(newFont);
 layOut.setConstraints(trans, ls);
 getContentPane().add(trans);

        ls.gridwidth = GridBagConstraints.REMAINDER;
        stroke = new JComboBox( new Object[]{
                                "Stroke",
                                "Fill",
                                "Stroke & Fill"}); 
        stroke.addItemListener(this);
        stroke.setFont(newFont);
        layOut.setConstraints(stroke, ls);
        getContentPane().add(stroke);

 GridBagConstraints button = new GridBagConstraints();
 button.gridwidth = GridBagConstraints.REMAINDER;
 redraw = new JButton("Redraw");
 redraw.addActionListener(this);
        redraw.setFont(newFont);
        layOut.setConstraints(redraw, button);
        getContentPane().add(redraw);

        GridBagConstraints tP = new GridBagConstraints();
        tP.fill = GridBagConstraints.BOTH;
        tP.weightx = 1.0;
        tP.weighty = 1.0;
        tP.gridwidth = GridBagConstraints.REMAINDER;
        display = new TransPanel();
        layOut.setConstraints(display, tP);
        display.setBackground(Color.white);
        getContentPane().add(display);

        validate();

    }

    public void itemStateChanged(ItemEvent e){}

   public void actionPerformed(ActionEvent e) {
        display.setTrans(trans.getSelectedIndex());
        display.renderShape();
   }
 

    public static void main( String[] argv ) {
        if ( argv.length > 0 && argv[0].equals( "-no2d" ) ) {
            Transform.no2D = true;
        }
        
        JFrame frame = new JFrame( "Transform" );
        frame.addWindowListener( new WindowAdapter(){
            public void windowClosing( WindowEvent e ){
                System.exit( 0 );
            }
        });                     

        JApplet applet = new Transform();
        frame.getContentPane().add( BorderLayout.CENTER, applet );
        
        applet.init();
        
        frame.setSize( 550, 400 );
        frame.setVisible(true);
   }

}

class TransPanel extends JPanel {
    AffineTransform at = new AffineTransform();
    int w, h;
    Shape shapes[] = new Shape[3];
    BufferedImage bi;
    boolean firstTime = true;

    public TransPanel(){
        setBackground(Color.white);
        shapes[0] = new Rectangle(0, 0, 100, 100);
        shapes[1] = new Ellipse2D.Double(0.0, 0.0, 100.0, 100.0);
        TextLayout textTl = new TextLayout("Text", new Font("Helvetica", 1, 96), new FontRenderContext(null, false, false));
 AffineTransform textAt = new AffineTransform();
 textAt.translate(0, (float)textTl.getBounds().getHeight());
        shapes[2] = textTl.getOutline(textAt);
    }

    public void setTrans(int transIndex) {
        // Sets the AffineTransform.
        switch ( transIndex ) {
        case 0 : at.setToIdentity();
            at.translate(w/2, h/2); break;
        case 1 : at.rotate(Math.toRadians(45)); break;
        case 2 : at.scale(0.5, 0.5); break;
        case 3 : at.shear(0.5, 0.0); break;
      }
    }
 
    public void renderShape() {
        repaint();
    }

    public void paintComponent(Graphics g) {
 super.paintComponent(g);
  
 if ( !Transform.no2D ) {
         Graphics2D g2 = (Graphics2D) g;
         Dimension d = getSize();
         w = d.width;
         h = d.height;


        // Prints out the intructions.
        String instruct = "Pick a primitive, line style, paint, transform,";
        TextLayout thisTl = new TextLayout(instruct, new Font("Helvetica", 0, 10), g2.getFontRenderContext());
        float width = (float)thisTl.getBounds().getWidth();
 float height = (float)thisTl.getBounds().getHeight();
        thisTl.draw(g2, w/2-width/2, 15);

 instruct = "and rendering method and click the Redraw button.";
        thisTl = new TextLayout(instruct, new Font("Helvetica", 0, 10), g2.getFontRenderContext());
        width = (float)thisTl.getBounds().getWidth();
        thisTl.draw(g2, w/2-width/2, height + 17);

 // Initialize the transform.
 if (firstTime) {
     at.setToIdentity();
            at.translate(w/2, h/2);
            firstTime = false;
 }

        // Sets the Stroke.
 Stroke oldStroke = g2.getStroke();

        switch ( Transform.line.getSelectedIndex() ) {
        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;
        }

        // Sets the Paint.
 Paint oldPaint = g2.getPaint();

        switch ( Transform.paint.getSelectedIndex() ) {
        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 buffi = new BufferedImage(15, 15, BufferedImage.TYPE_INT_RGB);
            Graphics2D buffig = buffi.createGraphics();
            buffig.setColor(Color.blue);
            buffig.fillRect(0, 0, 15, 15);
            buffig.setColor(Color.lightGray);
     buffig.translate((15/2)-(5/2), (15/2)-(5/2));
            buffig.fillOval(0, 0, 7, 7); 
            Rectangle r = new Rectangle(0,0,25,25);
            g2.setPaint(new TexturePaint(buffi, r));
            break;
        }



        // Sets the Shape.
        Shape shape = shapes[Transform.primitive.getSelectedIndex()];
        Rectangle r = shape.getBounds();

        // Sets the selected Shape to the center of the Canvas.
        AffineTransform saveXform = g2.getTransform();
  AffineTransform toCenterAt = new AffineTransform();
        toCenterAt.concatenate(at);
        toCenterAt.translate(-(r.width/2), -(r.height/2));

        g2.transform(toCenterAt);

        // Sets the rendering method.
        switch ( Transform.stroke.getSelectedIndex() ) {
        case 0 : g2.draw(shape); break;
        case 1 : g2.fill(shape); break;
        case 2 : Graphics2D tempg2 = g2;
            g2.fill(shape);
            g2.setColor(Color.darkGray);
            g2.draw(shape);
            g2.setPaint(tempg2.getPaint()); break;   
        }

 g2.setStroke(oldStroke);
 g2.setPaint(oldPaint);
        g2.setTransform(saveXform);
 
 }
}
}


When a transform is chosen from the Transform menu, the transform is concatenated onto the AffineTransform at:
public void setTrans(int transIndex) {
    // Sets the AffineTransform.
    switch ( transIndex ) {
    case 0 :
        at.setToIdentity();
        at.translate(w/2, h/2);
        break;
    case 1 :
        at.rotate(Math.toRadians(45));
        break;
    case 2 :
        at.scale(0.5, 0.5);
        break;
    case 3 :
        at.shear(0.5, 0.0);
        break;
    }
}
Before displaying the shape corresponding to the menu choices, the application first retrieves the current transform from the Graphics2D object:
AffineTransform saveXform = g2.getTransform();
This transform will be restored to the Graphics2D after rendering.
After retrieving the current transform, another AffineTransformtoCenterAt, is created that causes shapes to be rendered in the center of the panel. The atAffineTransform is concatenated onto toCenterAt:
AffineTransform toCenterAt = new AffineTransform();
toCenterAt.concatenate(at);
toCenterAt.translate(-(r.width/2), -(r.height/2));
The toCenterAt transform is concatenated onto the Graphics2D transform with the transform method:
g2.transform(toCenterAt);
After rendering is completed, the original transform is restored using the setTransform method:
g2.setTransform(saveXform);

Note: Never use the setTransform method to concatenate a coordinate transform onto an existing transform. The setTransform method overwrites the Graphics2D object's current transform, which might be needed for other reasons, such as positioning Swing and lightweight components in a window. Use these steps to perform transformations:
  1. Use the getTransform method to get the current transform.
  2. Use transformtranslatescaleshear, or rotate to concatenate a transform.
  3. Perform the rendering.
  4. Restore the original transform using the setTransform method.