ddraw: Beware of the surface type when checking for format support.
[wine] / dlls / gdiplus / graphicspath.c
1 /*
2  * Copyright (C) 2007 Google (Evan Stade)
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  *
18  */
19
20 #include <stdarg.h>
21 #include <math.h>
22
23 #include "windef.h"
24 #include "winbase.h"
25 #include "winuser.h"
26 #include "wingdi.h"
27
28 #include "objbase.h"
29
30 #include "gdiplus.h"
31 #include "gdiplus_private.h"
32 #include "wine/debug.h"
33
34 WINE_DEFAULT_DEBUG_CHANNEL(gdiplus);
35
36 GpStatus WINGDIPAPI GdipAddPathArc(GpPath *path, REAL x1, REAL y1, REAL x2,
37     REAL y2, REAL startAngle, REAL sweepAngle)
38 {
39     INT count, old_count, i;
40
41     TRACE("(%p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n",
42           path, x1, y1, x2, y2, startAngle, sweepAngle);
43
44     if(!path)
45         return InvalidParameter;
46
47     count = arc2polybezier(NULL, x1, y1, x2, y2, startAngle, sweepAngle);
48
49     if(count == 0)
50         return Ok;
51     if(!lengthen_path(path, count))
52         return OutOfMemory;
53
54     old_count = path->pathdata.Count;
55     arc2polybezier(&path->pathdata.Points[old_count], x1, y1, x2, y2,
56                    startAngle, sweepAngle);
57
58     for(i = 0; i < count; i++){
59         path->pathdata.Types[old_count + i] = PathPointTypeBezier;
60     }
61
62     path->pathdata.Types[old_count] =
63         (path->newfigure ? PathPointTypeStart : PathPointTypeLine);
64     path->newfigure = FALSE;
65     path->pathdata.Count += count;
66
67     return Ok;
68 }
69
70 GpStatus WINGDIPAPI GdipAddPathArcI(GpPath *path, INT x1, INT y1, INT x2,
71    INT y2, REAL startAngle, REAL sweepAngle)
72 {
73     TRACE("(%p, %d, %d, %d, %d, %.2f, %.2f)\n",
74           path, x1, y1, x2, y2, startAngle, sweepAngle);
75
76     return GdipAddPathArc(path,(REAL)x1,(REAL)y1,(REAL)x2,(REAL)y2,startAngle,sweepAngle);
77 }
78
79 GpStatus WINGDIPAPI GdipAddPathBezier(GpPath *path, REAL x1, REAL y1, REAL x2,
80     REAL y2, REAL x3, REAL y3, REAL x4, REAL y4)
81 {
82     INT old_count;
83
84     TRACE("(%p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n",
85           path, x1, y1, x2, y2, x3, y3, x4, y4);
86
87     if(!path)
88         return InvalidParameter;
89
90     if(!lengthen_path(path, 4))
91         return OutOfMemory;
92
93     old_count = path->pathdata.Count;
94
95     path->pathdata.Points[old_count].X = x1;
96     path->pathdata.Points[old_count].Y = y1;
97     path->pathdata.Points[old_count + 1].X = x2;
98     path->pathdata.Points[old_count + 1].Y = y2;
99     path->pathdata.Points[old_count + 2].X = x3;
100     path->pathdata.Points[old_count + 2].Y = y3;
101     path->pathdata.Points[old_count + 3].X = x4;
102     path->pathdata.Points[old_count + 3].Y = y4;
103
104     path->pathdata.Types[old_count] =
105         (path->newfigure ? PathPointTypeStart : PathPointTypeLine);
106     path->pathdata.Types[old_count + 1] = PathPointTypeBezier;
107     path->pathdata.Types[old_count + 2] = PathPointTypeBezier;
108     path->pathdata.Types[old_count + 3] = PathPointTypeBezier;
109
110     path->newfigure = FALSE;
111     path->pathdata.Count += 4;
112
113     return Ok;
114 }
115
116 GpStatus WINGDIPAPI GdipAddPathBezierI(GpPath *path, INT x1, INT y1, INT x2,
117     INT y2, INT x3, INT y3, INT x4, INT y4)
118 {
119     TRACE("(%p, %d, %d, %d, %d, %d, %d, %d, %d)\n",
120           path, x1, y1, x2, y2, x3, y3, x4, y4);
121
122     return GdipAddPathBezier(path,(REAL)x1,(REAL)y1,(REAL)x2,(REAL)y2,(REAL)x3,(REAL)y3,
123                                   (REAL)x4,(REAL)y4);
124 }
125
126 GpStatus WINGDIPAPI GdipAddPathBeziers(GpPath *path, GDIPCONST GpPointF *points,
127     INT count)
128 {
129     INT i, old_count;
130
131     TRACE("(%p, %p, %d)\n", path, points, count);
132
133     if(!path || !points || ((count - 1) % 3))
134         return InvalidParameter;
135
136     if(!lengthen_path(path, count))
137         return OutOfMemory;
138
139     old_count = path->pathdata.Count;
140
141     for(i = 0; i < count; i++){
142         path->pathdata.Points[old_count + i].X = points[i].X;
143         path->pathdata.Points[old_count + i].Y = points[i].Y;
144         path->pathdata.Types[old_count + i] = PathPointTypeBezier;
145     }
146
147     path->pathdata.Types[old_count] =
148         (path->newfigure ? PathPointTypeStart : PathPointTypeLine);
149     path->newfigure = FALSE;
150     path->pathdata.Count += count;
151
152     return Ok;
153 }
154
155 GpStatus WINGDIPAPI GdipAddPathBeziersI(GpPath *path, GDIPCONST GpPoint *points,
156     INT count)
157 {
158     GpPointF *ptsF;
159     GpStatus ret;
160     INT i;
161
162     TRACE("(%p, %p, %d)\n", path, points, count);
163
164     if(!points || ((count - 1) % 3))
165         return InvalidParameter;
166
167     ptsF = GdipAlloc(sizeof(GpPointF) * count);
168     if(!ptsF)
169         return OutOfMemory;
170
171     for(i = 0; i < count; i++){
172         ptsF[i].X = (REAL)points[i].X;
173         ptsF[i].Y = (REAL)points[i].Y;
174     }
175
176     ret = GdipAddPathBeziers(path, ptsF, count);
177     GdipFree(ptsF);
178
179     return ret;
180 }
181
182 GpStatus WINGDIPAPI GdipAddPathClosedCurve(GpPath *path, GDIPCONST GpPointF *points,
183     INT count)
184 {
185     TRACE("(%p, %p, %d)\n", path, points, count);
186
187     return GdipAddPathClosedCurve2(path, points, count, 1.0);
188 }
189
190 GpStatus WINGDIPAPI GdipAddPathClosedCurveI(GpPath *path, GDIPCONST GpPoint *points,
191     INT count)
192 {
193     TRACE("(%p, %p, %d)\n", path, points, count);
194
195     return GdipAddPathClosedCurve2I(path, points, count, 1.0);
196 }
197
198 GpStatus WINGDIPAPI GdipAddPathClosedCurve2(GpPath *path, GDIPCONST GpPointF *points,
199     INT count, REAL tension)
200 {
201     INT i, len_pt = (count + 1)*3-2;
202     GpPointF *pt;
203     GpPointF *pts;
204     REAL x1, x2, y1, y2;
205     GpStatus stat;
206
207     TRACE("(%p, %p, %d, %.2f)\n", path, points, count, tension);
208
209     if(!path || !points || count <= 1)
210         return InvalidParameter;
211
212     pt = GdipAlloc(len_pt * sizeof(GpPointF));
213     pts = GdipAlloc((count + 1)*sizeof(GpPointF));
214     if(!pt || !pts){
215         GdipFree(pt);
216         GdipFree(pts);
217         return OutOfMemory;
218     }
219
220     /* copy source points to extend with the last one */
221     memcpy(pts, points, sizeof(GpPointF)*count);
222     pts[count] = pts[0];
223
224     tension = tension * TENSION_CONST;
225
226     for(i = 0; i < count-1; i++){
227         calc_curve_bezier(&(pts[i]), tension, &x1, &y1, &x2, &y2);
228
229         pt[3*i+2].X = x1;
230         pt[3*i+2].Y = y1;
231         pt[3*i+3].X = pts[i+1].X;
232         pt[3*i+3].Y = pts[i+1].Y;
233         pt[3*i+4].X = x2;
234         pt[3*i+4].Y = y2;
235     }
236
237     /* points [len_pt-2] and [0] are calculated
238        separetely to connect splines properly */
239     pts[0] = points[count-1];
240     pts[1] = points[0]; /* equals to start and end of a resulting path */
241     pts[2] = points[1];
242
243     calc_curve_bezier(pts, tension, &x1, &y1, &x2, &y2);
244     pt[len_pt-2].X = x1;
245     pt[len_pt-2].Y = y1;
246     pt[0].X = pts[1].X;
247     pt[0].Y = pts[1].Y;
248     pt[1].X = x2;
249     pt[1].Y = y2;
250     /* close path */
251     pt[len_pt-1].X = pt[0].X;
252     pt[len_pt-1].Y = pt[0].Y;
253
254     stat = GdipAddPathBeziers(path, pt, len_pt);
255
256     /* close figure */
257     if(stat == Ok){
258         INT count = path->pathdata.Count;
259         path->pathdata.Types[count - 1] |= PathPointTypeCloseSubpath;
260         path->newfigure = TRUE;
261     }
262
263     GdipFree(pts);
264     GdipFree(pt);
265
266     return stat;
267 }
268
269 GpStatus WINGDIPAPI GdipAddPathClosedCurve2I(GpPath *path, GDIPCONST GpPoint *points,
270     INT count, REAL tension)
271 {
272     GpPointF *ptf;
273     INT i;
274     GpStatus stat;
275
276     TRACE("(%p, %p, %d, %.2f)\n", path, points, count, tension);
277
278     if(!path || !points || count <= 1)
279         return InvalidParameter;
280
281     ptf = GdipAlloc(sizeof(GpPointF)*count);
282     if(!ptf)
283         return OutOfMemory;
284
285     for(i = 0; i < count; i++){
286         ptf[i].X = (REAL)points[i].X;
287         ptf[i].Y = (REAL)points[i].Y;
288     }
289
290     stat = GdipAddPathClosedCurve2(path, ptf, count, tension);
291
292     GdipFree(ptf);
293
294     return stat;
295 }
296
297 GpStatus WINGDIPAPI GdipAddPathCurve(GpPath *path, GDIPCONST GpPointF *points, INT count)
298 {
299     TRACE("(%p, %p, %d)\n", path, points, count);
300
301     if(!path || !points || count <= 1)
302         return InvalidParameter;
303
304     return GdipAddPathCurve2(path, points, count, 1.0);
305 }
306
307 GpStatus WINGDIPAPI GdipAddPathCurveI(GpPath *path, GDIPCONST GpPoint *points, INT count)
308 {
309     TRACE("(%p, %p, %d)\n", path, points, count);
310
311     if(!path || !points || count <= 1)
312         return InvalidParameter;
313
314     return GdipAddPathCurve2I(path, points, count, 1.0);
315 }
316
317 GpStatus WINGDIPAPI GdipAddPathCurve2(GpPath *path, GDIPCONST GpPointF *points, INT count,
318     REAL tension)
319 {
320     INT i, len_pt = count*3-2;
321     GpPointF *pt;
322     REAL x1, x2, y1, y2;
323     GpStatus stat;
324
325     TRACE("(%p, %p, %d, %.2f)\n", path, points, count, tension);
326
327     if(!path || !points || count <= 1)
328         return InvalidParameter;
329
330     pt = GdipAlloc(len_pt * sizeof(GpPointF));
331     if(!pt)
332         return OutOfMemory;
333
334     tension = tension * TENSION_CONST;
335
336     calc_curve_bezier_endp(points[0].X, points[0].Y, points[1].X, points[1].Y,
337         tension, &x1, &y1);
338
339     pt[0].X = points[0].X;
340     pt[0].Y = points[0].Y;
341     pt[1].X = x1;
342     pt[1].Y = y1;
343
344     for(i = 0; i < count-2; i++){
345         calc_curve_bezier(&(points[i]), tension, &x1, &y1, &x2, &y2);
346
347         pt[3*i+2].X = x1;
348         pt[3*i+2].Y = y1;
349         pt[3*i+3].X = points[i+1].X;
350         pt[3*i+3].Y = points[i+1].Y;
351         pt[3*i+4].X = x2;
352         pt[3*i+4].Y = y2;
353     }
354
355     calc_curve_bezier_endp(points[count-1].X, points[count-1].Y,
356         points[count-2].X, points[count-2].Y, tension, &x1, &y1);
357
358     pt[len_pt-2].X = x1;
359     pt[len_pt-2].Y = y1;
360     pt[len_pt-1].X = points[count-1].X;
361     pt[len_pt-1].Y = points[count-1].Y;
362
363     stat = GdipAddPathBeziers(path, pt, len_pt);
364
365     GdipFree(pt);
366
367     return stat;
368 }
369
370 GpStatus WINGDIPAPI GdipAddPathCurve2I(GpPath *path, GDIPCONST GpPoint *points,
371     INT count, REAL tension)
372 {
373     GpPointF *ptf;
374     INT i;
375     GpStatus stat;
376
377     TRACE("(%p, %p, %d, %.2f)\n", path, points, count, tension);
378
379     if(!path || !points || count <= 1)
380         return InvalidParameter;
381
382     ptf = GdipAlloc(sizeof(GpPointF)*count);
383     if(!ptf)
384         return OutOfMemory;
385
386     for(i = 0; i < count; i++){
387         ptf[i].X = (REAL)points[i].X;
388         ptf[i].Y = (REAL)points[i].Y;
389     }
390
391     stat = GdipAddPathCurve2(path, ptf, count, tension);
392
393     GdipFree(ptf);
394
395     return stat;
396 }
397
398 GpStatus WINGDIPAPI GdipAddPathEllipse(GpPath *path, REAL x, REAL y, REAL width,
399     REAL height)
400 {
401     INT old_count, numpts;
402
403     TRACE("(%p, %.2f, %.2f, %.2f, %.2f)\n", path, x, y, width, height);
404
405     if(!path)
406         return InvalidParameter;
407
408     if(!lengthen_path(path, MAX_ARC_PTS))
409         return OutOfMemory;
410
411     old_count = path->pathdata.Count;
412     if((numpts = arc2polybezier(&path->pathdata.Points[old_count],  x, y, width,
413                                height, 0.0, 360.0)) != MAX_ARC_PTS){
414         ERR("expected %d points but got %d\n", MAX_ARC_PTS, numpts);
415         return GenericError;
416     }
417
418     memset(&path->pathdata.Types[old_count + 1], PathPointTypeBezier,
419            MAX_ARC_PTS - 1);
420
421     /* An ellipse is an intrinsic figure (always is its own subpath). */
422     path->pathdata.Types[old_count] = PathPointTypeStart;
423     path->pathdata.Types[old_count + MAX_ARC_PTS - 1] |= PathPointTypeCloseSubpath;
424     path->newfigure = TRUE;
425     path->pathdata.Count += MAX_ARC_PTS;
426
427     return Ok;
428 }
429
430 GpStatus WINGDIPAPI GdipAddPathEllipseI(GpPath *path, INT x, INT y, INT width,
431     INT height)
432 {
433     TRACE("(%p, %d, %d, %d, %d)\n", path, x, y, width, height);
434
435     return GdipAddPathEllipse(path,(REAL)x,(REAL)y,(REAL)width,(REAL)height);
436 }
437
438 GpStatus WINGDIPAPI GdipAddPathLine2(GpPath *path, GDIPCONST GpPointF *points,
439     INT count)
440 {
441     INT i, old_count;
442
443     TRACE("(%p, %p, %d)\n", path, points, count);
444
445     if(!path || !points)
446         return InvalidParameter;
447
448     if(!lengthen_path(path, count))
449         return OutOfMemory;
450
451     old_count = path->pathdata.Count;
452
453     for(i = 0; i < count; i++){
454         path->pathdata.Points[old_count + i].X = points[i].X;
455         path->pathdata.Points[old_count + i].Y = points[i].Y;
456         path->pathdata.Types[old_count + i] = PathPointTypeLine;
457     }
458
459     if(path->newfigure){
460         path->pathdata.Types[old_count] = PathPointTypeStart;
461         path->newfigure = FALSE;
462     }
463
464     path->pathdata.Count += count;
465
466     return Ok;
467 }
468
469 GpStatus WINGDIPAPI GdipAddPathLine2I(GpPath *path, GDIPCONST GpPoint *points, INT count)
470 {
471     GpPointF *pointsF;
472     INT i;
473     GpStatus stat;
474
475     TRACE("(%p, %p, %d)\n", path, points, count);
476
477     if(count <= 0)
478         return InvalidParameter;
479
480     pointsF = GdipAlloc(sizeof(GpPointF) * count);
481     if(!pointsF)    return OutOfMemory;
482
483     for(i = 0;i < count; i++){
484         pointsF[i].X = (REAL)points[i].X;
485         pointsF[i].Y = (REAL)points[i].Y;
486     }
487
488     stat = GdipAddPathLine2(path, pointsF, count);
489
490     GdipFree(pointsF);
491
492     return stat;
493 }
494
495 GpStatus WINGDIPAPI GdipAddPathLine(GpPath *path, REAL x1, REAL y1, REAL x2, REAL y2)
496 {
497     INT old_count;
498
499     TRACE("(%p, %.2f, %.2f, %.2f, %.2f)\n", path, x1, y1, x2, y2);
500
501     if(!path)
502         return InvalidParameter;
503
504     if(!lengthen_path(path, 2))
505         return OutOfMemory;
506
507     old_count = path->pathdata.Count;
508
509     path->pathdata.Points[old_count].X = x1;
510     path->pathdata.Points[old_count].Y = y1;
511     path->pathdata.Points[old_count + 1].X = x2;
512     path->pathdata.Points[old_count + 1].Y = y2;
513
514     path->pathdata.Types[old_count] =
515         (path->newfigure ? PathPointTypeStart : PathPointTypeLine);
516     path->pathdata.Types[old_count + 1] = PathPointTypeLine;
517
518     path->newfigure = FALSE;
519     path->pathdata.Count += 2;
520
521     return Ok;
522 }
523
524 GpStatus WINGDIPAPI GdipAddPathLineI(GpPath *path, INT x1, INT y1, INT x2, INT y2)
525 {
526     TRACE("(%p, %d, %d, %d, %d)\n", path, x1, y1, x2, y2);
527
528     return GdipAddPathLine(path, (REAL)x1, (REAL)y1, (REAL)x2, (REAL)y2);
529 }
530
531 GpStatus WINGDIPAPI GdipAddPathPath(GpPath *path, GDIPCONST GpPath* addingPath,
532     BOOL connect)
533 {
534     INT old_count, count;
535
536     TRACE("(%p, %p, %d)\n", path, addingPath, connect);
537
538     if(!path || !addingPath)
539         return InvalidParameter;
540
541     old_count = path->pathdata.Count;
542     count = addingPath->pathdata.Count;
543
544     if(!lengthen_path(path, count))
545         return OutOfMemory;
546
547     memcpy(&path->pathdata.Points[old_count], addingPath->pathdata.Points,
548            count * sizeof(GpPointF));
549     memcpy(&path->pathdata.Types[old_count], addingPath->pathdata.Types, count);
550
551     if(path->newfigure || !connect)
552         path->pathdata.Types[old_count] = PathPointTypeStart;
553     else
554         path->pathdata.Types[old_count] = PathPointTypeLine;
555
556     path->newfigure = FALSE;
557     path->pathdata.Count += count;
558
559     return Ok;
560 }
561
562 GpStatus WINGDIPAPI GdipAddPathPie(GpPath *path, REAL x, REAL y, REAL width, REAL height,
563     REAL startAngle, REAL sweepAngle)
564 {
565     GpPointF *ptf;
566     GpStatus status;
567     INT i, count;
568
569     TRACE("(%p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n",
570           path, x, y, width, height, startAngle, sweepAngle);
571
572     if(!path)
573         return InvalidParameter;
574
575     count = arc2polybezier(NULL, x, y, width, height, startAngle, sweepAngle);
576
577     if(count == 0)
578         return Ok;
579
580     ptf = GdipAlloc(sizeof(GpPointF)*count);
581     if(!ptf)
582         return OutOfMemory;
583
584     arc2polybezier(ptf, x, y, width, height, startAngle, sweepAngle);
585
586     status = GdipAddPathLine(path, (width - x)/2, (height - y)/2, ptf[0].X, ptf[0].Y);
587     if(status != Ok){
588         GdipFree(ptf);
589         return status;
590     }
591     /* one spline is already added as a line endpoint */
592     if(!lengthen_path(path, count - 1)){
593         GdipFree(ptf);
594         return OutOfMemory;
595     }
596
597     memcpy(&(path->pathdata.Points[path->pathdata.Count]), &(ptf[1]),sizeof(GpPointF)*(count-1));
598     for(i = 0; i < count-1; i++)
599         path->pathdata.Types[path->pathdata.Count+i] = PathPointTypeBezier;
600
601     path->pathdata.Count += count-1;
602
603     GdipClosePathFigure(path);
604
605     GdipFree(ptf);
606
607     return status;
608 }
609
610 GpStatus WINGDIPAPI GdipAddPathPieI(GpPath *path, INT x, INT y, INT width, INT height,
611     REAL startAngle, REAL sweepAngle)
612 {
613     TRACE("(%p, %d, %d, %d, %d, %.2f, %.2f)\n",
614           path, x, y, width, height, startAngle, sweepAngle);
615
616     return GdipAddPathPieI(path, (REAL)x, (REAL)y, (REAL)width, (REAL)height, startAngle, sweepAngle);
617 }
618
619 GpStatus WINGDIPAPI GdipAddPathPolygon(GpPath *path, GDIPCONST GpPointF *points, INT count)
620 {
621     INT old_count;
622
623     TRACE("(%p, %p, %d)\n", path, points, count);
624
625     if(!path || !points || count < 3)
626         return InvalidParameter;
627
628     if(!lengthen_path(path, count))
629         return OutOfMemory;
630
631     old_count = path->pathdata.Count;
632
633     memcpy(&path->pathdata.Points[old_count], points, count*sizeof(GpPointF));
634     memset(&path->pathdata.Types[old_count + 1], PathPointTypeLine, count - 1);
635
636     /* A polygon is an intrinsic figure */
637     path->pathdata.Types[old_count] = PathPointTypeStart;
638     path->pathdata.Types[old_count + count - 1] |= PathPointTypeCloseSubpath;
639     path->newfigure = TRUE;
640     path->pathdata.Count += count;
641
642     return Ok;
643 }
644
645 GpStatus WINGDIPAPI GdipAddPathPolygonI(GpPath *path, GDIPCONST GpPoint *points, INT count)
646 {
647     GpPointF *ptf;
648     GpStatus status;
649     INT i;
650
651     TRACE("(%p, %p, %d)\n", path, points, count);
652
653     if(!points || count < 3)
654         return InvalidParameter;
655
656     ptf = GdipAlloc(sizeof(GpPointF) * count);
657     if(!ptf)
658         return OutOfMemory;
659
660     for(i = 0; i < count; i++){
661         ptf[i].X = (REAL)points[i].X;
662         ptf[i].Y = (REAL)points[i].Y;
663     }
664
665     status = GdipAddPathPolygon(path, ptf, count);
666
667     GdipFree(ptf);
668
669     return status;
670 }
671
672 GpStatus WINGDIPAPI GdipClonePath(GpPath* path, GpPath **clone)
673 {
674     TRACE("(%p, %p)\n", path, clone);
675
676     if(!path || !clone)
677         return InvalidParameter;
678
679     *clone = GdipAlloc(sizeof(GpPath));
680     if(!*clone) return OutOfMemory;
681
682     **clone = *path;
683
684     (*clone)->pathdata.Points = GdipAlloc(path->datalen * sizeof(PointF));
685     (*clone)->pathdata.Types = GdipAlloc(path->datalen);
686     if(!(*clone)->pathdata.Points || !(*clone)->pathdata.Types){
687         GdipFree(*clone);
688         GdipFree((*clone)->pathdata.Points);
689         GdipFree((*clone)->pathdata.Types);
690         return OutOfMemory;
691     }
692
693     memcpy((*clone)->pathdata.Points, path->pathdata.Points,
694            path->datalen * sizeof(PointF));
695     memcpy((*clone)->pathdata.Types, path->pathdata.Types, path->datalen);
696
697     return Ok;
698 }
699
700 GpStatus WINGDIPAPI GdipClosePathFigure(GpPath* path)
701 {
702     TRACE("(%p)\n", path);
703
704     if(!path)
705         return InvalidParameter;
706
707     if(path->pathdata.Count > 0){
708         path->pathdata.Types[path->pathdata.Count - 1] |= PathPointTypeCloseSubpath;
709         path->newfigure = TRUE;
710     }
711
712     return Ok;
713 }
714
715 GpStatus WINGDIPAPI GdipClosePathFigures(GpPath* path)
716 {
717     INT i;
718
719     TRACE("(%p)\n", path);
720
721     if(!path)
722         return InvalidParameter;
723
724     for(i = 1; i < path->pathdata.Count; i++){
725         if(path->pathdata.Types[i] == PathPointTypeStart)
726             path->pathdata.Types[i-1] |= PathPointTypeCloseSubpath;
727     }
728
729     path->newfigure = TRUE;
730
731     return Ok;
732 }
733
734 GpStatus WINGDIPAPI GdipCreatePath(GpFillMode fill, GpPath **path)
735 {
736     TRACE("(%d, %p)\n", fill, path);
737
738     if(!path)
739         return InvalidParameter;
740
741     *path = GdipAlloc(sizeof(GpPath));
742     if(!*path)  return OutOfMemory;
743
744     (*path)->fill = fill;
745     (*path)->newfigure = TRUE;
746
747     return Ok;
748 }
749
750 GpStatus WINGDIPAPI GdipCreatePath2(GDIPCONST GpPointF* points,
751     GDIPCONST BYTE* types, INT count, GpFillMode fill, GpPath **path)
752 {
753     TRACE("(%p, %p, %d, %d, %p)\n", points, types, count, fill, path);
754
755     if(!path)
756         return InvalidParameter;
757
758     *path = GdipAlloc(sizeof(GpPath));
759     if(!*path)  return OutOfMemory;
760
761     (*path)->pathdata.Points = GdipAlloc(count * sizeof(PointF));
762     (*path)->pathdata.Types = GdipAlloc(count);
763
764     if(!(*path)->pathdata.Points || !(*path)->pathdata.Types){
765         GdipFree((*path)->pathdata.Points);
766         GdipFree((*path)->pathdata.Types);
767         GdipFree(*path);
768         return OutOfMemory;
769     }
770
771     memcpy((*path)->pathdata.Points, points, count * sizeof(PointF));
772     memcpy((*path)->pathdata.Types, types, count);
773     (*path)->pathdata.Count = count;
774     (*path)->datalen = count;
775
776     (*path)->fill = fill;
777     (*path)->newfigure = TRUE;
778
779     return Ok;
780 }
781
782 GpStatus WINGDIPAPI GdipCreatePath2I(GDIPCONST GpPoint* points,
783     GDIPCONST BYTE* types, INT count, GpFillMode fill, GpPath **path)
784 {
785     GpPointF *ptF;
786     GpStatus ret;
787     INT i;
788
789     TRACE("(%p, %p, %d, %d, %p)\n", points, types, count, fill, path);
790
791     ptF = GdipAlloc(sizeof(GpPointF)*count);
792
793     for(i = 0;i < count; i++){
794         ptF[i].X = (REAL)points[i].X;
795         ptF[i].Y = (REAL)points[i].Y;
796     }
797
798     ret = GdipCreatePath2(ptF, types, count, fill, path);
799
800     GdipFree(ptF);
801
802     return ret;
803 }
804
805 GpStatus WINGDIPAPI GdipDeletePath(GpPath *path)
806 {
807     TRACE("(%p)\n", path);
808
809     if(!path)
810         return InvalidParameter;
811
812     GdipFree(path->pathdata.Points);
813     GdipFree(path->pathdata.Types);
814     GdipFree(path);
815
816     return Ok;
817 }
818
819 GpStatus WINGDIPAPI GdipFlattenPath(GpPath *path, GpMatrix* matrix, REAL flatness)
820 {
821     static int calls;
822
823     if(!path)
824         return InvalidParameter;
825
826     if(!(calls++))
827         FIXME("not implemented\n");
828
829     return NotImplemented;
830 }
831
832 GpStatus WINGDIPAPI GdipGetPathData(GpPath *path, GpPathData* pathData)
833 {
834     TRACE("(%p, %p)\n", path, pathData);
835
836     if(!path || !pathData)
837         return InvalidParameter;
838
839     /* Only copy data. pathData allocation/freeing controlled by wrapper class.
840        Assumed that pathData is enough wide to get all data - controlled by wrapper too. */
841     memcpy(pathData->Points, path->pathdata.Points, sizeof(PointF) * pathData->Count);
842     memcpy(pathData->Types , path->pathdata.Types , pathData->Count);
843
844     return Ok;
845 }
846
847 GpStatus WINGDIPAPI GdipGetPathFillMode(GpPath *path, GpFillMode *fillmode)
848 {
849     TRACE("(%p, %p)\n", path, fillmode);
850
851     if(!path || !fillmode)
852         return InvalidParameter;
853
854     *fillmode = path->fill;
855
856     return Ok;
857 }
858
859 GpStatus WINGDIPAPI GdipGetPathLastPoint(GpPath* path, GpPointF* lastPoint)
860 {
861     INT count;
862
863     TRACE("(%p, %p)\n", path, lastPoint);
864
865     if(!path || !lastPoint)
866         return InvalidParameter;
867
868     count = path->pathdata.Count;
869     if(count > 0)
870         *lastPoint = path->pathdata.Points[count-1];
871
872     return Ok;
873 }
874
875 GpStatus WINGDIPAPI GdipGetPathPoints(GpPath *path, GpPointF* points, INT count)
876 {
877     TRACE("(%p, %p, %d)\n", path, points, count);
878
879     if(!path)
880         return InvalidParameter;
881
882     if(count < path->pathdata.Count)
883         return InsufficientBuffer;
884
885     memcpy(points, path->pathdata.Points, path->pathdata.Count * sizeof(GpPointF));
886
887     return Ok;
888 }
889
890 GpStatus WINGDIPAPI GdipGetPathPointsI(GpPath *path, GpPoint* points, INT count)
891 {
892     GpStatus ret;
893     GpPointF *ptf;
894     INT i;
895
896     TRACE("(%p, %p, %d)\n", path, points, count);
897
898     if(count <= 0)
899         return InvalidParameter;
900
901     ptf = GdipAlloc(sizeof(GpPointF)*count);
902     if(!ptf)    return OutOfMemory;
903
904     ret = GdipGetPathPoints(path,ptf,count);
905     if(ret == Ok)
906         for(i = 0;i < count;i++){
907             points[i].X = roundr(ptf[i].X);
908             points[i].Y = roundr(ptf[i].Y);
909         };
910     GdipFree(ptf);
911
912     return ret;
913 }
914
915 GpStatus WINGDIPAPI GdipGetPathTypes(GpPath *path, BYTE* types, INT count)
916 {
917     TRACE("(%p, %p, %d)\n", path, types, count);
918
919     if(!path)
920         return InvalidParameter;
921
922     if(count < path->pathdata.Count)
923         return InsufficientBuffer;
924
925     memcpy(types, path->pathdata.Types, path->pathdata.Count);
926
927     return Ok;
928 }
929
930 /* Windows expands the bounding box to the maximum possible bounding box
931  * for a given pen.  For example, if a line join can extend past the point
932  * it's joining by x units, the bounding box is extended by x units in every
933  * direction (even though this is too conservative for most cases). */
934 GpStatus WINGDIPAPI GdipGetPathWorldBounds(GpPath* path, GpRectF* bounds,
935     GDIPCONST GpMatrix *matrix, GDIPCONST GpPen *pen)
936 {
937     GpPointF * points, temp_pts[4];
938     INT count, i;
939     REAL path_width = 1.0, width, height, temp, low_x, low_y, high_x, high_y;
940
941     TRACE("(%p, %p, %p, %p)\n", path, bounds, matrix, pen);
942
943     /* Matrix and pen can be null. */
944     if(!path || !bounds)
945         return InvalidParameter;
946
947     /* If path is empty just return. */
948     count = path->pathdata.Count;
949     if(count == 0){
950         bounds->X = bounds->Y = bounds->Width = bounds->Height = 0.0;
951         return Ok;
952     }
953
954     points = path->pathdata.Points;
955
956     low_x = high_x = points[0].X;
957     low_y = high_y = points[0].Y;
958
959     for(i = 1; i < count; i++){
960         low_x = min(low_x, points[i].X);
961         low_y = min(low_y, points[i].Y);
962         high_x = max(high_x, points[i].X);
963         high_y = max(high_y, points[i].Y);
964     }
965
966     width = high_x - low_x;
967     height = high_y - low_y;
968
969     /* This looks unusual but it's the only way I can imitate windows. */
970     if(matrix){
971         temp_pts[0].X = low_x;
972         temp_pts[0].Y = low_y;
973         temp_pts[1].X = low_x;
974         temp_pts[1].Y = high_y;
975         temp_pts[2].X = high_x;
976         temp_pts[2].Y = high_y;
977         temp_pts[3].X = high_x;
978         temp_pts[3].Y = low_y;
979
980         GdipTransformMatrixPoints((GpMatrix*)matrix, temp_pts, 4);
981         low_x = temp_pts[0].X;
982         low_y = temp_pts[0].Y;
983
984         for(i = 1; i < 4; i++){
985             low_x = min(low_x, temp_pts[i].X);
986             low_y = min(low_y, temp_pts[i].Y);
987         }
988
989         temp = width;
990         width = height * fabs(matrix->matrix[2]) + width * fabs(matrix->matrix[0]);
991         height = height * fabs(matrix->matrix[3]) + temp * fabs(matrix->matrix[1]);
992     }
993
994     if(pen){
995         path_width = pen->width / 2.0;
996
997         if(count > 2)
998             path_width = max(path_width,  pen->width * pen->miterlimit / 2.0);
999         /* FIXME: this should probably also check for the startcap */
1000         if(pen->endcap & LineCapNoAnchor)
1001             path_width = max(path_width,  pen->width * 2.2);
1002
1003         low_x -= path_width;
1004         low_y -= path_width;
1005         width += 2.0 * path_width;
1006         height += 2.0 * path_width;
1007     }
1008
1009     bounds->X = low_x;
1010     bounds->Y = low_y;
1011     bounds->Width = width;
1012     bounds->Height = height;
1013
1014     return Ok;
1015 }
1016
1017 GpStatus WINGDIPAPI GdipGetPathWorldBoundsI(GpPath* path, GpRect* bounds,
1018     GDIPCONST GpMatrix *matrix, GDIPCONST GpPen *pen)
1019 {
1020     GpStatus ret;
1021     GpRectF boundsF;
1022
1023     TRACE("(%p, %p, %p, %p)\n", path, bounds, matrix, pen);
1024
1025     ret = GdipGetPathWorldBounds(path,&boundsF,matrix,pen);
1026
1027     if(ret == Ok){
1028         bounds->X      = roundr(boundsF.X);
1029         bounds->Y      = roundr(boundsF.Y);
1030         bounds->Width  = roundr(boundsF.Width);
1031         bounds->Height = roundr(boundsF.Height);
1032     }
1033
1034     return ret;
1035 }
1036
1037 GpStatus WINGDIPAPI GdipGetPointCount(GpPath *path, INT *count)
1038 {
1039     TRACE("(%p, %p)\n", path, count);
1040
1041     if(!path)
1042         return InvalidParameter;
1043
1044     *count = path->pathdata.Count;
1045
1046     return Ok;
1047 }
1048
1049 GpStatus WINGDIPAPI GdipReversePath(GpPath* path)
1050 {
1051     INT i, count;
1052     INT start = 0; /* position in reversed path */
1053     GpPathData revpath;
1054
1055     TRACE("(%p)\n", path);
1056
1057     if(!path)
1058         return InvalidParameter;
1059
1060     count = path->pathdata.Count;
1061
1062     if(count == 0) return Ok;
1063
1064     revpath.Points = GdipAlloc(sizeof(GpPointF)*count);
1065     revpath.Types  = GdipAlloc(sizeof(BYTE)*count);
1066     revpath.Count  = count;
1067     if(!revpath.Points || !revpath.Types){
1068         GdipFree(revpath.Points);
1069         GdipFree(revpath.Types);
1070         return OutOfMemory;
1071     }
1072
1073     for(i = 0; i < count; i++){
1074
1075         /* find next start point */
1076         if(path->pathdata.Types[count-i-1] == PathPointTypeStart){
1077             INT j;
1078             for(j = start; j <= i; j++){
1079                 revpath.Points[j] = path->pathdata.Points[count-j-1];
1080                 revpath.Types[j] = path->pathdata.Types[count-j-1];
1081             }
1082             /* mark start point */
1083             revpath.Types[start] = PathPointTypeStart;
1084             /* set 'figure' endpoint type */
1085             if(i-start > 1){
1086                 revpath.Types[i] = path->pathdata.Types[count-start-1] & ~PathPointTypePathTypeMask;
1087                 revpath.Types[i] |= revpath.Types[i-1];
1088             }
1089             else
1090                 revpath.Types[i] = path->pathdata.Types[start];
1091
1092             start = i+1;
1093         }
1094     }
1095
1096     memcpy(path->pathdata.Points, revpath.Points, sizeof(GpPointF)*count);
1097     memcpy(path->pathdata.Types,  revpath.Types,  sizeof(BYTE)*count);
1098
1099     GdipFree(revpath.Points);
1100     GdipFree(revpath.Types);
1101
1102     return Ok;
1103 }
1104
1105 GpStatus WINGDIPAPI GdipIsOutlineVisiblePathPointI(GpPath* path, INT x, INT y,
1106     GpPen *pen, GpGraphics *graphics, BOOL *result)
1107 {
1108     TRACE("(%p, %d, %d, %p, %p, %p)\n", path, x, y, pen, graphics, result);
1109
1110     return GdipIsOutlineVisiblePathPoint(path, x, y, pen, graphics, result);
1111 }
1112
1113 GpStatus WINGDIPAPI GdipIsOutlineVisiblePathPoint(GpPath* path, REAL x, REAL y,
1114     GpPen *pen, GpGraphics *graphics, BOOL *result)
1115 {
1116     static int calls;
1117
1118     if(!path || !pen)
1119         return InvalidParameter;
1120
1121     if(!(calls++))
1122         FIXME("not implemented\n");
1123
1124     return NotImplemented;
1125 }
1126
1127 GpStatus WINGDIPAPI GdipIsVisiblePathPointI(GpPath* path, INT x, INT y, GpGraphics *graphics, BOOL *result)
1128 {
1129     TRACE("(%p, %d, %d, %p, %p)\n", path, x, y, graphics, result);
1130
1131     return GdipIsVisiblePathPoint(path, x, y, graphics, result);
1132 }
1133
1134 GpStatus WINGDIPAPI GdipIsVisiblePathPoint(GpPath* path, REAL x, REAL y, GpGraphics *graphics, BOOL *result)
1135 {
1136     static int calls;
1137
1138     if(!path) return InvalidParameter;
1139
1140     if(!(calls++))
1141         FIXME("not implemented\n");
1142
1143     return NotImplemented;
1144 }
1145
1146 GpStatus WINGDIPAPI GdipStartPathFigure(GpPath *path)
1147 {
1148     TRACE("(%p)\n", path);
1149
1150     if(!path)
1151         return InvalidParameter;
1152
1153     path->newfigure = TRUE;
1154
1155     return Ok;
1156 }
1157
1158 GpStatus WINGDIPAPI GdipResetPath(GpPath *path)
1159 {
1160     TRACE("(%p)\n", path);
1161
1162     if(!path)
1163         return InvalidParameter;
1164
1165     path->pathdata.Count = 0;
1166     path->newfigure = TRUE;
1167     path->fill = FillModeAlternate;
1168
1169     return Ok;
1170 }
1171
1172 GpStatus WINGDIPAPI GdipSetPathFillMode(GpPath *path, GpFillMode fill)
1173 {
1174     TRACE("(%p, %d)\n", path, fill);
1175
1176     if(!path)
1177         return InvalidParameter;
1178
1179     path->fill = fill;
1180
1181     return Ok;
1182 }
1183
1184 GpStatus WINGDIPAPI GdipTransformPath(GpPath *path, GpMatrix *matrix)
1185 {
1186     TRACE("(%p, %p)\n", path, matrix);
1187
1188     if(!path)
1189         return InvalidParameter;
1190
1191     if(path->pathdata.Count == 0)
1192         return Ok;
1193
1194     return GdipTransformMatrixPoints(matrix, path->pathdata.Points,
1195                                      path->pathdata.Count);
1196 }
1197
1198 GpStatus WINGDIPAPI GdipAddPathRectangle(GpPath *path, REAL x, REAL y,
1199     REAL width, REAL height)
1200 {
1201     GpPath *backup;
1202     GpPointF ptf[2];
1203     GpStatus retstat;
1204     BOOL old_new;
1205
1206     TRACE("(%p, %.2f, %.2f, %.2f, %.2f)\n", path, x, y, width, height);
1207
1208     if(!path || width < 0.0 || height < 0.0)
1209         return InvalidParameter;
1210
1211     /* make a backup copy of path data */
1212     if((retstat = GdipClonePath(path, &backup)) != Ok)
1213         return retstat;
1214
1215     /* rectangle should start as new path */
1216     old_new = path->newfigure;
1217     path->newfigure = TRUE;
1218     if((retstat = GdipAddPathLine(path,x,y,x+width,y)) != Ok){
1219         path->newfigure = old_new;
1220         goto fail;
1221     }
1222
1223     ptf[0].X = x+width;
1224     ptf[0].Y = y+height;
1225     ptf[1].X = x;
1226     ptf[1].Y = y+height;
1227
1228     if((retstat = GdipAddPathLine2(path, ptf, 2)) != Ok)  goto fail;
1229     path->pathdata.Types[path->pathdata.Count-1] |= PathPointTypeCloseSubpath;
1230
1231     /* free backup */
1232     GdipDeletePath(backup);
1233     return Ok;
1234
1235 fail:
1236     /* reverting */
1237     GdipDeletePath(path);
1238     GdipClonePath(backup, &path);
1239     GdipDeletePath(backup);
1240
1241     return retstat;
1242 }
1243
1244 GpStatus WINGDIPAPI GdipAddPathRectangleI(GpPath *path, INT x, INT y,
1245     INT width, INT height)
1246 {
1247     TRACE("(%p, %d, %d, %d, %d)\n", path, x, y, width, height);
1248
1249     return GdipAddPathRectangle(path,(REAL)x,(REAL)y,(REAL)width,(REAL)height);
1250 }
1251
1252 GpStatus WINGDIPAPI GdipAddPathRectangles(GpPath *path, GDIPCONST GpRectF *rects, INT count)
1253 {
1254     GpPath *backup;
1255     GpStatus retstat;
1256     INT i;
1257
1258     TRACE("(%p, %p, %d)\n", path, rects, count);
1259
1260     /* count == 0 - verified condition  */
1261     if(!path || !rects || count == 0)
1262         return InvalidParameter;
1263
1264     if(count < 0)
1265         return OutOfMemory;
1266
1267     /* make a backup copy */
1268     if((retstat = GdipClonePath(path, &backup)) != Ok)
1269         return retstat;
1270
1271     for(i = 0; i < count; i++){
1272         if((retstat = GdipAddPathRectangle(path,rects[i].X,rects[i].Y,rects[i].Width,rects[i].Height)) != Ok)
1273             goto fail;
1274     }
1275
1276     /* free backup */
1277     GdipDeletePath(backup);
1278     return Ok;
1279
1280 fail:
1281     /* reverting */
1282     GdipDeletePath(path);
1283     GdipClonePath(backup, &path);
1284     GdipDeletePath(backup);
1285
1286     return retstat;
1287 }
1288
1289 GpStatus WINGDIPAPI GdipAddPathRectanglesI(GpPath *path, GDIPCONST GpRect *rects, INT count)
1290 {
1291     GpRectF *rectsF;
1292     GpStatus retstat;
1293     INT i;
1294
1295     TRACE("(%p, %p, %d)\n", path, rects, count);
1296
1297     if(!rects || count == 0)
1298         return InvalidParameter;
1299
1300     if(count < 0)
1301         return OutOfMemory;
1302
1303     rectsF = GdipAlloc(sizeof(GpRectF)*count);
1304
1305     for(i = 0;i < count;i++){
1306         rectsF[i].X      = (REAL)rects[i].X;
1307         rectsF[i].Y      = (REAL)rects[i].Y;
1308         rectsF[i].Width  = (REAL)rects[i].Width;
1309         rectsF[i].Height = (REAL)rects[i].Height;
1310     }
1311
1312     retstat = GdipAddPathRectangles(path, rectsF, count);
1313     GdipFree(rectsF);
1314
1315     return retstat;
1316 }
1317
1318 GpStatus WINGDIPAPI GdipSetPathMarker(GpPath* path)
1319 {
1320     INT count;
1321
1322     TRACE("(%p)\n", path);
1323
1324     if(!path)
1325         return InvalidParameter;
1326
1327     count = path->pathdata.Count;
1328
1329     /* set marker flag */
1330     if(count > 0)
1331         path->pathdata.Types[count-1] |= PathPointTypePathMarker;
1332
1333     return Ok;
1334 }
1335
1336 GpStatus WINGDIPAPI GdipClearPathMarkers(GpPath* path)
1337 {
1338     INT count;
1339     INT i;
1340
1341     TRACE("(%p)\n", path);
1342
1343     if(!path)
1344         return InvalidParameter;
1345
1346     count = path->pathdata.Count;
1347
1348     for(i = 0; i < count - 1; i++){
1349         path->pathdata.Types[i] &= ~PathPointTypePathMarker;
1350     }
1351
1352     return Ok;
1353 }