1 /*
2 * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26 package java.awt;
27
28 /**
29 * The <code>BasicStroke</code> class defines a basic set of rendering
30 * attributes for the outlines of graphics primitives, which are rendered
31 * with a {@link Graphics2D} object that has its Stroke attribute set to
32 * this <code>BasicStroke</code>.
33 * The rendering attributes defined by <code>BasicStroke</code> describe
34 * the shape of the mark made by a pen drawn along the outline of a
35 * {@link Shape} and the decorations applied at the ends and joins of
36 * path segments of the <code>Shape</code>.
37 * These rendering attributes include:
38 * <dl compact>
39 * <dt><i>width</i>
40 * <dd>The pen width, measured perpendicularly to the pen trajectory.
41 * <dt><i>end caps</i>
42 * <dd>The decoration applied to the ends of unclosed subpaths and
43 * dash segments. Subpaths that start and end on the same point are
44 * still considered unclosed if they do not have a CLOSE segment.
45 * See {@link java.awt.geom.PathIterator#SEG_CLOSE SEG_CLOSE}
46 * for more information on the CLOSE segment.
47 * The three different decorations are: {@link #CAP_BUTT},
48 * {@link #CAP_ROUND}, and {@link #CAP_SQUARE}.
49 * <dt><i>line joins</i>
50 * <dd>The decoration applied at the intersection of two path segments
51 * and at the intersection of the endpoints of a subpath that is closed
52 * using {@link java.awt.geom.PathIterator#SEG_CLOSE SEG_CLOSE}.
53 * The three different decorations are: {@link #JOIN_BEVEL},
54 * {@link #JOIN_MITER}, and {@link #JOIN_ROUND}.
55 * <dt><i>miter limit</i>
56 * <dd>The limit to trim a line join that has a JOIN_MITER decoration.
57 * A line join is trimmed when the ratio of miter length to stroke
58 * width is greater than the miterlimit value. The miter length is
59 * the diagonal length of the miter, which is the distance between
60 * the inside corner and the outside corner of the intersection.
61 * The smaller the angle formed by two line segments, the longer
62 * the miter length and the sharper the angle of intersection. The
63 * default miterlimit value of 10.0f causes all angles less than
64 * 11 degrees to be trimmed. Trimming miters converts
65 * the decoration of the line join to bevel.
66 * <dt><i>dash attributes</i>
67 * <dd>The definition of how to make a dash pattern by alternating
68 * between opaque and transparent sections.
69 * </dl>
70 * All attributes that specify measurements and distances controlling
71 * the shape of the returned outline are measured in the same
72 * coordinate system as the original unstroked <code>Shape</code>
73 * argument. When a <code>Graphics2D</code> object uses a
74 * <code>Stroke</code> object to redefine a path during the execution
75 * of one of its <code>draw</code> methods, the geometry is supplied
76 * in its original form before the <code>Graphics2D</code> transform
77 * attribute is applied. Therefore, attributes such as the pen width
78 * are interpreted in the user space coordinate system of the
79 * <code>Graphics2D</code> object and are subject to the scaling and
80 * shearing effects of the user-space-to-device-space transform in that
81 * particular <code>Graphics2D</code>.
82 * For example, the width of a rendered shape's outline is determined
83 * not only by the width attribute of this <code>BasicStroke</code>,
84 * but also by the transform attribute of the
85 * <code>Graphics2D</code> object. Consider this code:
86 * <blockquote><tt>
87 * // sets the Graphics2D object's Tranform attribute
88 * g2d.scale(10, 10);
89 * // sets the Graphics2D object's Stroke attribute
90 * g2d.setStroke(new BasicStroke(1.5f));
91 * </tt></blockquote>
92 * Assuming there are no other scaling transforms added to the
93 * <code>Graphics2D</code> object, the resulting line
94 * will be approximately 15 pixels wide.
95 * As the example code demonstrates, a floating-point line
96 * offers better precision, especially when large transforms are
97 * used with a <code>Graphics2D</code> object.
98 * When a line is diagonal, the exact width depends on how the
99 * rendering pipeline chooses which pixels to fill as it traces the
100 * theoretical widened outline. The choice of which pixels to turn
101 * on is affected by the antialiasing attribute because the
102 * antialiasing rendering pipeline can choose to color
103 * partially-covered pixels.
104 * <p>
105 * For more information on the user space coordinate system and the
106 * rendering process, see the <code>Graphics2D</code> class comments.
107 * @see Graphics2D
108 * @author Jim Graham
109 */
110 public class BasicStroke implements Stroke {
111
112 /**
113 * Joins path segments by extending their outside edges until
114 * they meet.
115 */
116 public final static int JOIN_MITER = 0;
117
118 /**
119 * Joins path segments by rounding off the corner at a radius
120 * of half the line width.
121 */
122 public final static int JOIN_ROUND = 1;
123
124 /**
125 * Joins path segments by connecting the outer corners of their
126 * wide outlines with a straight segment.
127 */
128 public final static int JOIN_BEVEL = 2;
129
130 /**
131 * Ends unclosed subpaths and dash segments with no added
132 * decoration.
133 */
134 public final static int CAP_BUTT = 0;
135
136 /**
137 * Ends unclosed subpaths and dash segments with a round
138 * decoration that has a radius equal to half of the width
139 * of the pen.
140 */
141 public final static int CAP_ROUND = 1;
142
143 /**
144 * Ends unclosed subpaths and dash segments with a square
145 * projection that extends beyond the end of the segment
146 * to a distance equal to half of the line width.
147 */
148 public final static int CAP_SQUARE = 2;
149
150 float width;
151
152 int join;
153 int cap;
154 float miterlimit;
155
156 float dash[];
157 float dash_phase;
158
159 /**
160 * Constructs a new <code>BasicStroke</code> with the specified
161 * attributes.
162 * @param width the width of this <code>BasicStroke</code>. The
163 * width must be greater than or equal to 0.0f. If width is
164 * set to 0.0f, the stroke is rendered as the thinnest
165 * possible line for the target device and the antialias
166 * hint setting.
167 * @param cap the decoration of the ends of a <code>BasicStroke</code>
168 * @param join the decoration applied where path segments meet
169 * @param miterlimit the limit to trim the miter join. The miterlimit
170 * must be greater than or equal to 1.0f.
171 * @param dash the array representing the dashing pattern
172 * @param dash_phase the offset to start the dashing pattern
173 * @throws IllegalArgumentException if <code>width</code> is negative
174 * @throws IllegalArgumentException if <code>cap</code> is not either
175 * CAP_BUTT, CAP_ROUND or CAP_SQUARE
176 * @throws IllegalArgumentException if <code>miterlimit</code> is less
177 * than 1 and <code>join</code> is JOIN_MITER
178 * @throws IllegalArgumentException if <code>join</code> is not
179 * either JOIN_ROUND, JOIN_BEVEL, or JOIN_MITER
180 * @throws IllegalArgumentException if <code>dash_phase</code>
181 * is negative and <code>dash</code> is not <code>null</code>
182 * @throws IllegalArgumentException if the length of
183 * <code>dash</code> is zero
184 * @throws IllegalArgumentException if dash lengths are all zero.
185 */
186 public BasicStroke(float width, int cap, int join, float miterlimit,
187 float dash[], float dash_phase) {
188 if (width < 0.0f) {
189 throw new IllegalArgumentException("negative width");
190 }
191 if (cap != CAP_BUTT && cap != CAP_ROUND && cap != CAP_SQUARE) {
192 throw new IllegalArgumentException("illegal end cap value");
193 }
194 if (join == JOIN_MITER) {
195 if (miterlimit < 1.0f) {
196 throw new IllegalArgumentException("miter limit < 1");
197 }
198 } else if (join != JOIN_ROUND && join != JOIN_BEVEL) {
199 throw new IllegalArgumentException("illegal line join value");
200 }
201 if (dash != null) {
202 if (dash_phase < 0.0f) {
203 throw new IllegalArgumentException("negative dash phase");
204 }
205 boolean allzero = true;
206 for (int i = 0; i < dash.length; i++) {
207 float d = dash[i];
208 if (d > 0.0) {
209 allzero = false;
210 } else if (d < 0.0) {
211 throw new IllegalArgumentException("negative dash length");
212 }
213 }
214 if (allzero) {
215 throw new IllegalArgumentException("dash lengths all zero");
216 }
217 }
218 this.width = width;
219 this.cap = cap;
220 this.join = join;
221 this.miterlimit = miterlimit;
222 if (dash != null) {
223 this.dash = (float []) dash.clone();
224 }
225 this.dash_phase = dash_phase;
226 }
227
228 /**
229 * Constructs a solid <code>BasicStroke</code> with the specified
230 * attributes.
231 * @param width the width of the <code>BasicStroke</code>
232 * @param cap the decoration of the ends of a <code>BasicStroke</code>
233 * @param join the decoration applied where path segments meet
234 * @param miterlimit the limit to trim the miter join
235 * @throws IllegalArgumentException if <code>width</code> is negative
236 * @throws IllegalArgumentException if <code>cap</code> is not either
237 * CAP_BUTT, CAP_ROUND or CAP_SQUARE
238 * @throws IllegalArgumentException if <code>miterlimit</code> is less
239 * than 1 and <code>join</code> is JOIN_MITER
240 * @throws IllegalArgumentException if <code>join</code> is not
241 * either JOIN_ROUND, JOIN_BEVEL, or JOIN_MITER
242 */
243 public BasicStroke(float width, int cap, int join, float miterlimit) {
244 this(width, cap, join, miterlimit, null, 0.0f);
245 }
246
247 /**
248 * Constructs a solid <code>BasicStroke</code> with the specified
249 * attributes. The <code>miterlimit</code> parameter is
250 * unnecessary in cases where the default is allowable or the
251 * line joins are not specified as JOIN_MITER.
252 * @param width the width of the <code>BasicStroke</code>
253 * @param cap the decoration of the ends of a <code>BasicStroke</code>
254 * @param join the decoration applied where path segments meet
255 * @throws IllegalArgumentException if <code>width</code> is negative
256 * @throws IllegalArgumentException if <code>cap</code> is not either
257 * CAP_BUTT, CAP_ROUND or CAP_SQUARE
258 * @throws IllegalArgumentException if <code>join</code> is not
259 * either JOIN_ROUND, JOIN_BEVEL, or JOIN_MITER
260 */
261 public BasicStroke(float width, int cap, int join) {
262 this(width, cap, join, 10.0f, null, 0.0f);
263 }
264
265 /**
266 * Constructs a solid <code>BasicStroke</code> with the specified
267 * line width and with default values for the cap and join
268 * styles.
269 * @param width the width of the <code>BasicStroke</code>
270 * @throws IllegalArgumentException if <code>width</code> is negative
271 */
272 public BasicStroke(float width) {
273 this(width, CAP_SQUARE, JOIN_MITER, 10.0f, null, 0.0f);
274 }
275
276 /**
277 * Constructs a new <code>BasicStroke</code> with defaults for all
278 * attributes.
279 * The default attributes are a solid line of width 1.0, CAP_SQUARE,
280 * JOIN_MITER, a miter limit of 10.0.
281 */
282 public BasicStroke() {
283 this(1.0f, CAP_SQUARE, JOIN_MITER, 10.0f, null, 0.0f);
284 }
285
286
287 /**
288 * Returns a <code>Shape</code> whose interior defines the
289 * stroked outline of a specified <code>Shape</code>.
290 * @param s the <code>Shape</code> boundary be stroked
291 * @return the <code>Shape</code> of the stroked outline.
292 */
293 public Shape createStrokedShape(Shape s) {
294 sun.java2d.pipe.RenderingEngine re =
295 sun.java2d.pipe.RenderingEngine.getInstance();
296 return re.createStrokedShape(s, width, cap, join, miterlimit,
297 dash, dash_phase);
298 }
299
300 /**
301 * Returns the line width. Line width is represented in user space,
302 * which is the default-coordinate space used by Java 2D. See the
303 * <code>Graphics2D</code> class comments for more information on
304 * the user space coordinate system.
305 * @return the line width of this <code>BasicStroke</code>.
306 * @see Graphics2D
307 */
308 public float getLineWidth() {
309 return width;
310 }
311
312 /**
313 * Returns the end cap style.
314 * @return the end cap style of this <code>BasicStroke</code> as one
315 * of the static <code>int</code> values that define possible end cap
316 * styles.
317 */
318 public int getEndCap() {
319 return cap;
320 }
321
322 /**
323 * Returns the line join style.
324 * @return the line join style of the <code>BasicStroke</code> as one
325 * of the static <code>int</code> values that define possible line
326 * join styles.
327 */
328 public int getLineJoin() {
329 return join;
330 }
331
332 /**
333 * Returns the limit of miter joins.
334 * @return the limit of miter joins of the <code>BasicStroke</code>.
335 */
336 public float getMiterLimit() {
337 return miterlimit;
338 }
339
340 /**
341 * Returns the array representing the lengths of the dash segments.
342 * Alternate entries in the array represent the user space lengths
343 * of the opaque and transparent segments of the dashes.
344 * As the pen moves along the outline of the <code>Shape</code>
345 * to be stroked, the user space
346 * distance that the pen travels is accumulated. The distance
347 * value is used to index into the dash array.
348 * The pen is opaque when its current cumulative distance maps
349 * to an even element of the dash array and transparent otherwise.
350 * @return the dash array.
351 */
352 public float[] getDashArray() {
353 if (dash == null) {
354 return null;
355 }
356
357 return (float[]) dash.clone();
358 }
359
360 /**
361 * Returns the current dash phase.
362 * The dash phase is a distance specified in user coordinates that
363 * represents an offset into the dashing pattern. In other words, the dash
364 * phase defines the point in the dashing pattern that will correspond to
365 * the beginning of the stroke.
366 * @return the dash phase as a <code>float</code> value.
367 */
368 public float getDashPhase() {
369 return dash_phase;
370 }
371
372 /**
373 * Returns the hashcode for this stroke.
374 * @return a hash code for this stroke.
375 */
376 public int hashCode() {
377 int hash = Float.floatToIntBits(width);
378 hash = hash * 31 + join;
379 hash = hash * 31 + cap;
380 hash = hash * 31 + Float.floatToIntBits(miterlimit);
381 if (dash != null) {
382 hash = hash * 31 + Float.floatToIntBits(dash_phase);
383 for (int i = 0; i < dash.length; i++) {
384 hash = hash * 31 + Float.floatToIntBits(dash[i]);
385 }
386 }
387 return hash;
388 }
389
390 /**
391 * Returns true if this BasicStroke represents the same
392 * stroking operation as the given argument.
393 */
394 /**
395 * Tests if a specified object is equal to this <code>BasicStroke</code>
396 * by first testing if it is a <code>BasicStroke</code> and then comparing
397 * its width, join, cap, miter limit, dash, and dash phase attributes with
398 * those of this <code>BasicStroke</code>.
399 * @param obj the specified object to compare to this
400 * <code>BasicStroke</code>
401 * @return <code>true</code> if the width, join, cap, miter limit, dash, and
402 * dash phase are the same for both objects;
403 * <code>false</code> otherwise.
404 */
405 public boolean equals(Object obj) {
406 if (!(obj instanceof BasicStroke)) {
407 return false;
408 }
409
410 BasicStroke bs = (BasicStroke) obj;
411 if (width != bs.width) {
412 return false;
413 }
414
415 if (join != bs.join) {
416 return false;
417 }
418
419 if (cap != bs.cap) {
420 return false;
421 }
422
423 if (miterlimit != bs.miterlimit) {
424 return false;
425 }
426
427 if (dash != null) {
428 if (dash_phase != bs.dash_phase) {
429 return false;
430 }
431
432 if (!java.util.Arrays.equals(dash, bs.dash)) {
433 return false;
434 }
435 }
436 else if (bs.dash != null) {
437 return false;
438 }
439
440 return true;
441 }
442 }