Release 9.11.
[wine.git] / dlls / gdiplus / graphicspath.c
blob44077950a8733955f5952566473ee5f152bd340e
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 #define SQRT3 1.73205080757
38 typedef struct path_list_node_t path_list_node_t;
39 struct path_list_node_t {
40 GpPointF pt;
41 BYTE type; /* PathPointTypeStart or PathPointTypeLine */
42 path_list_node_t *next;
45 /* init list */
46 static BOOL init_path_list(path_list_node_t **node, REAL x, REAL y)
48 *node = calloc(1, sizeof(path_list_node_t));
49 if(!*node)
50 return FALSE;
52 (*node)->pt.X = x;
53 (*node)->pt.Y = y;
54 (*node)->type = PathPointTypeStart;
55 (*node)->next = NULL;
57 return TRUE;
60 /* free all nodes including argument */
61 static void free_path_list(path_list_node_t *node)
63 path_list_node_t *n = node;
65 while(n){
66 n = n->next;
67 free(node);
68 node = n;
72 /* Add a node after 'node' */
74 * Returns
75 * pointer on success
76 * NULL on allocation problems
78 static path_list_node_t* add_path_list_node(path_list_node_t *node, REAL x, REAL y, BOOL type)
80 path_list_node_t *new;
82 new = calloc(1, sizeof(path_list_node_t));
83 if(!new)
84 return NULL;
86 new->pt.X = x;
87 new->pt.Y = y;
88 new->type = type;
89 new->next = node->next;
90 node->next = new;
92 return new;
95 /* returns element count */
96 static INT path_list_count(path_list_node_t *node)
98 INT count = 0;
100 while(node)
102 ++count;
103 node = node->next;
106 return count;
109 static BOOL path_list_to_path(path_list_node_t *node, GpPath *path)
111 INT i, count = path_list_count(node);
112 GpPointF *Points;
113 BYTE *Types;
115 if (count == 0)
117 path->pathdata.Count = count;
118 return TRUE;
121 Points = calloc(count, sizeof(GpPointF));
122 Types = calloc(1, count);
124 if (!Points || !Types)
126 free(Points);
127 free(Types);
128 return FALSE;
131 for(i = 0; i < count; i++){
132 Points[i] = node->pt;
133 Types[i] = node->type;
134 node = node->next;
137 free(path->pathdata.Points);
138 free(path->pathdata.Types);
140 path->pathdata.Points = Points;
141 path->pathdata.Types = Types;
142 path->pathdata.Count = count;
143 path->datalen = count;
145 return TRUE;
148 struct flatten_bezier_job
150 path_list_node_t *start;
151 REAL x2;
152 REAL y2;
153 REAL x3;
154 REAL y3;
155 path_list_node_t *end;
156 struct list entry;
159 static BOOL flatten_bezier_add(struct list *jobs, path_list_node_t *start, REAL x2, REAL y2, REAL x3, REAL y3, path_list_node_t *end)
161 struct flatten_bezier_job *job = malloc(sizeof(struct flatten_bezier_job));
162 if (!job)
163 return FALSE;
165 job->start = start;
166 job->x2 = x2;
167 job->y2 = y2;
168 job->x3 = x3;
169 job->y3 = y3;
170 job->end = end;
172 list_add_after(jobs, &job->entry);
173 return TRUE;
176 /* GdipFlattenPath helper */
178 * Used to recursively flatten single Bezier curve
179 * Parameters:
180 * - start : pointer to start point node;
181 * - (x2, y2): first control point;
182 * - (x3, y3): second control point;
183 * - end : pointer to end point node
184 * - flatness: admissible error of linear approximation.
186 * Return value:
187 * TRUE : success
188 * FALSE: out of memory
190 * TODO: used quality criteria should be revised to match native as
191 * closer as possible.
193 static BOOL flatten_bezier(path_list_node_t *start, REAL x2, REAL y2, REAL x3, REAL y3,
194 path_list_node_t *end, REAL flatness)
196 /* this 5 middle points with start/end define to half-curves */
197 GpPointF mp[5];
198 GpPointF pt, pt_st;
199 path_list_node_t *node;
200 REAL area_triangle, distance_start_end;
201 BOOL ret = TRUE;
202 struct list jobs;
203 struct flatten_bezier_job *current, *next;
205 list_init( &jobs );
206 flatten_bezier_add(&jobs, start, x2, y2, x3, y3, end);
207 LIST_FOR_EACH_ENTRY( current, &jobs, struct flatten_bezier_job, entry )
209 start = current->start;
210 x2 = current->x2;
211 y2 = current->y2;
212 x3 = current->x3;
213 y3 = current->y3;
214 end = current->end;
216 /* middle point between control points */
217 pt.X = (x2 + x3) / 2.0;
218 pt.Y = (y2 + y3) / 2.0;
220 /* calculate bezier curve middle points == new control points */
221 mp[0].X = (start->pt.X + x2) / 2.0;
222 mp[0].Y = (start->pt.Y + y2) / 2.0;
223 mp[1].X = (mp[0].X + pt.X) / 2.0;
224 mp[1].Y = (mp[0].Y + pt.Y) / 2.0;
225 mp[4].X = (end->pt.X + x3) / 2.0;
226 mp[4].Y = (end->pt.Y + y3) / 2.0;
227 mp[3].X = (mp[4].X + pt.X) / 2.0;
228 mp[3].Y = (mp[4].Y + pt.Y) / 2.0;
230 /* middle point between new control points */
231 mp[2].X = (mp[1].X + mp[3].X) / 2.0;
232 mp[2].Y = (mp[1].Y + mp[3].Y) / 2.0;
234 pt = end->pt;
235 pt_st = start->pt;
237 /* Test for closely spaced points that don't need to be flattened
238 * Also avoids limited-precision errors in flatness check
240 if((fabs(pt.X - mp[2].X) + fabs(pt.Y - mp[2].Y) +
241 fabs(pt_st.X - mp[2].X) + fabs(pt_st.Y - mp[2].Y) ) <= flatness * 0.5)
242 continue;
244 /* check flatness as a half of distance between middle point and a linearized path
245 * formula for distance point from line for point (x0, y0) and line (x1, y1) (x2, y2)
246 * is defined as (area_triangle / distance_start_end):
247 * | (x2 - x1) * (y1 - y0) - (x1 - x0) * (y2 - y1) / sqrt( (x2 - x1)^2 + (y2 - y1)^2 ) |
248 * Here rearranged to avoid division and simplified:
249 * x0(y2 - y1) + y0(x1 - x2) + (x2*y1 - x1*y2)
251 area_triangle = (pt.Y - pt_st.Y)*mp[2].X + (pt_st.X - pt.X)*mp[2].Y + (pt_st.Y*pt.X - pt_st.X*pt.Y);
252 distance_start_end = hypotf(pt.Y - pt_st.Y, pt_st.X - pt.X);
253 if(fabs(area_triangle) <= (0.5 * flatness * distance_start_end)){
254 continue;
256 else
257 /* add a middle point */
258 if(!(node = add_path_list_node(start, mp[2].X, mp[2].Y, PathPointTypeLine)))
260 ret = FALSE;
261 break;
264 /* do the same with halves */
265 if (!flatten_bezier_add(&current->entry, node, mp[3].X, mp[3].Y, mp[4].X, mp[4].Y, end))
266 break;
267 if (!flatten_bezier_add(&current->entry, start, mp[0].X, mp[0].Y, mp[1].X, mp[1].Y, node))
268 break;
271 /* Cleanup */
272 LIST_FOR_EACH_ENTRY_SAFE( current, next, &jobs, struct flatten_bezier_job, entry )
274 list_remove(&current->entry);
275 free(current);
278 return ret;
281 /* GdipAddPath* helper
283 * Several GdipAddPath functions are expected to add onto an open figure.
284 * So if the first point being added is an exact match to the last point
285 * of the existing line, that point should not be added.
287 * Parameters:
288 * path : path to which points should be added
289 * points : array of points to add
290 * count : number of points to add (at least 1)
291 * type : type of the points being added
293 * Return value:
294 * OutOfMemory : out of memory, could not lengthen path
295 * Ok : success
297 static GpStatus extend_current_figure(GpPath *path, GDIPCONST PointF *points, INT count, BYTE type)
299 INT insert_index = path->pathdata.Count;
300 BYTE first_point_type = (path->newfigure ? PathPointTypeStart : PathPointTypeLine);
302 if(!path->newfigure &&
303 path->pathdata.Points[insert_index-1].X == points[0].X &&
304 path->pathdata.Points[insert_index-1].Y == points[0].Y)
306 points++;
307 count--;
308 first_point_type = type;
311 if(!count)
312 return Ok;
314 if(!lengthen_path(path, count))
315 return OutOfMemory;
317 memcpy(path->pathdata.Points + insert_index, points, sizeof(GpPointF)*count);
318 path->pathdata.Types[insert_index] = first_point_type;
319 memset(path->pathdata.Types + insert_index + 1, type, count - 1);
321 path->newfigure = FALSE;
322 path->pathdata.Count += count;
324 return Ok;
327 /*******************************************************************************
328 * GdipAddPathArc [GDIPLUS.1]
330 * Add an elliptical arc to the given path.
332 * PARAMS
333 * path [I/O] Path that the arc is appended to
334 * x [I] X coordinate of the boundary rectangle
335 * y [I] Y coordinate of the boundary rectangle
336 * width [I] Width of the boundary rectangle
337 * height [I] Height of the boundary rectangle
338 * startAngle [I] Starting angle of the arc, clockwise
339 * sweepAngle [I] Angle of the arc, clockwise
341 * RETURNS
342 * InvalidParameter If the given path is invalid
343 * OutOfMemory If memory allocation fails, i.e. the path cannot be lengthened
344 * Ok If everything works out as expected
346 * NOTES
347 * This functions takes the newfigure value of the given path into account,
348 * i.e. the arc is connected to the end of the given path if it was set to
349 * FALSE, otherwise the arc's first point gets the PathPointTypeStart value.
350 * In both cases, the value of newfigure of the given path is FALSE
351 * afterwards.
353 GpStatus WINGDIPAPI GdipAddPathArc(GpPath *path, REAL x, REAL y, REAL width,
354 REAL height, REAL startAngle, REAL sweepAngle)
356 GpPointF *points;
357 GpStatus status;
358 INT count;
360 TRACE("(%p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n",
361 path, x, y, width, height, startAngle, sweepAngle);
363 if(!path || width <= 0.0f || height <= 0.0f)
364 return InvalidParameter;
366 count = arc2polybezier(NULL, x, y, width, height, startAngle, sweepAngle);
367 if(count == 0)
368 return Ok;
370 points = malloc(sizeof(GpPointF) * count);
371 if(!points)
372 return OutOfMemory;
374 arc2polybezier(points, x, y, width, height, startAngle, sweepAngle);
376 status = extend_current_figure(path, points, count, PathPointTypeBezier);
378 free(points);
379 return status;
382 /*******************************************************************************
383 * GdipAddPathArcI [GDUPLUS.2]
385 * See GdipAddPathArc
387 GpStatus WINGDIPAPI GdipAddPathArcI(GpPath *path, INT x1, INT y1, INT x2,
388 INT y2, REAL startAngle, REAL sweepAngle)
390 TRACE("(%p, %d, %d, %d, %d, %.2f, %.2f)\n",
391 path, x1, y1, x2, y2, startAngle, sweepAngle);
393 return GdipAddPathArc(path,(REAL)x1,(REAL)y1,(REAL)x2,(REAL)y2,startAngle,sweepAngle);
396 GpStatus WINGDIPAPI GdipAddPathBezier(GpPath *path, REAL x1, REAL y1, REAL x2,
397 REAL y2, REAL x3, REAL y3, REAL x4, REAL y4)
399 PointF points[4];
401 TRACE("(%p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n",
402 path, x1, y1, x2, y2, x3, y3, x4, y4);
404 if(!path)
405 return InvalidParameter;
407 points[0].X = x1;
408 points[0].Y = y1;
409 points[1].X = x2;
410 points[1].Y = y2;
411 points[2].X = x3;
412 points[2].Y = y3;
413 points[3].X = x4;
414 points[3].Y = y4;
416 return extend_current_figure(path, points, 4, PathPointTypeBezier);
419 GpStatus WINGDIPAPI GdipAddPathBezierI(GpPath *path, INT x1, INT y1, INT x2,
420 INT y2, INT x3, INT y3, INT x4, INT y4)
422 TRACE("(%p, %d, %d, %d, %d, %d, %d, %d, %d)\n",
423 path, x1, y1, x2, y2, x3, y3, x4, y4);
425 return GdipAddPathBezier(path,(REAL)x1,(REAL)y1,(REAL)x2,(REAL)y2,(REAL)x3,(REAL)y3,
426 (REAL)x4,(REAL)y4);
429 GpStatus WINGDIPAPI GdipAddPathBeziers(GpPath *path, GDIPCONST GpPointF *points,
430 INT count)
432 TRACE("(%p, %p, %d)\n", path, points, count);
434 if(!path || !points || ((count - 1) % 3))
435 return InvalidParameter;
437 return extend_current_figure(path, points, count, PathPointTypeBezier);
440 GpStatus WINGDIPAPI GdipAddPathBeziersI(GpPath *path, GDIPCONST GpPoint *points,
441 INT count)
443 GpPointF *ptsF;
444 GpStatus ret;
445 INT i;
447 TRACE("(%p, %p, %d)\n", path, points, count);
449 if(!points || ((count - 1) % 3))
450 return InvalidParameter;
452 ptsF = malloc(sizeof(GpPointF) * count);
453 if(!ptsF)
454 return OutOfMemory;
456 for(i = 0; i < count; i++){
457 ptsF[i].X = (REAL)points[i].X;
458 ptsF[i].Y = (REAL)points[i].Y;
461 ret = GdipAddPathBeziers(path, ptsF, count);
462 free(ptsF);
464 return ret;
467 GpStatus WINGDIPAPI GdipAddPathClosedCurve(GpPath *path, GDIPCONST GpPointF *points,
468 INT count)
470 TRACE("(%p, %p, %d)\n", path, points, count);
472 return GdipAddPathClosedCurve2(path, points, count, 0.5);
475 GpStatus WINGDIPAPI GdipAddPathClosedCurveI(GpPath *path, GDIPCONST GpPoint *points,
476 INT count)
478 TRACE("(%p, %p, %d)\n", path, points, count);
480 return GdipAddPathClosedCurve2I(path, points, count, 0.5);
483 GpStatus WINGDIPAPI GdipAddPathClosedCurve2(GpPath *path, GDIPCONST GpPointF *points,
484 INT count, REAL tension)
486 INT i, len_pt = (count + 1)*3-2;
487 GpPointF *pt;
488 GpPointF *pts;
489 REAL x1, x2, y1, y2;
490 GpStatus stat;
492 TRACE("(%p, %p, %d, %.2f)\n", path, points, count, tension);
494 if(!path || !points || count <= 1)
495 return InvalidParameter;
497 pt = malloc(len_pt * sizeof(GpPointF));
498 pts = malloc((count + 1) * sizeof(GpPointF));
499 if(!pt || !pts){
500 free(pt);
501 free(pts);
502 return OutOfMemory;
505 /* copy source points to extend with the last one */
506 memcpy(pts, points, sizeof(GpPointF)*count);
507 pts[count] = pts[0];
509 tension = tension * TENSION_CONST;
511 for(i = 0; i < count-1; i++){
512 calc_curve_bezier(&(pts[i]), tension, &x1, &y1, &x2, &y2);
514 pt[3*i+2].X = x1;
515 pt[3*i+2].Y = y1;
516 pt[3*i+3].X = pts[i+1].X;
517 pt[3*i+3].Y = pts[i+1].Y;
518 pt[3*i+4].X = x2;
519 pt[3*i+4].Y = y2;
522 /* points [len_pt-2] and [0] are calculated
523 separately to connect splines properly */
524 pts[0] = points[count-1];
525 pts[1] = points[0]; /* equals to start and end of a resulting path */
526 pts[2] = points[1];
528 calc_curve_bezier(pts, tension, &x1, &y1, &x2, &y2);
529 pt[len_pt-2].X = x1;
530 pt[len_pt-2].Y = y1;
531 pt[0].X = pts[1].X;
532 pt[0].Y = pts[1].Y;
533 pt[1].X = x2;
534 pt[1].Y = y2;
535 /* close path */
536 pt[len_pt-1].X = pt[0].X;
537 pt[len_pt-1].Y = pt[0].Y;
539 stat = extend_current_figure(path, pt, len_pt, PathPointTypeBezier);
541 /* close figure */
542 if(stat == Ok){
543 path->pathdata.Types[path->pathdata.Count - 1] |= PathPointTypeCloseSubpath;
544 path->newfigure = TRUE;
547 free(pts);
548 free(pt);
550 return stat;
553 GpStatus WINGDIPAPI GdipAddPathClosedCurve2I(GpPath *path, GDIPCONST GpPoint *points,
554 INT count, REAL tension)
556 GpPointF *ptf;
557 INT i;
558 GpStatus stat;
560 TRACE("(%p, %p, %d, %.2f)\n", path, points, count, tension);
562 if(!path || !points || count <= 1)
563 return InvalidParameter;
565 ptf = malloc(sizeof(GpPointF) * count);
566 if(!ptf)
567 return OutOfMemory;
569 for(i = 0; i < count; i++){
570 ptf[i].X = (REAL)points[i].X;
571 ptf[i].Y = (REAL)points[i].Y;
574 stat = GdipAddPathClosedCurve2(path, ptf, count, tension);
576 free(ptf);
578 return stat;
581 GpStatus WINGDIPAPI GdipAddPathCurve(GpPath *path, GDIPCONST GpPointF *points, INT count)
583 TRACE("(%p, %p, %d)\n", path, points, count);
585 return GdipAddPathCurve3(path, points, count, 0, count - 1, 0.5);
588 GpStatus WINGDIPAPI GdipAddPathCurveI(GpPath *path, GDIPCONST GpPoint *points, INT count)
590 TRACE("(%p, %p, %d)\n", path, points, count);
592 return GdipAddPathCurve3I(path, points, count, 0, count - 1, 0.5);
595 GpStatus WINGDIPAPI GdipAddPathCurve3(GpPath *path, GDIPCONST GpPointF *points,
596 INT count, INT offset, INT nseg, REAL tension)
598 INT i, len_pt = nseg * 3 + 1;
599 GpPointF *pt;
600 REAL x1, x2, y1, y2;
601 GpStatus stat;
602 TRACE("(%p, %p, %d, %d, %d, %.2f)\n", path, points, count, offset, nseg, tension);
604 if(!path || !points || offset + 1 >= count || count - offset < nseg + 1 || nseg < 1)
605 return InvalidParameter;
607 pt = calloc(len_pt, sizeof(GpPointF));
608 if(!pt)
609 return OutOfMemory;
611 tension = tension * TENSION_CONST;
613 pt[0].X = points[offset].X;
614 pt[0].Y = points[offset].Y;
615 if (offset > 0)
617 calc_curve_bezier(&(points[offset - 1]), tension, &x1, &y1, &x2, &y2);
618 pt[1].X = x2;
619 pt[1].Y = y2;
621 else
623 calc_curve_bezier_endp(points[offset].X, points[offset].Y,
624 points[offset + 1].X, points[offset + 1].Y, tension, &x1, &y1);
625 pt[1].X = x1;
626 pt[1].Y = y1;
629 for (i = 0; i < nseg - 1; i++){
630 calc_curve_bezier(&(points[offset + i]), tension, &x1, &y1, &x2, &y2);
632 pt[3*i+2].X = x1;
633 pt[3*i+2].Y = y1;
634 pt[3*i+3].X = points[offset + i + 1].X;
635 pt[3*i+3].Y = points[offset + i + 1].Y;
636 pt[3*i+4].X = x2;
637 pt[3*i+4].Y = y2;
640 if (offset + nseg + 1 < count)
641 /* If there are one more point in points table then use it for curve calculation */
642 calc_curve_bezier(&(points[offset + nseg - 1]), tension, &x1, &y1, &x2, &y2);
643 else
644 calc_curve_bezier_endp(points[offset + nseg].X, points[offset + nseg].Y,
645 points[offset + nseg - 1].X, points[offset + nseg - 1].Y, tension, &x1, &y1);
647 pt[len_pt-2].X = x1;
648 pt[len_pt-2].Y = y1;
649 pt[len_pt-1].X = points[offset + nseg].X;
650 pt[len_pt-1].Y = points[offset + nseg].Y;
652 stat = extend_current_figure(path, pt, len_pt, PathPointTypeBezier);
654 free(pt);
656 return stat;
659 GpStatus WINGDIPAPI GdipAddPathCurve2I(GpPath *path, GDIPCONST GpPoint *points,
660 INT count, REAL tension)
662 TRACE("(%p, %p, %d, %.2f)\n", path, points, count, tension);
664 return GdipAddPathCurve3I(path, points, count, 0, count - 1, tension);
667 GpStatus WINGDIPAPI GdipAddPathCurve2(GpPath *path, GDIPCONST GpPointF *points, INT count,
668 REAL tension)
670 TRACE("(%p, %p, %d, %.2f)\n", path, points, count, tension);
672 return GdipAddPathCurve3(path, points, count, 0, count - 1, tension);
675 GpStatus WINGDIPAPI GdipAddPathCurve3I(GpPath *path, GDIPCONST GpPoint *points,
676 INT count, INT offset, INT nseg, REAL tension)
678 GpPointF *ptf;
679 INT i;
680 GpStatus stat;
682 TRACE("(%p, %p, %d, %d, %d, %.2f)\n", path, points, count, offset, nseg, tension);
684 if(!path || !points || offset + 1 >= count || count - offset < nseg + 1 || nseg < 1)
685 return InvalidParameter;
687 ptf = malloc(sizeof(GpPointF) * count);
688 if(!ptf)
689 return OutOfMemory;
691 for(i = 0; i < count; i++) {
692 ptf[i].X = (REAL)points[i].X;
693 ptf[i].Y = (REAL)points[i].Y;
696 stat = GdipAddPathCurve3(path, ptf, count, offset, nseg, tension);
698 free(ptf);
700 return stat;
703 GpStatus WINGDIPAPI GdipAddPathEllipse(GpPath *path, REAL x, REAL y, REAL width,
704 REAL height)
706 INT old_count, numpts;
708 TRACE("(%p, %.2f, %.2f, %.2f, %.2f)\n", path, x, y, width, height);
710 if(!path)
711 return InvalidParameter;
713 if(!lengthen_path(path, MAX_ARC_PTS))
714 return OutOfMemory;
716 old_count = path->pathdata.Count;
717 if((numpts = arc2polybezier(&path->pathdata.Points[old_count], x, y, width,
718 height, 0.0, 360.0)) != MAX_ARC_PTS){
719 ERR("expected %d points but got %d\n", MAX_ARC_PTS, numpts);
720 return GenericError;
723 memset(&path->pathdata.Types[old_count + 1], PathPointTypeBezier,
724 MAX_ARC_PTS - 1);
726 /* An ellipse is an intrinsic figure (always is its own subpath). */
727 path->pathdata.Types[old_count] = PathPointTypeStart;
728 path->pathdata.Types[old_count + MAX_ARC_PTS - 1] |= PathPointTypeCloseSubpath;
729 path->newfigure = TRUE;
730 path->pathdata.Count += MAX_ARC_PTS;
732 return Ok;
735 GpStatus WINGDIPAPI GdipAddPathEllipseI(GpPath *path, INT x, INT y, INT width,
736 INT height)
738 TRACE("(%p, %d, %d, %d, %d)\n", path, x, y, width, height);
740 return GdipAddPathEllipse(path,(REAL)x,(REAL)y,(REAL)width,(REAL)height);
743 GpStatus WINGDIPAPI GdipAddPathLine2(GpPath *path, GDIPCONST GpPointF *points,
744 INT count)
746 TRACE("(%p, %p, %d)\n", path, points, count);
748 if(!path || !points || count < 1)
749 return InvalidParameter;
751 return extend_current_figure(path, points, count, PathPointTypeLine);
754 GpStatus WINGDIPAPI GdipAddPathLine2I(GpPath *path, GDIPCONST GpPoint *points, INT count)
756 GpPointF *pointsF;
757 INT i;
758 GpStatus stat;
760 TRACE("(%p, %p, %d)\n", path, points, count);
762 if(count <= 0)
763 return InvalidParameter;
765 pointsF = malloc(sizeof(GpPointF) * count);
766 if(!pointsF) return OutOfMemory;
768 for(i = 0;i < count; i++){
769 pointsF[i].X = (REAL)points[i].X;
770 pointsF[i].Y = (REAL)points[i].Y;
773 stat = GdipAddPathLine2(path, pointsF, count);
775 free(pointsF);
777 return stat;
780 /*************************************************************************
781 * GdipAddPathLine [GDIPLUS.21]
783 * Add two points to the given path.
785 * PARAMS
786 * path [I/O] Path that the line is appended to
787 * x1 [I] X coordinate of the first point of the line
788 * y1 [I] Y coordinate of the first point of the line
789 * x2 [I] X coordinate of the second point of the line
790 * y2 [I] Y coordinate of the second point of the line
792 * RETURNS
793 * InvalidParameter If the first parameter is not a valid path
794 * OutOfMemory If the path cannot be lengthened, i.e. memory allocation fails
795 * Ok If everything works out as expected
797 * NOTES
798 * This functions takes the newfigure value of the given path into account,
799 * i.e. the two new points are connected to the end of the given path if it
800 * was set to FALSE, otherwise the first point is given the PathPointTypeStart
801 * value. In both cases, the value of newfigure of the given path is FALSE
802 * afterwards.
804 GpStatus WINGDIPAPI GdipAddPathLine(GpPath *path, REAL x1, REAL y1, REAL x2, REAL y2)
806 PointF points[2];
808 TRACE("(%p, %.2f, %.2f, %.2f, %.2f)\n", path, x1, y1, x2, y2);
810 if(!path)
811 return InvalidParameter;
813 points[0].X = x1;
814 points[0].Y = y1;
815 points[1].X = x2;
816 points[1].Y = y2;
818 return extend_current_figure(path, points, 2, PathPointTypeLine);
821 /*************************************************************************
822 * GdipAddPathLineI [GDIPLUS.21]
824 * See GdipAddPathLine
826 GpStatus WINGDIPAPI GdipAddPathLineI(GpPath *path, INT x1, INT y1, INT x2, INT y2)
828 TRACE("(%p, %d, %d, %d, %d)\n", path, x1, y1, x2, y2);
830 return GdipAddPathLine(path, (REAL)x1, (REAL)y1, (REAL)x2, (REAL)y2);
833 GpStatus WINGDIPAPI GdipAddPathPath(GpPath *path, GDIPCONST GpPath* addingPath,
834 BOOL connect)
836 INT old_count, count;
838 TRACE("(%p, %p, %d)\n", path, addingPath, connect);
840 if(!path || !addingPath)
841 return InvalidParameter;
843 old_count = path->pathdata.Count;
844 count = addingPath->pathdata.Count;
846 if(!lengthen_path(path, count))
847 return OutOfMemory;
849 memcpy(&path->pathdata.Points[old_count], addingPath->pathdata.Points,
850 count * sizeof(GpPointF));
851 memcpy(&path->pathdata.Types[old_count], addingPath->pathdata.Types, count);
853 if(path->newfigure || !connect)
854 path->pathdata.Types[old_count] = PathPointTypeStart;
855 else
856 path->pathdata.Types[old_count] = PathPointTypeLine;
858 path->newfigure = FALSE;
859 path->pathdata.Count += count;
861 return Ok;
864 GpStatus WINGDIPAPI GdipAddPathPie(GpPath *path, REAL x, REAL y, REAL width, REAL height,
865 REAL startAngle, REAL sweepAngle)
867 GpPointF *ptf;
868 GpStatus status;
869 INT i, count;
871 TRACE("(%p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n",
872 path, x, y, width, height, startAngle, sweepAngle);
874 if(!path)
875 return InvalidParameter;
877 /* on zero width/height only start point added */
878 if(width <= 1e-7 || height <= 1e-7){
879 if(!lengthen_path(path, 1))
880 return OutOfMemory;
881 path->pathdata.Points[0].X = x + width / 2.0;
882 path->pathdata.Points[0].Y = y + height / 2.0;
883 path->pathdata.Types[0] = PathPointTypeStart | PathPointTypeCloseSubpath;
884 path->pathdata.Count = 1;
885 return InvalidParameter;
888 count = arc2polybezier(NULL, x, y, width, height, startAngle, sweepAngle);
890 if(count == 0)
891 return Ok;
893 ptf = malloc(sizeof(GpPointF) * count);
894 if(!ptf)
895 return OutOfMemory;
897 arc2polybezier(ptf, x, y, width, height, startAngle, sweepAngle);
899 status = GdipAddPathLine(path, x + width/2, y + height/2, ptf[0].X, ptf[0].Y);
900 if(status != Ok){
901 free(ptf);
902 return status;
904 /* one spline is already added as a line endpoint */
905 if(!lengthen_path(path, count - 1)){
906 free(ptf);
907 return OutOfMemory;
910 memcpy(&(path->pathdata.Points[path->pathdata.Count]), &(ptf[1]),sizeof(GpPointF)*(count-1));
911 for(i = 0; i < count-1; i++)
912 path->pathdata.Types[path->pathdata.Count+i] = PathPointTypeBezier;
914 path->pathdata.Count += count-1;
916 GdipClosePathFigure(path);
918 free(ptf);
920 return status;
923 GpStatus WINGDIPAPI GdipAddPathPieI(GpPath *path, INT x, INT y, INT width, INT height,
924 REAL startAngle, REAL sweepAngle)
926 TRACE("(%p, %d, %d, %d, %d, %.2f, %.2f)\n",
927 path, x, y, width, height, startAngle, sweepAngle);
929 return GdipAddPathPie(path, (REAL)x, (REAL)y, (REAL)width, (REAL)height, startAngle, sweepAngle);
932 GpStatus WINGDIPAPI GdipAddPathPolygon(GpPath *path, GDIPCONST GpPointF *points, INT count)
934 INT old_count;
936 TRACE("(%p, %p, %d)\n", path, points, count);
938 if(!path || !points || count < 3)
939 return InvalidParameter;
941 if(!lengthen_path(path, count))
942 return OutOfMemory;
944 old_count = path->pathdata.Count;
946 memcpy(&path->pathdata.Points[old_count], points, count*sizeof(GpPointF));
947 memset(&path->pathdata.Types[old_count + 1], PathPointTypeLine, count - 1);
949 /* A polygon is an intrinsic figure */
950 path->pathdata.Types[old_count] = PathPointTypeStart;
951 path->pathdata.Types[old_count + count - 1] |= PathPointTypeCloseSubpath;
952 path->newfigure = TRUE;
953 path->pathdata.Count += count;
955 return Ok;
958 GpStatus WINGDIPAPI GdipAddPathPolygonI(GpPath *path, GDIPCONST GpPoint *points, INT count)
960 GpPointF *ptf;
961 GpStatus status;
962 INT i;
964 TRACE("(%p, %p, %d)\n", path, points, count);
966 if(!points || count < 3)
967 return InvalidParameter;
969 ptf = malloc(sizeof(GpPointF) * count);
970 if(!ptf)
971 return OutOfMemory;
973 for(i = 0; i < count; i++){
974 ptf[i].X = (REAL)points[i].X;
975 ptf[i].Y = (REAL)points[i].Y;
978 status = GdipAddPathPolygon(path, ptf, count);
980 free(ptf);
982 return status;
985 static float fromfixedpoint(const FIXED v)
987 float f = ((float)v.fract) / (1<<(sizeof(v.fract)*8));
988 f += v.value;
989 return f;
992 struct format_string_args
994 GpPath *path;
995 float maxY;
996 float scale;
997 float ascent;
1000 static GpStatus format_string_callback(struct gdip_format_string_info* info)
1002 static const MAT2 identity = { {0,1}, {0,0}, {0,0}, {0,1} };
1003 struct format_string_args *args = info->user_data;
1004 struct gdip_font_link_section *section = LIST_ENTRY(list_head(&info->font_link_info.sections), struct gdip_font_link_section, entry);
1005 HFONT hfont = NULL, oldhfont = NULL;
1006 int section_start = -1;
1007 GpPath *path = args->path;
1008 GpStatus status = Ok;
1009 float x = info->rect->X + (info->bounds->X - info->rect->X) * args->scale;
1010 float y = info->rect->Y + (info->bounds->Y - info->rect->Y) * args->scale;
1011 int i;
1013 if (info->underlined_index_count)
1014 FIXME("hotkey underlines not drawn yet\n");
1016 if (y + info->bounds->Height * args->scale > args->maxY)
1017 args->maxY = y + info->bounds->Height * args->scale;
1019 for (i = info->index; i < info->length + info->index; ++i)
1021 GLYPHMETRICS gm;
1022 TTPOLYGONHEADER *ph = NULL, *origph;
1023 char *start;
1024 DWORD len, ofs = 0;
1026 while (i >= section->end)
1027 section = LIST_ENTRY(list_next(&info->font_link_info.sections, &section->entry), struct gdip_font_link_section, entry);
1029 if (section_start != section->start)
1031 if (hfont)
1033 SelectObject(info->hdc, oldhfont);
1034 DeleteObject(hfont);
1036 get_font_hfont(info->graphics, section->font, NULL, &hfont, NULL, NULL);
1037 oldhfont = SelectObject(info->hdc, hfont);
1038 section_start = section->start;
1041 len = GetGlyphOutlineW(info->hdc, info->string[i], GGO_BEZIER, &gm, 0, NULL, &identity);
1042 if (len == GDI_ERROR)
1044 status = GenericError;
1045 break;
1047 origph = ph = calloc(1, len);
1048 start = (char *)ph;
1049 if (!ph || !lengthen_path(path, len / sizeof(POINTFX)))
1051 free(ph);
1052 status = OutOfMemory;
1053 break;
1055 GetGlyphOutlineW(info->hdc, info->string[i], GGO_BEZIER, &gm, len, start, &identity);
1057 ofs = 0;
1058 while (ofs < len)
1060 DWORD ofs_start = ofs;
1061 ph = (TTPOLYGONHEADER*)&start[ofs];
1062 path->pathdata.Types[path->pathdata.Count] = PathPointTypeStart;
1063 path->pathdata.Points[path->pathdata.Count].X = x + fromfixedpoint(ph->pfxStart.x) * args->scale;
1064 path->pathdata.Points[path->pathdata.Count++].Y = y + args->ascent - fromfixedpoint(ph->pfxStart.y) * args->scale;
1065 TRACE("Starting at count %i with pos %f, %f)\n", path->pathdata.Count, x, y);
1066 ofs += sizeof(*ph);
1067 while (ofs - ofs_start < ph->cb)
1069 TTPOLYCURVE *curve = (TTPOLYCURVE*)&start[ofs];
1070 int j;
1071 ofs += sizeof(TTPOLYCURVE) + (curve->cpfx - 1) * sizeof(POINTFX);
1073 switch (curve->wType)
1075 case TT_PRIM_LINE:
1076 for (j = 0; j < curve->cpfx; ++j)
1078 path->pathdata.Types[path->pathdata.Count] = PathPointTypeLine;
1079 path->pathdata.Points[path->pathdata.Count].X = x + fromfixedpoint(curve->apfx[j].x) * args->scale;
1080 path->pathdata.Points[path->pathdata.Count++].Y = y + args->ascent - fromfixedpoint(curve->apfx[j].y) * args->scale;
1082 break;
1083 case TT_PRIM_CSPLINE:
1084 for (j = 0; j < curve->cpfx; ++j)
1086 path->pathdata.Types[path->pathdata.Count] = PathPointTypeBezier;
1087 path->pathdata.Points[path->pathdata.Count].X = x + fromfixedpoint(curve->apfx[j].x) * args->scale;
1088 path->pathdata.Points[path->pathdata.Count++].Y = y + args->ascent - fromfixedpoint(curve->apfx[j].y) * args->scale;
1090 break;
1091 default:
1092 ERR("Unhandled type: %u\n", curve->wType);
1093 status = GenericError;
1096 path->pathdata.Types[path->pathdata.Count - 1] |= PathPointTypeCloseSubpath;
1098 path->newfigure = TRUE;
1099 x += gm.gmCellIncX * args->scale;
1100 y += gm.gmCellIncY * args->scale;
1102 free(origph);
1103 if (status != Ok)
1104 break;
1107 if (hfont)
1109 SelectObject(info->hdc, oldhfont);
1110 DeleteObject(hfont);
1113 return status;
1116 GpStatus WINGDIPAPI GdipAddPathString(GpPath* path, GDIPCONST WCHAR* string, INT length, GDIPCONST GpFontFamily* family, INT style, REAL emSize, GDIPCONST RectF* layoutRect, GDIPCONST GpStringFormat* format)
1118 GpFont *font;
1119 GpStatus status;
1120 LOGFONTW lfw;
1121 HANDLE hfont;
1122 HDC dc;
1123 GpGraphics *graphics;
1124 GpPath *backup;
1125 struct format_string_args args;
1126 int i;
1127 UINT16 native_height;
1128 RectF scaled_layout_rect;
1129 TEXTMETRICW textmetric;
1131 TRACE("(%p, %s, %d, %p, %d, %f, %p, %p)\n", path, debugstr_w(string), length, family, style, emSize, layoutRect, format);
1132 if (!path || !string || !family || !emSize || !layoutRect)
1133 return InvalidParameter;
1135 if (!format)
1136 format = &default_drawstring_format;
1138 status = GdipGetEmHeight(family, style, &native_height);
1139 if (status != Ok)
1140 return status;
1142 scaled_layout_rect.X = layoutRect->X;
1143 scaled_layout_rect.Y = layoutRect->Y;
1144 scaled_layout_rect.Width = layoutRect->Width * native_height / emSize;
1145 scaled_layout_rect.Height = layoutRect->Height * native_height / emSize;
1147 if ((status = GdipClonePath(path, &backup)) != Ok)
1148 return status;
1150 dc = CreateCompatibleDC(0);
1151 status = GdipCreateFromHDC(dc, &graphics);
1152 if (status != Ok)
1154 DeleteDC(dc);
1155 GdipDeletePath(backup);
1156 return status;
1159 status = GdipCreateFont(family, native_height, style, UnitPixel, &font);
1160 if (status != Ok)
1162 GdipDeleteGraphics(graphics);
1163 DeleteDC(dc);
1164 GdipDeletePath(backup);
1165 return status;
1168 get_log_fontW(font, graphics, &lfw);
1170 hfont = CreateFontIndirectW(&lfw);
1171 if (!hfont)
1173 WARN("Failed to create font\n");
1174 DeleteDC(dc);
1175 GdipDeletePath(backup);
1176 GdipDeleteFont(font);
1177 return GenericError;
1180 SelectObject(dc, hfont);
1182 GetTextMetricsW(dc, &textmetric);
1184 args.path = path;
1185 args.maxY = 0;
1186 args.scale = emSize / native_height;
1187 args.ascent = textmetric.tmAscent * args.scale;
1188 status = gdip_format_string(graphics, dc, string, length, font, &scaled_layout_rect,
1189 format, TRUE, format_string_callback, &args);
1191 DeleteDC(dc);
1192 DeleteObject(hfont);
1193 GdipDeleteFont(font);
1194 GdipDeleteGraphics(graphics);
1196 if (status != Ok) /* free backup */
1198 free(path->pathdata.Points);
1199 free(path->pathdata.Types);
1200 *path = *backup;
1201 free(backup);
1202 return status;
1204 if (format->line_align == StringAlignmentCenter && layoutRect->Y + args.maxY < layoutRect->Height)
1206 float inc = layoutRect->Height + layoutRect->Y - args.maxY;
1207 inc /= 2;
1208 for (i = backup->pathdata.Count; i < path->pathdata.Count; ++i)
1209 path->pathdata.Points[i].Y += inc;
1210 } else if (format->line_align == StringAlignmentFar) {
1211 float inc = layoutRect->Height + layoutRect->Y - args.maxY;
1212 for (i = backup->pathdata.Count; i < path->pathdata.Count; ++i)
1213 path->pathdata.Points[i].Y += inc;
1215 GdipDeletePath(backup);
1216 return status;
1219 GpStatus WINGDIPAPI GdipAddPathStringI(GpPath* path, GDIPCONST WCHAR* string, INT length, GDIPCONST GpFontFamily* family,
1220 INT style, REAL emSize, GDIPCONST Rect* layoutRect, GDIPCONST GpStringFormat* format)
1222 RectF rect;
1224 if (!layoutRect)
1225 return InvalidParameter;
1227 set_rect(&rect, layoutRect->X, layoutRect->Y, layoutRect->Width, layoutRect->Height);
1228 return GdipAddPathString(path, string, length, family, style, emSize, &rect, format);
1231 /*************************************************************************
1232 * GdipClonePath [GDIPLUS.53]
1234 * Duplicate the given path in memory.
1236 * PARAMS
1237 * path [I] The path to be duplicated
1238 * clone [O] Pointer to the new path
1240 * RETURNS
1241 * InvalidParameter If the input path is invalid
1242 * OutOfMemory If allocation of needed memory fails
1243 * Ok If everything works out as expected
1245 GpStatus WINGDIPAPI GdipClonePath(GpPath* path, GpPath **clone)
1247 TRACE("(%p, %p)\n", path, clone);
1249 if(!path || !clone)
1250 return InvalidParameter;
1252 if (path->pathdata.Count)
1253 return GdipCreatePath2(path->pathdata.Points, path->pathdata.Types, path->pathdata.Count,
1254 path->fill, clone);
1255 else
1257 *clone = calloc(1, sizeof(GpPath));
1258 if(!*clone) return OutOfMemory;
1261 return Ok;
1264 GpStatus WINGDIPAPI GdipClosePathFigure(GpPath* path)
1266 TRACE("(%p)\n", path);
1268 if(!path)
1269 return InvalidParameter;
1271 if(path->pathdata.Count > 0){
1272 path->pathdata.Types[path->pathdata.Count - 1] |= PathPointTypeCloseSubpath;
1273 path->newfigure = TRUE;
1276 return Ok;
1279 GpStatus WINGDIPAPI GdipClosePathFigures(GpPath* path)
1281 INT i;
1283 TRACE("(%p)\n", path);
1285 if(!path)
1286 return InvalidParameter;
1288 for(i = 1; i < path->pathdata.Count; i++){
1289 if(path->pathdata.Types[i] == PathPointTypeStart)
1290 path->pathdata.Types[i-1] |= PathPointTypeCloseSubpath;
1293 path->newfigure = TRUE;
1295 return Ok;
1298 GpStatus WINGDIPAPI GdipCreatePath(GpFillMode fill, GpPath **path)
1300 TRACE("(%d, %p)\n", fill, path);
1302 if(!path)
1303 return InvalidParameter;
1305 *path = calloc(1, sizeof(GpPath));
1306 if(!*path) return OutOfMemory;
1308 (*path)->fill = fill;
1309 (*path)->newfigure = TRUE;
1311 return Ok;
1314 GpStatus WINGDIPAPI GdipCreatePath2(GDIPCONST GpPointF* points,
1315 GDIPCONST BYTE* types, INT count, GpFillMode fill, GpPath **path)
1317 int i;
1319 TRACE("(%p, %p, %d, %d, %p)\n", points, types, count, fill, path);
1321 if(!points || !types || !path)
1322 return InvalidParameter;
1324 if(count <= 0) {
1325 *path = NULL;
1326 return OutOfMemory;
1329 *path = calloc(1, sizeof(GpPath));
1330 if(!*path) return OutOfMemory;
1332 if(count > 1 && (types[count-1] & PathPointTypePathTypeMask) == PathPointTypeStart)
1333 count = 0;
1335 for(i = 1; i < count; i++) {
1336 if((types[i] & PathPointTypePathTypeMask) == PathPointTypeBezier) {
1337 if(i+2 < count &&
1338 (types[i+1] & PathPointTypePathTypeMask) == PathPointTypeBezier &&
1339 (types[i+2] & PathPointTypePathTypeMask) == PathPointTypeBezier)
1340 i += 2;
1341 else {
1342 count = 0;
1343 break;
1348 (*path)->pathdata.Points = malloc(count * sizeof(PointF));
1349 (*path)->pathdata.Types = malloc(count);
1351 if(!(*path)->pathdata.Points || !(*path)->pathdata.Types){
1352 free((*path)->pathdata.Points);
1353 free((*path)->pathdata.Types);
1354 free(*path);
1355 return OutOfMemory;
1358 memcpy((*path)->pathdata.Points, points, count * sizeof(PointF));
1359 memcpy((*path)->pathdata.Types, types, count);
1360 if(count > 0)
1361 (*path)->pathdata.Types[0] = PathPointTypeStart;
1362 (*path)->pathdata.Count = count;
1363 (*path)->datalen = count;
1365 (*path)->fill = fill;
1366 (*path)->newfigure = TRUE;
1368 return Ok;
1371 GpStatus WINGDIPAPI GdipCreatePath2I(GDIPCONST GpPoint* points,
1372 GDIPCONST BYTE* types, INT count, GpFillMode fill, GpPath **path)
1374 GpPointF *ptF;
1375 GpStatus ret;
1376 INT i;
1378 TRACE("(%p, %p, %d, %d, %p)\n", points, types, count, fill, path);
1380 ptF = malloc(sizeof(GpPointF) * count);
1382 for(i = 0;i < count; i++){
1383 ptF[i].X = (REAL)points[i].X;
1384 ptF[i].Y = (REAL)points[i].Y;
1387 ret = GdipCreatePath2(ptF, types, count, fill, path);
1389 free(ptF);
1391 return ret;
1394 GpStatus WINGDIPAPI GdipDeletePath(GpPath *path)
1396 TRACE("(%p)\n", path);
1398 if(!path)
1399 return InvalidParameter;
1401 free(path->pathdata.Points);
1402 free(path->pathdata.Types);
1403 free(path);
1405 return Ok;
1408 GpStatus WINGDIPAPI GdipFlattenPath(GpPath *path, GpMatrix* matrix, REAL flatness)
1410 path_list_node_t *list, *node;
1411 GpPointF pt;
1412 INT i = 1;
1413 INT startidx = 0;
1414 GpStatus stat;
1416 TRACE("(%p, %p, %.2f)\n", path, matrix, flatness);
1418 if(!path)
1419 return InvalidParameter;
1421 if(path->pathdata.Count == 0)
1422 return Ok;
1424 stat = GdipTransformPath(path, matrix);
1425 if(stat != Ok)
1426 return stat;
1428 pt = path->pathdata.Points[0];
1429 if(!init_path_list(&list, pt.X, pt.Y))
1430 return OutOfMemory;
1432 node = list;
1434 while(i < path->pathdata.Count){
1436 BYTE type = path->pathdata.Types[i] & PathPointTypePathTypeMask;
1437 path_list_node_t *start;
1439 pt = path->pathdata.Points[i];
1441 /* save last start point index */
1442 if(type == PathPointTypeStart)
1443 startidx = i;
1445 /* always add line points and start points */
1446 if((type == PathPointTypeStart) || (type == PathPointTypeLine)){
1447 if(!add_path_list_node(node, pt.X, pt.Y, path->pathdata.Types[i]))
1448 goto memout;
1450 node = node->next;
1451 ++i;
1452 continue;
1455 /* Bezier curve */
1457 /* test for closed figure */
1458 if(path->pathdata.Types[i+1] & PathPointTypeCloseSubpath){
1459 pt = path->pathdata.Points[startidx];
1460 ++i;
1462 else
1464 i += 2;
1465 pt = path->pathdata.Points[i];
1468 start = node;
1469 /* add Bezier end point */
1470 type = (path->pathdata.Types[i] & ~PathPointTypePathTypeMask) | PathPointTypeLine;
1471 if(!add_path_list_node(node, pt.X, pt.Y, type))
1472 goto memout;
1473 node = node->next;
1475 /* flatten curve */
1476 if(!flatten_bezier(start, path->pathdata.Points[i-2].X, path->pathdata.Points[i-2].Y,
1477 path->pathdata.Points[i-1].X, path->pathdata.Points[i-1].Y,
1478 node, flatness))
1479 goto memout;
1481 ++i;
1482 }/* while */
1484 if (!path_list_to_path(list, path)) goto memout;
1485 free_path_list(list);
1486 return Ok;
1488 memout:
1489 free_path_list(list);
1490 return OutOfMemory;
1493 GpStatus WINGDIPAPI GdipGetPathData(GpPath *path, GpPathData* pathData)
1495 TRACE("(%p, %p)\n", path, pathData);
1497 if(!path || !pathData)
1498 return InvalidParameter;
1500 /* Only copy data. pathData allocation/freeing controlled by wrapper class.
1501 Assumed that pathData is enough wide to get all data - controlled by wrapper too. */
1502 memcpy(pathData->Points, path->pathdata.Points, sizeof(PointF) * pathData->Count);
1503 memcpy(pathData->Types , path->pathdata.Types , pathData->Count);
1505 return Ok;
1508 GpStatus WINGDIPAPI GdipGetPathFillMode(GpPath *path, GpFillMode *fillmode)
1510 TRACE("(%p, %p)\n", path, fillmode);
1512 if(!path || !fillmode)
1513 return InvalidParameter;
1515 *fillmode = path->fill;
1517 return Ok;
1520 GpStatus WINGDIPAPI GdipGetPathLastPoint(GpPath* path, GpPointF* lastPoint)
1522 INT count;
1524 TRACE("(%p, %p)\n", path, lastPoint);
1526 if(!path || !lastPoint)
1527 return InvalidParameter;
1529 count = path->pathdata.Count;
1530 if(count > 0)
1531 *lastPoint = path->pathdata.Points[count-1];
1533 return Ok;
1536 GpStatus WINGDIPAPI GdipGetPathPoints(GpPath *path, GpPointF* points, INT count)
1538 TRACE("(%p, %p, %d)\n", path, points, count);
1540 if(!path)
1541 return InvalidParameter;
1543 if(count < path->pathdata.Count)
1544 return InsufficientBuffer;
1546 memcpy(points, path->pathdata.Points, path->pathdata.Count * sizeof(GpPointF));
1548 return Ok;
1551 GpStatus WINGDIPAPI GdipGetPathPointsI(GpPath *path, GpPoint* points, INT count)
1553 GpStatus ret;
1554 GpPointF *ptf;
1555 INT i;
1557 TRACE("(%p, %p, %d)\n", path, points, count);
1559 if(count <= 0)
1560 return InvalidParameter;
1562 ptf = malloc(sizeof(GpPointF) * count);
1563 if(!ptf) return OutOfMemory;
1565 ret = GdipGetPathPoints(path,ptf,count);
1566 if(ret == Ok)
1567 for(i = 0;i < count;i++){
1568 points[i].X = gdip_round(ptf[i].X);
1569 points[i].Y = gdip_round(ptf[i].Y);
1571 free(ptf);
1573 return ret;
1576 GpStatus WINGDIPAPI GdipGetPathTypes(GpPath *path, BYTE* types, INT count)
1578 TRACE("(%p, %p, %d)\n", path, types, count);
1580 if(!path)
1581 return InvalidParameter;
1583 if(count < path->pathdata.Count)
1584 return InsufficientBuffer;
1586 memcpy(types, path->pathdata.Types, path->pathdata.Count);
1588 return Ok;
1591 /* Windows expands the bounding box to the maximum possible bounding box
1592 * for a given pen. For example, if a line join can extend past the point
1593 * it's joining by x units, the bounding box is extended by x units in every
1594 * direction (even though this is too conservative for most cases). */
1595 GpStatus WINGDIPAPI GdipGetPathWorldBounds(GpPath* path, GpRectF* bounds,
1596 GDIPCONST GpMatrix *matrix, GDIPCONST GpPen *pen)
1598 GpPointF * points, temp_pts[4];
1599 INT count, i;
1600 REAL path_width = 1.0, width, height, temp, low_x, low_y, high_x, high_y;
1602 TRACE("(%p, %p, %s, %p)\n", path, bounds, debugstr_matrix(matrix), pen);
1604 /* Matrix and pen can be null. */
1605 if(!path || !bounds)
1606 return InvalidParameter;
1608 /* If path is empty just return. */
1609 count = path->pathdata.Count;
1610 if(count == 0){
1611 bounds->X = bounds->Y = bounds->Width = bounds->Height = 0.0;
1612 return Ok;
1615 points = path->pathdata.Points;
1617 low_x = high_x = points[0].X;
1618 low_y = high_y = points[0].Y;
1620 for(i = 1; i < count; i++){
1621 low_x = min(low_x, points[i].X);
1622 low_y = min(low_y, points[i].Y);
1623 high_x = max(high_x, points[i].X);
1624 high_y = max(high_y, points[i].Y);
1627 width = high_x - low_x;
1628 height = high_y - low_y;
1630 /* This looks unusual but it's the only way I can imitate windows. */
1631 if(matrix){
1632 temp_pts[0].X = low_x;
1633 temp_pts[0].Y = low_y;
1634 temp_pts[1].X = low_x;
1635 temp_pts[1].Y = high_y;
1636 temp_pts[2].X = high_x;
1637 temp_pts[2].Y = high_y;
1638 temp_pts[3].X = high_x;
1639 temp_pts[3].Y = low_y;
1641 GdipTransformMatrixPoints((GpMatrix*)matrix, temp_pts, 4);
1642 low_x = temp_pts[0].X;
1643 low_y = temp_pts[0].Y;
1645 for(i = 1; i < 4; i++){
1646 low_x = min(low_x, temp_pts[i].X);
1647 low_y = min(low_y, temp_pts[i].Y);
1650 temp = width;
1651 width = height * fabs(matrix->matrix[2]) + width * fabs(matrix->matrix[0]);
1652 height = height * fabs(matrix->matrix[3]) + temp * fabs(matrix->matrix[1]);
1655 if(pen){
1656 path_width = pen->width / 2.0;
1658 if(count > 2)
1659 path_width = max(path_width, pen->width * pen->miterlimit / 2.0);
1660 /* FIXME: this should probably also check for the startcap */
1661 if(pen->endcap & LineCapNoAnchor)
1662 path_width = max(path_width, pen->width * 2.2);
1664 low_x -= path_width;
1665 low_y -= path_width;
1666 width += 2.0 * path_width;
1667 height += 2.0 * path_width;
1670 bounds->X = low_x;
1671 bounds->Y = low_y;
1672 bounds->Width = width;
1673 bounds->Height = height;
1675 return Ok;
1678 GpStatus WINGDIPAPI GdipGetPathWorldBoundsI(GpPath* path, GpRect* bounds,
1679 GDIPCONST GpMatrix *matrix, GDIPCONST GpPen *pen)
1681 GpStatus ret;
1682 GpRectF boundsF;
1684 TRACE("(%p, %p, %s, %p)\n", path, bounds, debugstr_matrix(matrix), pen);
1686 ret = GdipGetPathWorldBounds(path,&boundsF,matrix,pen);
1688 if(ret == Ok){
1689 bounds->X = gdip_round(boundsF.X);
1690 bounds->Y = gdip_round(boundsF.Y);
1691 bounds->Width = gdip_round(boundsF.Width);
1692 bounds->Height = gdip_round(boundsF.Height);
1695 return ret;
1698 GpStatus WINGDIPAPI GdipGetPointCount(GpPath *path, INT *count)
1700 TRACE("(%p, %p)\n", path, count);
1702 if(!path)
1703 return InvalidParameter;
1705 *count = path->pathdata.Count;
1707 return Ok;
1710 GpStatus WINGDIPAPI GdipReversePath(GpPath* path)
1712 INT i, count;
1713 INT start = 0; /* position in reversed path */
1714 GpPathData revpath;
1716 TRACE("(%p)\n", path);
1718 if(!path)
1719 return InvalidParameter;
1721 count = path->pathdata.Count;
1723 if(count == 0) return Ok;
1725 revpath.Points = calloc(count, sizeof(GpPointF));
1726 revpath.Types = calloc(count, sizeof(BYTE));
1727 revpath.Count = count;
1728 if(!revpath.Points || !revpath.Types){
1729 free(revpath.Points);
1730 free(revpath.Types);
1731 return OutOfMemory;
1734 for(i = 0; i < count; i++){
1736 /* find next start point */
1737 if(path->pathdata.Types[count-i-1] == PathPointTypeStart){
1738 INT j;
1739 for(j = start; j <= i; j++){
1740 revpath.Points[j] = path->pathdata.Points[count-j-1];
1741 revpath.Types[j] = path->pathdata.Types[count-j-1];
1743 /* mark start point */
1744 revpath.Types[start] = PathPointTypeStart;
1745 /* set 'figure' endpoint type */
1746 if(i-start > 1){
1747 revpath.Types[i] = path->pathdata.Types[count-start-1] & ~PathPointTypePathTypeMask;
1748 revpath.Types[i] |= revpath.Types[i-1];
1750 else
1751 revpath.Types[i] = path->pathdata.Types[start];
1753 start = i+1;
1757 memcpy(path->pathdata.Points, revpath.Points, sizeof(GpPointF)*count);
1758 memcpy(path->pathdata.Types, revpath.Types, sizeof(BYTE)*count);
1760 free(revpath.Points);
1761 free(revpath.Types);
1763 return Ok;
1766 GpStatus WINGDIPAPI GdipIsOutlineVisiblePathPointI(GpPath* path, INT x, INT y,
1767 GpPen *pen, GpGraphics *graphics, BOOL *result)
1769 TRACE("(%p, %d, %d, %p, %p, %p)\n", path, x, y, pen, graphics, result);
1771 return GdipIsOutlineVisiblePathPoint(path, x, y, pen, graphics, result);
1774 GpStatus WINGDIPAPI GdipIsOutlineVisiblePathPoint(GpPath* path, REAL x, REAL y,
1775 GpPen *pen, GpGraphics *graphics, BOOL *result)
1777 GpStatus stat;
1778 GpPath *wide_path;
1779 GpPointF pt = {x, y};
1780 GpMatrix *transform = NULL;
1782 TRACE("(%p, %0.2f, %0.2f, %p, %p, %p)\n", path, x, y, pen, graphics, result);
1784 if(!path || !pen)
1785 return InvalidParameter;
1787 stat = GdipClonePath(path, &wide_path);
1789 if (stat != Ok)
1790 return stat;
1792 if (pen->unit == UnitPixel && graphics != NULL)
1794 stat = GdipCreateMatrix(&transform);
1796 if (stat == Ok)
1797 stat = get_graphics_transform(graphics, CoordinateSpaceDevice,
1798 CoordinateSpaceWorld, transform);
1799 if (stat == Ok)
1800 GdipTransformMatrixPoints(transform, &pt, 1);
1803 if (stat == Ok)
1804 stat = GdipWidenPath(wide_path, pen, transform, 0.25f);
1806 if (stat == Ok)
1807 stat = GdipIsVisiblePathPoint(wide_path, pt.X, pt.Y, graphics, result);
1809 GdipDeleteMatrix(transform);
1811 GdipDeletePath(wide_path);
1813 return stat;
1816 GpStatus WINGDIPAPI GdipIsVisiblePathPointI(GpPath* path, INT x, INT y, GpGraphics *graphics, BOOL *result)
1818 TRACE("(%p, %d, %d, %p, %p)\n", path, x, y, graphics, result);
1820 return GdipIsVisiblePathPoint(path, x, y, graphics, result);
1823 /*****************************************************************************
1824 * GdipIsVisiblePathPoint [GDIPLUS.@]
1826 GpStatus WINGDIPAPI GdipIsVisiblePathPoint(GpPath* path, REAL x, REAL y, GpGraphics *graphics, BOOL *result)
1828 GpRegion *region;
1829 HRGN hrgn;
1830 GpStatus status;
1832 if(!path || !result) return InvalidParameter;
1834 status = GdipCreateRegionPath(path, &region);
1835 if(status != Ok)
1836 return status;
1838 status = GdipGetRegionHRgn(region, NULL, &hrgn);
1839 if(status != Ok){
1840 GdipDeleteRegion(region);
1841 return status;
1844 *result = PtInRegion(hrgn, gdip_round(x), gdip_round(y));
1846 DeleteObject(hrgn);
1847 GdipDeleteRegion(region);
1849 return Ok;
1852 GpStatus WINGDIPAPI GdipStartPathFigure(GpPath *path)
1854 TRACE("(%p)\n", path);
1856 if(!path)
1857 return InvalidParameter;
1859 path->newfigure = TRUE;
1861 return Ok;
1864 GpStatus WINGDIPAPI GdipResetPath(GpPath *path)
1866 TRACE("(%p)\n", path);
1868 if(!path)
1869 return InvalidParameter;
1871 path->pathdata.Count = 0;
1872 path->newfigure = TRUE;
1873 path->fill = FillModeAlternate;
1875 return Ok;
1878 GpStatus WINGDIPAPI GdipSetPathFillMode(GpPath *path, GpFillMode fill)
1880 TRACE("(%p, %d)\n", path, fill);
1882 if(!path)
1883 return InvalidParameter;
1885 path->fill = fill;
1887 return Ok;
1890 GpStatus WINGDIPAPI GdipTransformPath(GpPath *path, GpMatrix *matrix)
1892 TRACE("(%p, %s)\n", path, debugstr_matrix(matrix));
1894 if(!path)
1895 return InvalidParameter;
1897 if(path->pathdata.Count == 0 || !matrix)
1898 return Ok;
1900 return GdipTransformMatrixPoints(matrix, path->pathdata.Points,
1901 path->pathdata.Count);
1904 GpStatus WINGDIPAPI GdipWarpPath(GpPath *path, GpMatrix* matrix,
1905 GDIPCONST GpPointF *points, INT count, REAL x, REAL y, REAL width,
1906 REAL height, WarpMode warpmode, REAL flatness)
1908 FIXME("(%p,%s,%p,%i,%0.2f,%0.2f,%0.2f,%0.2f,%i,%0.2f)\n", path, debugstr_matrix(matrix),
1909 points, count, x, y, width, height, warpmode, flatness);
1911 return NotImplemented;
1914 static void add_bevel_point(const GpPointF *endpoint, const GpPointF *nextpoint,
1915 REAL pen_width, int right_side, path_list_node_t **last_point)
1917 REAL segment_dy = nextpoint->Y-endpoint->Y;
1918 REAL segment_dx = nextpoint->X-endpoint->X;
1919 REAL segment_length = hypotf(segment_dy, segment_dx);
1920 REAL distance = pen_width / 2.0;
1921 REAL bevel_dx, bevel_dy;
1923 if (segment_length == 0.0)
1925 *last_point = add_path_list_node(*last_point, endpoint->X,
1926 endpoint->Y, PathPointTypeLine);
1927 return;
1930 if (right_side)
1932 bevel_dx = -distance * segment_dy / segment_length;
1933 bevel_dy = distance * segment_dx / segment_length;
1935 else
1937 bevel_dx = distance * segment_dy / segment_length;
1938 bevel_dy = -distance * segment_dx / segment_length;
1941 *last_point = add_path_list_node(*last_point, endpoint->X + bevel_dx,
1942 endpoint->Y + bevel_dy, PathPointTypeLine);
1945 static void widen_joint(const GpPointF *p1, const GpPointF *p2, const GpPointF *p3,
1946 GpPen* pen, REAL pen_width, path_list_node_t **last_point)
1948 switch (pen->join)
1950 case LineJoinMiter:
1951 case LineJoinMiterClipped:
1952 if ((p2->X - p1->X) * (p3->Y - p1->Y) > (p2->Y - p1->Y) * (p3->X - p1->X))
1954 float distance = pen_width / 2.0;
1955 float length_0 = hypotf(p2->X - p1->X, p2->Y - p1->Y);
1956 float length_1 = hypotf(p3->X - p2->X, p3->Y - p2->Y);
1957 float dx0 = distance * (p2->X - p1->X) / length_0;
1958 float dy0 = distance * (p2->Y - p1->Y) / length_0;
1959 float dx1 = distance * (p3->X - p2->X) / length_1;
1960 float dy1 = distance * (p3->Y - p2->Y) / length_1;
1961 float det = (dy0*dx1 - dx0*dy1);
1962 float dx = (dx0*dx1*(dx0-dx1) + dy0*dy0*dx1 - dy1*dy1*dx0)/det;
1963 float dy = (dy0*dy1*(dy0-dy1) + dx0*dx0*dy1 - dx1*dx1*dy0)/det;
1964 if (dx*dx + dy*dy < pen->miterlimit*pen->miterlimit * distance*distance)
1966 *last_point = add_path_list_node(*last_point, p2->X + dx,
1967 p2->Y + dy, PathPointTypeLine);
1968 break;
1970 else if (pen->join == LineJoinMiter)
1972 static int once;
1973 if (!once++)
1974 FIXME("should add a clipped corner\n");
1976 /* else fall-through */
1978 /* else fall-through */
1979 default:
1980 case LineJoinBevel:
1981 add_bevel_point(p2, p1, pen_width, 1, last_point);
1982 add_bevel_point(p2, p3, pen_width, 0, last_point);
1983 break;
1987 static void widen_cap(const GpPointF *endpoint, const GpPointF *nextpoint,
1988 REAL pen_width, GpLineCap cap, GpCustomLineCap* custom_cap, int add_first_points,
1989 int add_last_point, path_list_node_t **last_point)
1991 switch (cap)
1993 default:
1994 case LineCapFlat:
1995 if (add_first_points)
1996 add_bevel_point(endpoint, nextpoint, pen_width, 1, last_point);
1997 if (add_last_point)
1998 add_bevel_point(endpoint, nextpoint, pen_width, 0, last_point);
1999 break;
2000 case LineCapSquare:
2001 case LineCapCustom:
2002 case LineCapArrowAnchor:
2004 REAL segment_dy = nextpoint->Y-endpoint->Y;
2005 REAL segment_dx = nextpoint->X-endpoint->X;
2006 REAL segment_length = hypotf(segment_dy, segment_dx);
2007 REAL distance = pen_width / 2.0;
2008 REAL bevel_dx, bevel_dy;
2009 REAL extend_dx, extend_dy;
2011 extend_dx = distance * segment_dx / segment_length;
2012 extend_dy = distance * segment_dy / segment_length;
2014 bevel_dx = -extend_dy;
2015 bevel_dy = extend_dx;
2017 if (cap == LineCapCustom)
2019 extend_dx = -2.0 * custom_cap->inset * extend_dx;
2020 extend_dy = -2.0 * custom_cap->inset * extend_dy;
2022 else if (cap == LineCapArrowAnchor)
2024 extend_dx = -3.0 * extend_dx;
2025 extend_dy = -3.0 * extend_dy;
2028 if (add_first_points)
2029 *last_point = add_path_list_node(*last_point, endpoint->X - extend_dx + bevel_dx,
2030 endpoint->Y - extend_dy + bevel_dy, PathPointTypeLine);
2032 if (add_last_point)
2033 *last_point = add_path_list_node(*last_point, endpoint->X - extend_dx - bevel_dx,
2034 endpoint->Y - extend_dy - bevel_dy, PathPointTypeLine);
2035 break;
2037 case LineCapRound:
2039 REAL segment_dy = nextpoint->Y-endpoint->Y;
2040 REAL segment_dx = nextpoint->X-endpoint->X;
2041 REAL segment_length = hypotf(segment_dy, segment_dx);
2042 REAL distance = pen_width / 2.0;
2043 REAL dx, dy, dx2, dy2;
2044 const REAL control_point_distance = 0.5522847498307935; /* 4/3 * (sqrt(2) - 1) */
2046 if (add_first_points)
2048 dx = -distance * segment_dx / segment_length;
2049 dy = -distance * segment_dy / segment_length;
2051 dx2 = dx * control_point_distance;
2052 dy2 = dy * control_point_distance;
2054 /* first 90-degree arc */
2055 *last_point = add_path_list_node(*last_point, endpoint->X + dy,
2056 endpoint->Y - dx, PathPointTypeLine);
2058 *last_point = add_path_list_node(*last_point, endpoint->X + dy + dx2,
2059 endpoint->Y - dx + dy2, PathPointTypeBezier);
2061 *last_point = add_path_list_node(*last_point, endpoint->X + dx + dy2,
2062 endpoint->Y + dy - dx2, PathPointTypeBezier);
2064 /* midpoint */
2065 *last_point = add_path_list_node(*last_point, endpoint->X + dx,
2066 endpoint->Y + dy, PathPointTypeBezier);
2068 /* second 90-degree arc */
2069 *last_point = add_path_list_node(*last_point, endpoint->X + dx - dy2,
2070 endpoint->Y + dy + dx2, PathPointTypeBezier);
2072 *last_point = add_path_list_node(*last_point, endpoint->X - dy + dx2,
2073 endpoint->Y + dx + dy2, PathPointTypeBezier);
2075 *last_point = add_path_list_node(*last_point, endpoint->X - dy,
2076 endpoint->Y + dx, PathPointTypeBezier);
2078 else if (add_last_point)
2079 add_bevel_point(endpoint, nextpoint, pen_width, 0, last_point);
2080 break;
2082 case LineCapTriangle:
2084 REAL segment_dy = nextpoint->Y-endpoint->Y;
2085 REAL segment_dx = nextpoint->X-endpoint->X;
2086 REAL segment_length = hypotf(segment_dy, segment_dx);
2087 REAL distance = pen_width / 2.0;
2088 REAL dx, dy;
2090 dx = distance * segment_dx / segment_length;
2091 dy = distance * segment_dy / segment_length;
2093 if (add_first_points) {
2094 add_bevel_point(endpoint, nextpoint, pen_width, 1, last_point);
2096 *last_point = add_path_list_node(*last_point, endpoint->X - dx,
2097 endpoint->Y - dy, PathPointTypeLine);
2099 if (add_first_points || add_last_point)
2100 add_bevel_point(endpoint, nextpoint, pen_width, 0, last_point);
2101 break;
2106 static void widen_open_figure(const GpPointF *points, int start, int end,
2107 GpPen *pen, REAL pen_width, GpLineCap start_cap, GpCustomLineCap* custom_start,
2108 GpLineCap end_cap, GpCustomLineCap* custom_end, path_list_node_t **last_point);
2110 static void widen_closed_figure(const GpPointF *points, int start, int end,
2111 GpPen *pen, REAL pen_width, path_list_node_t **last_point);
2113 static void add_anchor(const GpPointF *endpoint, const GpPointF *nextpoint,
2114 GpPen *pen, REAL pen_width, GpLineCap cap, GpCustomLineCap *custom, path_list_node_t **last_point)
2116 switch (cap)
2118 default:
2119 case LineCapNoAnchor:
2120 return;
2121 case LineCapSquareAnchor:
2123 REAL segment_dy = nextpoint->Y-endpoint->Y;
2124 REAL segment_dx = nextpoint->X-endpoint->X;
2125 REAL segment_length = hypotf(segment_dy, segment_dx);
2126 REAL distance = pen_width / sqrtf(2.0);
2127 REAL par_dx, par_dy;
2128 REAL perp_dx, perp_dy;
2130 par_dx = -distance * segment_dx / segment_length;
2131 par_dy = -distance * segment_dy / segment_length;
2133 perp_dx = -distance * segment_dy / segment_length;
2134 perp_dy = distance * segment_dx / segment_length;
2136 *last_point = add_path_list_node(*last_point, endpoint->X - par_dx - perp_dx,
2137 endpoint->Y - par_dy - perp_dy, PathPointTypeStart);
2138 *last_point = add_path_list_node(*last_point, endpoint->X - par_dx + perp_dx,
2139 endpoint->Y - par_dy + perp_dy, PathPointTypeLine);
2140 *last_point = add_path_list_node(*last_point, endpoint->X + par_dx + perp_dx,
2141 endpoint->Y + par_dy + perp_dy, PathPointTypeLine);
2142 *last_point = add_path_list_node(*last_point, endpoint->X + par_dx - perp_dx,
2143 endpoint->Y + par_dy - perp_dy, PathPointTypeLine);
2144 break;
2146 case LineCapRoundAnchor:
2148 REAL segment_dy = nextpoint->Y-endpoint->Y;
2149 REAL segment_dx = nextpoint->X-endpoint->X;
2150 REAL segment_length = hypotf(segment_dy, segment_dx);
2151 REAL dx, dy, dx2, dy2;
2152 const REAL control_point_distance = 0.55228475; /* 4/3 * (sqrt(2) - 1) */
2154 dx = -pen_width * segment_dx / segment_length;
2155 dy = -pen_width * segment_dy / segment_length;
2157 dx2 = dx * control_point_distance;
2158 dy2 = dy * control_point_distance;
2160 /* starting point */
2161 *last_point = add_path_list_node(*last_point, endpoint->X + dy,
2162 endpoint->Y - dx, PathPointTypeStart);
2164 /* first 90-degree arc */
2165 *last_point = add_path_list_node(*last_point, endpoint->X + dy + dx2,
2166 endpoint->Y - dx + dy2, PathPointTypeBezier);
2167 *last_point = add_path_list_node(*last_point, endpoint->X + dx + dy2,
2168 endpoint->Y + dy - dx2, PathPointTypeBezier);
2169 *last_point = add_path_list_node(*last_point, endpoint->X + dx,
2170 endpoint->Y + dy, PathPointTypeBezier);
2172 /* second 90-degree arc */
2173 *last_point = add_path_list_node(*last_point, endpoint->X + dx - dy2,
2174 endpoint->Y + dy + dx2, PathPointTypeBezier);
2175 *last_point = add_path_list_node(*last_point, endpoint->X - dy + dx2,
2176 endpoint->Y + dx + dy2, PathPointTypeBezier);
2177 *last_point = add_path_list_node(*last_point, endpoint->X - dy,
2178 endpoint->Y + dx, PathPointTypeBezier);
2180 /* third 90-degree arc */
2181 *last_point = add_path_list_node(*last_point, endpoint->X - dy - dx2,
2182 endpoint->Y + dx - dy2, PathPointTypeBezier);
2183 *last_point = add_path_list_node(*last_point, endpoint->X - dx - dy2,
2184 endpoint->Y - dy + dx2, PathPointTypeBezier);
2185 *last_point = add_path_list_node(*last_point, endpoint->X - dx,
2186 endpoint->Y - dy, PathPointTypeBezier);
2188 /* fourth 90-degree arc */
2189 *last_point = add_path_list_node(*last_point, endpoint->X - dx + dy2,
2190 endpoint->Y - dy - dx2, PathPointTypeBezier);
2191 *last_point = add_path_list_node(*last_point, endpoint->X + dy - dx2,
2192 endpoint->Y - dx - dy2, PathPointTypeBezier);
2193 *last_point = add_path_list_node(*last_point, endpoint->X + dy,
2194 endpoint->Y - dx, PathPointTypeBezier);
2196 break;
2198 case LineCapDiamondAnchor:
2200 REAL segment_dy = nextpoint->Y-endpoint->Y;
2201 REAL segment_dx = nextpoint->X-endpoint->X;
2202 REAL segment_length = hypotf(segment_dy, segment_dx);
2203 REAL par_dx, par_dy;
2204 REAL perp_dx, perp_dy;
2206 par_dx = -pen_width * segment_dx / segment_length;
2207 par_dy = -pen_width * segment_dy / segment_length;
2209 perp_dx = -pen_width * segment_dy / segment_length;
2210 perp_dy = pen_width * segment_dx / segment_length;
2212 *last_point = add_path_list_node(*last_point, endpoint->X + par_dx,
2213 endpoint->Y + par_dy, PathPointTypeStart);
2214 *last_point = add_path_list_node(*last_point, endpoint->X - perp_dx,
2215 endpoint->Y - perp_dy, PathPointTypeLine);
2216 *last_point = add_path_list_node(*last_point, endpoint->X - par_dx,
2217 endpoint->Y - par_dy, PathPointTypeLine);
2218 *last_point = add_path_list_node(*last_point, endpoint->X + perp_dx,
2219 endpoint->Y + perp_dy, PathPointTypeLine);
2220 break;
2222 case LineCapArrowAnchor:
2224 REAL segment_dy = nextpoint->Y - endpoint->Y;
2225 REAL segment_dx = nextpoint->X - endpoint->X;
2226 REAL segment_length = hypotf(segment_dy, segment_dx);
2227 REAL par_dx = pen_width * segment_dx / segment_length;
2228 REAL par_dy = pen_width * segment_dy / segment_length;
2229 REAL perp_dx = -par_dy;
2230 REAL perp_dy = par_dx;
2232 *last_point = add_path_list_node(*last_point, endpoint->X,
2233 endpoint->Y, PathPointTypeStart);
2234 *last_point = add_path_list_node(*last_point, endpoint->X + SQRT3 * par_dx - perp_dx,
2235 endpoint->Y + SQRT3 * par_dy - perp_dy, PathPointTypeLine);
2236 *last_point = add_path_list_node(*last_point, endpoint->X + SQRT3 * par_dx + perp_dx,
2237 endpoint->Y + SQRT3 * par_dy + perp_dy, PathPointTypeLine);
2238 break;
2240 case LineCapCustom:
2242 REAL segment_dy = nextpoint->Y - endpoint->Y;
2243 REAL segment_dx = nextpoint->X - endpoint->X;
2244 REAL segment_length = sqrtf(segment_dy * segment_dy + segment_dx * segment_dx);
2245 REAL posx, posy;
2246 REAL perp_dx, perp_dy;
2247 REAL sina, cosa;
2248 GpPointF *tmp_points;
2250 if(!custom)
2251 break;
2253 if (custom->type == CustomLineCapTypeAdjustableArrow)
2255 GpAdjustableArrowCap *arrow = (GpAdjustableArrowCap *)custom;
2256 TRACE("GpAdjustableArrowCap middle_inset: %f height: %f width: %f\n",
2257 arrow->middle_inset, arrow->height, arrow->width);
2259 else
2260 TRACE("GpCustomLineCap fill: %d basecap: %d inset: %f join: %d scale: %f pen_width:%f\n",
2261 custom->fill, custom->basecap, custom->inset, custom->join, custom->scale, pen_width);
2263 sina = pen_width * custom->scale * segment_dx / segment_length;
2264 cosa = -pen_width * custom->scale * segment_dy / segment_length;
2266 /* Coordination where cap needs to be drawn */
2267 posx = endpoint->X;
2268 posy = endpoint->Y;
2270 if (!custom->fill)
2272 tmp_points = malloc(custom->pathdata.Count * sizeof(GpPoint));
2273 if (!tmp_points) {
2274 ERR("Out of memory\n");
2275 return;
2278 for (INT i = 0; i < custom->pathdata.Count; i++)
2280 tmp_points[i].X = posx + custom->pathdata.Points[i].X * cosa - custom->pathdata.Points[i].Y * sina;
2281 tmp_points[i].Y = posy + custom->pathdata.Points[i].X * sina + custom->pathdata.Points[i].Y * cosa;
2283 if ((custom->pathdata.Types[custom->pathdata.Count - 1] & PathPointTypeCloseSubpath) == PathPointTypeCloseSubpath)
2284 widen_closed_figure(tmp_points, 0, custom->pathdata.Count - 1, pen, pen_width, last_point);
2285 else
2286 widen_open_figure(tmp_points, 0, custom->pathdata.Count - 1, pen, pen_width, custom->strokeEndCap, NULL, custom->strokeStartCap, NULL, last_point);
2287 free(tmp_points);
2289 else
2291 for (INT i = 0; i < custom->pathdata.Count; i++)
2293 /* rotation of CustomCap according to line */
2294 perp_dx = custom->pathdata.Points[i].X * cosa - custom->pathdata.Points[i].Y * sina;
2295 perp_dy = custom->pathdata.Points[i].X * sina + custom->pathdata.Points[i].Y * cosa;
2296 *last_point = add_path_list_node(*last_point, posx + perp_dx,
2297 posy + perp_dy, custom->pathdata.Types[i]);
2300 /* FIXME: The line should be adjusted by the inset value of the custom cap. */
2301 break;
2305 (*last_point)->type |= PathPointTypeCloseSubpath;
2308 static void widen_open_figure(const GpPointF *points, int start, int end,
2309 GpPen *pen, REAL pen_width, GpLineCap start_cap, GpCustomLineCap* custom_start,
2310 GpLineCap end_cap, GpCustomLineCap* custom_end, path_list_node_t **last_point)
2312 int i;
2313 path_list_node_t *prev_point;
2315 if (end <= start || pen_width == 0.0)
2316 return;
2318 prev_point = *last_point;
2320 widen_cap(&points[start], &points[start+1],
2321 pen_width, start_cap, custom_start, FALSE, TRUE, last_point);
2323 for (i=start+1; i<end; i++)
2324 widen_joint(&points[i-1], &points[i], &points[i+1],
2325 pen, pen_width, last_point);
2327 widen_cap(&points[end], &points[end-1],
2328 pen_width, end_cap, custom_end, TRUE, TRUE, last_point);
2330 for (i=end-1; i>start; i--)
2331 widen_joint(&points[i+1], &points[i], &points[i-1],
2332 pen, pen_width, last_point);
2334 widen_cap(&points[start], &points[start+1],
2335 pen_width, start_cap, custom_start, TRUE, FALSE, last_point);
2337 prev_point->next->type = PathPointTypeStart;
2338 (*last_point)->type |= PathPointTypeCloseSubpath;
2341 static void widen_closed_figure(const GpPointF *points, int start, int end,
2342 GpPen *pen, REAL pen_width, path_list_node_t **last_point)
2344 int i;
2345 path_list_node_t *prev_point;
2347 if (end <= start || pen_width == 0.0)
2348 return;
2350 /* left outline */
2351 prev_point = *last_point;
2353 widen_joint(&points[end], &points[start],
2354 &points[start+1], pen, pen_width, last_point);
2356 for (i=start+1; i<end; i++)
2357 widen_joint(&points[i-1], &points[i],
2358 &points[i+1], pen, pen_width, last_point);
2360 widen_joint(&points[end-1], &points[end],
2361 &points[start], pen, pen_width, last_point);
2363 prev_point->next->type = PathPointTypeStart;
2364 (*last_point)->type |= PathPointTypeCloseSubpath;
2366 /* right outline */
2367 prev_point = *last_point;
2369 widen_joint(&points[start], &points[end],
2370 &points[end-1], pen, pen_width, last_point);
2372 for (i=end-1; i>start; i--)
2373 widen_joint(&points[i+1], &points[i],
2374 &points[i-1], pen, pen_width, last_point);
2376 widen_joint(&points[start+1], &points[start],
2377 &points[end], pen, pen_width, last_point);
2379 prev_point->next->type = PathPointTypeStart;
2380 (*last_point)->type |= PathPointTypeCloseSubpath;
2383 static void widen_dashed_figure(GpPath *path, int start, int end, int closed,
2384 GpPen *pen, REAL pen_width, path_list_node_t **last_point)
2386 int i, j;
2387 REAL dash_pos=0.0;
2388 int dash_index=0;
2389 const REAL *dash_pattern;
2390 REAL *dash_pattern_scaled;
2391 REAL dash_pattern_scaling = max(pen->width, 1.0);
2392 int dash_count;
2393 GpPointF *tmp_points;
2394 REAL segment_dy;
2395 REAL segment_dx;
2396 REAL segment_length;
2397 REAL segment_pos;
2398 int num_tmp_points=0;
2399 int draw_start_cap=0;
2400 static const REAL dash_dot_dot[6] = { 3.0, 1.0, 1.0, 1.0, 1.0, 1.0 };
2402 if (end <= start || pen_width == 0.0)
2403 return;
2405 switch (pen->dash)
2407 case DashStyleDash:
2408 default:
2409 dash_pattern = dash_dot_dot;
2410 dash_count = 2;
2411 break;
2412 case DashStyleDot:
2413 dash_pattern = &dash_dot_dot[2];
2414 dash_count = 2;
2415 break;
2416 case DashStyleDashDot:
2417 dash_pattern = dash_dot_dot;
2418 dash_count = 4;
2419 break;
2420 case DashStyleDashDotDot:
2421 dash_pattern = dash_dot_dot;
2422 dash_count = 6;
2423 break;
2424 case DashStyleCustom:
2425 dash_pattern = pen->dashes;
2426 dash_count = pen->numdashes;
2427 break;
2430 dash_pattern_scaled = malloc(dash_count * sizeof(REAL));
2431 if (!dash_pattern_scaled) return;
2433 for (i = 0; i < dash_count; i++)
2434 dash_pattern_scaled[i] = dash_pattern_scaling * dash_pattern[i];
2436 tmp_points = calloc(end - start + 2, sizeof(GpPoint));
2437 if (!tmp_points) {
2438 free(dash_pattern_scaled);
2439 return; /* FIXME */
2442 if (!closed)
2443 draw_start_cap = 1;
2445 for (j=start; j <= end; j++)
2447 if (j == start)
2449 if (closed)
2450 i = end;
2451 else
2452 continue;
2454 else
2455 i = j-1;
2457 segment_dy = path->pathdata.Points[j].Y - path->pathdata.Points[i].Y;
2458 segment_dx = path->pathdata.Points[j].X - path->pathdata.Points[i].X;
2459 segment_length = hypotf(segment_dy, segment_dx);
2460 segment_pos = 0.0;
2462 while (1)
2464 if (dash_pos == 0.0)
2466 if ((dash_index % 2) == 0)
2468 /* start dash */
2469 num_tmp_points = 1;
2470 tmp_points[0].X = path->pathdata.Points[i].X + segment_dx * segment_pos / segment_length;
2471 tmp_points[0].Y = path->pathdata.Points[i].Y + segment_dy * segment_pos / segment_length;
2473 else
2475 /* end dash */
2476 tmp_points[num_tmp_points].X = path->pathdata.Points[i].X + segment_dx * segment_pos / segment_length;
2477 tmp_points[num_tmp_points].Y = path->pathdata.Points[i].Y + segment_dy * segment_pos / segment_length;
2479 widen_open_figure(tmp_points, 0, num_tmp_points, pen, pen_width,
2480 draw_start_cap ? pen->startcap : LineCapFlat, pen->customstart,
2481 LineCapFlat, NULL, last_point);
2482 draw_start_cap = 0;
2483 num_tmp_points = 0;
2487 if (dash_pattern_scaled[dash_index] - dash_pos > segment_length - segment_pos)
2489 /* advance to next segment */
2490 if ((dash_index % 2) == 0)
2492 tmp_points[num_tmp_points] = path->pathdata.Points[j];
2493 num_tmp_points++;
2495 dash_pos += segment_length - segment_pos;
2496 break;
2498 else
2500 /* advance to next dash in pattern */
2501 segment_pos += dash_pattern_scaled[dash_index] - dash_pos;
2502 dash_pos = 0.0;
2503 if (++dash_index == dash_count)
2504 dash_index = 0;
2505 continue;
2510 if (dash_index % 2 == 0 && num_tmp_points != 0)
2512 /* last dash overflows last segment */
2513 widen_open_figure(tmp_points, 0, num_tmp_points-1, pen, pen_width,
2514 draw_start_cap ? pen->startcap : LineCapFlat, pen->customstart,
2515 closed ? LineCapFlat : pen->endcap, pen->customend, last_point);
2518 free(dash_pattern_scaled);
2519 free(tmp_points);
2522 void widen_anchors(GpPath *flat_path, GpPen *pen, REAL pen_width, path_list_node_t** last_point)
2524 BYTE *types = flat_path->pathdata.Types;
2525 int i, subpath_start=0;
2527 for (i=0; i < flat_path->pathdata.Count; i++)
2529 if ((types[i]&PathPointTypeCloseSubpath) == PathPointTypeCloseSubpath)
2530 continue;
2532 if ((types[i]&PathPointTypePathTypeMask) == PathPointTypeStart)
2533 subpath_start = i;
2535 if (i == flat_path->pathdata.Count-1 ||
2536 (types[i+1]&PathPointTypePathTypeMask) == PathPointTypeStart)
2538 if (pen->startcap & LineCapAnchorMask)
2539 add_anchor(&flat_path->pathdata.Points[subpath_start],
2540 &flat_path->pathdata.Points[subpath_start+1],
2541 pen, pen_width, pen->startcap, pen->customstart, last_point);
2543 if (pen->endcap & LineCapAnchorMask)
2544 add_anchor(&flat_path->pathdata.Points[i],
2545 &flat_path->pathdata.Points[i-1],
2546 pen, pen_width, pen->endcap, pen->customend, last_point);
2551 GpStatus widen_flat_path_anchors(GpPath *flat_path, GpPen *pen, REAL pen_width, GpPath **anchors)
2553 GpStatus stat;
2554 path_list_node_t *points=NULL, *last_point=NULL;
2556 if (!flat_path || !pen)
2557 return InvalidParameter;
2559 if (init_path_list(&points, 314.0, 22.0))
2561 last_point = points;
2563 stat = GdipCreatePath(flat_path->fill, anchors);
2564 if (stat == Ok)
2566 widen_anchors(flat_path, pen, pen_width, &last_point);
2568 if (!path_list_to_path(points->next, *anchors))
2569 stat = OutOfMemory;
2571 if (stat != Ok)
2573 GdipDeletePath(*anchors);
2574 *anchors = NULL;
2577 free_path_list(points);
2579 else
2580 stat = OutOfMemory;
2582 /* FIXME: Apply insets to flat_path */
2584 return stat;
2587 GpStatus WINGDIPAPI GdipWidenPath(GpPath *path, GpPen *pen, GpMatrix *matrix,
2588 REAL flatness)
2590 GpPath *flat_path=NULL;
2591 GpStatus status;
2592 path_list_node_t *points=NULL, *last_point=NULL;
2593 int i, subpath_start=0;
2595 TRACE("(%p,%p,%s,%0.2f)\n", path, pen, debugstr_matrix(matrix), flatness);
2597 if (!path || !pen)
2598 return InvalidParameter;
2600 if (path->pathdata.Count <= 1)
2601 return OutOfMemory;
2603 status = GdipClonePath(path, &flat_path);
2605 if (status == Ok)
2606 status = GdipFlattenPath(flat_path, pen->unit == UnitPixel ? matrix : NULL, flatness);
2608 if (status == Ok && !init_path_list(&points, 314.0, 22.0))
2609 status = OutOfMemory;
2611 if (status == Ok)
2613 REAL pen_width = (pen->unit == UnitWorld) ? max(pen->width, 1.0) : pen->width;
2614 BYTE *types = flat_path->pathdata.Types;
2616 last_point = points;
2618 if (pen->dashcap != DashCapFlat)
2619 FIXME("unimplemented dash cap %d\n", pen->dashcap);
2621 if (pen->join == LineJoinRound)
2622 FIXME("unimplemented line join %d\n", pen->join);
2624 if (pen->align != PenAlignmentCenter)
2625 FIXME("unimplemented pen alignment %d\n", pen->align);
2627 if (pen->compound_array_size != 0)
2628 FIXME("unimplemented pen compoundline. Solid line will be drawn instead: %d\n", pen->compound_array_size);
2630 for (i=0; i < flat_path->pathdata.Count; i++)
2632 if ((types[i]&PathPointTypePathTypeMask) == PathPointTypeStart)
2633 subpath_start = i;
2635 if ((types[i]&PathPointTypeCloseSubpath) == PathPointTypeCloseSubpath)
2637 if (pen->dash != DashStyleSolid)
2638 widen_dashed_figure(flat_path, subpath_start, i, 1, pen, pen_width, &last_point);
2639 else
2640 widen_closed_figure(flat_path->pathdata.Points, subpath_start, i, pen, pen_width, &last_point);
2642 else if (i == flat_path->pathdata.Count-1 ||
2643 (types[i+1]&PathPointTypePathTypeMask) == PathPointTypeStart)
2645 if (pen->dash != DashStyleSolid)
2646 widen_dashed_figure(flat_path, subpath_start, i, 0, pen, pen_width, &last_point);
2647 else
2648 widen_open_figure(flat_path->pathdata.Points, subpath_start, i, pen, pen_width,
2649 pen->startcap, pen->customstart, pen->endcap, pen->customend, &last_point);
2653 widen_anchors(flat_path, pen, fmax(pen->width, 2.0), &last_point);
2655 if (!path_list_to_path(points->next, path))
2656 status = OutOfMemory;
2658 path->fill = FillModeWinding;
2661 free_path_list(points);
2663 GdipDeletePath(flat_path);
2665 if (status == Ok && pen->unit != UnitPixel)
2666 status = GdipTransformPath(path, matrix);
2668 return status;
2671 GpStatus WINGDIPAPI GdipAddPathRectangle(GpPath *path, REAL x, REAL y,
2672 REAL width, REAL height)
2674 GpPath *backup;
2675 GpPointF ptf[2];
2676 GpStatus retstat;
2677 BOOL old_new;
2679 TRACE("(%p, %.2f, %.2f, %.2f, %.2f)\n", path, x, y, width, height);
2681 if(!path)
2682 return InvalidParameter;
2684 if (width <= 0.0 || height <= 0.0)
2685 return Ok;
2687 /* make a backup copy of path data */
2688 if((retstat = GdipClonePath(path, &backup)) != Ok)
2689 return retstat;
2691 /* rectangle should start as new path */
2692 old_new = path->newfigure;
2693 path->newfigure = TRUE;
2694 if((retstat = GdipAddPathLine(path,x,y,x+width,y)) != Ok){
2695 path->newfigure = old_new;
2696 goto fail;
2699 ptf[0].X = x+width;
2700 ptf[0].Y = y+height;
2701 ptf[1].X = x;
2702 ptf[1].Y = y+height;
2704 if((retstat = GdipAddPathLine2(path, ptf, 2)) != Ok) goto fail;
2705 path->pathdata.Types[path->pathdata.Count-1] |= PathPointTypeCloseSubpath;
2707 /* free backup */
2708 GdipDeletePath(backup);
2709 return Ok;
2711 fail:
2712 /* reverting */
2713 free(path->pathdata.Points);
2714 free(path->pathdata.Types);
2715 memcpy(path, backup, sizeof(*path));
2716 free(backup);
2718 return retstat;
2721 GpStatus WINGDIPAPI GdipAddPathRectangleI(GpPath *path, INT x, INT y,
2722 INT width, INT height)
2724 TRACE("(%p, %d, %d, %d, %d)\n", path, x, y, width, height);
2726 return GdipAddPathRectangle(path,(REAL)x,(REAL)y,(REAL)width,(REAL)height);
2729 GpStatus WINGDIPAPI GdipAddPathRectangles(GpPath *path, GDIPCONST GpRectF *rects, INT count)
2731 GpPath *backup;
2732 GpStatus retstat;
2733 INT i;
2735 TRACE("(%p, %p, %d)\n", path, rects, count);
2737 /* count == 0 - verified condition */
2738 if(!path || !rects || count == 0)
2739 return InvalidParameter;
2741 if(count < 0)
2742 return OutOfMemory;
2744 /* make a backup copy */
2745 if((retstat = GdipClonePath(path, &backup)) != Ok)
2746 return retstat;
2748 for(i = 0; i < count; i++){
2749 if((retstat = GdipAddPathRectangle(path,rects[i].X,rects[i].Y,rects[i].Width,rects[i].Height)) != Ok)
2750 goto fail;
2753 /* free backup */
2754 GdipDeletePath(backup);
2755 return Ok;
2757 fail:
2758 /* reverting */
2759 free(path->pathdata.Points);
2760 free(path->pathdata.Types);
2761 memcpy(path, backup, sizeof(*path));
2762 free(backup);
2764 return retstat;
2767 GpStatus WINGDIPAPI GdipAddPathRectanglesI(GpPath *path, GDIPCONST GpRect *rects, INT count)
2769 GpRectF *rectsF;
2770 GpStatus retstat;
2771 INT i;
2773 TRACE("(%p, %p, %d)\n", path, rects, count);
2775 if(!rects || count == 0)
2776 return InvalidParameter;
2778 if(count < 0)
2779 return OutOfMemory;
2781 rectsF = malloc(sizeof(GpRectF) * count);
2783 for(i = 0;i < count;i++)
2784 set_rect(&rectsF[i], rects[i].X, rects[i].Y, rects[i].Width, rects[i].Height);
2786 retstat = GdipAddPathRectangles(path, rectsF, count);
2787 free(rectsF);
2789 return retstat;
2792 GpStatus WINGDIPAPI GdipSetPathMarker(GpPath* path)
2794 INT count;
2796 TRACE("(%p)\n", path);
2798 if(!path)
2799 return InvalidParameter;
2801 count = path->pathdata.Count;
2803 /* set marker flag */
2804 if(count > 0)
2805 path->pathdata.Types[count-1] |= PathPointTypePathMarker;
2807 return Ok;
2810 GpStatus WINGDIPAPI GdipClearPathMarkers(GpPath* path)
2812 INT count;
2813 INT i;
2815 TRACE("(%p)\n", path);
2817 if(!path)
2818 return InvalidParameter;
2820 count = path->pathdata.Count;
2822 for(i = 0; i < count - 1; i++){
2823 path->pathdata.Types[i] &= ~PathPointTypePathMarker;
2826 return Ok;
2829 GpStatus WINGDIPAPI GdipWindingModeOutline(GpPath *path, GpMatrix *matrix, REAL flatness)
2831 FIXME("stub: %p, %p, %.2f\n", path, matrix, flatness);
2832 return NotImplemented;
2835 #define FLAGS_INTPATH 0x4000
2837 struct path_header
2839 DWORD version;
2840 DWORD count;
2841 DWORD flags;
2844 /* Test to see if the path could be stored as an array of shorts */
2845 static BOOL is_integer_path(const GpPath *path)
2847 int i;
2849 if (!path->pathdata.Count) return FALSE;
2851 for (i = 0; i < path->pathdata.Count; i++)
2853 short x, y;
2854 x = gdip_round(path->pathdata.Points[i].X);
2855 y = gdip_round(path->pathdata.Points[i].Y);
2856 if (path->pathdata.Points[i].X != (REAL)x || path->pathdata.Points[i].Y != (REAL)y)
2857 return FALSE;
2859 return TRUE;
2862 DWORD write_path_data(GpPath *path, void *data)
2864 struct path_header *header = data;
2865 BOOL integer_path = is_integer_path(path);
2866 DWORD i, size;
2867 BYTE *types;
2869 size = sizeof(struct path_header) + path->pathdata.Count;
2870 if (integer_path)
2871 size += sizeof(short[2]) * path->pathdata.Count;
2872 else
2873 size += sizeof(float[2]) * path->pathdata.Count;
2874 size = (size + 3) & ~3;
2876 if (!data) return size;
2878 header->version = VERSION_MAGIC2;
2879 header->count = path->pathdata.Count;
2880 header->flags = integer_path ? FLAGS_INTPATH : 0;
2882 if (integer_path)
2884 short *points = (short*)(header + 1);
2885 for (i = 0; i < path->pathdata.Count; i++)
2887 points[2*i] = path->pathdata.Points[i].X;
2888 points[2*i + 1] = path->pathdata.Points[i].Y;
2890 types = (BYTE*)(points + 2*i);
2892 else
2894 float *points = (float*)(header + 1);
2895 for (i = 0; i < path->pathdata.Count; i++)
2897 points[2*i] = path->pathdata.Points[i].X;
2898 points[2*i + 1] = path->pathdata.Points[i].Y;
2900 types = (BYTE*)(points + 2*i);
2903 for (i=0; i<path->pathdata.Count; i++)
2904 types[i] = path->pathdata.Types[i];
2905 memset(types + i, 0, ((path->pathdata.Count + 3) & ~3) - path->pathdata.Count);
2906 return size;