6c1b99f5289d327c97885c401e1e8ecdf5469dd4
[mono.git] / mcs / class / System.Drawing / gdiplus / graphics-path.c
1 /*
2  * graphics-path.c
3  * 
4  * Author: Duncan Mak (duncan@ximian.com)
5  *
6  * Copyright (C) 2003, Novell Inc.
7  *
8  */
9  
10 #include <math.h>
11 #include "gdip.h"
12 #include "graphics-path.h"
13
14 static GArray *
15 array_to_g_array (const GpPointF *pt, int length)
16 {
17         GArray *p = g_array_sized_new (FALSE, TRUE, sizeof (GpPointF), length);
18         g_array_append_vals (p, pt, length);
19         return p;
20 }
21
22 static GpPointF *
23 g_array_to_array (GArray *p)
24 {
25         int length = p->len;
26         GpPointF *pts = (GpPointF *) GdipAlloc (sizeof (GpPointF) * length);
27
28         memcpy (pts, p->data, p->len * sizeof (GpPointF));        
29         
30         return pts;
31 }
32
33 static byte *
34 g_byte_array_to_array (GByteArray *p)
35 {
36         int length = p->len;
37         byte *types = (byte *) GdipAlloc (sizeof (byte) * length);
38
39         memcpy (types, p->data, p->len * sizeof (byte));
40         
41         return types;
42 }
43
44 static GByteArray *
45 array_to_g_byte_array (const byte *types, int count)
46 {
47         GByteArray *p = g_byte_array_sized_new (count);
48         g_byte_array_append (p, types, count);
49         return p;
50 }
51
52 static GpPoint *
53 float_to_int (const GpPointF *pts, int count)
54 {
55         GpPoint *p = (GpPoint *) GdipAlloc (sizeof (GpPoint) * count);
56         GpPointF *tmp = (GpPointF *) pts;
57         int i;
58         
59         for (i = 0; i < count; i++, p++, tmp++) {
60                 p->X = (int) tmp->X;
61                 p->Y = (int) tmp->Y;
62         }
63         
64         return p;
65 }
66
67 static GpPointF *
68 int_to_float (const GpPoint *pts, int count)
69 {
70         GpPointF *p = (GpPointF *) GdipAlloc (sizeof (GpPointF) * count);
71         GpPoint *tmp = (GpPoint *) pts;
72         int i;
73
74         for (i = 0; i < count; i++, p++, tmp++) {
75                 p->X = (float) tmp->X;
76                 p->Y = (float) tmp->Y;
77         }
78
79         return p;
80 }
81
82 static void
83 append (GpPath *path, float x, float y, GpPathPointType type)
84 {
85         byte t = (byte) type;
86         GpPointF pt = { x, y };
87         g_array_append_val (path->points, pt);
88         g_byte_array_append (path->types, &t, 1);
89 }
90
91 static void
92 append_point (GpPath *path, GpPointF pt, GpPathPointType type)
93 {
94         byte t = (byte) type;
95         g_array_append_val (path->points, pt);
96         g_byte_array_append (path->types, &t, 1);
97 }
98
99 static void
100 append_bezier (GpPath *path, float x1, float y1, float x2, float y2, float x3, float y3)
101 {
102         append (path, x1, y1, PathPointTypeBezier3);
103         append (path, x2, y2, PathPointTypeBezier3);
104         append (path, x3, y3, PathPointTypeBezier3);
105 }
106
107 GpStatus
108 GdipCreatePath (GpFillMode brushMode, GpPath **path)
109 {
110         *path = (GpPath *) GdipAlloc (sizeof (GpPath));
111
112         (*path)->fill_mode = brushMode;
113         (*path)->points = NULL;
114         (*path)->types = NULL;
115         (*path)->count = 0;
116
117         return Ok;
118 }
119
120 GpStatus
121 GdipCreatePath2 (const GpPointF *points, const byte *types,
122                 int count, GpFillMode fillMode, GpPath **path)
123 {
124         GArray *pts = array_to_g_array (points, count);
125         GByteArray *t = array_to_g_byte_array (types, count);
126         
127         *path = (GpPath *) GdipAlloc (sizeof (GpPath));
128         (*path)->fill_mode = fillMode;
129         (*path)->count = count;
130         (*path)->points = pts;
131         (*path)->types = t;
132         
133         return Ok;
134 }
135
136 GpStatus
137 GdipClonePath (GpPath *path, GpPath **clonePath)
138 {
139         *clonePath = (GpPath *) GdipAlloc (sizeof (GpPath));
140         (*clonePath)->fill_mode = path->fill_mode;
141         (*clonePath)->count = path->count;
142         (*clonePath)->points = path->points;
143         (*clonePath)->types = path->types;
144         
145         return Ok;
146 }
147
148 GpStatus
149 GdipDeletePath (GpPath *path)
150 {
151         if (path->count != 0) {
152                 if (path->points != NULL)
153                         g_array_free (path->points, TRUE);
154                 
155                 if (path->types != NULL)
156                         g_byte_array_free (path->types, TRUE);
157         }
158         
159         GdipFree (path);
160         return Ok;
161 }
162
163 GpStatus
164 GdipResetPath (GpPath *path)
165 {
166         path->points = NULL;
167         path->types = NULL;
168         path->count = 0;
169         
170         return Ok;
171 }
172
173 GpStatus
174 GdipGetPointCount (GpPath *path, int *count)
175 {
176         *count = path->count;
177         return Ok;
178 }
179
180 GpStatus
181 GdipGetPathTypes (GpPath *path, byte *types, int *count)
182 {
183         *count = path->count;
184         types = g_byte_array_to_array (path->types);
185         
186         return Ok;
187 }
188
189 GpStatus
190 GdipGetPathPoints (GpPath *path, GpPointF *points, int *count)
191 {
192         *count = path->count;
193         points = g_array_to_array (path->points);
194         
195         return Ok;
196 }
197
198 GpStatus
199 GdipGetPathPointsI (GpPath *path, GpPoint *points, int *count)
200 {
201         *count = path->count;
202         PointF *tmp = g_array_to_array (path->points);
203
204         points = float_to_int (tmp, path->count);
205
206         GdipFree (tmp);
207         
208         return Ok;
209 }
210
211 GpStatus
212 GdipGetPathFillMode (GpPath *path, GpFillMode *fillmode)
213 {
214         *fillmode = path->fill_mode;
215         
216         return Ok;
217 }
218
219 GpStatus
220 GdipSetPathFillMode (GpPath *path, GpFillMode fillmode)
221 {
222         path->fill_mode = fillmode;
223         
224         return Ok;
225 }
226
227 GpStatus
228 GdipGetPathData (GpPath *path, GpPathData *pathData)
229 {
230         pathData->Count = path->count;
231         pathData->Points = g_array_to_array (path->points);
232         pathData->Types = g_byte_array_to_array (path->types);
233         
234         return Ok;
235 }
236
237 GpStatus
238 GdipStartPathFigure (GpPath *path)
239 {
240         return NotImplemented;
241 }
242
243 GpStatus
244 GdipClosePathFigure (GpPath *path)
245 {
246         return NotImplemented;
247 }
248
249 GpStatus
250 GdipClosePathFigures (GpPath *path)
251 {
252         return NotImplemented;
253 }
254
255 GpStatus
256 GdipSetPathMarker (GpPath *path)
257 {
258         return NotImplemented;
259 }
260
261 GpStatus
262 GdipClearPathMarkers (GpPath *path)
263 {
264         return NotImplemented;
265 }
266
267 GpStatus
268 GdipReversePath (GpPath *path)
269 {
270         int length= path->count;
271         GByteArray *types = g_byte_array_sized_new (length);
272         GArray *points = g_array_sized_new (FALSE, TRUE, sizeof (GpPointF), length);
273         int i;
274         for (i = length; i > 0; i--) {
275                 byte t = g_array_index (path->types, byte, i);
276                 GpPointF pt = g_array_index (path->points, GpPointF, i);
277                 
278                 g_byte_array_append (types, &t, 1);
279                 g_array_append_val (points, pt);
280         }
281         path->points = points;
282         path->types = types;
283         
284         return Ok;
285 }
286
287 GpStatus
288 GdipGetPathLastPoint (GpPath *path, GpPointF *lastPoint)
289 {
290         *lastPoint = g_array_index (path->points, GpPointF, path->count);
291         return Ok;
292 }
293
294 GpStatus
295 GdipAddPathLine (GpPath *path, float x1, float y1, float x2, float y2)
296 {
297         PointF p1 = { x1, y1 };
298         PointF p2 = { x2, y2 };        
299         append_point (path, p1, PathPointTypeStart);
300         append_point (path, p2, PathPointTypeLine);
301
302         return Ok;
303 }
304
305 GpStatus
306 GdipAddPathLine2 (GpPath *path, const GpPointF *points, int count)
307 {
308         int i;
309         GpPointF *tmp = (GpPointF *) points;
310
311         for (i = 0; i < count; i++, tmp++)
312                 append_point (path, *tmp, PathPointTypeLine);
313         
314         return Ok;
315 }
316
317 GpStatus
318 GdipAddPathArc (GpPath *path, float x, float y, 
319                 float width, float height, float startAngle, float sweepAngle)
320 {
321         float rx = width / 2;
322         float ry = height / 2;
323         
324         /* center */
325         int cx = x + rx;
326         int cy = y + ry;
327
328         /* angles in radians */        
329         float alpha = startAngle * PI / 180;
330         float beta = sweepAngle * PI / 180;
331
332         float delta = beta - alpha;
333         float bcp = 4.0 / 3 * (1 - cos (delta / 2)) / sin (delta /2);
334
335         float sin_alpha = sin (alpha);
336         float sin_beta = sin (beta);
337         float cos_alpha = cos (alpha);
338         float cos_beta = cos (beta);
339
340         append (path,
341                 cx + rx * cos_alpha,
342                 cy + ry * sin_alpha,
343                 PathPointTypeStart);
344
345         append_bezier (path, 
346                        cx + rx * (cos_alpha - bcp * sin_alpha),
347                        cy + ry * (sin_alpha + bcp * cos_alpha),
348                        cx + rx * (cos_beta  + bcp * sin_beta),
349                        cy + ry * (sin_beta  - bcp * cos_beta),
350                        cx + rx *  cos_beta,
351                        cy + ry *  sin_beta);
352
353         return Ok;
354 }
355
356 GpStatus
357 GdipAddPathBezier (GpPath *path, 
358         float x1, float y1, float x2, float y2, 
359         float x3, float y3, float x4, float y4)
360 {
361         append (path, x1, y1, PathPointTypeStart);
362         append_bezier (path, x2, y2, x3, y3, x4, y4);
363         
364         return Ok;
365 }
366
367 GpStatus
368 GdipAddPathBeziers (GpPath *path, const GpPointF *points, int count)
369 {
370         int i;
371         GpPointF *tmp = (GpPointF *) points;
372         
373         append_point (path, *tmp, PathPointTypeStart);
374         tmp++;
375
376         for (i = 1; i < count; i++, tmp++)
377                 append_point (path, *tmp, PathPointTypeBezier3);
378
379         return Ok;
380 }
381
382 GpStatus
383 GdipAddPathCurve (GpPath *path, const GpPointF *points, int count)
384 {
385         return NotImplemented;
386 }
387
388 GpStatus
389 GdipAddPathCurve2 (GpPath *path, const GpPointF *points, int count, float tension)
390 {
391         return NotImplemented;
392 }
393
394 GpStatus
395 GdipAddPathCurve3 (GpPath *path, const GpPointF *points, int count, 
396         int offset, int numberOfSegments, float tension)
397 {
398         return NotImplemented;
399 }
400
401 GpStatus
402 GdipAddPathClosedCurve (GpPath *path, const GpPointF *points, int count)
403 {
404         return GdipAddPathClosedCurve2 (path, points, count, 0.5);
405 }
406
407 GpStatus
408 GdipAddPathClosedCurve2 (GpPath *path, const GpPointF *points, int count, float tension)
409 {
410         return NotImplemented;
411 }
412
413 GpStatus
414 GdipAddPathRectangle (GpPath *path, float x, float y, float width, float height)
415 {
416         append (path, x, y, PathPointTypeLine);
417         append (path, x + width, y, PathPointTypeLine);
418         append (path, x + width, y + height, PathPointTypeLine);
419         append (path, x, y + height, PathPointTypeLine);
420         
421         return Ok;
422 }
423
424 GpStatus
425 GdipAddPathRectangles (GpPath *path, const GpRectF *rects, int count)
426 {
427         int i;
428         for (i = 0; i < count; i++, rects++) {
429                 float x = rects->left;
430                 float y = rects->top;
431                 float width = rects->right - rects->left;
432                 float height = rects->bottom - rects->top;
433                 GdipAddPathRectangle (path, x, y, width, height);
434         }
435         
436         return Ok;
437 }
438
439 GpStatus
440 GdipAddPathEllipse (GpPath *path, float x, float y, float width, float height)
441 {
442         float C1 = 0.552285;
443         double rx = width / 2;
444         double ry = height / 2;
445         double cx = x + rx;
446         double cy = y + ry;
447
448         /* origin */
449         append (path, cx + rx, cy, PathPointTypeStart);
450
451         /* quadrant I */
452         append_bezier (path, 
453                        cx + rx, cy - C1 * ry, 
454                        cx + C1 * rx, cy - ry, 
455                        cx, cy - ry);
456
457         /* quadrant II */
458         append_bezier (path,
459                        cx - C1 * rx, cy - ry, 
460                        cx - rx, cy - C1 * ry, 
461                        cx - rx, cy);
462
463         /* quadrant III */
464         append_bezier (path,
465                        cx - rx, cy + C1 * ry, 
466                        cx - C1 * rx, cy + ry, 
467                        cx, cy + ry);
468
469         /* quadrant IV */
470         append_bezier (path,
471                        cx + C1 * rx, cy + ry, 
472                        cx + rx, cy + C1 * ry, 
473                        cx + rx, cy);
474         
475         return Ok;
476 }
477
478 GpStatus
479 GdipAddPathPie (GpPath *path, float x, float y, float width, float height, float startAngle, float sweepAngle)
480 {
481         float rx = width / 2;
482         float ry = height / 2;
483         int cx = x + rx;
484         int cy = y + ry;
485
486         /* angles in radians */        
487         float alpha = startAngle * PI / 180;
488         float beta = sweepAngle * PI / 180;
489
490         float delta = beta - alpha;
491         float bcp = 4.0 / 3 * (1 - cos (delta / 2)) / sin (delta /2);
492
493         float sin_alpha = sin (alpha);
494         float sin_beta = sin (beta);
495         float cos_alpha = cos (alpha);
496         float cos_beta = cos (beta);
497
498         /* move to center */
499         append (path, cx, cy, PathPointTypeStart);
500         
501
502         /* draw pie edge */
503         append (path, cx + rx * cos_alpha, cy + ry * sin_alpha,
504                 PathPointTypeLine);
505
506         /* draw arc */
507         append_bezier (path,
508                        cx + rx * (cos_alpha - bcp * sin_alpha),
509                        cy + ry * (sin_alpha + bcp * cos_alpha),
510                        cx + rx * (cos_beta  + bcp * sin_beta),
511                        cy + ry * (sin_beta  - bcp * cos_beta),
512                        cx + rx *  cos_beta,
513                        cy + ry *  sin_beta);
514         
515         /* draw pie edge */
516         append (path, cx, cy, PathPointTypeLine);
517
518         return Ok;
519 }
520
521 GpStatus
522 GdipAddPathPolygon (GpPath *path, const GpPointF *points, int count)
523 {
524         int i;
525         GpPointF *tmp = (GpPointF *) points;
526         
527         append_point (path, *tmp, PathPointTypeStart);
528         tmp ++;
529
530         for (i = 1; i < count; i++, tmp++)
531                 append_point (path, *tmp, PathPointTypeLine);
532
533         return Ok;
534 }
535
536 GpStatus
537 GdipAddPathPath (GpPath *path, GpPath *addingPath, bool connect)
538 {
539         /* XXX:need to understand the connect argument */
540
541         return NotImplemented;
542 }
543
544 /* XXX: This one is really hard. They really translate a string into bezier points and what not */
545 /*
546  * GpStatus 
547  * GdipAddString (GpPath *path, const char *string, int length, 
548  *                const GpFontFamily *family, int style, float emSize, const GpRectF *layoutRect, const GpStringFormat *format)
549  * { 
550  *         return NotImplemented; 
551  * }
552  */
553
554 /*
555  * GpStatus
556  * GdipAddString (GpPath *path, const char *string, int length,
557  *                const GpFontFamily *family, int style, float emSize, const GpRect *layoutRect, const GpStringFormat *format)
558  * {
559  *          return NotImplemented;
560  * }
561  */
562
563 GpStatus
564 GdipAddPathLineI (GpPath *path, int x1, int y1, int x2, int y2)
565 {
566         append (path, x1, y1, PathPointTypeStart);
567         append (path, x2, y2, PathPointTypeLine);
568
569         return Ok;
570 }
571
572 GpStatus
573 GdipAddPathLine2I (GpPath* path, const GpPoint *points, int count)
574 {
575         int i;
576         GpPointF *tmp = int_to_float (points, count);
577         
578         append_point (path, *tmp, PathPointTypeStart);
579         tmp++;
580
581         for (i = 1; i < count; i++, tmp++)
582                 append_point (path, *tmp, PathPointTypeLine);
583
584         GdipFree (tmp);
585
586         return Ok;
587 }
588
589 GpStatus
590 GdipAddPathArcI (GpPath *path, int x, int y, int width, int height, float startAngle, float sweepAngle)
591 {
592         return GdipAddPathArc (path, x, y, width, height, startAngle, sweepAngle);
593 }
594
595 GpStatus
596 GdipAddPathBezierI (GpPath *path, int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4)
597 {
598         return GdipAddPathBezier (path, x1, y1, x2, y2, x3, y3, x4, y4);
599 }
600
601 GpStatus
602 GdipAddPathBeziersI (GpPath *path, const GpPoint *points, int count)
603 {
604         GpPointF *tmp = int_to_float (points, count);
605         Status s = GdipAddPathBeziers (path, tmp, count);
606
607         GdipFree (tmp);
608
609         return s;
610 }
611
612 GpStatus
613 GdipAddPathCurveI (GpPath *path, const GpPoint *points, int count)
614 {
615         return GdipAddPathCurve2I (path, points, count, 0.5);
616 }
617
618 GpStatus
619 GdipAddPathCurve2I (GpPath *path, const GpPoint *points, int count, float tension)
620 {
621         return NotImplemented;
622 }
623
624 GpStatus
625 GdipAddPathCurve3I (GpPath *path, const GpPoint *points, 
626                     int count, int offset, int numberOfSegments, float tension)
627 {
628         return NotImplemented;
629 }
630
631 GpStatus
632 GdipAddPathClosedCurveI (GpPath *path, const GpPoint *points, int count)
633 {
634         return GdipAddPathClosedCurve2I (path, points, count, 0.5);
635 }
636
637 GpStatus
638 GdipAddPathClosedCurve2I (GpPath *path, const GpPoint *points, int count, float tension)
639 {
640         return NotImplemented;
641 }
642
643 GpStatus
644 GdipAddPathRectangleI (GpPath *path, int x, int y, int width, int height)
645 {
646         return GdipAddPathRectangle (path, x, y, width, height);
647 }
648
649 GpStatus
650 GdipAddPathRectanglesI (GpPath *path, const GpRect *rects, int count)
651 {
652         int i;
653         for (i = 0; i < count; i++, rects++) {
654                 float x = (float) rects->left;
655                 float y = (float) rects->top;
656                 float width =  (float) (rects->right - rects->left);
657                 float height =  (float) (rects->bottom - rects->top);
658                 GdipAddPathRectangle (path, x, y, width, height);
659         }
660
661         return Ok;
662 }
663
664 GpStatus
665 GdipAddPathEllipseI (GpPath *path, int x, int y, int width, int height)
666 {
667         return GdipAddPathEllipse (path, x, y, width, height);
668 }
669
670 GpStatus
671 GdipAddPathPieI (GpPath *path, int x, int y, int width, int height, float startAngle, float sweepAngle)
672 {
673         return GdipAddPathPie (path, x, y, width, height, startAngle, sweepAngle);
674 }
675
676 GpStatus
677 GdipAddPathPolygonI (GpPath *path, const GpPoint *points, int count)
678 {
679         GpPointF *tmp = int_to_float (points, count);
680
681         Status s = GdipAddPathPolygon (path, tmp, count);
682
683         GdipFree (tmp);
684
685         return s;
686 }
687
688 GpStatus 
689 GdipFlattenPath (GpPath *path, GpMatrix *matrix, float flatness)
690 {
691         return NotImplemented;
692 }
693
694 GpStatus 
695 GdipWindingModeOutline (GpPath *path, GpMatrix *matrix, float flatness)
696 {
697         return NotImplemented;
698 }
699
700 GpStatus 
701 GdipWidenPath (GpPath *nativePath, GpPen *pen, GpMatrix *matrix, float flatness)
702 {
703         return NotImplemented;
704 }
705
706 GpStatus 
707 GdipWarpPath (GpPath *nativePath, GpMatrix *matrix, const GpPointF *points, int count, 
708                 float src, float srcy, float srcwidth, float srcheight, WarpMode warpMode, float flatness)
709 {
710         return NotImplemented;
711 }
712
713 GpStatus 
714 GdipTransformPath (GpPath* path, GpMatrix *matrix)
715 {
716         PointF *points = g_array_to_array (path->points);
717         Status s = GdipTransformMatrixPoints (matrix, points, path->count);
718
719         GdipFree (points);
720
721         return s;
722 }
723
724 GpStatus 
725 GdipGetPathWorldBounds (GpPath *path, GpRectF *bounds, const GpMatrix *matrix, const GpPen *pen)
726 {
727         return NotImplemented;
728 }
729
730 GpStatus 
731 GdipGetPathWorldBoundsI (GpPath *path, GpRect *bounds, const GpMatrix *matrix, const GpPen *pen)
732 {
733         return NotImplemented;
734 }
735
736 GpStatus 
737 GdipIsVisiblePathPoint (GpPath *path, float x, float y, GpGraphics *graphics, bool *result)
738 {
739         return NotImplemented;
740 }
741
742 GpStatus 
743 GdipIsVisiblePathPointI (GpPath *path, int x, int y, GpGraphics *graphics, bool *result)
744 {
745         return NotImplemented;
746 }
747
748 GpStatus 
749 GdipIsOutlineVisiblePathPoint (GpPath *path, float x, float y, GpGraphics *graphics, bool *result)
750 {
751         return NotImplemented;
752 }
753
754 GpStatus 
755 GdipIsOutlineVisiblePathPointI (GpPath *path, int x, int y, GpGraphics *graphics, bool *result)
756 {
757         return NotImplemented;
758 }