push 6495605bb529da27066f1c178d57d902552737a0
[wine/hacks.git] / dlls / gdiplus / graphicspath.c
blobd465aaf8bda1629a0a3e9272714e47e288bb2f95
1 /*
2 * Copyright (C) 2007 Google (Evan Stade)
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.
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.
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
20 #include <stdarg.h>
21 #include <math.h>
23 #include "windef.h"
24 #include "winbase.h"
25 #include "winuser.h"
26 #include "wingdi.h"
28 #include "objbase.h"
30 #include "gdiplus.h"
31 #include "gdiplus_private.h"
32 #include "wine/debug.h"
34 WINE_DEFAULT_DEBUG_CHANNEL(gdiplus);
36 GpStatus WINGDIPAPI GdipAddPathArc(GpPath *path, REAL x1, REAL y1, REAL x2,
37 REAL y2, REAL startAngle, REAL sweepAngle)
39 INT count, old_count, i;
41 if(!path)
42 return InvalidParameter;
44 count = arc2polybezier(NULL, x1, y1, x2, y2, startAngle, sweepAngle);
46 if(count == 0)
47 return Ok;
48 if(!lengthen_path(path, count))
49 return OutOfMemory;
51 old_count = path->pathdata.Count;
52 arc2polybezier(&path->pathdata.Points[old_count], x1, y1, x2, y2,
53 startAngle, sweepAngle);
55 for(i = 0; i < count; i++){
56 path->pathdata.Types[old_count + i] = PathPointTypeBezier;
59 path->pathdata.Types[old_count] =
60 (path->newfigure ? PathPointTypeStart : PathPointTypeLine);
61 path->newfigure = FALSE;
62 path->pathdata.Count += count;
64 return Ok;
67 GpStatus WINGDIPAPI GdipAddPathArcI(GpPath *path, INT x1, INT y1, INT x2,
68 INT y2, REAL startAngle, REAL sweepAngle)
70 return GdipAddPathArc(path,(REAL)x1,(REAL)y1,(REAL)x2,(REAL)y2,startAngle,sweepAngle);
73 GpStatus WINGDIPAPI GdipAddPathBezier(GpPath *path, REAL x1, REAL y1, REAL x2,
74 REAL y2, REAL x3, REAL y3, REAL x4, REAL y4)
76 INT old_count;
78 if(!path)
79 return InvalidParameter;
81 if(!lengthen_path(path, 4))
82 return OutOfMemory;
84 old_count = path->pathdata.Count;
86 path->pathdata.Points[old_count].X = x1;
87 path->pathdata.Points[old_count].Y = y1;
88 path->pathdata.Points[old_count + 1].X = x2;
89 path->pathdata.Points[old_count + 1].Y = y2;
90 path->pathdata.Points[old_count + 2].X = x3;
91 path->pathdata.Points[old_count + 2].Y = y3;
92 path->pathdata.Points[old_count + 3].X = x4;
93 path->pathdata.Points[old_count + 3].Y = y4;
95 path->pathdata.Types[old_count] =
96 (path->newfigure ? PathPointTypeStart : PathPointTypeLine);
97 path->pathdata.Types[old_count + 1] = PathPointTypeBezier;
98 path->pathdata.Types[old_count + 2] = PathPointTypeBezier;
99 path->pathdata.Types[old_count + 3] = PathPointTypeBezier;
101 path->newfigure = FALSE;
102 path->pathdata.Count += 4;
104 return Ok;
107 GpStatus WINGDIPAPI GdipAddPathBezierI(GpPath *path, INT x1, INT y1, INT x2,
108 INT y2, INT x3, INT y3, INT x4, INT y4)
110 return GdipAddPathBezier(path,(REAL)x1,(REAL)y1,(REAL)x2,(REAL)y2,(REAL)x3,(REAL)y3,
111 (REAL)x4,(REAL)y4);
114 GpStatus WINGDIPAPI GdipAddPathBeziers(GpPath *path, GDIPCONST GpPointF *points,
115 INT count)
117 INT i, old_count;
119 if(!path || !points || ((count - 1) % 3))
120 return InvalidParameter;
122 if(!lengthen_path(path, count))
123 return OutOfMemory;
125 old_count = path->pathdata.Count;
127 for(i = 0; i < count; i++){
128 path->pathdata.Points[old_count + i].X = points[i].X;
129 path->pathdata.Points[old_count + i].Y = points[i].Y;
130 path->pathdata.Types[old_count + i] = PathPointTypeBezier;
133 path->pathdata.Types[old_count] =
134 (path->newfigure ? PathPointTypeStart : PathPointTypeLine);
135 path->newfigure = FALSE;
136 path->pathdata.Count += count;
138 return Ok;
141 GpStatus WINGDIPAPI GdipAddPathBeziersI(GpPath *path, GDIPCONST GpPoint *points,
142 INT count)
144 GpPointF *ptsF;
145 GpStatus ret;
146 INT i;
148 if(!points || ((count - 1) % 3))
149 return InvalidParameter;
151 ptsF = GdipAlloc(sizeof(GpPointF) * count);
152 if(!ptsF)
153 return OutOfMemory;
155 for(i = 0; i < count; i++){
156 ptsF[i].X = (REAL)points[i].X;
157 ptsF[i].Y = (REAL)points[i].Y;
160 ret = GdipAddPathBeziers(path, ptsF, count);
161 GdipFree(ptsF);
163 return ret;
166 GpStatus WINGDIPAPI GdipAddPathClosedCurve(GpPath *path, GDIPCONST GpPointF *points,
167 INT count)
169 return GdipAddPathClosedCurve2(path, points, count, 1.0);
172 GpStatus WINGDIPAPI GdipAddPathClosedCurveI(GpPath *path, GDIPCONST GpPoint *points,
173 INT count)
175 return GdipAddPathClosedCurve2I(path, points, count, 1.0);
178 GpStatus WINGDIPAPI GdipAddPathClosedCurve2(GpPath *path, GDIPCONST GpPointF *points,
179 INT count, REAL tension)
181 INT i, len_pt = (count + 1)*3-2;
182 GpPointF *pt;
183 GpPointF *pts;
184 REAL x1, x2, y1, y2;
185 GpStatus stat;
187 if(!path || !points || count <= 1)
188 return InvalidParameter;
190 pt = GdipAlloc(len_pt * sizeof(GpPointF));
191 pts = GdipAlloc((count + 1)*sizeof(GpPointF));
192 if(!pt || !pts){
193 GdipFree(pt);
194 GdipFree(pts);
195 return OutOfMemory;
198 /* copy source points to extend with the last one */
199 memcpy(pts, points, sizeof(GpPointF)*count);
200 pts[count] = pts[0];
202 tension = tension * TENSION_CONST;
204 for(i = 0; i < count-1; i++){
205 calc_curve_bezier(&(pts[i]), tension, &x1, &y1, &x2, &y2);
207 pt[3*i+2].X = x1;
208 pt[3*i+2].Y = y1;
209 pt[3*i+3].X = pts[i+1].X;
210 pt[3*i+3].Y = pts[i+1].Y;
211 pt[3*i+4].X = x2;
212 pt[3*i+4].Y = y2;
215 /* points [len_pt-2] and [0] are calculated
216 separetely to connect splines properly */
217 pts[0] = points[count-1];
218 pts[1] = points[0]; /* equals to start and end of a resulting path */
219 pts[2] = points[1];
221 calc_curve_bezier(pts, tension, &x1, &y1, &x2, &y2);
222 pt[len_pt-2].X = x1;
223 pt[len_pt-2].Y = y1;
224 pt[0].X = pts[1].X;
225 pt[0].Y = pts[1].Y;
226 pt[1].X = x2;
227 pt[1].Y = y2;
228 /* close path */
229 pt[len_pt-1].X = pt[0].X;
230 pt[len_pt-1].Y = pt[0].Y;
232 stat = GdipAddPathBeziers(path, pt, len_pt);
234 /* close figure */
235 if(stat == Ok){
236 INT count = path->pathdata.Count;
237 path->pathdata.Types[count - 1] |= PathPointTypeCloseSubpath;
238 path->newfigure = TRUE;
241 GdipFree(pts);
242 GdipFree(pt);
244 return stat;
247 GpStatus WINGDIPAPI GdipAddPathClosedCurve2I(GpPath *path, GDIPCONST GpPoint *points,
248 INT count, REAL tension)
250 GpPointF *ptf;
251 INT i;
252 GpStatus stat;
254 if(!path || !points || count <= 1)
255 return InvalidParameter;
257 ptf = GdipAlloc(sizeof(GpPointF)*count);
258 if(!ptf)
259 return OutOfMemory;
261 for(i = 0; i < count; i++){
262 ptf[i].X = (REAL)points[i].X;
263 ptf[i].Y = (REAL)points[i].Y;
266 stat = GdipAddPathClosedCurve2(path, ptf, count, tension);
268 GdipFree(ptf);
270 return stat;
273 GpStatus WINGDIPAPI GdipAddPathCurve(GpPath *path, GDIPCONST GpPointF *points, INT count)
275 if(!path || !points || count <= 1)
276 return InvalidParameter;
278 return GdipAddPathCurve2(path, points, count, 1.0);
281 GpStatus WINGDIPAPI GdipAddPathCurveI(GpPath *path, GDIPCONST GpPoint *points, INT count)
283 if(!path || !points || count <= 1)
284 return InvalidParameter;
286 return GdipAddPathCurve2I(path, points, count, 1.0);
289 GpStatus WINGDIPAPI GdipAddPathCurve2(GpPath *path, GDIPCONST GpPointF *points, INT count,
290 REAL tension)
292 INT i, len_pt = count*3-2;
293 GpPointF *pt;
294 REAL x1, x2, y1, y2;
295 GpStatus stat;
297 if(!path || !points || count <= 1)
298 return InvalidParameter;
300 pt = GdipAlloc(len_pt * sizeof(GpPointF));
301 if(!pt)
302 return OutOfMemory;
304 tension = tension * TENSION_CONST;
306 calc_curve_bezier_endp(points[0].X, points[0].Y, points[1].X, points[1].Y,
307 tension, &x1, &y1);
309 pt[0].X = points[0].X;
310 pt[0].Y = points[0].Y;
311 pt[1].X = x1;
312 pt[1].Y = y1;
314 for(i = 0; i < count-2; i++){
315 calc_curve_bezier(&(points[i]), tension, &x1, &y1, &x2, &y2);
317 pt[3*i+2].X = x1;
318 pt[3*i+2].Y = y1;
319 pt[3*i+3].X = points[i+1].X;
320 pt[3*i+3].Y = points[i+1].Y;
321 pt[3*i+4].X = x2;
322 pt[3*i+4].Y = y2;
325 calc_curve_bezier_endp(points[count-1].X, points[count-1].Y,
326 points[count-2].X, points[count-2].Y, tension, &x1, &y1);
328 pt[len_pt-2].X = x1;
329 pt[len_pt-2].Y = y1;
330 pt[len_pt-1].X = points[count-1].X;
331 pt[len_pt-1].Y = points[count-1].Y;
333 stat = GdipAddPathBeziers(path, pt, len_pt);
335 GdipFree(pt);
337 return stat;
340 GpStatus WINGDIPAPI GdipAddPathCurve2I(GpPath *path, GDIPCONST GpPoint *points,
341 INT count, REAL tension)
343 GpPointF *ptf;
344 INT i;
345 GpStatus stat;
347 if(!path || !points || count <= 1)
348 return InvalidParameter;
350 ptf = GdipAlloc(sizeof(GpPointF)*count);
351 if(!ptf)
352 return OutOfMemory;
354 for(i = 0; i < count; i++){
355 ptf[i].X = (REAL)points[i].X;
356 ptf[i].Y = (REAL)points[i].Y;
359 stat = GdipAddPathCurve2(path, ptf, count, tension);
361 GdipFree(ptf);
363 return stat;
366 GpStatus WINGDIPAPI GdipAddPathEllipse(GpPath *path, REAL x, REAL y, REAL width,
367 REAL height)
369 INT old_count, numpts;
371 if(!path)
372 return InvalidParameter;
374 if(!lengthen_path(path, MAX_ARC_PTS))
375 return OutOfMemory;
377 old_count = path->pathdata.Count;
378 if((numpts = arc2polybezier(&path->pathdata.Points[old_count], x, y, width,
379 height, 0.0, 360.0)) != MAX_ARC_PTS){
380 ERR("expected %d points but got %d\n", MAX_ARC_PTS, numpts);
381 return GenericError;
384 memset(&path->pathdata.Types[old_count + 1], PathPointTypeBezier,
385 MAX_ARC_PTS - 1);
387 /* An ellipse is an intrinsic figure (always is its own subpath). */
388 path->pathdata.Types[old_count] = PathPointTypeStart;
389 path->pathdata.Types[old_count + MAX_ARC_PTS - 1] |= PathPointTypeCloseSubpath;
390 path->newfigure = TRUE;
391 path->pathdata.Count += MAX_ARC_PTS;
393 return Ok;
396 GpStatus WINGDIPAPI GdipAddPathEllipseI(GpPath *path, INT x, INT y, INT width,
397 INT height)
399 return GdipAddPathEllipse(path,(REAL)x,(REAL)y,(REAL)width,(REAL)height);
402 GpStatus WINGDIPAPI GdipAddPathLine2(GpPath *path, GDIPCONST GpPointF *points,
403 INT count)
405 INT i, old_count;
407 if(!path || !points)
408 return InvalidParameter;
410 if(!lengthen_path(path, count))
411 return OutOfMemory;
413 old_count = path->pathdata.Count;
415 for(i = 0; i < count; i++){
416 path->pathdata.Points[old_count + i].X = points[i].X;
417 path->pathdata.Points[old_count + i].Y = points[i].Y;
418 path->pathdata.Types[old_count + i] = PathPointTypeLine;
421 if(path->newfigure){
422 path->pathdata.Types[old_count] = PathPointTypeStart;
423 path->newfigure = FALSE;
426 path->pathdata.Count += count;
428 return Ok;
431 GpStatus WINGDIPAPI GdipAddPathLine2I(GpPath *path, GDIPCONST GpPoint *points, INT count)
433 GpPointF *pointsF;
434 INT i;
435 GpStatus stat;
437 if(count <= 0)
438 return InvalidParameter;
440 pointsF = GdipAlloc(sizeof(GpPointF) * count);
441 if(!pointsF) return OutOfMemory;
443 for(i = 0;i < count; i++){
444 pointsF[i].X = (REAL)points[i].X;
445 pointsF[i].Y = (REAL)points[i].Y;
448 stat = GdipAddPathLine2(path, pointsF, count);
450 GdipFree(pointsF);
452 return stat;
455 GpStatus WINGDIPAPI GdipAddPathLine(GpPath *path, REAL x1, REAL y1, REAL x2, REAL y2)
457 INT old_count;
459 if(!path)
460 return InvalidParameter;
462 if(!lengthen_path(path, 2))
463 return OutOfMemory;
465 old_count = path->pathdata.Count;
467 path->pathdata.Points[old_count].X = x1;
468 path->pathdata.Points[old_count].Y = y1;
469 path->pathdata.Points[old_count + 1].X = x2;
470 path->pathdata.Points[old_count + 1].Y = y2;
472 path->pathdata.Types[old_count] =
473 (path->newfigure ? PathPointTypeStart : PathPointTypeLine);
474 path->pathdata.Types[old_count + 1] = PathPointTypeLine;
476 path->newfigure = FALSE;
477 path->pathdata.Count += 2;
479 return Ok;
482 GpStatus WINGDIPAPI GdipAddPathLineI(GpPath *path, INT x1, INT y1, INT x2, INT y2)
484 return GdipAddPathLine(path, (REAL)x1, (REAL)y1, (REAL)x2, (REAL)y2);
487 GpStatus WINGDIPAPI GdipAddPathPath(GpPath *path, GDIPCONST GpPath* addingPath,
488 BOOL connect)
490 INT old_count, count;
492 if(!path || !addingPath)
493 return InvalidParameter;
495 old_count = path->pathdata.Count;
496 count = addingPath->pathdata.Count;
498 if(!lengthen_path(path, count))
499 return OutOfMemory;
501 memcpy(&path->pathdata.Points[old_count], addingPath->pathdata.Points,
502 count * sizeof(GpPointF));
503 memcpy(&path->pathdata.Types[old_count], addingPath->pathdata.Types, count);
505 if(path->newfigure || !connect)
506 path->pathdata.Types[old_count] = PathPointTypeStart;
507 else
508 path->pathdata.Types[old_count] = PathPointTypeLine;
510 path->newfigure = FALSE;
511 path->pathdata.Count += count;
513 return Ok;
516 GpStatus WINGDIPAPI GdipAddPathPie(GpPath *path, REAL x, REAL y, REAL width, REAL height,
517 REAL startAngle, REAL sweepAngle)
519 GpPointF *ptf;
520 GpStatus status;
521 INT i, count;
523 if(!path)
524 return InvalidParameter;
526 count = arc2polybezier(NULL, x, y, width, height, startAngle, sweepAngle);
528 if(count == 0)
529 return Ok;
531 ptf = GdipAlloc(sizeof(GpPointF)*count);
532 if(!ptf)
533 return OutOfMemory;
535 arc2polybezier(ptf, x, y, width, height, startAngle, sweepAngle);
537 status = GdipAddPathLine(path, (width - x)/2, (height - y)/2, ptf[0].X, ptf[0].Y);
538 if(status != Ok){
539 GdipFree(ptf);
540 return status;
542 /* one spline is already added as a line endpoint */
543 if(!lengthen_path(path, count - 1)){
544 GdipFree(ptf);
545 return OutOfMemory;
548 memcpy(&(path->pathdata.Points[path->pathdata.Count]), &(ptf[1]),sizeof(GpPointF)*(count-1));
549 for(i = 0; i < count-1; i++)
550 path->pathdata.Types[path->pathdata.Count+i] = PathPointTypeBezier;
552 path->pathdata.Count += count-1;
554 GdipClosePathFigure(path);
556 GdipFree(ptf);
558 return status;
561 GpStatus WINGDIPAPI GdipAddPathPieI(GpPath *path, INT x, INT y, INT width, INT height,
562 REAL startAngle, REAL sweepAngle)
564 return GdipAddPathPieI(path, (REAL)x, (REAL)y, (REAL)width, (REAL)height, startAngle, sweepAngle);
567 GpStatus WINGDIPAPI GdipAddPathPolygon(GpPath *path, GDIPCONST GpPointF *points, INT count)
569 INT old_count;
571 if(!path || !points || count < 3)
572 return InvalidParameter;
574 if(!lengthen_path(path, count))
575 return OutOfMemory;
577 old_count = path->pathdata.Count;
579 memcpy(&path->pathdata.Points[old_count], points, count*sizeof(GpPointF));
580 memset(&path->pathdata.Types[old_count + 1], PathPointTypeLine, count - 1);
582 /* A polygon is an intrinsic figure */
583 path->pathdata.Types[old_count] = PathPointTypeStart;
584 path->pathdata.Types[old_count + count - 1] |= PathPointTypeCloseSubpath;
585 path->newfigure = TRUE;
586 path->pathdata.Count += count;
588 return Ok;
591 GpStatus WINGDIPAPI GdipAddPathPolygonI(GpPath *path, GDIPCONST GpPoint *points, INT count)
593 GpPointF *ptf;
594 GpStatus status;
595 INT i;
597 if(!points || count < 3)
598 return InvalidParameter;
600 ptf = GdipAlloc(sizeof(GpPointF) * count);
601 if(!ptf)
602 return OutOfMemory;
604 for(i = 0; i < count; i++){
605 ptf[i].X = (REAL)points[i].X;
606 ptf[i].Y = (REAL)points[i].Y;
609 status = GdipAddPathPolygon(path, ptf, count);
611 GdipFree(ptf);
613 return status;
616 GpStatus WINGDIPAPI GdipClonePath(GpPath* path, GpPath **clone)
618 if(!path || !clone)
619 return InvalidParameter;
621 *clone = GdipAlloc(sizeof(GpPath));
622 if(!*clone) return OutOfMemory;
624 **clone = *path;
626 (*clone)->pathdata.Points = GdipAlloc(path->datalen * sizeof(PointF));
627 (*clone)->pathdata.Types = GdipAlloc(path->datalen);
628 if(!(*clone)->pathdata.Points || !(*clone)->pathdata.Types){
629 GdipFree(*clone);
630 GdipFree((*clone)->pathdata.Points);
631 GdipFree((*clone)->pathdata.Types);
632 return OutOfMemory;
635 memcpy((*clone)->pathdata.Points, path->pathdata.Points,
636 path->datalen * sizeof(PointF));
637 memcpy((*clone)->pathdata.Types, path->pathdata.Types, path->datalen);
639 return Ok;
642 GpStatus WINGDIPAPI GdipClosePathFigure(GpPath* path)
644 if(!path)
645 return InvalidParameter;
647 if(path->pathdata.Count > 0){
648 path->pathdata.Types[path->pathdata.Count - 1] |= PathPointTypeCloseSubpath;
649 path->newfigure = TRUE;
652 return Ok;
655 GpStatus WINGDIPAPI GdipClosePathFigures(GpPath* path)
657 INT i;
659 if(!path)
660 return InvalidParameter;
662 for(i = 1; i < path->pathdata.Count; i++){
663 if(path->pathdata.Types[i] == PathPointTypeStart)
664 path->pathdata.Types[i-1] |= PathPointTypeCloseSubpath;
667 path->newfigure = TRUE;
669 return Ok;
672 GpStatus WINGDIPAPI GdipCreatePath(GpFillMode fill, GpPath **path)
674 if(!path)
675 return InvalidParameter;
677 *path = GdipAlloc(sizeof(GpPath));
678 if(!*path) return OutOfMemory;
680 (*path)->fill = fill;
681 (*path)->newfigure = TRUE;
683 return Ok;
686 GpStatus WINGDIPAPI GdipCreatePath2(GDIPCONST GpPointF* points,
687 GDIPCONST BYTE* types, INT count, GpFillMode fill, GpPath **path)
689 if(!path)
690 return InvalidParameter;
692 *path = GdipAlloc(sizeof(GpPath));
693 if(!*path) return OutOfMemory;
695 (*path)->pathdata.Points = GdipAlloc(count * sizeof(PointF));
696 (*path)->pathdata.Types = GdipAlloc(count);
698 if(!(*path)->pathdata.Points || !(*path)->pathdata.Types){
699 GdipFree((*path)->pathdata.Points);
700 GdipFree((*path)->pathdata.Types);
701 GdipFree(*path);
702 return OutOfMemory;
705 memcpy((*path)->pathdata.Points, points, count * sizeof(PointF));
706 memcpy((*path)->pathdata.Types, types, count);
707 (*path)->pathdata.Count = count;
708 (*path)->datalen = count;
710 (*path)->fill = fill;
711 (*path)->newfigure = TRUE;
713 return Ok;
716 GpStatus WINGDIPAPI GdipCreatePath2I(GDIPCONST GpPoint* points,
717 GDIPCONST BYTE* types, INT count, GpFillMode fill, GpPath **path)
719 GpPointF *ptF;
720 GpStatus ret;
721 INT i;
723 ptF = GdipAlloc(sizeof(GpPointF)*count);
725 for(i = 0;i < count; i++){
726 ptF[i].X = (REAL)points[i].X;
727 ptF[i].Y = (REAL)points[i].Y;
730 ret = GdipCreatePath2(ptF, types, count, fill, path);
732 GdipFree(ptF);
734 return ret;
737 GpStatus WINGDIPAPI GdipDeletePath(GpPath *path)
739 if(!path)
740 return InvalidParameter;
742 GdipFree(path->pathdata.Points);
743 GdipFree(path->pathdata.Types);
744 GdipFree(path);
746 return Ok;
749 GpStatus WINGDIPAPI GdipGetPathData(GpPath *path, GpPathData* pathData)
751 if(!path || !pathData)
752 return InvalidParameter;
754 /* Only copy data. pathData allocation/freeing controlled by wrapper class.
755 Assumed that pathData is enough wide to get all data - controlled by wrapper too. */
756 memcpy(pathData->Points, path->pathdata.Points, sizeof(PointF) * pathData->Count);
757 memcpy(pathData->Types , path->pathdata.Types , pathData->Count);
759 return Ok;
762 GpStatus WINGDIPAPI GdipGetPathFillMode(GpPath *path, GpFillMode *fillmode)
764 if(!path || !fillmode)
765 return InvalidParameter;
767 *fillmode = path->fill;
769 return Ok;
772 GpStatus WINGDIPAPI GdipGetPathLastPoint(GpPath* path, GpPointF* lastPoint)
774 INT count;
776 if(!path || !lastPoint)
777 return InvalidParameter;
779 count = path->pathdata.Count;
780 if(count > 0)
781 *lastPoint = path->pathdata.Points[count-1];
783 return Ok;
786 GpStatus WINGDIPAPI GdipGetPathPoints(GpPath *path, GpPointF* points, INT count)
788 if(!path)
789 return InvalidParameter;
791 if(count < path->pathdata.Count)
792 return InsufficientBuffer;
794 memcpy(points, path->pathdata.Points, path->pathdata.Count * sizeof(GpPointF));
796 return Ok;
799 GpStatus WINGDIPAPI GdipGetPathPointsI(GpPath *path, GpPoint* points, INT count)
801 GpStatus ret;
802 GpPointF *ptf;
803 INT i;
805 if(count <= 0)
806 return InvalidParameter;
808 ptf = GdipAlloc(sizeof(GpPointF)*count);
809 if(!ptf) return OutOfMemory;
811 ret = GdipGetPathPoints(path,ptf,count);
812 if(ret == Ok)
813 for(i = 0;i < count;i++){
814 points[i].X = roundr(ptf[i].X);
815 points[i].Y = roundr(ptf[i].Y);
817 GdipFree(ptf);
819 return ret;
822 GpStatus WINGDIPAPI GdipGetPathTypes(GpPath *path, BYTE* types, INT count)
824 if(!path)
825 return InvalidParameter;
827 if(count < path->pathdata.Count)
828 return InsufficientBuffer;
830 memcpy(types, path->pathdata.Types, path->pathdata.Count);
832 return Ok;
835 /* Windows expands the bounding box to the maximum possible bounding box
836 * for a given pen. For example, if a line join can extend past the point
837 * it's joining by x units, the bounding box is extended by x units in every
838 * direction (even though this is too conservative for most cases). */
839 GpStatus WINGDIPAPI GdipGetPathWorldBounds(GpPath* path, GpRectF* bounds,
840 GDIPCONST GpMatrix *matrix, GDIPCONST GpPen *pen)
842 GpPointF * points, temp_pts[4];
843 INT count, i;
844 REAL path_width = 1.0, width, height, temp, low_x, low_y, high_x, high_y;
846 /* Matrix and pen can be null. */
847 if(!path || !bounds)
848 return InvalidParameter;
850 /* If path is empty just return. */
851 count = path->pathdata.Count;
852 if(count == 0){
853 bounds->X = bounds->Y = bounds->Width = bounds->Height = 0.0;
854 return Ok;
857 points = path->pathdata.Points;
859 low_x = high_x = points[0].X;
860 low_y = high_y = points[0].Y;
862 for(i = 1; i < count; i++){
863 low_x = min(low_x, points[i].X);
864 low_y = min(low_y, points[i].Y);
865 high_x = max(high_x, points[i].X);
866 high_y = max(high_y, points[i].Y);
869 width = high_x - low_x;
870 height = high_y - low_y;
872 /* This looks unusual but it's the only way I can imitate windows. */
873 if(matrix){
874 temp_pts[0].X = low_x;
875 temp_pts[0].Y = low_y;
876 temp_pts[1].X = low_x;
877 temp_pts[1].Y = high_y;
878 temp_pts[2].X = high_x;
879 temp_pts[2].Y = high_y;
880 temp_pts[3].X = high_x;
881 temp_pts[3].Y = low_y;
883 GdipTransformMatrixPoints((GpMatrix*)matrix, temp_pts, 4);
884 low_x = temp_pts[0].X;
885 low_y = temp_pts[0].Y;
887 for(i = 1; i < 4; i++){
888 low_x = min(low_x, temp_pts[i].X);
889 low_y = min(low_y, temp_pts[i].Y);
892 temp = width;
893 width = height * fabs(matrix->matrix[2]) + width * fabs(matrix->matrix[0]);
894 height = height * fabs(matrix->matrix[3]) + temp * fabs(matrix->matrix[1]);
897 if(pen){
898 path_width = pen->width / 2.0;
900 if(count > 2)
901 path_width = max(path_width, pen->width * pen->miterlimit / 2.0);
902 /* FIXME: this should probably also check for the startcap */
903 if(pen->endcap & LineCapNoAnchor)
904 path_width = max(path_width, pen->width * 2.2);
906 low_x -= path_width;
907 low_y -= path_width;
908 width += 2.0 * path_width;
909 height += 2.0 * path_width;
912 bounds->X = low_x;
913 bounds->Y = low_y;
914 bounds->Width = width;
915 bounds->Height = height;
917 return Ok;
920 GpStatus WINGDIPAPI GdipGetPathWorldBoundsI(GpPath* path, GpRect* bounds,
921 GDIPCONST GpMatrix *matrix, GDIPCONST GpPen *pen)
923 GpStatus ret;
924 GpRectF boundsF;
926 ret = GdipGetPathWorldBounds(path,&boundsF,matrix,pen);
928 if(ret == Ok){
929 bounds->X = roundr(boundsF.X);
930 bounds->Y = roundr(boundsF.Y);
931 bounds->Width = roundr(boundsF.Width);
932 bounds->Height = roundr(boundsF.Height);
935 return ret;
938 GpStatus WINGDIPAPI GdipGetPointCount(GpPath *path, INT *count)
940 if(!path)
941 return InvalidParameter;
943 *count = path->pathdata.Count;
945 return Ok;
948 GpStatus WINGDIPAPI GdipReversePath(GpPath* path)
950 INT i, count;
951 INT start = 0; /* position in reversed path */
952 GpPathData revpath;
954 if(!path)
955 return InvalidParameter;
957 count = path->pathdata.Count;
959 if(count == 0) return Ok;
961 revpath.Points = GdipAlloc(sizeof(GpPointF)*count);
962 revpath.Types = GdipAlloc(sizeof(BYTE)*count);
963 revpath.Count = count;
964 if(!revpath.Points || !revpath.Types){
965 GdipFree(revpath.Points);
966 GdipFree(revpath.Types);
967 return OutOfMemory;
970 for(i = 0; i < count; i++){
972 /* find next start point */
973 if(path->pathdata.Types[count-i-1] == PathPointTypeStart){
974 INT j;
975 for(j = start; j <= i; j++){
976 revpath.Points[j] = path->pathdata.Points[count-j-1];
977 revpath.Types[j] = path->pathdata.Types[count-j-1];
979 /* mark start point */
980 revpath.Types[start] = PathPointTypeStart;
981 /* set 'figure' endpoint type */
982 if(i-start > 1){
983 revpath.Types[i] = path->pathdata.Types[count-start-1] & ~PathPointTypePathTypeMask;
984 revpath.Types[i] |= revpath.Types[i-1];
986 else
987 revpath.Types[i] = path->pathdata.Types[start];
989 start = i+1;
993 memcpy(path->pathdata.Points, revpath.Points, sizeof(GpPointF)*count);
994 memcpy(path->pathdata.Types, revpath.Types, sizeof(BYTE)*count);
996 GdipFree(revpath.Points);
997 GdipFree(revpath.Types);
999 return Ok;
1002 GpStatus WINGDIPAPI GdipIsOutlineVisiblePathPointI(GpPath* path, INT x, INT y,
1003 GpPen *pen, GpGraphics *graphics, BOOL *result)
1005 return GdipIsOutlineVisiblePathPoint(path, x, y, pen, graphics, result);
1008 GpStatus WINGDIPAPI GdipIsOutlineVisiblePathPoint(GpPath* path, REAL x, REAL y,
1009 GpPen *pen, GpGraphics *graphics, BOOL *result)
1011 static int calls;
1013 if(!path || !pen)
1014 return InvalidParameter;
1016 if(!(calls++))
1017 FIXME("not implemented\n");
1019 return NotImplemented;
1022 GpStatus WINGDIPAPI GdipIsVisiblePathPointI(GpPath* path, INT x, INT y, GpGraphics *graphics, BOOL *result)
1024 return GdipIsVisiblePathPoint(path, x, y, graphics, result);
1027 GpStatus WINGDIPAPI GdipIsVisiblePathPoint(GpPath* path, REAL x, REAL y, GpGraphics *graphics, BOOL *result)
1029 static int calls;
1031 if(!path) return InvalidParameter;
1033 if(!(calls++))
1034 FIXME("not implemented\n");
1036 return NotImplemented;
1039 GpStatus WINGDIPAPI GdipStartPathFigure(GpPath *path)
1041 if(!path)
1042 return InvalidParameter;
1044 path->newfigure = TRUE;
1046 return Ok;
1049 GpStatus WINGDIPAPI GdipResetPath(GpPath *path)
1051 if(!path)
1052 return InvalidParameter;
1054 path->pathdata.Count = 0;
1055 path->newfigure = TRUE;
1056 path->fill = FillModeAlternate;
1058 return Ok;
1061 GpStatus WINGDIPAPI GdipSetPathFillMode(GpPath *path, GpFillMode fill)
1063 if(!path)
1064 return InvalidParameter;
1066 path->fill = fill;
1068 return Ok;
1071 GpStatus WINGDIPAPI GdipTransformPath(GpPath *path, GpMatrix *matrix)
1073 if(!path)
1074 return InvalidParameter;
1076 if(path->pathdata.Count == 0)
1077 return Ok;
1079 return GdipTransformMatrixPoints(matrix, path->pathdata.Points,
1080 path->pathdata.Count);
1083 GpStatus WINGDIPAPI GdipAddPathRectangle(GpPath *path, REAL x, REAL y,
1084 REAL width, REAL height)
1086 GpPath *backup;
1087 GpPointF ptf[2];
1088 GpStatus retstat;
1089 BOOL old_new;
1091 if(!path || width < 0.0 || height < 0.0)
1092 return InvalidParameter;
1094 /* make a backup copy of path data */
1095 if((retstat = GdipClonePath(path, &backup)) != Ok)
1096 return retstat;
1098 /* rectangle should start as new path */
1099 old_new = path->newfigure;
1100 path->newfigure = TRUE;
1101 if((retstat = GdipAddPathLine(path,x,y,x+width,y)) != Ok){
1102 path->newfigure = old_new;
1103 goto fail;
1106 ptf[0].X = x+width;
1107 ptf[0].Y = y+height;
1108 ptf[1].X = x;
1109 ptf[1].Y = y+height;
1111 if((retstat = GdipAddPathLine2(path, ptf, 2)) != Ok) goto fail;
1112 path->pathdata.Types[path->pathdata.Count-1] |= PathPointTypeCloseSubpath;
1114 /* free backup */
1115 GdipDeletePath(backup);
1116 return Ok;
1118 fail:
1119 /* reverting */
1120 GdipDeletePath(path);
1121 GdipClonePath(backup, &path);
1122 GdipDeletePath(backup);
1124 return retstat;
1127 GpStatus WINGDIPAPI GdipAddPathRectangleI(GpPath *path, INT x, INT y,
1128 INT width, INT height)
1130 return GdipAddPathRectangle(path,(REAL)x,(REAL)y,(REAL)width,(REAL)height);
1133 GpStatus WINGDIPAPI GdipAddPathRectangles(GpPath *path, GDIPCONST GpRectF *rects, INT count)
1135 GpPath *backup;
1136 GpStatus retstat;
1137 INT i;
1139 /* count == 0 - verified condition */
1140 if(!path || !rects || count == 0)
1141 return InvalidParameter;
1143 if(count < 0)
1144 return OutOfMemory;
1146 /* make a backup copy */
1147 if((retstat = GdipClonePath(path, &backup)) != Ok)
1148 return retstat;
1150 for(i = 0; i < count; i++){
1151 if((retstat = GdipAddPathRectangle(path,rects[i].X,rects[i].Y,rects[i].Width,rects[i].Height)) != Ok)
1152 goto fail;
1155 /* free backup */
1156 GdipDeletePath(backup);
1157 return Ok;
1159 fail:
1160 /* reverting */
1161 GdipDeletePath(path);
1162 GdipClonePath(backup, &path);
1163 GdipDeletePath(backup);
1165 return retstat;
1168 GpStatus WINGDIPAPI GdipAddPathRectanglesI(GpPath *path, GDIPCONST GpRect *rects, INT count)
1170 GpRectF *rectsF;
1171 GpStatus retstat;
1172 INT i;
1174 if(!rects || count == 0)
1175 return InvalidParameter;
1177 if(count < 0)
1178 return OutOfMemory;
1180 rectsF = GdipAlloc(sizeof(GpRectF)*count);
1182 for(i = 0;i < count;i++){
1183 rectsF[i].X = (REAL)rects[i].X;
1184 rectsF[i].Y = (REAL)rects[i].Y;
1185 rectsF[i].Width = (REAL)rects[i].Width;
1186 rectsF[i].Height = (REAL)rects[i].Height;
1189 retstat = GdipAddPathRectangles(path, rectsF, count);
1190 GdipFree(rectsF);
1192 return retstat;
1195 GpStatus WINGDIPAPI GdipSetPathMarker(GpPath* path)
1197 INT count;
1199 if(!path)
1200 return InvalidParameter;
1202 count = path->pathdata.Count;
1204 /* set marker flag */
1205 if(count > 0)
1206 path->pathdata.Types[count-1] |= PathPointTypePathMarker;
1208 return Ok;
1211 GpStatus WINGDIPAPI GdipClearPathMarkers(GpPath* path)
1213 INT count;
1214 INT i;
1216 if(!path)
1217 return InvalidParameter;
1219 count = path->pathdata.Count;
1221 for(i = 0; i < count - 1; i++){
1222 path->pathdata.Types[i] &= ~PathPointTypePathMarker;
1225 return Ok;