Areas
An
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.
To construct a complex area, you start out with a default area object.
java.awt.geom.Area
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.
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.
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.
java.awt.Graphics2D 1.2
java.awt.BasicStroke 1.2
You construct a GradientPaint object by specifying two points and the colors that you want at these two points.
java.awt.Graphics2D 1.2
java.awt.GradientPaint 1.2
java.awt.TexturePaint 1.2
When a transform is chosen from the Transform menu, the transform is concatenated onto the
Before displaying the shape corresponding to the menu choices, the application first retrieves the current transform from the
This transform will be restored to the
After retrieving the current transform, another
The
After rendering is completed, the original transform is restored using the
Note: Never use the
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 addition
, subtraction
, intersection
, 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 square cap adds a half-square to the end of the stroke.
Figure 7-12. End cap styles
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. |
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:widthThe width of the pencapThe end cap style, one of CAP_BUTT, CAP_ROUND, and CAP_SQUAREjoinThe join style, one of JOIN_BEVEL, JOIN_MITER, and JOIN_ROUNDmiterlimitThe angle, in degrees, below which a miter join is rendered as a bevel joindashAn array of the lengths of the alternating filled and blank portions of a dashed strokedashPhase
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
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 p1The start pointcolor1The color to use for the start pointx2, y2, or p2The end pointcolor2The color to use for the end pointcyclictrue 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:textureThe texture to use for filling shapesanchor
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; } }
Graphics2D
object:AffineTransform saveXform = g2.getTransform();
Graphics2D
after rendering.After retrieving the current transform, another
AffineTransform
, toCenterAt
, is created that causes shapes to be rendered in the center of the panel. The at
AffineTransform
is concatenated onto toCenterAt
:AffineTransform toCenterAt = new AffineTransform(); toCenterAt.concatenate(at); toCenterAt.translate(-(r.width/2), -(r.height/2));
toCenterAt
transform is concatenated onto the Graphics2D
transform with the transform
method:g2.transform(toCenterAt);
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:- Use the
getTransform
method to get the current transform. - Use
transform
,translate
,scale
,shear
, orrotate
to concatenate a transform. - Perform the rendering.
- Restore the original transform using the
setTransform
method.
0 comments:
Post a Comment