2 using System.Drawing.Drawing2D;
\r
10 namespace System.Drawing {
\r
12 internal enum PenFit {
\r
18 internal class AdvancedStroke : Stroke {
\r
20 public const float PenUnits = 0.01f;
21 public const int MinPenUnits = 100;
22 public const int MinPenUnitsAA = 20;
23 public const float MinPenSizeAA = PenUnits * MinPenUnitsAA;
\r
24 public const double MinPenSizeAASquared = (MinPenSizeAA * MinPenSizeAA);
25 public const double MinPenSizeSquared = 1.000000001;
\r
26 public const double MinPenSizeNorm = 1.5;
\r
27 public const double MinPenSizeSquaredNorm = (MinPenSizeNorm * MinPenSizeNorm);
\r
30 * Joins path segments by extending their outside edges until
\r
33 public const int JOIN_MITER = 0;
\r
36 * Joins path segments by rounding off the corner at a radius
\r
37 * of half the line width.
\r
39 public const int JOIN_ROUND = 1;
\r
42 * Joins path segments by connecting the outer corners of their
\r
43 * wide outlines with a straight segment.
\r
45 public const int JOIN_BEVEL = 2;
\r
48 * Ends unclosed subpaths and dash segments with no added
\r
51 public const int CAP_BUTT = 0;
\r
54 * Ends unclosed subpaths and dash segments with a round
\r
55 * decoration that has a radius equal to half of the width
\r
58 public const int CAP_ROUND = 1;
\r
61 * Ends unclosed subpaths and dash segments with a square
\r
62 * projection that extends beyond the end of the segment
\r
63 * to a distance equal to half of the line width.
\r
65 public const int CAP_SQUARE = 2;
\r
76 AffineTransform _penTransform;
\r
77 AffineTransform _outputTransform;
\r
81 * Constructs a new <code>AdvancedStroke</code> with the specified
\r
83 * @param width the width of this <code>AdvancedStroke</code>. The
\r
84 * width must be greater than or equal to 0.0f. If width is
\r
85 * set to 0.0f, the stroke is rendered as the thinnest
\r
86 * possible line for the target device and the antialias
\r
88 * @param cap the decoration of the ends of a <code>AdvancedStroke</code>
\r
89 * @param join the decoration applied where path segments meet
\r
90 * @param miterlimit the limit to trim the miter join. The miterlimit
\r
91 * must be greater than or equal to 1.0f.
\r
92 * @param dash the array representing the dashing pattern
\r
93 * @param dash_phase the offset to start the dashing pattern
\r
94 * @throws IllegalArgumentException if <code>width</code> is negative
\r
95 * @throws IllegalArgumentException if <code>cap</code> is not either
\r
96 * CAP_BUTT, CAP_ROUND or CAP_SQUARE
\r
97 * @throws IllegalArgumentException if <code>miterlimit</code> is less
\r
98 * than 1 and <code>join</code> is JOIN_MITER
\r
99 * @throws IllegalArgumentException if <code>join</code> is not
\r
100 * either JOIN_ROUND, JOIN_BEVEL, or JOIN_MITER
\r
101 * @throws IllegalArgumentException if <code>dash_phase</code>
\r
102 * is negative and <code>dash</code> is not <code>null</code>
\r
103 * @throws IllegalArgumentException if the length of
\r
104 * <code>dash</code> is zero
\r
105 * @throws IllegalArgumentException if dash lengths are all zero.
\r
107 public AdvancedStroke(float width, int cap, int join, float miterlimit,
\r
108 float[] dash, float dash_phase, AffineTransform penTransform,
\r
109 AffineTransform outputTransform, PenFit penFit) {
\r
110 if (width < 0.0f) {
\r
111 throw new IllegalArgumentException("negative width");
\r
113 if (cap != CAP_BUTT && cap != CAP_ROUND && cap != CAP_SQUARE) {
\r
114 throw new IllegalArgumentException("illegal end cap value");
\r
116 if (join == JOIN_MITER) {
\r
117 if (miterlimit < 1.0f) {
\r
118 throw new IllegalArgumentException("miter limit < 1");
\r
120 } else if (join != JOIN_ROUND && join != JOIN_BEVEL) {
\r
121 throw new IllegalArgumentException("illegal line join value");
\r
123 if (dash != null) {
\r
124 if (dash_phase < 0.0f) {
\r
125 throw new IllegalArgumentException("negative dash phase");
\r
127 bool allzero = true;
\r
128 for (int i = 0; i < dash.Length; i++) {
\r
132 } else if (d < 0.0) {
\r
133 throw new IllegalArgumentException("negative dash length");
\r
137 throw new IllegalArgumentException("dash lengths all zero");
\r
140 this.width = width;
\r
143 this.miterlimit = miterlimit;
\r
144 if (dash != null) {
\r
145 this.dash = (float []) dash.Clone();
\r
147 this.dash_phase = dash_phase;
\r
148 this._penTransform = penTransform;
\r
149 this._outputTransform = outputTransform;
\r
150 this._penFit = penFit;
\r
154 * Constructs a solid <code>AdvancedStroke</code> with the specified
\r
156 * @param width the width of the <code>AdvancedStroke</code>
\r
157 * @param cap the decoration of the ends of a <code>AdvancedStroke</code>
\r
158 * @param join the decoration applied where path segments meet
\r
159 * @param miterlimit the limit to trim the miter join
\r
160 * @throws IllegalArgumentException if <code>width</code> is negative
\r
161 * @throws IllegalArgumentException if <code>cap</code> is not either
\r
162 * CAP_BUTT, CAP_ROUND or CAP_SQUARE
\r
163 * @throws IllegalArgumentException if <code>miterlimit</code> is less
\r
164 * than 1 and <code>join</code> is JOIN_MITER
\r
165 * @throws IllegalArgumentException if <code>join</code> is not
\r
166 * either JOIN_ROUND, JOIN_BEVEL, or JOIN_MITER
\r
168 public AdvancedStroke(float width, int cap, int join, float miterlimit) :
\r
169 this(width, cap, join, miterlimit, null, 0.0f, null, null, PenFit.NotThin) {
\r
173 * Constructs a solid <code>AdvancedStroke</code> with the specified
\r
174 * attributes. The <code>miterlimit</code> parameter is
\r
175 * unnecessary in cases where the default is allowable or the
\r
176 * line joins are not specified as JOIN_MITER.
\r
177 * @param width the width of the <code>AdvancedStroke</code>
\r
178 * @param cap the decoration of the ends of a <code>AdvancedStroke</code>
\r
179 * @param join the decoration applied where path segments meet
\r
180 * @throws IllegalArgumentException if <code>width</code> is negative
\r
181 * @throws IllegalArgumentException if <code>cap</code> is not either
\r
182 * CAP_BUTT, CAP_ROUND or CAP_SQUARE
\r
183 * @throws IllegalArgumentException if <code>join</code> is not
\r
184 * either JOIN_ROUND, JOIN_BEVEL, or JOIN_MITER
\r
186 public AdvancedStroke(float width, int cap, int join) :
\r
187 this(width, cap, join, 10.0f, null, 0.0f, null, null, PenFit.NotThin) {
\r
191 * Constructs a solid <code>AdvancedStroke</code> with the specified
\r
192 * line width and with default values for the cap and join
\r
194 * @param width the width of the <code>AdvancedStroke</code>
\r
195 * @throws IllegalArgumentException if <code>width</code> is negative
\r
197 public AdvancedStroke(float width) :
\r
198 this(width, CAP_SQUARE, JOIN_MITER, 10.0f, null, 0.0f, null, null, PenFit.NotThin) {
\r
202 * Constructs a new <code>AdvancedStroke</code> with defaults for all
\r
204 * The default attributes are a solid line of width 1.0, CAP_SQUARE,
\r
205 * JOIN_MITER, a miter limit of 10.0.
\r
207 public AdvancedStroke() :
\r
208 this(1.0f, CAP_SQUARE, JOIN_MITER, 10.0f, null, 0.0f, null, null, PenFit.NotThin) {
\r
213 * Returns a <code>Shape</code> whose interior defines the
\r
214 * stroked outline of a specified <code>Shape</code>.
\r
215 * @param s the <code>Shape</code> boundary be stroked
\r
216 * @return the <code>Shape</code> of the stroked outline.
\r
218 public Shape createStrokedShape(Shape s) {
\r
219 FillAdapter filler = new FillAdapter();
\r
220 PathStroker stroker = new PathStroker(filler);
\r
221 PathConsumer consumer;
\r
223 stroker.setPenDiameter(width);
\r
226 stroker.setPenFitting(PenUnits, MinPenUnits);
\r
228 case PenFit.ThinAntiAlias:
\r
229 stroker.setPenFitting(PenUnits, MinPenUnitsAA);
\r
234 if (PenTransform != null && !PenTransform.isIdentity() && (PenTransform.getDeterminant() > 1e-25)) {
\r
236 (float)PenTransform.getScaleX(), (float)PenTransform.getShearY(),
\r
237 (float)PenTransform.getShearX(), (float)PenTransform.getScaleY()
\r
242 if (OutputTransform != null && !OutputTransform.isIdentity()) {
\r
244 (float)OutputTransform.getScaleX(), (float)OutputTransform.getShearY(),
\r
245 (float)OutputTransform.getShearX(), (float)OutputTransform.getScaleY(),
\r
246 (float)OutputTransform.getTranslateX(), (float)OutputTransform.getTranslateY()
\r
250 stroker.setPenT4(t4);
\r
251 stroker.setOutputT6(t6);
\r
252 stroker.setCaps(RasterizerCaps[cap]);
\r
253 stroker.setCorners(RasterizerCorners[join], miterlimit);
\r
254 if (dash != null) {
\r
255 PathDasher dasher = new PathDasher(stroker);
\r
256 dasher.setDash(dash, dash_phase);
\r
257 dasher.setDashT4(t4);
\r
260 consumer = stroker;
\r
263 PathIterator pi = s.getPathIterator(null);
\r
266 consumer.beginPath();
\r
267 bool pathClosed = false;
\r
270 float[] point = new float[6];
\r
272 while (!pi.isDone()) {
\r
273 int type = pi.currentSegment(point);
\r
274 if (pathClosed == true) {
\r
275 pathClosed = false;
\r
276 if (type != PathIterator__Finals.SEG_MOVETO) {
\r
277 // Force current point back to last moveto point
\r
278 consumer.beginSubpath(mx, my);
\r
281 switch ((GraphicsPath.JPI)type) {
\r
282 case GraphicsPath.JPI.SEG_MOVETO:
\r
285 consumer.beginSubpath(point[0], point[1]);
\r
287 case GraphicsPath.JPI.SEG_LINETO:
\r
288 consumer.appendLine(point[0], point[1]);
\r
290 case GraphicsPath.JPI.SEG_QUADTO:
\r
291 // Quadratic curves take two points
\r
292 consumer.appendQuadratic(point[0], point[1],
\r
293 point[2], point[3]);
\r
295 case GraphicsPath.JPI.SEG_CUBICTO:
\r
296 // Cubic curves take three points
\r
297 consumer.appendCubic(point[0], point[1],
\r
298 point[2], point[3],
\r
299 point[4], point[5]);
\r
301 case GraphicsPath.JPI.SEG_CLOSE:
\r
302 consumer.closedSubpath();
\r
309 consumer.endPath();
\r
310 } catch (PathException e) {
\r
311 throw new InternalError("Unable to Stroke shape ("+
\r
315 return filler.getShape();
\r
319 * Returns the line width. Line width is represented in user space,
\r
320 * which is the default-coordinate space used by Java 2D. See the
\r
321 * <code>Graphics2D</code> class comments for more information on
\r
322 * the user space coordinate system.
\r
323 * @return the line width of this <code>AdvancedStroke</code>.
\r
326 public float getLineWidth() {
\r
331 * Returns the end cap style.
\r
332 * @return the end cap style of this <code>AdvancedStroke</code> as one
\r
333 * of the static <code>int</code> values that define possible end cap
\r
336 public int getEndCap() {
\r
341 * Returns the line join style.
\r
342 * @return the line join style of the <code>AdvancedStroke</code> as one
\r
343 * of the static <code>int</code> values that define possible line
\r
346 public int getLineJoin() {
\r
351 * Returns the limit of miter joins.
\r
352 * @return the limit of miter joins of the <code>AdvancedStroke</code>.
\r
354 public float getMiterLimit() {
\r
359 * Returns the array representing the lengths of the dash segments.
\r
360 * Alternate entries in the array represent the user space lengths
\r
361 * of the opaque and transparent segments of the dashes.
\r
362 * As the pen moves along the outline of the <code>Shape</code>
\r
363 * to be stroked, the user space
\r
364 * distance that the pen travels is accumulated. The distance
\r
365 * value is used to index into the dash array.
\r
366 * The pen is opaque when its current cumulative distance maps
\r
367 * to an even element of the dash array and transparent otherwise.
\r
368 * @return the dash array.
\r
370 public float[] getDashArray() {
\r
371 if (dash == null) {
\r
375 return (float[]) dash.Clone();
\r
379 * Returns the current dash phase.
\r
380 * The dash phase is a distance specified in user coordinates that
\r
381 * represents an offset into the dashing pattern. In other words, the dash
\r
382 * phase defines the point in the dashing pattern that will correspond to
\r
383 * the beginning of the stroke.
\r
384 * @return the dash phase as a <code>float</code> value.
\r
386 public float getDashPhase() {
\r
391 * Returns the hashcode for this stroke.
\r
392 * @return a hash code for this stroke.
\r
394 public override int GetHashCode() {
\r
395 int hash = Float.floatToIntBits(width);
\r
396 hash = hash * 31 + join;
\r
397 hash = hash * 31 + cap;
\r
398 hash = hash * 31 + Float.floatToIntBits(miterlimit);
\r
399 if (dash != null) {
\r
400 hash = hash * 31 + Float.floatToIntBits(dash_phase);
\r
401 for (int i = 0; i < dash.Length; i++) {
\r
402 hash = hash * 31 + Float.floatToIntBits(dash[i]);
\r
409 * Returns true if this AdvancedStroke represents the same
\r
410 * stroking operation as the given argument.
\r
413 * Tests if a specified object is equal to this <code>AdvancedStroke</code>
\r
414 * by first testing if it is a <code>AdvancedStroke</code> and then comparing
\r
415 * its width, join, cap, miter limit, dash, and dash phase attributes with
\r
416 * those of this <code>AdvancedStroke</code>.
\r
417 * @param obj the specified object to compare to this
\r
418 * <code>AdvancedStroke</code>
\r
419 * @return <code>true</code> if the width, join, cap, miter limit, dash, and
\r
420 * dash phase are the same for both objects;
\r
421 * <code>false</code> otherwise.
\r
423 public override bool Equals(object obj) {
\r
424 if (!(obj is AdvancedStroke)) {
\r
428 AdvancedStroke bs = (AdvancedStroke) obj;
\r
429 if (width != bs.width) {
\r
433 if (join != bs.join) {
\r
437 if (cap != bs.cap) {
\r
441 if (miterlimit != bs.miterlimit) {
\r
445 if (dash != null) {
\r
446 if (dash_phase != bs.dash_phase) {
\r
450 if (!java.util.Arrays.equals(dash, bs.dash)) {
\r
454 else if (bs.dash != null) {
\r
461 public AffineTransform PenTransform {
\r
463 return _penTransform;
\r
466 _penTransform = value;
\r
470 public AffineTransform OutputTransform {
\r
472 return _outputTransform;
\r
475 _outputTransform = value;
\r
479 private static readonly int[] RasterizerCaps = {
\r
480 Rasterizer.BUTT, Rasterizer.ROUND, Rasterizer.SQUARE
\r
483 private static readonly int[] RasterizerCorners = {
\r
484 Rasterizer.MITER, Rasterizer.ROUND, Rasterizer.BEVEL
\r
487 #region FillAdapter
\r
489 private class FillAdapter : PathConsumer {
\r
493 public FillAdapter() {
\r
494 path = new GeneralPath(GeneralPath.WIND_NON_ZERO);
\r
497 public Shape getShape() {
\r
501 public void beginPath() {}
\r
503 public void beginSubpath(float x0, float y0) {
\r
508 path.moveTo(x0, y0);
\r
511 public void appendLine(float x1, float y1) {
\r
512 path.lineTo(x1, y1);
\r
515 public void appendQuadratic(float xm, float ym, float x1, float y1) {
\r
516 path.quadTo(xm, ym, x1, y1);
\r
519 public void appendCubic(float xm, float ym,
\r
520 float xn, float yn,
\r
521 float x1, float y1) {
\r
522 path.curveTo(xm, ym, xn, yn, x1, y1);
\r
525 public void closedSubpath() {
\r
529 public void endPath() {
\r
536 public void useProxy(FastPathProducer proxy) {
\r
537 proxy.sendTo(this);
\r
540 public long getCPathConsumer() {
\r
544 public void dispose() {
\r