KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > java > awt > BasicStroke


1 /*
2  * @(#)BasicStroke.java 1.42 05/01/04
3  *
4  * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7
8 package java.awt;
9
10 import java.awt.geom.GeneralPath JavaDoc;
11 import java.awt.geom.PathIterator JavaDoc;
12 import sun.dc.path.FastPathProducer;
13 import sun.dc.path.PathConsumer;
14 import sun.dc.path.PathException;
15 import sun.dc.pr.PathStroker;
16 import sun.dc.pr.PathDasher;
17 import sun.dc.pr.Rasterizer;
18
19 /**
20  * The <code>BasicStroke</code> class defines a basic set of rendering
21  * attributes for the outlines of graphics primitives, which are rendered
22  * with a {@link Graphics2D} object that has its Stroke attribute set to
23  * this <code>BasicStroke</code>.
24  * The rendering attributes defined by <code>BasicStroke</code> describe
25  * the shape of the mark made by a pen drawn along the outline of a
26  * {@link Shape} and the decorations applied at the ends and joins of
27  * path segments of the <code>Shape</code>.
28  * These rendering attributes include:
29  * <dl compact>
30  * <dt><i>width</i>
31  * <dd>The pen width, measured perpendicularly to the pen trajectory.
32  * <dt><i>end caps</i>
33  * <dd>The decoration applied to the ends of unclosed subpaths and
34  * dash segments. Subpaths that start and end on the same point are
35  * still considered unclosed if they do not have a CLOSE segment.
36  * See {@link java.awt.geom.PathIterator#SEG_CLOSE SEG_CLOSE}
37  * for more information on the CLOSE segment.
38  * The three different decorations are: {@link #CAP_BUTT},
39  * {@link #CAP_ROUND}, and {@link #CAP_SQUARE}.
40  * <dt><i>line joins</i>
41  * <dd>The decoration applied at the intersection of two path segments
42  * and at the intersection of the endpoints of a subpath that is closed
43  * using {@link java.awt.geom.PathIterator#SEG_CLOSE SEG_CLOSE}.
44  * The three different decorations are: {@link #JOIN_BEVEL},
45  * {@link #JOIN_MITER}, and {@link #JOIN_ROUND}.
46  * <dt><i>miter limit</i>
47  * <dd>The limit to trim a line join that has a JOIN_MITER decoration.
48  * A line join is trimmed when the ratio of miter length to stroke
49  * width is greater than the miterlimit value. The miter length is
50  * the diagonal length of the miter, which is the distance between
51  * the inside corner and the outside corner of the intersection.
52  * The smaller the angle formed by two line segments, the longer
53  * the miter length and the sharper the angle of intersection. The
54  * default miterlimit value of 10.0f causes all angles less than
55  * 11 degrees to be trimmed. Trimming miters converts
56  * the decoration of the line join to bevel.
57  * <dt><i>dash attributes</i>
58  * <dd>The definition of how to make a dash pattern by alternating
59  * between opaque and transparent sections.
60  * </dl>
61  * All attributes that specify measurements and distances controlling
62  * the shape of the returned outline are measured in the same
63  * coordinate system as the original unstroked <code>Shape</code>
64  * argument. When a <code>Graphics2D</code> object uses a
65  * <code>Stroke</code> object to redefine a path during the execution
66  * of one of its <code>draw</code> methods, the geometry is supplied
67  * in its original form before the <code>Graphics2D</code> transform
68  * attribute is applied. Therefore, attributes such as the pen width
69  * are interpreted in the user space coordinate system of the
70  * <code>Graphics2D</code> object and are subject to the scaling and
71  * shearing effects of the user-space-to-device-space transform in that
72  * particular <code>Graphics2D</code>.
73  * For example, the width of a rendered shape's outline is determined
74  * not only by the width attribute of this <code>BasicStroke</code>,
75  * but also by the transform attribute of the
76  * <code>Graphics2D</code> object. Consider this code:
77  * <blockquote><tt>
78  * // sets the Graphics2D object's Tranform attribute
79  * g2d.scale(10, 10);
80  * // sets the Graphics2D object's Stroke attribute
81  * g2d.setStroke(new BasicStroke(1.5f));
82  * </tt></blockquote>
83  * Assuming there are no other scaling transforms added to the
84  * <code>Graphics2D</code> object, the resulting line
85  * will be approximately 15 pixels wide.
86  * As the example code demonstrates, a floating-point line
87  * offers better precision, especially when large transforms are
88  * used with a <code>Graphics2D</code> object.
89  * When a line is diagonal, the exact width depends on how the
90  * rendering pipeline chooses which pixels to fill as it traces the
91  * theoretical widened outline. The choice of which pixels to turn
92  * on is affected by the antialiasing attribute because the
93  * antialiasing rendering pipeline can choose to color
94  * partially-covered pixels.
95  * <p>
96  * For more information on the user space coordinate system and the
97  * rendering process, see the <code>Graphics2D</code> class comments.
98  * @see Graphics2D
99  * @version 1.42, 01/04/05
100  * @author Jim Graham
101  */

102 public class BasicStroke implements Stroke JavaDoc {
103
104     /**
105      * Joins path segments by extending their outside edges until
106      * they meet.
107      */

108     public final static int JOIN_MITER = 0;
109
110     /**
111      * Joins path segments by rounding off the corner at a radius
112      * of half the line width.
113      */

114     public final static int JOIN_ROUND = 1;
115
116     /**
117      * Joins path segments by connecting the outer corners of their
118      * wide outlines with a straight segment.
119      */

120     public final static int JOIN_BEVEL = 2;
121
122     /**
123      * Ends unclosed subpaths and dash segments with no added
124      * decoration.
125      */

126     public final static int CAP_BUTT = 0;
127
128     /**
129      * Ends unclosed subpaths and dash segments with a round
130      * decoration that has a radius equal to half of the width
131      * of the pen.
132      */

133     public final static int CAP_ROUND = 1;
134
135     /**
136      * Ends unclosed subpaths and dash segments with a square
137      * projection that extends beyond the end of the segment
138      * to a distance equal to half of the line width.
139      */

140     public final static int CAP_SQUARE = 2;
141
142     float width;
143
144     int join;
145     int cap;
146     float miterlimit;
147
148     float dash[];
149     float dash_phase;
150
151     /**
152      * Constructs a new <code>BasicStroke</code> with the specified
153      * attributes.
154      * @param width the width of this <code>BasicStroke</code>. The
155      * width must be greater than or equal to 0.0f. If width is
156      * set to 0.0f, the stroke is rendered as the thinnest
157      * possible line for the target device and the antialias
158      * hint setting.
159      * @param cap the decoration of the ends of a <code>BasicStroke</code>
160      * @param join the decoration applied where path segments meet
161      * @param miterlimit the limit to trim the miter join. The miterlimit
162      * must be greater than or equal to 1.0f.
163      * @param dash the array representing the dashing pattern
164      * @param dash_phase the offset to start the dashing pattern
165      * @throws IllegalArgumentException if <code>width</code> is negative
166      * @throws IllegalArgumentException if <code>cap</code> is not either
167      * CAP_BUTT, CAP_ROUND or CAP_SQUARE
168      * @throws IllegalArgumentException if <code>miterlimit</code> is less
169      * than 1 and <code>join</code> is JOIN_MITER
170      * @throws IllegalArgumentException if <code>join</code> is not
171      * either JOIN_ROUND, JOIN_BEVEL, or JOIN_MITER
172      * @throws IllegalArgumentException if <code>dash_phase</code>
173      * is negative and <code>dash</code> is not <code>null</code>
174      * @throws IllegalArgumentException if the length of
175      * <code>dash</code> is zero
176      * @throws IllegalArgumentException if dash lengths are all zero.
177      */

178     public BasicStroke(float width, int cap, int join, float miterlimit,
179                float dash[], float dash_phase) {
180     if (width < 0.0f) {
181         throw new IllegalArgumentException JavaDoc("negative width");
182     }
183     if (cap != CAP_BUTT && cap != CAP_ROUND && cap != CAP_SQUARE) {
184         throw new IllegalArgumentException JavaDoc("illegal end cap value");
185     }
186     if (join == JOIN_MITER) {
187         if (miterlimit < 1.0f) {
188         throw new IllegalArgumentException JavaDoc("miter limit < 1");
189         }
190     } else if (join != JOIN_ROUND && join != JOIN_BEVEL) {
191         throw new IllegalArgumentException JavaDoc("illegal line join value");
192     }
193     if (dash != null) {
194         if (dash_phase < 0.0f) {
195         throw new IllegalArgumentException JavaDoc("negative dash phase");
196         }
197         boolean allzero = true;
198         for (int i = 0; i < dash.length; i++) {
199         float d = dash[i];
200         if (d > 0.0) {
201             allzero = false;
202         } else if (d < 0.0) {
203             throw new IllegalArgumentException JavaDoc("negative dash length");
204         }
205         }
206         if (allzero) {
207         throw new IllegalArgumentException JavaDoc("dash lengths all zero");
208         }
209     }
210     this.width = width;
211     this.cap = cap;
212     this.join = join;
213     this.miterlimit = miterlimit;
214         if (dash != null) {
215             this.dash = (float []) dash.clone();
216         }
217     this.dash_phase = dash_phase;
218     }
219
220     /**
221      * Constructs a solid <code>BasicStroke</code> with the specified
222      * attributes.
223      * @param width the width of the <code>BasicStroke</code>
224      * @param cap the decoration of the ends of a <code>BasicStroke</code>
225      * @param join the decoration applied where path segments meet
226      * @param miterlimit the limit to trim the miter join
227      * @throws IllegalArgumentException if <code>width</code> is negative
228      * @throws IllegalArgumentException if <code>cap</code> is not either
229      * CAP_BUTT, CAP_ROUND or CAP_SQUARE
230      * @throws IllegalArgumentException if <code>miterlimit</code> is less
231      * than 1 and <code>join</code> is JOIN_MITER
232      * @throws IllegalArgumentException if <code>join</code> is not
233      * either JOIN_ROUND, JOIN_BEVEL, or JOIN_MITER
234      */

235     public BasicStroke(float width, int cap, int join, float miterlimit) {
236     this(width, cap, join, miterlimit, null, 0.0f);
237     }
238
239     /**
240      * Constructs a solid <code>BasicStroke</code> with the specified
241      * attributes. The <code>miterlimit</code> parameter is
242      * unnecessary in cases where the default is allowable or the
243      * line joins are not specified as JOIN_MITER.
244      * @param width the width of the <code>BasicStroke</code>
245      * @param cap the decoration of the ends of a <code>BasicStroke</code>
246      * @param join the decoration applied where path segments meet
247      * @throws IllegalArgumentException if <code>width</code> is negative
248      * @throws IllegalArgumentException if <code>cap</code> is not either
249      * CAP_BUTT, CAP_ROUND or CAP_SQUARE
250      * @throws IllegalArgumentException if <code>join</code> is not
251      * either JOIN_ROUND, JOIN_BEVEL, or JOIN_MITER
252      */

253     public BasicStroke(float width, int cap, int join) {
254     this(width, cap, join, 10.0f, null, 0.0f);
255     }
256
257     /**
258      * Constructs a solid <code>BasicStroke</code> with the specified
259      * line width and with default values for the cap and join
260      * styles.
261      * @param width the width of the <code>BasicStroke</code>
262      * @throws IllegalArgumentException if <code>width</code> is negative
263      */

264     public BasicStroke(float width) {
265     this(width, CAP_SQUARE, JOIN_MITER, 10.0f, null, 0.0f);
266     }
267
268     /**
269      * Constructs a new <code>BasicStroke</code> with defaults for all
270      * attributes.
271      * The default attributes are a solid line of width 1.0, CAP_SQUARE,
272      * JOIN_MITER, a miter limit of 10.0.
273      */

274     public BasicStroke() {
275     this(1.0f, CAP_SQUARE, JOIN_MITER, 10.0f, null, 0.0f);
276     }
277
278
279     /**
280      * Returns a <code>Shape</code> whose interior defines the
281      * stroked outline of a specified <code>Shape</code>.
282      * @param s the <code>Shape</code> boundary be stroked
283      * @return the <code>Shape</code> of the stroked outline.
284      */

285     public Shape JavaDoc createStrokedShape(Shape JavaDoc s) {
286     FillAdapter filler = new FillAdapter();
287     PathStroker stroker = new PathStroker(filler);
288     PathDasher dasher = null;
289
290     try {
291         PathConsumer consumer;
292
293         stroker.setPenDiameter(width);
294         stroker.setPenT4(null);
295         stroker.setCaps(RasterizerCaps[cap]);
296         stroker.setCorners(RasterizerCorners[join], miterlimit);
297         if (dash != null) {
298         dasher = new PathDasher(stroker);
299         dasher.setDash(dash, dash_phase);
300         dasher.setDashT4(null);
301         consumer = dasher;
302         } else {
303         consumer = stroker;
304         }
305
306         feedConsumer(consumer, s.getPathIterator(null));
307     } finally {
308         stroker.dispose();
309         if (dasher != null) {
310         dasher.dispose();
311         }
312     }
313
314     return filler.getShape();
315     }
316
317     private void feedConsumer(PathConsumer consumer, PathIterator JavaDoc pi) {
318     try {
319         consumer.beginPath();
320         boolean pathClosed = false;
321         float mx = 0.0f;
322         float my = 0.0f;
323         float point[] = new float[6];
324
325         while (!pi.isDone()) {
326         int type = pi.currentSegment(point);
327         if (pathClosed == true) {
328             pathClosed = false;
329             if (type != PathIterator.SEG_MOVETO) {
330             // Force current point back to last moveto point
331
consumer.beginSubpath(mx, my);
332             }
333         }
334         switch (type) {
335         case PathIterator.SEG_MOVETO:
336             mx = point[0];
337             my = point[1];
338             consumer.beginSubpath(point[0], point[1]);
339             break;
340         case PathIterator.SEG_LINETO:
341             consumer.appendLine(point[0], point[1]);
342             break;
343         case PathIterator.SEG_QUADTO:
344             // Quadratic curves take two points
345
consumer.appendQuadratic(point[0], point[1],
346                          point[2], point[3]);
347             break;
348         case PathIterator.SEG_CUBICTO:
349             // Cubic curves take three points
350
consumer.appendCubic(point[0], point[1],
351                      point[2], point[3],
352                      point[4], point[5]);
353             break;
354         case PathIterator.SEG_CLOSE:
355             consumer.closedSubpath();
356             pathClosed = true;
357             break;
358         }
359         pi.next();
360         }
361
362         consumer.endPath();
363     } catch (PathException e) {
364         throw new InternalError JavaDoc("Unable to Stroke shape ("+
365                     e.getMessage()+")");
366     }
367     }
368
369     /**
370      * Returns the line width. Line width is represented in user space,
371      * which is the default-coordinate space used by Java 2D. See the
372      * <code>Graphics2D</code> class comments for more information on
373      * the user space coordinate system.
374      * @return the line width of this <code>BasicStroke</code>.
375      * @see Graphics2D
376      */

377     public float getLineWidth() {
378     return width;
379     }
380
381     /**
382      * Returns the end cap style.
383      * @return the end cap style of this <code>BasicStroke</code> as one
384      * of the static <code>int</code> values that define possible end cap
385      * styles.
386      */

387     public int getEndCap() {
388     return cap;
389     }
390
391     /**
392      * Returns the line join style.
393      * @return the line join style of the <code>BasicStroke</code> as one
394      * of the static <code>int</code> values that define possible line
395      * join styles.
396      */

397     public int getLineJoin() {
398     return join;
399     }
400
401     /**
402      * Returns the limit of miter joins.
403      * @return the limit of miter joins of the <code>BasicStroke</code>.
404      */

405     public float getMiterLimit() {
406     return miterlimit;
407     }
408
409     /**
410      * Returns the array representing the lengths of the dash segments.
411      * Alternate entries in the array represent the user space lengths
412      * of the opaque and transparent segments of the dashes.
413      * As the pen moves along the outline of the <code>Shape</code>
414      * to be stroked, the user space
415      * distance that the pen travels is accumulated. The distance
416      * value is used to index into the dash array.
417      * The pen is opaque when its current cumulative distance maps
418      * to an even element of the dash array and transparent otherwise.
419      * @return the dash array.
420      */

421     public float[] getDashArray() {
422         if (dash == null) {
423             return null;
424         }
425
426         return (float[]) dash.clone();
427     }
428
429     /**
430      * Returns the current dash phase.
431      * The dash phase is a distance specified in user coordinates that
432      * represents an offset into the dashing pattern. In other words, the dash
433      * phase defines the point in the dashing pattern that will correspond to
434      * the beginning of the stroke.
435      * @return the dash phase as a <code>float</code> value.
436      */

437     public float getDashPhase() {
438     return dash_phase;
439     }
440
441     /**
442      * Returns the hashcode for this stroke.
443      * @return a hash code for this stroke.
444      */

445     public int hashCode() {
446     int hash = Float.floatToIntBits(width);
447     hash = hash * 31 + join;
448     hash = hash * 31 + cap;
449     hash = hash * 31 + Float.floatToIntBits(miterlimit);
450     if (dash != null) {
451         hash = hash * 31 + Float.floatToIntBits(dash_phase);
452         for (int i = 0; i < dash.length; i++) {
453         hash = hash * 31 + Float.floatToIntBits(dash[i]);
454         }
455     }
456     return hash;
457     }
458
459     /**
460      * Returns true if this BasicStroke represents the same
461      * stroking operation as the given argument.
462      */

463    /**
464     * Tests if a specified object is equal to this <code>BasicStroke</code>
465     * by first testing if it is a <code>BasicStroke</code> and then comparing
466     * its width, join, cap, miter limit, dash, and dash phase attributes with
467     * those of this <code>BasicStroke</code>.
468     * @param obj the specified object to compare to this
469     * <code>BasicStroke</code>
470     * @return <code>true</code> if the width, join, cap, miter limit, dash, and
471     * dash phase are the same for both objects;
472     * <code>false</code> otherwise.
473     */

474     public boolean equals(Object JavaDoc obj) {
475         if (!(obj instanceof BasicStroke JavaDoc)) {
476             return false;
477         }
478
479         BasicStroke JavaDoc bs = (BasicStroke JavaDoc) obj;
480         if (width != bs.width) {
481             return false;
482         }
483
484         if (join != bs.join) {
485             return false;
486         }
487
488         if (cap != bs.cap) {
489             return false;
490         }
491
492         if (miterlimit != bs.miterlimit) {
493             return false;
494         }
495
496         if (dash != null) {
497         if (dash_phase != bs.dash_phase) {
498         return false;
499         }
500
501         if (!java.util.Arrays.equals(dash, bs.dash)) {
502         return false;
503         }
504         }
505         else if (bs.dash != null) {
506             return false;
507         }
508
509         return true;
510     }
511
512     private static final int RasterizerCaps[] = {
513     Rasterizer.BUTT, Rasterizer.ROUND, Rasterizer.SQUARE
514     };
515
516     private static final int RasterizerCorners[] = {
517     Rasterizer.MITER, Rasterizer.ROUND, Rasterizer.BEVEL
518     };
519
520     private class FillAdapter implements PathConsumer {
521     boolean closed;
522     GeneralPath JavaDoc path;
523
524     public FillAdapter() {
525         path = new GeneralPath JavaDoc(GeneralPath.WIND_NON_ZERO);
526     }
527
528     public Shape JavaDoc getShape() {
529         return path;
530     }
531
532     public void dispose() {
533     }
534
535     public PathConsumer getConsumer() {
536         return null;
537     }
538
539     public void beginPath() {}
540
541     public void beginSubpath(float x0, float y0) {
542         if (closed) {
543         path.closePath();
544         closed = false;
545         }
546         path.moveTo(x0, y0);
547     }
548
549     public void appendLine(float x1, float y1) {
550         path.lineTo(x1, y1);
551     }
552
553     public void appendQuadratic(float xm, float ym, float x1, float y1) {
554         path.quadTo(xm, ym, x1, y1);
555     }
556
557     public void appendCubic(float xm, float ym,
558                 float xn, float yn,
559                 float x1, float y1) {
560         path.curveTo(xm, ym, xn, yn, x1, y1);
561     }
562
563     public void closedSubpath() {
564         closed = true;
565     }
566
567     public void endPath() {
568         if (closed) {
569         path.closePath();
570         closed = false;
571         }
572     }
573
574     public void useProxy(FastPathProducer proxy)
575         throws PathException
576     {
577         proxy.sendTo(this);
578     }
579
580     public long getCPathConsumer() {
581         return 0;
582     }
583     }
584 }
585
Popular Tags