2 using System.Drawing.Drawing2D;
\r
10 namespace System.Drawing {
\r
11 internal class AdvancedStroke : Stroke {
\r
13 public const float PenUnits = 0.01f;
14 public const int MinPenUnits = 100;
15 public const int MinPenUnitsAA = 20;
16 public const float MinPenSizeAA = PenUnits * MinPenUnitsAA;
\r
17 public const double MinPenSizeAASquared = (MinPenSizeAA * MinPenSizeAA);
18 public const double MinPenSizeSquared = 1.000000001;
\r
19 public const double MinPenSizeNorm = 1.5;
\r
20 public const double MinPenSizeSquaredNorm = (MinPenSizeNorm * MinPenSizeNorm);
\r
23 * Joins path segments by extending their outside edges until
\r
26 public const int JOIN_MITER = 0;
\r
29 * Joins path segments by rounding off the corner at a radius
\r
30 * of half the line width.
\r
32 public const int JOIN_ROUND = 1;
\r
35 * Joins path segments by connecting the outer corners of their
\r
36 * wide outlines with a straight segment.
\r
38 public const int JOIN_BEVEL = 2;
\r
41 * Ends unclosed subpaths and dash segments with no added
\r
44 public const int CAP_BUTT = 0;
\r
47 * Ends unclosed subpaths and dash segments with a round
\r
48 * decoration that has a radius equal to half of the width
\r
51 public const int CAP_ROUND = 1;
\r
54 * Ends unclosed subpaths and dash segments with a square
\r
55 * projection that extends beyond the end of the segment
\r
56 * to a distance equal to half of the line width.
\r
58 public const int CAP_SQUARE = 2;
\r
69 AffineTransform _penTransform;
\r
70 AffineTransform _outputTransform;
\r
74 * Constructs a new <code>AdvancedStroke</code> with the specified
\r
76 * @param width the width of this <code>AdvancedStroke</code>. The
\r
77 * width must be greater than or equal to 0.0f. If width is
\r
78 * set to 0.0f, the stroke is rendered as the thinnest
\r
79 * possible line for the target device and the antialias
\r
81 * @param cap the decoration of the ends of a <code>AdvancedStroke</code>
\r
82 * @param join the decoration applied where path segments meet
\r
83 * @param miterlimit the limit to trim the miter join. The miterlimit
\r
84 * must be greater than or equal to 1.0f.
\r
85 * @param dash the array representing the dashing pattern
\r
86 * @param dash_phase the offset to start the dashing pattern
\r
87 * @throws IllegalArgumentException if <code>width</code> is negative
\r
88 * @throws IllegalArgumentException if <code>cap</code> is not either
\r
89 * CAP_BUTT, CAP_ROUND or CAP_SQUARE
\r
90 * @throws IllegalArgumentException if <code>miterlimit</code> is less
\r
91 * than 1 and <code>join</code> is JOIN_MITER
\r
92 * @throws IllegalArgumentException if <code>join</code> is not
\r
93 * either JOIN_ROUND, JOIN_BEVEL, or JOIN_MITER
\r
94 * @throws IllegalArgumentException if <code>dash_phase</code>
\r
95 * is negative and <code>dash</code> is not <code>null</code>
\r
96 * @throws IllegalArgumentException if the length of
\r
97 * <code>dash</code> is zero
\r
98 * @throws IllegalArgumentException if dash lengths are all zero.
\r
100 public AdvancedStroke(float width, int cap, int join, float miterlimit,
\r
101 float[] dash, float dash_phase, AffineTransform penTransform,
\r
102 AffineTransform outputTransform, bool fitPen) {
\r
103 if (width < 0.0f) {
\r
104 throw new IllegalArgumentException("negative width");
\r
106 if (cap != CAP_BUTT && cap != CAP_ROUND && cap != CAP_SQUARE) {
\r
107 throw new IllegalArgumentException("illegal end cap value");
\r
109 if (join == JOIN_MITER) {
\r
110 if (miterlimit < 1.0f) {
\r
111 throw new IllegalArgumentException("miter limit < 1");
\r
113 } else if (join != JOIN_ROUND && join != JOIN_BEVEL) {
\r
114 throw new IllegalArgumentException("illegal line join value");
\r
116 if (dash != null) {
\r
117 if (dash_phase < 0.0f) {
\r
118 throw new IllegalArgumentException("negative dash phase");
\r
120 bool allzero = true;
\r
121 for (int i = 0; i < dash.Length; i++) {
\r
125 } else if (d < 0.0) {
\r
126 throw new IllegalArgumentException("negative dash length");
\r
130 throw new IllegalArgumentException("dash lengths all zero");
\r
133 this.width = width;
\r
136 this.miterlimit = miterlimit;
\r
137 if (dash != null) {
\r
138 this.dash = (float []) dash.Clone();
\r
140 this.dash_phase = dash_phase;
\r
141 this._penTransform = penTransform;
\r
142 this._outputTransform = outputTransform;
\r
143 this._fitPen = fitPen;
\r
147 * Constructs a solid <code>AdvancedStroke</code> with the specified
\r
149 * @param width the width of the <code>AdvancedStroke</code>
\r
150 * @param cap the decoration of the ends of a <code>AdvancedStroke</code>
\r
151 * @param join the decoration applied where path segments meet
\r
152 * @param miterlimit the limit to trim the miter join
\r
153 * @throws IllegalArgumentException if <code>width</code> is negative
\r
154 * @throws IllegalArgumentException if <code>cap</code> is not either
\r
155 * CAP_BUTT, CAP_ROUND or CAP_SQUARE
\r
156 * @throws IllegalArgumentException if <code>miterlimit</code> is less
\r
157 * than 1 and <code>join</code> is JOIN_MITER
\r
158 * @throws IllegalArgumentException if <code>join</code> is not
\r
159 * either JOIN_ROUND, JOIN_BEVEL, or JOIN_MITER
\r
161 public AdvancedStroke(float width, int cap, int join, float miterlimit) :
\r
162 this(width, cap, join, miterlimit, null, 0.0f, null, null, false) {
\r
166 * Constructs a solid <code>AdvancedStroke</code> with the specified
\r
167 * attributes. The <code>miterlimit</code> parameter is
\r
168 * unnecessary in cases where the default is allowable or the
\r
169 * line joins are not specified as JOIN_MITER.
\r
170 * @param width the width of the <code>AdvancedStroke</code>
\r
171 * @param cap the decoration of the ends of a <code>AdvancedStroke</code>
\r
172 * @param join the decoration applied where path segments meet
\r
173 * @throws IllegalArgumentException if <code>width</code> is negative
\r
174 * @throws IllegalArgumentException if <code>cap</code> is not either
\r
175 * CAP_BUTT, CAP_ROUND or CAP_SQUARE
\r
176 * @throws IllegalArgumentException if <code>join</code> is not
\r
177 * either JOIN_ROUND, JOIN_BEVEL, or JOIN_MITER
\r
179 public AdvancedStroke(float width, int cap, int join) :
\r
180 this(width, cap, join, 10.0f, null, 0.0f, null, null, false) {
\r
184 * Constructs a solid <code>AdvancedStroke</code> with the specified
\r
185 * line width and with default values for the cap and join
\r
187 * @param width the width of the <code>AdvancedStroke</code>
\r
188 * @throws IllegalArgumentException if <code>width</code> is negative
\r
190 public AdvancedStroke(float width) :
\r
191 this(width, CAP_SQUARE, JOIN_MITER, 10.0f, null, 0.0f, null, null, false) {
\r
195 * Constructs a new <code>AdvancedStroke</code> with defaults for all
\r
197 * The default attributes are a solid line of width 1.0, CAP_SQUARE,
\r
198 * JOIN_MITER, a miter limit of 10.0.
\r
200 public AdvancedStroke() :
\r
201 this(1.0f, CAP_SQUARE, JOIN_MITER, 10.0f, null, 0.0f, null, null, false) {
\r
206 * Returns a <code>Shape</code> whose interior defines the
\r
207 * stroked outline of a specified <code>Shape</code>.
\r
208 * @param s the <code>Shape</code> boundary be stroked
\r
209 * @return the <code>Shape</code> of the stroked outline.
\r
211 public Shape createStrokedShape(Shape s) {
\r
212 FillAdapter filler = new FillAdapter();
\r
213 PathStroker stroker = new PathStroker(filler);
\r
214 PathConsumer consumer;
\r
216 stroker.setPenDiameter(width);
\r
218 stroker.setPenFitting(PenUnits, MinPenUnitsAA);
\r
221 if (PenTransform != null && !PenTransform.isIdentity()) {
\r
223 (float)PenTransform.getScaleX(), (float)PenTransform.getShearY(),
\r
224 (float)PenTransform.getShearX(), (float)PenTransform.getScaleY()
\r
229 if (OutputTransform != null && !OutputTransform.isIdentity()) {
\r
231 (float)OutputTransform.getScaleX(), (float)OutputTransform.getShearY(),
\r
232 (float)OutputTransform.getShearX(), (float)OutputTransform.getScaleY(),
\r
233 (float)OutputTransform.getTranslateX(), (float)OutputTransform.getTranslateY()
\r
237 stroker.setPenT4(t4);
\r
238 stroker.setOutputT6(t6);
\r
239 stroker.setCaps(RasterizerCaps[cap]);
\r
240 stroker.setCorners(RasterizerCorners[join], miterlimit);
\r
241 if (dash != null) {
\r
242 PathDasher dasher = new PathDasher(stroker);
\r
243 dasher.setDash(dash, dash_phase);
\r
244 dasher.setDashT4(t4);
\r
247 consumer = stroker;
\r
250 PathIterator pi = s.getPathIterator(null);
\r
253 consumer.beginPath();
\r
254 bool pathClosed = false;
\r
257 float[] point = new float[6];
\r
259 while (!pi.isDone()) {
\r
260 int type = pi.currentSegment(point);
\r
261 if (pathClosed == true) {
\r
262 pathClosed = false;
\r
263 if (type != PathIterator__Finals.SEG_MOVETO) {
\r
264 // Force current point back to last moveto point
\r
265 consumer.beginSubpath(mx, my);
\r
268 switch ((GraphicsPath.JPI)type) {
\r
269 case GraphicsPath.JPI.SEG_MOVETO:
\r
272 consumer.beginSubpath(point[0], point[1]);
\r
274 case GraphicsPath.JPI.SEG_LINETO:
\r
275 consumer.appendLine(point[0], point[1]);
\r
277 case GraphicsPath.JPI.SEG_QUADTO:
\r
278 // Quadratic curves take two points
\r
279 consumer.appendQuadratic(point[0], point[1],
\r
280 point[2], point[3]);
\r
282 case GraphicsPath.JPI.SEG_CUBICTO:
\r
283 // Cubic curves take three points
\r
284 consumer.appendCubic(point[0], point[1],
\r
285 point[2], point[3],
\r
286 point[4], point[5]);
\r
288 case GraphicsPath.JPI.SEG_CLOSE:
\r
289 consumer.closedSubpath();
\r
296 consumer.endPath();
\r
297 } catch (PathException e) {
\r
298 throw new InternalError("Unable to Stroke shape ("+
\r
302 return filler.getShape();
\r
306 * Returns the line width. Line width is represented in user space,
\r
307 * which is the default-coordinate space used by Java 2D. See the
\r
308 * <code>Graphics2D</code> class comments for more information on
\r
309 * the user space coordinate system.
\r
310 * @return the line width of this <code>AdvancedStroke</code>.
\r
313 public float getLineWidth() {
\r
318 * Returns the end cap style.
\r
319 * @return the end cap style of this <code>AdvancedStroke</code> as one
\r
320 * of the static <code>int</code> values that define possible end cap
\r
323 public int getEndCap() {
\r
328 * Returns the line join style.
\r
329 * @return the line join style of the <code>AdvancedStroke</code> as one
\r
330 * of the static <code>int</code> values that define possible line
\r
333 public int getLineJoin() {
\r
338 * Returns the limit of miter joins.
\r
339 * @return the limit of miter joins of the <code>AdvancedStroke</code>.
\r
341 public float getMiterLimit() {
\r
346 * Returns the array representing the lengths of the dash segments.
\r
347 * Alternate entries in the array represent the user space lengths
\r
348 * of the opaque and transparent segments of the dashes.
\r
349 * As the pen moves along the outline of the <code>Shape</code>
\r
350 * to be stroked, the user space
\r
351 * distance that the pen travels is accumulated. The distance
\r
352 * value is used to index into the dash array.
\r
353 * The pen is opaque when its current cumulative distance maps
\r
354 * to an even element of the dash array and transparent otherwise.
\r
355 * @return the dash array.
\r
357 public float[] getDashArray() {
\r
358 if (dash == null) {
\r
362 return (float[]) dash.Clone();
\r
366 * Returns the current dash phase.
\r
367 * The dash phase is a distance specified in user coordinates that
\r
368 * represents an offset into the dashing pattern. In other words, the dash
\r
369 * phase defines the point in the dashing pattern that will correspond to
\r
370 * the beginning of the stroke.
\r
371 * @return the dash phase as a <code>float</code> value.
\r
373 public float getDashPhase() {
\r
378 * Returns the hashcode for this stroke.
\r
379 * @return a hash code for this stroke.
\r
381 public override int GetHashCode() {
\r
382 int hash = Float.floatToIntBits(width);
\r
383 hash = hash * 31 + join;
\r
384 hash = hash * 31 + cap;
\r
385 hash = hash * 31 + Float.floatToIntBits(miterlimit);
\r
386 if (dash != null) {
\r
387 hash = hash * 31 + Float.floatToIntBits(dash_phase);
\r
388 for (int i = 0; i < dash.Length; i++) {
\r
389 hash = hash * 31 + Float.floatToIntBits(dash[i]);
\r
396 * Returns true if this AdvancedStroke represents the same
\r
397 * stroking operation as the given argument.
\r
400 * Tests if a specified object is equal to this <code>AdvancedStroke</code>
\r
401 * by first testing if it is a <code>AdvancedStroke</code> and then comparing
\r
402 * its width, join, cap, miter limit, dash, and dash phase attributes with
\r
403 * those of this <code>AdvancedStroke</code>.
\r
404 * @param obj the specified object to compare to this
\r
405 * <code>AdvancedStroke</code>
\r
406 * @return <code>true</code> if the width, join, cap, miter limit, dash, and
\r
407 * dash phase are the same for both objects;
\r
408 * <code>false</code> otherwise.
\r
410 public override bool Equals(object obj) {
\r
411 if (!(obj is AdvancedStroke)) {
\r
415 AdvancedStroke bs = (AdvancedStroke) obj;
\r
416 if (width != bs.width) {
\r
420 if (join != bs.join) {
\r
424 if (cap != bs.cap) {
\r
428 if (miterlimit != bs.miterlimit) {
\r
432 if (dash != null) {
\r
433 if (dash_phase != bs.dash_phase) {
\r
437 if (!java.util.Arrays.equals(dash, bs.dash)) {
\r
441 else if (bs.dash != null) {
\r
448 public AffineTransform PenTransform {
\r
450 return _penTransform;
\r
453 _penTransform = value;
\r
457 public AffineTransform OutputTransform {
\r
459 return _outputTransform;
\r
462 _outputTransform = value;
\r
466 private static readonly int[] RasterizerCaps = {
\r
467 Rasterizer.BUTT, Rasterizer.ROUND, Rasterizer.SQUARE
\r
470 private static readonly int[] RasterizerCorners = {
\r
471 Rasterizer.MITER, Rasterizer.ROUND, Rasterizer.BEVEL
\r
474 #region FillAdapter
\r
476 private class FillAdapter : PathConsumer {
\r
480 public FillAdapter() {
\r
481 path = new GeneralPath(GeneralPath.WIND_NON_ZERO);
\r
484 public Shape getShape() {
\r
488 public void beginPath() {}
\r
490 public void beginSubpath(float x0, float y0) {
\r
495 path.moveTo(x0, y0);
\r
498 public void appendLine(float x1, float y1) {
\r
499 path.lineTo(x1, y1);
\r
502 public void appendQuadratic(float xm, float ym, float x1, float y1) {
\r
503 path.quadTo(xm, ym, x1, y1);
\r
506 public void appendCubic(float xm, float ym,
\r
507 float xn, float yn,
\r
508 float x1, float y1) {
\r
509 path.curveTo(xm, ym, xn, yn, x1, y1);
\r
512 public void closedSubpath() {
\r
516 public void endPath() {
\r
523 public void useProxy(FastPathProducer proxy) {
\r
524 proxy.sendTo(this);
\r
527 public long getCPathConsumer() {
\r
531 public void dispose() {
\r