shell32: Fix shlexec crashing on Vista.
[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 /* make sure path has enough space for len more points */
37 static BOOL lengthen_path(GpPath *path, INT len)
38 {
39     /* initial allocation */
40     if(path->datalen == 0){
41         path->datalen = len * 2;
42
43         path->pathdata.Points = GdipAlloc(path->datalen * sizeof(PointF));
44         if(!path->pathdata.Points)   return FALSE;
45
46         path->pathdata.Types = GdipAlloc(path->datalen);
47         if(!path->pathdata.Types){
48             GdipFree(path->pathdata.Points);
49             return FALSE;
50         }
51     }
52     /* reallocation, double size of arrays */
53     else if(path->datalen - path->pathdata.Count < len){
54         while(path->datalen - path->pathdata.Count < len)
55             path->datalen *= 2;
56
57         path->pathdata.Points = HeapReAlloc(GetProcessHeap(), 0,
58             path->pathdata.Points, path->datalen * sizeof(PointF));
59         if(!path->pathdata.Points)  return FALSE;
60
61         path->pathdata.Types = HeapReAlloc(GetProcessHeap(), 0,
62             path->pathdata.Types, path->datalen);
63         if(!path->pathdata.Types)   return FALSE;
64     }
65
66     return TRUE;
67 }
68
69 GpStatus WINGDIPAPI GdipAddPathArc(GpPath *path, REAL x1, REAL y1, REAL x2,
70     REAL y2, REAL startAngle, REAL sweepAngle)
71 {
72     INT count, old_count, i;
73
74     if(!path)
75         return InvalidParameter;
76
77     count = arc2polybezier(NULL, x1, y1, x2, y2, startAngle, sweepAngle);
78
79     if(count == 0)
80         return Ok;
81     if(!lengthen_path(path, count))
82         return OutOfMemory;
83
84     old_count = path->pathdata.Count;
85     arc2polybezier(&path->pathdata.Points[old_count], x1, y1, x2, y2,
86                    startAngle, sweepAngle);
87
88     for(i = 0; i < count; i++){
89         path->pathdata.Types[old_count + i] = PathPointTypeBezier;
90     }
91
92     path->pathdata.Types[old_count] =
93         (path->newfigure ? PathPointTypeStart : PathPointTypeLine);
94     path->newfigure = FALSE;
95     path->pathdata.Count += count;
96
97     return Ok;
98 }
99
100 GpStatus WINGDIPAPI GdipAddPathArcI(GpPath *path, INT x1, INT y1, INT x2,
101    INT y2, REAL startAngle, REAL sweepAngle)
102 {
103     return GdipAddPathArc(path,(REAL)x1,(REAL)y1,(REAL)x2,(REAL)y2,startAngle,sweepAngle);
104 }
105
106 GpStatus WINGDIPAPI GdipAddPathBezier(GpPath *path, REAL x1, REAL y1, REAL x2,
107     REAL y2, REAL x3, REAL y3, REAL x4, REAL y4)
108 {
109     INT old_count;
110
111     if(!path)
112         return InvalidParameter;
113
114     if(!lengthen_path(path, 4))
115         return OutOfMemory;
116
117     old_count = path->pathdata.Count;
118
119     path->pathdata.Points[old_count].X = x1;
120     path->pathdata.Points[old_count].Y = y1;
121     path->pathdata.Points[old_count + 1].X = x2;
122     path->pathdata.Points[old_count + 1].Y = y2;
123     path->pathdata.Points[old_count + 2].X = x3;
124     path->pathdata.Points[old_count + 2].Y = y3;
125     path->pathdata.Points[old_count + 3].X = x4;
126     path->pathdata.Points[old_count + 3].Y = y4;
127
128     path->pathdata.Types[old_count] =
129         (path->newfigure ? PathPointTypeStart : PathPointTypeLine);
130     path->pathdata.Types[old_count + 1] = PathPointTypeBezier;
131     path->pathdata.Types[old_count + 2] = PathPointTypeBezier;
132     path->pathdata.Types[old_count + 3] = PathPointTypeBezier;
133
134     path->newfigure = FALSE;
135     path->pathdata.Count += 4;
136
137     return Ok;
138 }
139
140 GpStatus WINGDIPAPI GdipAddPathBezierI(GpPath *path, INT x1, INT y1, INT x2,
141     INT y2, INT x3, INT y3, INT x4, INT y4)
142 {
143     return GdipAddPathBezier(path,(REAL)x1,(REAL)y1,(REAL)x2,(REAL)y2,(REAL)x3,(REAL)y3,
144                                   (REAL)x4,(REAL)y4);
145 }
146
147 GpStatus WINGDIPAPI GdipAddPathBeziers(GpPath *path, GDIPCONST GpPointF *points,
148     INT count)
149 {
150     INT i, old_count;
151
152     if(!path || !points || ((count - 1) % 3))
153         return InvalidParameter;
154
155     if(!lengthen_path(path, count))
156         return OutOfMemory;
157
158     old_count = path->pathdata.Count;
159
160     for(i = 0; i < count; i++){
161         path->pathdata.Points[old_count + i].X = points[i].X;
162         path->pathdata.Points[old_count + i].Y = points[i].Y;
163         path->pathdata.Types[old_count + i] = PathPointTypeBezier;
164     }
165
166     path->pathdata.Types[old_count] =
167         (path->newfigure ? PathPointTypeStart : PathPointTypeLine);
168     path->newfigure = FALSE;
169     path->pathdata.Count += count;
170
171     return Ok;
172 }
173
174 GpStatus WINGDIPAPI GdipAddPathBeziersI(GpPath *path, GDIPCONST GpPoint *points,
175     INT count)
176 {
177     GpPointF *ptsF;
178     GpStatus ret;
179     INT i;
180
181     if(!points || ((count - 1) % 3))
182         return InvalidParameter;
183
184     ptsF = GdipAlloc(sizeof(GpPointF) * count);
185     if(!ptsF)
186         return OutOfMemory;
187
188     for(i = 0; i < count; i++){
189         ptsF[i].X = (REAL)points[i].X;
190         ptsF[i].Y = (REAL)points[i].Y;
191     }
192
193     ret = GdipAddPathBeziers(path, ptsF, count);
194     GdipFree(ptsF);
195
196     return ret;
197 }
198
199 GpStatus WINGDIPAPI GdipAddPathEllipse(GpPath *path, REAL x, REAL y, REAL width,
200     REAL height)
201 {
202     INT old_count, numpts;
203
204     if(!path)
205         return InvalidParameter;
206
207     if(!lengthen_path(path, MAX_ARC_PTS))
208         return OutOfMemory;
209
210     old_count = path->pathdata.Count;
211     if((numpts = arc2polybezier(&path->pathdata.Points[old_count],  x, y, width,
212                                height, 0.0, 360.0)) != MAX_ARC_PTS){
213         ERR("expected %d points but got %d\n", MAX_ARC_PTS, numpts);
214         return GenericError;
215     }
216
217     memset(&path->pathdata.Types[old_count + 1], PathPointTypeBezier,
218            MAX_ARC_PTS - 1);
219
220     /* An ellipse is an intrinsic figure (always is its own subpath). */
221     path->pathdata.Types[old_count] = PathPointTypeStart;
222     path->pathdata.Types[old_count + MAX_ARC_PTS - 1] |= PathPointTypeCloseSubpath;
223     path->newfigure = TRUE;
224     path->pathdata.Count += MAX_ARC_PTS;
225
226     return Ok;
227 }
228
229 GpStatus WINGDIPAPI GdipAddPathEllipseI(GpPath *path, INT x, INT y, INT width,
230     INT height)
231 {
232     return GdipAddPathEllipse(path,(REAL)x,(REAL)y,(REAL)width,(REAL)height);
233 }
234
235 GpStatus WINGDIPAPI GdipAddPathLine2(GpPath *path, GDIPCONST GpPointF *points,
236     INT count)
237 {
238     INT i, old_count;
239
240     if(!path || !points)
241         return InvalidParameter;
242
243     if(!lengthen_path(path, count))
244         return OutOfMemory;
245
246     old_count = path->pathdata.Count;
247
248     for(i = 0; i < count; i++){
249         path->pathdata.Points[old_count + i].X = points[i].X;
250         path->pathdata.Points[old_count + i].Y = points[i].Y;
251         path->pathdata.Types[old_count + i] = PathPointTypeLine;
252     }
253
254     if(path->newfigure){
255         path->pathdata.Types[old_count] = PathPointTypeStart;
256         path->newfigure = FALSE;
257     }
258
259     path->pathdata.Count += count;
260
261     return Ok;
262 }
263
264 GpStatus WINGDIPAPI GdipAddPathLine2I(GpPath *path, GDIPCONST GpPoint *points, INT count)
265 {
266     GpPointF *pointsF;
267     INT i;
268     GpStatus stat;
269
270     if(count <= 0)
271         return InvalidParameter;
272
273     pointsF = GdipAlloc(sizeof(GpPointF) * count);
274     if(!pointsF)    return OutOfMemory;
275
276     for(i = 0;i < count; i++){
277         pointsF[i].X = (REAL)points[i].X;
278         pointsF[i].Y = (REAL)points[i].Y;
279     }
280
281     stat = GdipAddPathLine2(path, pointsF, count);
282
283     GdipFree(pointsF);
284
285     return stat;
286 }
287
288 GpStatus WINGDIPAPI GdipAddPathLine(GpPath *path, REAL x1, REAL y1, REAL x2, REAL y2)
289 {
290     INT old_count;
291
292     if(!path)
293         return InvalidParameter;
294
295     if(!lengthen_path(path, 2))
296         return OutOfMemory;
297
298     old_count = path->pathdata.Count;
299
300     path->pathdata.Points[old_count].X = x1;
301     path->pathdata.Points[old_count].Y = y1;
302     path->pathdata.Points[old_count + 1].X = x2;
303     path->pathdata.Points[old_count + 1].Y = y2;
304
305     path->pathdata.Types[old_count] =
306         (path->newfigure ? PathPointTypeStart : PathPointTypeLine);
307     path->pathdata.Types[old_count + 1] = PathPointTypeLine;
308
309     path->newfigure = FALSE;
310     path->pathdata.Count += 2;
311
312     return Ok;
313 }
314
315 GpStatus WINGDIPAPI GdipAddPathLineI(GpPath *path, INT x1, INT y1, INT x2, INT y2)
316 {
317     return GdipAddPathLine(path, (REAL)x1, (REAL)y1, (REAL)x2, (REAL)y2);
318 }
319
320 GpStatus WINGDIPAPI GdipAddPathPath(GpPath *path, GDIPCONST GpPath* addingPath,
321     BOOL connect)
322 {
323     INT old_count, count;
324
325     if(!path || !addingPath)
326         return InvalidParameter;
327
328     old_count = path->pathdata.Count;
329     count = addingPath->pathdata.Count;
330
331     if(!lengthen_path(path, count))
332         return OutOfMemory;
333
334     memcpy(&path->pathdata.Points[old_count], addingPath->pathdata.Points,
335            count * sizeof(GpPointF));
336     memcpy(&path->pathdata.Types[old_count], addingPath->pathdata.Types, count);
337
338     if(path->newfigure || !connect)
339         path->pathdata.Types[old_count] = PathPointTypeStart;
340     else
341         path->pathdata.Types[old_count] = PathPointTypeLine;
342
343     path->newfigure = FALSE;
344     path->pathdata.Count += count;
345
346     return Ok;
347 }
348
349 GpStatus WINGDIPAPI GdipAddPathPolygon(GpPath *path, GDIPCONST GpPointF *points, INT count)
350 {
351     INT old_count;
352
353     if(!path || !points || count < 3)
354         return InvalidParameter;
355
356     if(!lengthen_path(path, count))
357         return OutOfMemory;
358
359     old_count = path->pathdata.Count;
360
361     memcpy(&path->pathdata.Points[old_count], points, count*sizeof(GpPointF));
362     memset(&path->pathdata.Types[old_count + 1], PathPointTypeLine, count - 1);
363
364     /* A polygon is an intrinsic figure */
365     path->pathdata.Types[old_count] = PathPointTypeStart;
366     path->pathdata.Types[old_count + count - 1] |= PathPointTypeCloseSubpath;
367     path->newfigure = TRUE;
368     path->pathdata.Count += count;
369
370     return Ok;
371 }
372
373 GpStatus WINGDIPAPI GdipAddPathPolygonI(GpPath *path, GDIPCONST GpPoint *points, INT count)
374 {
375     GpPointF *ptf;
376     GpStatus status;
377     INT i;
378
379     if(!points || count < 3)
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     status = GdipAddPathPolygon(path, ptf, count);
392
393     GdipFree(ptf);
394
395     return status;
396 }
397
398 GpStatus WINGDIPAPI GdipClonePath(GpPath* path, GpPath **clone)
399 {
400     if(!path || !clone)
401         return InvalidParameter;
402
403     *clone = GdipAlloc(sizeof(GpPath));
404     if(!*clone) return OutOfMemory;
405
406     **clone = *path;
407
408     (*clone)->pathdata.Points = GdipAlloc(path->datalen * sizeof(PointF));
409     (*clone)->pathdata.Types = GdipAlloc(path->datalen);
410     if(!(*clone)->pathdata.Points || !(*clone)->pathdata.Types){
411         GdipFree(*clone);
412         GdipFree((*clone)->pathdata.Points);
413         GdipFree((*clone)->pathdata.Types);
414         return OutOfMemory;
415     }
416
417     memcpy((*clone)->pathdata.Points, path->pathdata.Points,
418            path->datalen * sizeof(PointF));
419     memcpy((*clone)->pathdata.Types, path->pathdata.Types, path->datalen);
420
421     return Ok;
422 }
423
424 GpStatus WINGDIPAPI GdipClosePathFigure(GpPath* path)
425 {
426     if(!path)
427         return InvalidParameter;
428
429     if(path->pathdata.Count > 0){
430         path->pathdata.Types[path->pathdata.Count - 1] |= PathPointTypeCloseSubpath;
431         path->newfigure = TRUE;
432     }
433
434     return Ok;
435 }
436
437 GpStatus WINGDIPAPI GdipClosePathFigures(GpPath* path)
438 {
439     INT i;
440
441     if(!path)
442         return InvalidParameter;
443
444     for(i = 1; i < path->pathdata.Count; i++){
445         if(path->pathdata.Types[i] == PathPointTypeStart)
446             path->pathdata.Types[i-1] |= PathPointTypeCloseSubpath;
447     }
448
449     path->newfigure = TRUE;
450
451     return Ok;
452 }
453
454 GpStatus WINGDIPAPI GdipCreatePath(GpFillMode fill, GpPath **path)
455 {
456     if(!path)
457         return InvalidParameter;
458
459     *path = GdipAlloc(sizeof(GpPath));
460     if(!*path)  return OutOfMemory;
461
462     (*path)->fill = fill;
463     (*path)->newfigure = TRUE;
464
465     return Ok;
466 }
467
468 GpStatus WINGDIPAPI GdipCreatePath2(GDIPCONST GpPointF* points,
469     GDIPCONST BYTE* types, INT count, GpFillMode fill, GpPath **path)
470 {
471     if(!path)
472         return InvalidParameter;
473
474     *path = GdipAlloc(sizeof(GpPath));
475     if(!*path)  return OutOfMemory;
476
477     (*path)->pathdata.Points = GdipAlloc(count * sizeof(PointF));
478     (*path)->pathdata.Types = GdipAlloc(count);
479
480     if(!(*path)->pathdata.Points || !(*path)->pathdata.Types){
481         GdipFree((*path)->pathdata.Points);
482         GdipFree((*path)->pathdata.Types);
483         GdipFree(*path);
484         return OutOfMemory;
485     }
486
487     memcpy((*path)->pathdata.Points, points, count * sizeof(PointF));
488     memcpy((*path)->pathdata.Types, types, count);
489     (*path)->pathdata.Count = count;
490     (*path)->datalen = count;
491
492     (*path)->fill = fill;
493     (*path)->newfigure = TRUE;
494
495     return Ok;
496 }
497
498 GpStatus WINGDIPAPI GdipCreatePath2I(GDIPCONST GpPoint* points,
499     GDIPCONST BYTE* types, INT count, GpFillMode fill, GpPath **path)
500 {
501     GpPointF *ptF;
502     GpStatus ret;
503     INT i;
504
505     ptF = GdipAlloc(sizeof(GpPointF)*count);
506
507     for(i = 0;i < count; i++){
508         ptF[i].X = (REAL)points[i].X;
509         ptF[i].Y = (REAL)points[i].Y;
510     }
511
512     ret = GdipCreatePath2(ptF, types, count, fill, path);
513
514     GdipFree(ptF);
515
516     return ret;
517 }
518
519 GpStatus WINGDIPAPI GdipDeletePath(GpPath *path)
520 {
521     if(!path)
522         return InvalidParameter;
523
524     GdipFree(path->pathdata.Points);
525     GdipFree(path->pathdata.Types);
526     GdipFree(path);
527
528     return Ok;
529 }
530
531 GpStatus WINGDIPAPI GdipGetPathData(GpPath *path, GpPathData* pathData)
532 {
533     if(!path || !pathData)
534         return InvalidParameter;
535
536     /* Only copy data. pathData allocation/freeing controlled by wrapper class.
537        Assumed that pathData is enough wide to get all data - controlled by wrapper too. */
538     memcpy(pathData->Points, path->pathdata.Points, sizeof(PointF) * pathData->Count);
539     memcpy(pathData->Types , path->pathdata.Types , pathData->Count);
540
541     return Ok;
542 }
543
544 GpStatus WINGDIPAPI GdipGetPathFillMode(GpPath *path, GpFillMode *fillmode)
545 {
546     if(!path || !fillmode)
547         return InvalidParameter;
548
549     *fillmode = path->fill;
550
551     return Ok;
552 }
553
554 GpStatus WINGDIPAPI GdipGetPathLastPoint(GpPath* path, GpPointF* lastPoint)
555 {
556     INT count;
557
558     if(!path || !lastPoint)
559         return InvalidParameter;
560
561     count = path->pathdata.Count;
562     if(count > 0)
563         *lastPoint = path->pathdata.Points[count-1];
564
565     return Ok;
566 }
567
568 GpStatus WINGDIPAPI GdipGetPathPoints(GpPath *path, GpPointF* points, INT count)
569 {
570     if(!path)
571         return InvalidParameter;
572
573     if(count < path->pathdata.Count)
574         return InsufficientBuffer;
575
576     memcpy(points, path->pathdata.Points, path->pathdata.Count * sizeof(GpPointF));
577
578     return Ok;
579 }
580
581 GpStatus WINGDIPAPI GdipGetPathPointsI(GpPath *path, GpPoint* points, INT count)
582 {
583     GpStatus ret;
584     GpPointF *ptf;
585     INT i;
586
587     if(count <= 0)
588         return InvalidParameter;
589
590     ptf = GdipAlloc(sizeof(GpPointF)*count);
591     if(!ptf)    return OutOfMemory;
592
593     ret = GdipGetPathPoints(path,ptf,count);
594     if(ret == Ok)
595         for(i = 0;i < count;i++){
596             points[i].X = roundr(ptf[i].X);
597             points[i].Y = roundr(ptf[i].Y);
598         };
599     GdipFree(ptf);
600
601     return ret;
602 }
603
604 GpStatus WINGDIPAPI GdipGetPathTypes(GpPath *path, BYTE* types, INT count)
605 {
606     if(!path)
607         return InvalidParameter;
608
609     if(count < path->pathdata.Count)
610         return InsufficientBuffer;
611
612     memcpy(types, path->pathdata.Types, path->pathdata.Count);
613
614     return Ok;
615 }
616
617 /* Windows expands the bounding box to the maximum possible bounding box
618  * for a given pen.  For example, if a line join can extend past the point
619  * it's joining by x units, the bounding box is extended by x units in every
620  * direction (even though this is too conservative for most cases). */
621 GpStatus WINGDIPAPI GdipGetPathWorldBounds(GpPath* path, GpRectF* bounds,
622     GDIPCONST GpMatrix *matrix, GDIPCONST GpPen *pen)
623 {
624     GpPointF * points, temp_pts[4];
625     INT count, i;
626     REAL path_width = 1.0, width, height, temp, low_x, low_y, high_x, high_y;
627
628     /* Matrix and pen can be null. */
629     if(!path || !bounds)
630         return InvalidParameter;
631
632     /* If path is empty just return. */
633     count = path->pathdata.Count;
634     if(count == 0){
635         bounds->X = bounds->Y = bounds->Width = bounds->Height = 0.0;
636         return Ok;
637     }
638
639     points = path->pathdata.Points;
640
641     low_x = high_x = points[0].X;
642     low_y = high_y = points[0].Y;
643
644     for(i = 1; i < count; i++){
645         low_x = min(low_x, points[i].X);
646         low_y = min(low_y, points[i].Y);
647         high_x = max(high_x, points[i].X);
648         high_y = max(high_y, points[i].Y);
649     }
650
651     width = high_x - low_x;
652     height = high_y - low_y;
653
654     /* This looks unusual but it's the only way I can imitate windows. */
655     if(matrix){
656         temp_pts[0].X = low_x;
657         temp_pts[0].Y = low_y;
658         temp_pts[1].X = low_x;
659         temp_pts[1].Y = high_y;
660         temp_pts[2].X = high_x;
661         temp_pts[2].Y = high_y;
662         temp_pts[3].X = high_x;
663         temp_pts[3].Y = low_y;
664
665         GdipTransformMatrixPoints((GpMatrix*)matrix, temp_pts, 4);
666         low_x = temp_pts[0].X;
667         low_y = temp_pts[0].Y;
668
669         for(i = 1; i < 4; i++){
670             low_x = min(low_x, temp_pts[i].X);
671             low_y = min(low_y, temp_pts[i].Y);
672         }
673
674         temp = width;
675         width = height * fabs(matrix->matrix[2]) + width * fabs(matrix->matrix[0]);
676         height = height * fabs(matrix->matrix[3]) + temp * fabs(matrix->matrix[1]);
677     }
678
679     if(pen){
680         path_width = pen->width / 2.0;
681
682         if(count > 2)
683             path_width = max(path_width,  pen->width * pen->miterlimit / 2.0);
684         /* FIXME: this should probably also check for the startcap */
685         if(pen->endcap & LineCapNoAnchor)
686             path_width = max(path_width,  pen->width * 2.2);
687
688         low_x -= path_width;
689         low_y -= path_width;
690         width += 2.0 * path_width;
691         height += 2.0 * path_width;
692     }
693
694     bounds->X = low_x;
695     bounds->Y = low_y;
696     bounds->Width = width;
697     bounds->Height = height;
698
699     return Ok;
700 }
701
702 GpStatus WINGDIPAPI GdipGetPathWorldBoundsI(GpPath* path, GpRect* bounds,
703     GDIPCONST GpMatrix *matrix, GDIPCONST GpPen *pen)
704 {
705     GpStatus ret;
706     GpRectF boundsF;
707
708     ret = GdipGetPathWorldBounds(path,&boundsF,matrix,pen);
709
710     if(ret == Ok){
711         bounds->X      = roundr(boundsF.X);
712         bounds->Y      = roundr(boundsF.Y);
713         bounds->Width  = roundr(boundsF.Width);
714         bounds->Height = roundr(boundsF.Height);
715     }
716
717     return ret;
718 }
719
720 GpStatus WINGDIPAPI GdipGetPointCount(GpPath *path, INT *count)
721 {
722     if(!path)
723         return InvalidParameter;
724
725     *count = path->pathdata.Count;
726
727     return Ok;
728 }
729
730 GpStatus WINGDIPAPI GdipIsOutlineVisiblePathPointI(GpPath* path, INT x, INT y,
731     GpPen *pen, GpGraphics *graphics, BOOL *result)
732 {
733     return GdipIsOutlineVisiblePathPoint(path, x, y, pen, graphics, result);
734 }
735
736 GpStatus WINGDIPAPI GdipIsOutlineVisiblePathPoint(GpPath* path, REAL x, REAL y,
737     GpPen *pen, GpGraphics *graphics, BOOL *result)
738 {
739     static int calls;
740
741     if(!path || !pen)
742         return InvalidParameter;
743
744     if(!(calls++))
745         FIXME("not implemented\n");
746
747     return NotImplemented;
748 }
749
750 GpStatus WINGDIPAPI GdipIsVisiblePathPointI(GpPath* path, INT x, INT y, GpGraphics *graphics, BOOL *result)
751 {
752     return GdipIsVisiblePathPoint(path, x, y, graphics, result);
753 }
754
755 GpStatus WINGDIPAPI GdipIsVisiblePathPoint(GpPath* path, REAL x, REAL y, GpGraphics *graphics, BOOL *result)
756 {
757     static int calls;
758
759     if(!path) return InvalidParameter;
760
761     if(!(calls++))
762         FIXME("not implemented\n");
763
764     return NotImplemented;
765 }
766
767 GpStatus WINGDIPAPI GdipStartPathFigure(GpPath *path)
768 {
769     if(!path)
770         return InvalidParameter;
771
772     path->newfigure = TRUE;
773
774     return Ok;
775 }
776
777 GpStatus WINGDIPAPI GdipResetPath(GpPath *path)
778 {
779     if(!path)
780         return InvalidParameter;
781
782     path->pathdata.Count = 0;
783     path->newfigure = TRUE;
784     path->fill = FillModeAlternate;
785
786     return Ok;
787 }
788
789 GpStatus WINGDIPAPI GdipSetPathFillMode(GpPath *path, GpFillMode fill)
790 {
791     if(!path)
792         return InvalidParameter;
793
794     path->fill = fill;
795
796     return Ok;
797 }
798
799 GpStatus WINGDIPAPI GdipTransformPath(GpPath *path, GpMatrix *matrix)
800 {
801     if(!path)
802         return InvalidParameter;
803
804     if(path->pathdata.Count == 0)
805         return Ok;
806
807     return GdipTransformMatrixPoints(matrix, path->pathdata.Points,
808                                      path->pathdata.Count);
809 }
810
811 GpStatus WINGDIPAPI GdipAddPathRectangle(GpPath *path, REAL x, REAL y,
812     REAL width, REAL height)
813 {
814     GpPath *backup;
815     GpPointF ptf[2];
816     GpStatus retstat;
817     BOOL old_new;
818
819     if(!path || width < 0.0 || height < 0.0)
820         return InvalidParameter;
821
822     /* make a backup copy of path data */
823     if((retstat = GdipClonePath(path, &backup)) != Ok)
824         return retstat;
825
826     /* rectangle should start as new path */
827     old_new = path->newfigure;
828     path->newfigure = TRUE;
829     if((retstat = GdipAddPathLine(path,x,y,x+width,y)) != Ok){
830         path->newfigure = old_new;
831         goto fail;
832     }
833
834     ptf[0].X = x+width;
835     ptf[0].Y = y+height;
836     ptf[1].X = x;
837     ptf[1].Y = y+height;
838
839     if((retstat = GdipAddPathLine2(path, ptf, 2)) != Ok)  goto fail;
840     path->pathdata.Types[path->pathdata.Count-1] |= PathPointTypeCloseSubpath;
841
842     /* free backup */
843     GdipDeletePath(backup);
844     return Ok;
845
846 fail:
847     /* reverting */
848     GdipDeletePath(path);
849     GdipClonePath(backup, &path);
850     GdipDeletePath(backup);
851
852     return retstat;
853 }
854
855 GpStatus WINGDIPAPI GdipAddPathRectangleI(GpPath *path, INT x, INT y,
856     INT width, INT height)
857 {
858     return GdipAddPathRectangle(path,(REAL)x,(REAL)y,(REAL)width,(REAL)height);
859 }
860
861 GpStatus WINGDIPAPI GdipAddPathRectangles(GpPath *path, GDIPCONST GpRectF *rects, INT count)
862 {
863     GpPath *backup;
864     GpStatus retstat;
865     INT i;
866
867     /* count == 0 - verified condition  */
868     if(!path || !rects || count == 0)
869         return InvalidParameter;
870
871     if(count < 0)
872         return OutOfMemory;
873
874     /* make a backup copy */
875     if((retstat = GdipClonePath(path, &backup)) != Ok)
876         return retstat;
877
878     for(i = 0; i < count; i++){
879         if((retstat = GdipAddPathRectangle(path,rects[i].X,rects[i].Y,rects[i].Width,rects[i].Height)) != Ok)
880             goto fail;
881     }
882
883     /* free backup */
884     GdipDeletePath(backup);
885     return Ok;
886
887 fail:
888     /* reverting */
889     GdipDeletePath(path);
890     GdipClonePath(backup, &path);
891     GdipDeletePath(backup);
892
893     return retstat;
894 }
895
896 GpStatus WINGDIPAPI GdipAddPathRectanglesI(GpPath *path, GDIPCONST GpRect *rects, INT count)
897 {
898     GpRectF *rectsF;
899     GpStatus retstat;
900     INT i;
901
902     if(!rects || count == 0)
903         return InvalidParameter;
904
905     if(count < 0)
906         return OutOfMemory;
907
908     rectsF = GdipAlloc(sizeof(GpRectF)*count);
909
910     for(i = 0;i < count;i++){
911         rectsF[i].X      = (REAL)rects[i].X;
912         rectsF[i].Y      = (REAL)rects[i].Y;
913         rectsF[i].Width  = (REAL)rects[i].Width;
914         rectsF[i].Height = (REAL)rects[i].Height;
915     }
916
917     retstat = GdipAddPathRectangles(path, rectsF, count);
918     GdipFree(rectsF);
919
920     return retstat;
921 }
922
923 GpStatus WINGDIPAPI GdipSetPathMarker(GpPath* path)
924 {
925     INT count;
926
927     if(!path)
928         return InvalidParameter;
929
930     count = path->pathdata.Count;
931
932     /* set marker flag */
933     if(count > 0)
934         path->pathdata.Types[count-1] |= PathPointTypePathMarker;
935
936     return Ok;
937 }
938
939 GpStatus WINGDIPAPI GdipClearPathMarkers(GpPath* path)
940 {
941     INT count;
942     INT i;
943
944     if(!path)
945         return InvalidParameter;
946
947     count = path->pathdata.Count;
948
949     for(i = 0; i < count - 1; i++){
950         path->pathdata.Types[i] &= ~PathPointTypePathMarker;
951     }
952
953     return Ok;
954 }