2005-10-04 Zoltan Varga <vargaz@freemail.hu>
[mono.git] / mcs / class / System.Drawing / System.Drawing / AdvancedStroke.jvm.cs
1 using System;\r
2 using System.Drawing.Drawing2D;\r
3 using java.lang;\r
4 \r
5 using java.awt;\r
6 using java.awt.geom;\r
7 using sun.dc.path;\r
8 using sun.dc.pr;\r
9 \r
10 namespace System.Drawing {\r
11         internal class AdvancedStroke : Stroke {\r
12 \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
21 \r
22                 /**\r
23                  * Joins path segments by extending their outside edges until\r
24                  * they meet.\r
25                  */\r
26                 public const int JOIN_MITER = 0;\r
27 \r
28                 /**\r
29                  * Joins path segments by rounding off the corner at a radius\r
30                  * of half the line width.\r
31                  */\r
32                 public const int JOIN_ROUND = 1;\r
33 \r
34                 /**\r
35                  * Joins path segments by connecting the outer corners of their\r
36                  * wide outlines with a straight segment.\r
37                  */\r
38                 public const int JOIN_BEVEL = 2;\r
39 \r
40                 /**\r
41                  * Ends unclosed subpaths and dash segments with no added\r
42                  * decoration.\r
43                  */\r
44                 public const int CAP_BUTT = 0;\r
45 \r
46                 /**\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
49                  * of the pen.\r
50                  */\r
51                 public const int CAP_ROUND = 1;\r
52 \r
53                 /**\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
57                  */\r
58                 public const int CAP_SQUARE = 2;\r
59 \r
60                 float width;\r
61 \r
62                 int join;\r
63                 int cap;\r
64                 float miterlimit;\r
65 \r
66                 float[] dash;\r
67                 float dash_phase;\r
68 \r
69                 AffineTransform _penTransform;\r
70                 AffineTransform _outputTransform;\r
71                 bool _fitPen;\r
72 \r
73                 /**\r
74                  * Constructs a new <code>AdvancedStroke</code> with the specified\r
75                  * attributes.\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
80                  *         hint setting.\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
99                  */\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
105                         }\r
106                         if (cap != CAP_BUTT && cap != CAP_ROUND && cap != CAP_SQUARE) {\r
107                                 throw new IllegalArgumentException("illegal end cap value");\r
108                         }\r
109                         if (join == JOIN_MITER) {\r
110                                 if (miterlimit < 1.0f) {\r
111                                         throw new IllegalArgumentException("miter limit < 1");\r
112                                 }\r
113                         } else if (join != JOIN_ROUND && join != JOIN_BEVEL) {\r
114                                 throw new IllegalArgumentException("illegal line join value");\r
115                         }\r
116                         if (dash != null) {\r
117                                 if (dash_phase < 0.0f) {\r
118                                         throw new IllegalArgumentException("negative dash phase");\r
119                                 }\r
120                                 bool allzero = true;\r
121                                 for (int i = 0; i < dash.Length; i++) {\r
122                                         float d = dash[i];\r
123                                         if (d > 0.0) {\r
124                                                 allzero = false;\r
125                                         } else if (d < 0.0) {\r
126                                                 throw new IllegalArgumentException("negative dash length");\r
127                                         }\r
128                                 }\r
129                                 if (allzero) {\r
130                                         throw new IllegalArgumentException("dash lengths all zero");\r
131                                 }\r
132                         }\r
133                         this.width      = width;\r
134                         this.cap        = cap;\r
135                         this.join       = join;\r
136                         this.miterlimit = miterlimit;\r
137                         if (dash != null) {\r
138                                 this.dash = (float []) dash.Clone();\r
139                         }\r
140                         this.dash_phase = dash_phase;\r
141                         this._penTransform = penTransform;\r
142                         this._outputTransform = outputTransform;\r
143                         this._fitPen = fitPen;\r
144                 }\r
145 \r
146                 /**\r
147                  * Constructs a solid <code>AdvancedStroke</code> with the specified \r
148                  * attributes.\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
160                  */\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
163                 }\r
164 \r
165                 /**\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
178                  */\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
181                 }\r
182 \r
183                 /**\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
186                  * styles.\r
187                  * @param width the width of the <code>AdvancedStroke</code>\r
188                  * @throws IllegalArgumentException if <code>width</code> is negative\r
189                  */\r
190                 public AdvancedStroke(float width) :\r
191                         this(width, CAP_SQUARE, JOIN_MITER, 10.0f, null, 0.0f, null, null, false) {\r
192                 }\r
193 \r
194                 /**\r
195                  * Constructs a new <code>AdvancedStroke</code> with defaults for all \r
196                  * attributes.\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
199                  */\r
200                 public AdvancedStroke() :\r
201                         this(1.0f, CAP_SQUARE, JOIN_MITER, 10.0f, null, 0.0f, null, null, false) {\r
202                 }\r
203 \r
204 \r
205                 /**\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
210                  */\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
215 \r
216                         stroker.setPenDiameter(width);\r
217                         if (_fitPen)\r
218                                 stroker.setPenFitting(PenUnits, MinPenUnitsAA);\r
219 \r
220                         float[] t4 = null;\r
221                         if (PenTransform != null && !PenTransform.isIdentity()) {\r
222                                 t4 = new float[]{\r
223                                         (float)PenTransform.getScaleX(), (float)PenTransform.getShearY(), \r
224                                         (float)PenTransform.getShearX(), (float)PenTransform.getScaleY()\r
225                                 };\r
226                         }\r
227 \r
228                         float[] t6 = null;\r
229                         if (OutputTransform != null && !OutputTransform.isIdentity()) {\r
230                                 t6 = new float[] {\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
234                                 };\r
235                         }\r
236 \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
245                                 consumer = dasher;\r
246                         } else {\r
247                                 consumer = stroker;\r
248                         }\r
249 \r
250                         PathIterator pi = s.getPathIterator(null);\r
251 \r
252                         try {\r
253                                 consumer.beginPath();\r
254                                 bool pathClosed = false;\r
255                                 float mx = 0.0f;\r
256                                 float my = 0.0f;\r
257                                 float[] point  = new float[6];\r
258 \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
266                                                 }\r
267                                         }\r
268                                         switch ((GraphicsPath.JPI)type) {\r
269                                                 case GraphicsPath.JPI.SEG_MOVETO:\r
270                                                         mx = point[0];\r
271                                                         my = point[1];\r
272                                                         consumer.beginSubpath(point[0], point[1]);\r
273                                                         break;\r
274                                                 case GraphicsPath.JPI.SEG_LINETO:\r
275                                                         consumer.appendLine(point[0], point[1]);\r
276                                                         break;\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
281                                                         break;\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
287                                                         break;\r
288                                                 case GraphicsPath.JPI.SEG_CLOSE:\r
289                                                         consumer.closedSubpath();\r
290                                                         pathClosed = true;\r
291                                                         break;\r
292                                         }\r
293                                         pi.next();\r
294                                 }\r
295 \r
296                                 consumer.endPath();\r
297                         } catch (PathException e) {\r
298                                 throw new InternalError("Unable to Stroke shape ("+\r
299                                         e.Message+")");\r
300                         }\r
301 \r
302                         return filler.getShape();\r
303                 }\r
304 \r
305                 /**\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
311                  * @see Graphics2D\r
312                  */\r
313                 public float getLineWidth() {\r
314                         return width;\r
315                 }\r
316 \r
317                 /**\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
321                  * styles.\r
322                  */\r
323                 public int getEndCap() {\r
324                         return cap;\r
325                 }\r
326 \r
327                 /**\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
331                  * join styles.\r
332                  */\r
333                 public int getLineJoin() {\r
334                         return join;\r
335                 }\r
336 \r
337                 /**\r
338                  * Returns the limit of miter joins.\r
339                  * @return the limit of miter joins of the <code>AdvancedStroke</code>.\r
340                  */\r
341                 public float getMiterLimit() {\r
342                         return miterlimit;\r
343                 }\r
344 \r
345                 /**\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
356                  */\r
357                 public float[] getDashArray() {\r
358                         if (dash == null) {\r
359                                 return null;\r
360                         }\r
361 \r
362                         return (float[]) dash.Clone();\r
363                 }\r
364 \r
365                 /**\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
372                  */\r
373                 public float getDashPhase() {\r
374                         return dash_phase;\r
375                 }\r
376 \r
377                 /**\r
378                  * Returns the hashcode for this stroke.\r
379                  * @return      a hash code for this stroke.\r
380                  */\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
390                                 }\r
391                         }\r
392                         return hash;\r
393                 }\r
394 \r
395                 /**\r
396                  * Returns true if this AdvancedStroke represents the same\r
397                  * stroking operation as the given argument.\r
398                  */\r
399                 /**\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
409                  */\r
410                 public override bool Equals(object obj) {\r
411                         if (!(obj is AdvancedStroke)) {\r
412                                 return false;\r
413                         }\r
414 \r
415                         AdvancedStroke bs = (AdvancedStroke) obj;\r
416                         if (width != bs.width) {\r
417                                 return false;\r
418                         }\r
419 \r
420                         if (join != bs.join) {\r
421                                 return false;\r
422                         }\r
423 \r
424                         if (cap != bs.cap) {\r
425                                 return false;\r
426                         }\r
427 \r
428                         if (miterlimit != bs.miterlimit) {\r
429                                 return false;\r
430                         }\r
431 \r
432                         if (dash != null) {\r
433                                 if (dash_phase != bs.dash_phase) {\r
434                                         return false;\r
435                                 }\r
436 \r
437                                 if (!java.util.Arrays.equals(dash, bs.dash)) {\r
438                                         return false;\r
439                                 }\r
440                         }\r
441                         else if (bs.dash != null) {\r
442                                 return false;\r
443                         }\r
444 \r
445                         return true;\r
446                 }\r
447 \r
448                 public AffineTransform PenTransform { \r
449                         get{\r
450                                 return _penTransform;\r
451                         }\r
452                         set{\r
453                                 _penTransform = value;\r
454                         }\r
455                 }\r
456 \r
457                 public AffineTransform OutputTransform {\r
458                         get {\r
459                                 return _outputTransform;\r
460                         }\r
461                         set {\r
462                                 _outputTransform = value;\r
463                         }\r
464                 }\r
465 \r
466                 private static readonly int[] RasterizerCaps = {\r
467                                                                                                                    Rasterizer.BUTT, Rasterizer.ROUND, Rasterizer.SQUARE\r
468                                                                                                            };\r
469 \r
470                 private static readonly int[] RasterizerCorners = {\r
471                                                                                                                           Rasterizer.MITER, Rasterizer.ROUND, Rasterizer.BEVEL\r
472                                                                                                                   };\r
473 \r
474                 #region FillAdapter\r
475 \r
476                 private class FillAdapter : PathConsumer {\r
477                         bool closed;\r
478                         GeneralPath path;\r
479 \r
480                         public FillAdapter() {\r
481                                 path = new GeneralPath(GeneralPath.WIND_NON_ZERO);\r
482                         }\r
483 \r
484                         public Shape getShape() {\r
485                                 return path;\r
486                         }\r
487 \r
488                         public void beginPath() {}\r
489 \r
490                         public void beginSubpath(float x0, float y0) {\r
491                                 if (closed) {\r
492                                         path.closePath();\r
493                                         closed = false;\r
494                                 }\r
495                                 path.moveTo(x0, y0);\r
496                         }\r
497 \r
498                         public void appendLine(float x1, float y1) {\r
499                                 path.lineTo(x1, y1);\r
500                         }\r
501 \r
502                         public void appendQuadratic(float xm, float ym, float x1, float y1) {\r
503                                 path.quadTo(xm, ym, x1, y1);\r
504                         }\r
505 \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
510                         }\r
511 \r
512                         public void closedSubpath() {\r
513                                 closed = true;\r
514                         }\r
515 \r
516                         public void endPath() {\r
517                                 if (closed) {\r
518                                         path.closePath();\r
519                                         closed = false;\r
520                                 }\r
521                         }\r
522 \r
523                         public void useProxy(FastPathProducer proxy) {\r
524                                 proxy.sendTo(this);\r
525                         }\r
526 \r
527                         public long getCPathConsumer() {\r
528                                 return 0;\r
529                         }\r
530 \r
531                         public void dispose() {\r
532                         }\r
533                 }\r
534 \r
535                 #endregion\r
536         }\r
537 }