[GDIPLUS] Sync with Wine Staging 2.16. CORE-13762
[reactos.git] / reactos / dll / win32 / gdiplus / graphicspath.c
1 /*
2 * Copyright (C) 2007 Google (Evan Stade)
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17 *
18 */
19
20 #include "gdiplus_private.h"
21
22 typedef struct path_list_node_t path_list_node_t;
23 struct path_list_node_t {
24 GpPointF pt;
25 BYTE type; /* PathPointTypeStart or PathPointTypeLine */
26 path_list_node_t *next;
27 };
28
29 /* init list */
30 static BOOL init_path_list(path_list_node_t **node, REAL x, REAL y)
31 {
32 *node = heap_alloc_zero(sizeof(path_list_node_t));
33 if(!*node)
34 return FALSE;
35
36 (*node)->pt.X = x;
37 (*node)->pt.Y = y;
38 (*node)->type = PathPointTypeStart;
39 (*node)->next = NULL;
40
41 return TRUE;
42 }
43
44 /* free all nodes including argument */
45 static void free_path_list(path_list_node_t *node)
46 {
47 path_list_node_t *n = node;
48
49 while(n){
50 n = n->next;
51 heap_free(node);
52 node = n;
53 }
54 }
55
56 /* Add a node after 'node' */
57 /*
58 * Returns
59 * pointer on success
60 * NULL on allocation problems
61 */
62 static path_list_node_t* add_path_list_node(path_list_node_t *node, REAL x, REAL y, BOOL type)
63 {
64 path_list_node_t *new;
65
66 new = heap_alloc_zero(sizeof(path_list_node_t));
67 if(!new)
68 return NULL;
69
70 new->pt.X = x;
71 new->pt.Y = y;
72 new->type = type;
73 new->next = node->next;
74 node->next = new;
75
76 return new;
77 }
78
79 /* returns element count */
80 static INT path_list_count(path_list_node_t *node)
81 {
82 INT count = 1;
83
84 while((node = node->next))
85 ++count;
86
87 return count;
88 }
89
90 /* GdipFlattenPath helper */
91 /*
92 * Used to recursively flatten single Bezier curve
93 * Parameters:
94 * - start : pointer to start point node;
95 * - (x2, y2): first control point;
96 * - (x3, y3): second control point;
97 * - end : pointer to end point node
98 * - flatness: admissible error of linear approximation.
99 *
100 * Return value:
101 * TRUE : success
102 * FALSE: out of memory
103 *
104 * TODO: used quality criteria should be revised to match native as
105 * closer as possible.
106 */
107 static BOOL flatten_bezier(path_list_node_t *start, REAL x2, REAL y2, REAL x3, REAL y3,
108 path_list_node_t *end, REAL flatness)
109 {
110 /* this 5 middle points with start/end define to half-curves */
111 GpPointF mp[5];
112 GpPointF pt, pt_st;
113 path_list_node_t *node;
114
115 /* calculate bezier curve middle points == new control points */
116 mp[0].X = (start->pt.X + x2) / 2.0;
117 mp[0].Y = (start->pt.Y + y2) / 2.0;
118 /* middle point between control points */
119 pt.X = (x2 + x3) / 2.0;
120 pt.Y = (y2 + y3) / 2.0;
121 mp[1].X = (mp[0].X + pt.X) / 2.0;
122 mp[1].Y = (mp[0].Y + pt.Y) / 2.0;
123 mp[4].X = (end->pt.X + x3) / 2.0;
124 mp[4].Y = (end->pt.Y + y3) / 2.0;
125 mp[3].X = (mp[4].X + pt.X) / 2.0;
126 mp[3].Y = (mp[4].Y + pt.Y) / 2.0;
127
128 mp[2].X = (mp[1].X + mp[3].X) / 2.0;
129 mp[2].Y = (mp[1].Y + mp[3].Y) / 2.0;
130
131 if ((x2 == mp[0].X && y2 == mp[0].Y && x3 == mp[1].X && y3 == mp[1].Y) ||
132 (x2 == mp[3].X && y2 == mp[3].Y && x3 == mp[4].X && y3 == mp[4].Y))
133 return TRUE;
134
135 pt = end->pt;
136 pt_st = start->pt;
137 /* check flatness as a half of distance between middle point and a linearized path */
138 if(fabs(((pt.Y - pt_st.Y)*mp[2].X + (pt_st.X - pt.X)*mp[2].Y +
139 (pt_st.Y*pt.X - pt_st.X*pt.Y))) <=
140 (0.5 * flatness*sqrtf((powf(pt.Y - pt_st.Y, 2.0) + powf(pt_st.X - pt.X, 2.0))))){
141 return TRUE;
142 }
143 else
144 /* add a middle point */
145 if(!(node = add_path_list_node(start, mp[2].X, mp[2].Y, PathPointTypeLine)))
146 return FALSE;
147
148 /* do the same with halves */
149 flatten_bezier(start, mp[0].X, mp[0].Y, mp[1].X, mp[1].Y, node, flatness);
150 flatten_bezier(node, mp[3].X, mp[3].Y, mp[4].X, mp[4].Y, end, flatness);
151
152 return TRUE;
153 }
154
155 /*******************************************************************************
156 * GdipAddPathArc [GDIPLUS.1]
157 *
158 * Add an elliptical arc to the given path.
159 *
160 * PARAMS
161 * path [I/O] Path that the arc is appended to
162 * x1 [I] X coordinate of the boundary box
163 * y1 [I] Y coordinate of the boundary box
164 * x2 [I] Width of the boundary box
165 * y2 [I] Height of the boundary box
166 * startAngle [I] Starting angle of the arc, clockwise
167 * sweepAngle [I] Angle of the arc, clockwise
168 *
169 * RETURNS
170 * InvalidParameter If the given path is invalid
171 * OutOfMemory If memory allocation fails, i.e. the path cannot be lengthened
172 * Ok If everything works out as expected
173 *
174 * NOTES
175 * This functions takes the newfigure value of the given path into account,
176 * i.e. the arc is connected to the end of the given path if it was set to
177 * FALSE, otherwise the arc's first point gets the PathPointTypeStart value.
178 * In both cases, the value of newfigure of the given path is FALSE
179 * afterwards.
180 */
181 GpStatus WINGDIPAPI GdipAddPathArc(GpPath *path, REAL x1, REAL y1, REAL x2,
182 REAL y2, REAL startAngle, REAL sweepAngle)
183 {
184 INT count, old_count, i;
185
186 TRACE("(%p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n",
187 path, x1, y1, x2, y2, startAngle, sweepAngle);
188
189 if(!path)
190 return InvalidParameter;
191
192 count = arc2polybezier(NULL, x1, y1, x2, y2, startAngle, sweepAngle);
193
194 if(count == 0)
195 return Ok;
196 if(!lengthen_path(path, count))
197 return OutOfMemory;
198
199 old_count = path->pathdata.Count;
200 arc2polybezier(&path->pathdata.Points[old_count], x1, y1, x2, y2,
201 startAngle, sweepAngle);
202
203 for(i = 0; i < count; i++){
204 path->pathdata.Types[old_count + i] = PathPointTypeBezier;
205 }
206
207 path->pathdata.Types[old_count] =
208 (path->newfigure ? PathPointTypeStart : PathPointTypeLine);
209 path->newfigure = FALSE;
210 path->pathdata.Count += count;
211
212 return Ok;
213 }
214
215 /*******************************************************************************
216 * GdipAddPathArcI [GDUPLUS.2]
217 *
218 * See GdipAddPathArc
219 */
220 GpStatus WINGDIPAPI GdipAddPathArcI(GpPath *path, INT x1, INT y1, INT x2,
221 INT y2, REAL startAngle, REAL sweepAngle)
222 {
223 TRACE("(%p, %d, %d, %d, %d, %.2f, %.2f)\n",
224 path, x1, y1, x2, y2, startAngle, sweepAngle);
225
226 return GdipAddPathArc(path,(REAL)x1,(REAL)y1,(REAL)x2,(REAL)y2,startAngle,sweepAngle);
227 }
228
229 GpStatus WINGDIPAPI GdipAddPathBezier(GpPath *path, REAL x1, REAL y1, REAL x2,
230 REAL y2, REAL x3, REAL y3, REAL x4, REAL y4)
231 {
232 INT old_count;
233
234 TRACE("(%p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n",
235 path, x1, y1, x2, y2, x3, y3, x4, y4);
236
237 if(!path)
238 return InvalidParameter;
239
240 if(!lengthen_path(path, 4))
241 return OutOfMemory;
242
243 old_count = path->pathdata.Count;
244
245 path->pathdata.Points[old_count].X = x1;
246 path->pathdata.Points[old_count].Y = y1;
247 path->pathdata.Points[old_count + 1].X = x2;
248 path->pathdata.Points[old_count + 1].Y = y2;
249 path->pathdata.Points[old_count + 2].X = x3;
250 path->pathdata.Points[old_count + 2].Y = y3;
251 path->pathdata.Points[old_count + 3].X = x4;
252 path->pathdata.Points[old_count + 3].Y = y4;
253
254 path->pathdata.Types[old_count] =
255 (path->newfigure ? PathPointTypeStart : PathPointTypeLine);
256 path->pathdata.Types[old_count + 1] = PathPointTypeBezier;
257 path->pathdata.Types[old_count + 2] = PathPointTypeBezier;
258 path->pathdata.Types[old_count + 3] = PathPointTypeBezier;
259
260 path->newfigure = FALSE;
261 path->pathdata.Count += 4;
262
263 return Ok;
264 }
265
266 GpStatus WINGDIPAPI GdipAddPathBezierI(GpPath *path, INT x1, INT y1, INT x2,
267 INT y2, INT x3, INT y3, INT x4, INT y4)
268 {
269 TRACE("(%p, %d, %d, %d, %d, %d, %d, %d, %d)\n",
270 path, x1, y1, x2, y2, x3, y3, x4, y4);
271
272 return GdipAddPathBezier(path,(REAL)x1,(REAL)y1,(REAL)x2,(REAL)y2,(REAL)x3,(REAL)y3,
273 (REAL)x4,(REAL)y4);
274 }
275
276 GpStatus WINGDIPAPI GdipAddPathBeziers(GpPath *path, GDIPCONST GpPointF *points,
277 INT count)
278 {
279 INT i, old_count;
280
281 TRACE("(%p, %p, %d)\n", path, points, count);
282
283 if(!path || !points || ((count - 1) % 3))
284 return InvalidParameter;
285
286 if(!lengthen_path(path, count))
287 return OutOfMemory;
288
289 old_count = path->pathdata.Count;
290
291 for(i = 0; i < count; i++){
292 path->pathdata.Points[old_count + i].X = points[i].X;
293 path->pathdata.Points[old_count + i].Y = points[i].Y;
294 path->pathdata.Types[old_count + i] = PathPointTypeBezier;
295 }
296
297 path->pathdata.Types[old_count] =
298 (path->newfigure ? PathPointTypeStart : PathPointTypeLine);
299 path->newfigure = FALSE;
300 path->pathdata.Count += count;
301
302 return Ok;
303 }
304
305 GpStatus WINGDIPAPI GdipAddPathBeziersI(GpPath *path, GDIPCONST GpPoint *points,
306 INT count)
307 {
308 GpPointF *ptsF;
309 GpStatus ret;
310 INT i;
311
312 TRACE("(%p, %p, %d)\n", path, points, count);
313
314 if(!points || ((count - 1) % 3))
315 return InvalidParameter;
316
317 ptsF = heap_alloc_zero(sizeof(GpPointF) * count);
318 if(!ptsF)
319 return OutOfMemory;
320
321 for(i = 0; i < count; i++){
322 ptsF[i].X = (REAL)points[i].X;
323 ptsF[i].Y = (REAL)points[i].Y;
324 }
325
326 ret = GdipAddPathBeziers(path, ptsF, count);
327 heap_free(ptsF);
328
329 return ret;
330 }
331
332 GpStatus WINGDIPAPI GdipAddPathClosedCurve(GpPath *path, GDIPCONST GpPointF *points,
333 INT count)
334 {
335 TRACE("(%p, %p, %d)\n", path, points, count);
336
337 return GdipAddPathClosedCurve2(path, points, count, 1.0);
338 }
339
340 GpStatus WINGDIPAPI GdipAddPathClosedCurveI(GpPath *path, GDIPCONST GpPoint *points,
341 INT count)
342 {
343 TRACE("(%p, %p, %d)\n", path, points, count);
344
345 return GdipAddPathClosedCurve2I(path, points, count, 1.0);
346 }
347
348 GpStatus WINGDIPAPI GdipAddPathClosedCurve2(GpPath *path, GDIPCONST GpPointF *points,
349 INT count, REAL tension)
350 {
351 INT i, len_pt = (count + 1)*3-2;
352 GpPointF *pt;
353 GpPointF *pts;
354 REAL x1, x2, y1, y2;
355 GpStatus stat;
356
357 TRACE("(%p, %p, %d, %.2f)\n", path, points, count, tension);
358
359 if(!path || !points || count <= 1)
360 return InvalidParameter;
361
362 pt = heap_alloc_zero(len_pt * sizeof(GpPointF));
363 pts = heap_alloc_zero((count + 1)*sizeof(GpPointF));
364 if(!pt || !pts){
365 heap_free(pt);
366 heap_free(pts);
367 return OutOfMemory;
368 }
369
370 /* copy source points to extend with the last one */
371 memcpy(pts, points, sizeof(GpPointF)*count);
372 pts[count] = pts[0];
373
374 tension = tension * TENSION_CONST;
375
376 for(i = 0; i < count-1; i++){
377 calc_curve_bezier(&(pts[i]), tension, &x1, &y1, &x2, &y2);
378
379 pt[3*i+2].X = x1;
380 pt[3*i+2].Y = y1;
381 pt[3*i+3].X = pts[i+1].X;
382 pt[3*i+3].Y = pts[i+1].Y;
383 pt[3*i+4].X = x2;
384 pt[3*i+4].Y = y2;
385 }
386
387 /* points [len_pt-2] and [0] are calculated
388 separately to connect splines properly */
389 pts[0] = points[count-1];
390 pts[1] = points[0]; /* equals to start and end of a resulting path */
391 pts[2] = points[1];
392
393 calc_curve_bezier(pts, tension, &x1, &y1, &x2, &y2);
394 pt[len_pt-2].X = x1;
395 pt[len_pt-2].Y = y1;
396 pt[0].X = pts[1].X;
397 pt[0].Y = pts[1].Y;
398 pt[1].X = x2;
399 pt[1].Y = y2;
400 /* close path */
401 pt[len_pt-1].X = pt[0].X;
402 pt[len_pt-1].Y = pt[0].Y;
403
404 stat = GdipAddPathBeziers(path, pt, len_pt);
405
406 /* close figure */
407 if(stat == Ok){
408 path->pathdata.Types[path->pathdata.Count - 1] |= PathPointTypeCloseSubpath;
409 path->newfigure = TRUE;
410 }
411
412 heap_free(pts);
413 heap_free(pt);
414
415 return stat;
416 }
417
418 GpStatus WINGDIPAPI GdipAddPathClosedCurve2I(GpPath *path, GDIPCONST GpPoint *points,
419 INT count, REAL tension)
420 {
421 GpPointF *ptf;
422 INT i;
423 GpStatus stat;
424
425 TRACE("(%p, %p, %d, %.2f)\n", path, points, count, tension);
426
427 if(!path || !points || count <= 1)
428 return InvalidParameter;
429
430 ptf = heap_alloc_zero(sizeof(GpPointF)*count);
431 if(!ptf)
432 return OutOfMemory;
433
434 for(i = 0; i < count; i++){
435 ptf[i].X = (REAL)points[i].X;
436 ptf[i].Y = (REAL)points[i].Y;
437 }
438
439 stat = GdipAddPathClosedCurve2(path, ptf, count, tension);
440
441 heap_free(ptf);
442
443 return stat;
444 }
445
446 GpStatus WINGDIPAPI GdipAddPathCurve(GpPath *path, GDIPCONST GpPointF *points, INT count)
447 {
448 TRACE("(%p, %p, %d)\n", path, points, count);
449
450 if(!path || !points || count <= 1)
451 return InvalidParameter;
452
453 return GdipAddPathCurve2(path, points, count, 1.0);
454 }
455
456 GpStatus WINGDIPAPI GdipAddPathCurveI(GpPath *path, GDIPCONST GpPoint *points, INT count)
457 {
458 TRACE("(%p, %p, %d)\n", path, points, count);
459
460 if(!path || !points || count <= 1)
461 return InvalidParameter;
462
463 return GdipAddPathCurve2I(path, points, count, 1.0);
464 }
465
466 GpStatus WINGDIPAPI GdipAddPathCurve2(GpPath *path, GDIPCONST GpPointF *points, INT count,
467 REAL tension)
468 {
469 INT i, len_pt = count*3-2;
470 GpPointF *pt;
471 REAL x1, x2, y1, y2;
472 GpStatus stat;
473
474 TRACE("(%p, %p, %d, %.2f)\n", path, points, count, tension);
475
476 if(!path || !points || count <= 1)
477 return InvalidParameter;
478
479 pt = heap_alloc_zero(len_pt * sizeof(GpPointF));
480 if(!pt)
481 return OutOfMemory;
482
483 tension = tension * TENSION_CONST;
484
485 calc_curve_bezier_endp(points[0].X, points[0].Y, points[1].X, points[1].Y,
486 tension, &x1, &y1);
487
488 pt[0].X = points[0].X;
489 pt[0].Y = points[0].Y;
490 pt[1].X = x1;
491 pt[1].Y = y1;
492
493 for(i = 0; i < count-2; i++){
494 calc_curve_bezier(&(points[i]), tension, &x1, &y1, &x2, &y2);
495
496 pt[3*i+2].X = x1;
497 pt[3*i+2].Y = y1;
498 pt[3*i+3].X = points[i+1].X;
499 pt[3*i+3].Y = points[i+1].Y;
500 pt[3*i+4].X = x2;
501 pt[3*i+4].Y = y2;
502 }
503
504 calc_curve_bezier_endp(points[count-1].X, points[count-1].Y,
505 points[count-2].X, points[count-2].Y, tension, &x1, &y1);
506
507 pt[len_pt-2].X = x1;
508 pt[len_pt-2].Y = y1;
509 pt[len_pt-1].X = points[count-1].X;
510 pt[len_pt-1].Y = points[count-1].Y;
511
512 stat = GdipAddPathBeziers(path, pt, len_pt);
513
514 heap_free(pt);
515
516 return stat;
517 }
518
519 GpStatus WINGDIPAPI GdipAddPathCurve2I(GpPath *path, GDIPCONST GpPoint *points,
520 INT count, REAL tension)
521 {
522 GpPointF *ptf;
523 INT i;
524 GpStatus stat;
525
526 TRACE("(%p, %p, %d, %.2f)\n", path, points, count, tension);
527
528 if(!path || !points || count <= 1)
529 return InvalidParameter;
530
531 ptf = heap_alloc_zero(sizeof(GpPointF)*count);
532 if(!ptf)
533 return OutOfMemory;
534
535 for(i = 0; i < count; i++){
536 ptf[i].X = (REAL)points[i].X;
537 ptf[i].Y = (REAL)points[i].Y;
538 }
539
540 stat = GdipAddPathCurve2(path, ptf, count, tension);
541
542 heap_free(ptf);
543
544 return stat;
545 }
546
547 GpStatus WINGDIPAPI GdipAddPathCurve3(GpPath *path, GDIPCONST GpPointF *points,
548 INT count, INT offset, INT nseg, REAL tension)
549 {
550 TRACE("(%p, %p, %d, %d, %d, %.2f)\n", path, points, count, offset, nseg, tension);
551
552 if(!path || !points || offset + 1 >= count || count - offset < nseg + 1)
553 return InvalidParameter;
554
555 return GdipAddPathCurve2(path, &points[offset], nseg + 1, tension);
556 }
557
558 GpStatus WINGDIPAPI GdipAddPathCurve3I(GpPath *path, GDIPCONST GpPoint *points,
559 INT count, INT offset, INT nseg, REAL tension)
560 {
561 TRACE("(%p, %p, %d, %d, %d, %.2f)\n", path, points, count, offset, nseg, tension);
562
563 if(!path || !points || offset + 1 >= count || count - offset < nseg + 1)
564 return InvalidParameter;
565
566 return GdipAddPathCurve2I(path, &points[offset], nseg + 1, tension);
567 }
568
569 GpStatus WINGDIPAPI GdipAddPathEllipse(GpPath *path, REAL x, REAL y, REAL width,
570 REAL height)
571 {
572 INT old_count, numpts;
573
574 TRACE("(%p, %.2f, %.2f, %.2f, %.2f)\n", path, x, y, width, height);
575
576 if(!path)
577 return InvalidParameter;
578
579 if(!lengthen_path(path, MAX_ARC_PTS))
580 return OutOfMemory;
581
582 old_count = path->pathdata.Count;
583 if((numpts = arc2polybezier(&path->pathdata.Points[old_count], x, y, width,
584 height, 0.0, 360.0)) != MAX_ARC_PTS){
585 ERR("expected %d points but got %d\n", MAX_ARC_PTS, numpts);
586 return GenericError;
587 }
588
589 memset(&path->pathdata.Types[old_count + 1], PathPointTypeBezier,
590 MAX_ARC_PTS - 1);
591
592 /* An ellipse is an intrinsic figure (always is its own subpath). */
593 path->pathdata.Types[old_count] = PathPointTypeStart;
594 path->pathdata.Types[old_count + MAX_ARC_PTS - 1] |= PathPointTypeCloseSubpath;
595 path->newfigure = TRUE;
596 path->pathdata.Count += MAX_ARC_PTS;
597
598 return Ok;
599 }
600
601 GpStatus WINGDIPAPI GdipAddPathEllipseI(GpPath *path, INT x, INT y, INT width,
602 INT height)
603 {
604 TRACE("(%p, %d, %d, %d, %d)\n", path, x, y, width, height);
605
606 return GdipAddPathEllipse(path,(REAL)x,(REAL)y,(REAL)width,(REAL)height);
607 }
608
609 GpStatus WINGDIPAPI GdipAddPathLine2(GpPath *path, GDIPCONST GpPointF *points,
610 INT count)
611 {
612 INT i, old_count;
613
614 TRACE("(%p, %p, %d)\n", path, points, count);
615
616 if(!path || !points)
617 return InvalidParameter;
618
619 if(!lengthen_path(path, count))
620 return OutOfMemory;
621
622 old_count = path->pathdata.Count;
623
624 for(i = 0; i < count; i++){
625 path->pathdata.Points[old_count + i].X = points[i].X;
626 path->pathdata.Points[old_count + i].Y = points[i].Y;
627 path->pathdata.Types[old_count + i] = PathPointTypeLine;
628 }
629
630 if(path->newfigure){
631 path->pathdata.Types[old_count] = PathPointTypeStart;
632 path->newfigure = FALSE;
633 }
634
635 path->pathdata.Count += count;
636
637 return Ok;
638 }
639
640 GpStatus WINGDIPAPI GdipAddPathLine2I(GpPath *path, GDIPCONST GpPoint *points, INT count)
641 {
642 GpPointF *pointsF;
643 INT i;
644 GpStatus stat;
645
646 TRACE("(%p, %p, %d)\n", path, points, count);
647
648 if(count <= 0)
649 return InvalidParameter;
650
651 pointsF = heap_alloc_zero(sizeof(GpPointF) * count);
652 if(!pointsF) return OutOfMemory;
653
654 for(i = 0;i < count; i++){
655 pointsF[i].X = (REAL)points[i].X;
656 pointsF[i].Y = (REAL)points[i].Y;
657 }
658
659 stat = GdipAddPathLine2(path, pointsF, count);
660
661 heap_free(pointsF);
662
663 return stat;
664 }
665
666 /*************************************************************************
667 * GdipAddPathLine [GDIPLUS.21]
668 *
669 * Add two points to the given path.
670 *
671 * PARAMS
672 * path [I/O] Path that the line is appended to
673 * x1 [I] X coordinate of the first point of the line
674 * y1 [I] Y coordinate of the first point of the line
675 * x2 [I] X coordinate of the second point of the line
676 * y2 [I] Y coordinate of the second point of the line
677 *
678 * RETURNS
679 * InvalidParameter If the first parameter is not a valid path
680 * OutOfMemory If the path cannot be lengthened, i.e. memory allocation fails
681 * Ok If everything works out as expected
682 *
683 * NOTES
684 * This functions takes the newfigure value of the given path into account,
685 * i.e. the two new points are connected to the end of the given path if it
686 * was set to FALSE, otherwise the first point is given the PathPointTypeStart
687 * value. In both cases, the value of newfigure of the given path is FALSE
688 * afterwards.
689 */
690 GpStatus WINGDIPAPI GdipAddPathLine(GpPath *path, REAL x1, REAL y1, REAL x2, REAL y2)
691 {
692 INT old_count;
693
694 TRACE("(%p, %.2f, %.2f, %.2f, %.2f)\n", path, x1, y1, x2, y2);
695
696 if(!path)
697 return InvalidParameter;
698
699 if(!lengthen_path(path, 2))
700 return OutOfMemory;
701
702 old_count = path->pathdata.Count;
703
704 path->pathdata.Points[old_count].X = x1;
705 path->pathdata.Points[old_count].Y = y1;
706 path->pathdata.Points[old_count + 1].X = x2;
707 path->pathdata.Points[old_count + 1].Y = y2;
708
709 path->pathdata.Types[old_count] =
710 (path->newfigure ? PathPointTypeStart : PathPointTypeLine);
711 path->pathdata.Types[old_count + 1] = PathPointTypeLine;
712
713 path->newfigure = FALSE;
714 path->pathdata.Count += 2;
715
716 return Ok;
717 }
718
719 /*************************************************************************
720 * GdipAddPathLineI [GDIPLUS.21]
721 *
722 * See GdipAddPathLine
723 */
724 GpStatus WINGDIPAPI GdipAddPathLineI(GpPath *path, INT x1, INT y1, INT x2, INT y2)
725 {
726 TRACE("(%p, %d, %d, %d, %d)\n", path, x1, y1, x2, y2);
727
728 return GdipAddPathLine(path, (REAL)x1, (REAL)y1, (REAL)x2, (REAL)y2);
729 }
730
731 GpStatus WINGDIPAPI GdipAddPathPath(GpPath *path, GDIPCONST GpPath* addingPath,
732 BOOL connect)
733 {
734 INT old_count, count;
735
736 TRACE("(%p, %p, %d)\n", path, addingPath, connect);
737
738 if(!path || !addingPath)
739 return InvalidParameter;
740
741 old_count = path->pathdata.Count;
742 count = addingPath->pathdata.Count;
743
744 if(!lengthen_path(path, count))
745 return OutOfMemory;
746
747 memcpy(&path->pathdata.Points[old_count], addingPath->pathdata.Points,
748 count * sizeof(GpPointF));
749 memcpy(&path->pathdata.Types[old_count], addingPath->pathdata.Types, count);
750
751 if(path->newfigure || !connect)
752 path->pathdata.Types[old_count] = PathPointTypeStart;
753 else
754 path->pathdata.Types[old_count] = PathPointTypeLine;
755
756 path->newfigure = FALSE;
757 path->pathdata.Count += count;
758
759 return Ok;
760 }
761
762 GpStatus WINGDIPAPI GdipAddPathPie(GpPath *path, REAL x, REAL y, REAL width, REAL height,
763 REAL startAngle, REAL sweepAngle)
764 {
765 GpPointF *ptf;
766 GpStatus status;
767 INT i, count;
768
769 TRACE("(%p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n",
770 path, x, y, width, height, startAngle, sweepAngle);
771
772 if(!path)
773 return InvalidParameter;
774
775 /* on zero width/height only start point added */
776 if(width <= 1e-7 || height <= 1e-7){
777 if(!lengthen_path(path, 1))
778 return OutOfMemory;
779 path->pathdata.Points[0].X = x + width / 2.0;
780 path->pathdata.Points[0].Y = y + height / 2.0;
781 path->pathdata.Types[0] = PathPointTypeStart | PathPointTypeCloseSubpath;
782 path->pathdata.Count = 1;
783 return InvalidParameter;
784 }
785
786 count = arc2polybezier(NULL, x, y, width, height, startAngle, sweepAngle);
787
788 if(count == 0)
789 return Ok;
790
791 ptf = heap_alloc_zero(sizeof(GpPointF)*count);
792 if(!ptf)
793 return OutOfMemory;
794
795 arc2polybezier(ptf, x, y, width, height, startAngle, sweepAngle);
796
797 status = GdipAddPathLine(path, x + width/2, y + height/2, ptf[0].X, ptf[0].Y);
798 if(status != Ok){
799 heap_free(ptf);
800 return status;
801 }
802 /* one spline is already added as a line endpoint */
803 if(!lengthen_path(path, count - 1)){
804 heap_free(ptf);
805 return OutOfMemory;
806 }
807
808 memcpy(&(path->pathdata.Points[path->pathdata.Count]), &(ptf[1]),sizeof(GpPointF)*(count-1));
809 for(i = 0; i < count-1; i++)
810 path->pathdata.Types[path->pathdata.Count+i] = PathPointTypeBezier;
811
812 path->pathdata.Count += count-1;
813
814 GdipClosePathFigure(path);
815
816 heap_free(ptf);
817
818 return status;
819 }
820
821 GpStatus WINGDIPAPI GdipAddPathPieI(GpPath *path, INT x, INT y, INT width, INT height,
822 REAL startAngle, REAL sweepAngle)
823 {
824 TRACE("(%p, %d, %d, %d, %d, %.2f, %.2f)\n",
825 path, x, y, width, height, startAngle, sweepAngle);
826
827 return GdipAddPathPie(path, (REAL)x, (REAL)y, (REAL)width, (REAL)height, startAngle, sweepAngle);
828 }
829
830 GpStatus WINGDIPAPI GdipAddPathPolygon(GpPath *path, GDIPCONST GpPointF *points, INT count)
831 {
832 INT old_count;
833
834 TRACE("(%p, %p, %d)\n", path, points, count);
835
836 if(!path || !points || count < 3)
837 return InvalidParameter;
838
839 if(!lengthen_path(path, count))
840 return OutOfMemory;
841
842 old_count = path->pathdata.Count;
843
844 memcpy(&path->pathdata.Points[old_count], points, count*sizeof(GpPointF));
845 memset(&path->pathdata.Types[old_count + 1], PathPointTypeLine, count - 1);
846
847 /* A polygon is an intrinsic figure */
848 path->pathdata.Types[old_count] = PathPointTypeStart;
849 path->pathdata.Types[old_count + count - 1] |= PathPointTypeCloseSubpath;
850 path->newfigure = TRUE;
851 path->pathdata.Count += count;
852
853 return Ok;
854 }
855
856 GpStatus WINGDIPAPI GdipAddPathPolygonI(GpPath *path, GDIPCONST GpPoint *points, INT count)
857 {
858 GpPointF *ptf;
859 GpStatus status;
860 INT i;
861
862 TRACE("(%p, %p, %d)\n", path, points, count);
863
864 if(!points || count < 3)
865 return InvalidParameter;
866
867 ptf = heap_alloc_zero(sizeof(GpPointF) * count);
868 if(!ptf)
869 return OutOfMemory;
870
871 for(i = 0; i < count; i++){
872 ptf[i].X = (REAL)points[i].X;
873 ptf[i].Y = (REAL)points[i].Y;
874 }
875
876 status = GdipAddPathPolygon(path, ptf, count);
877
878 heap_free(ptf);
879
880 return status;
881 }
882
883 static float fromfixedpoint(const FIXED v)
884 {
885 float f = ((float)v.fract) / (1<<(sizeof(v.fract)*8));
886 f += v.value;
887 return f;
888 }
889
890 struct format_string_args
891 {
892 GpPath *path;
893 float maxY;
894 float scale;
895 float ascent;
896 };
897
898 static GpStatus format_string_callback(HDC dc,
899 GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font,
900 GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format,
901 INT lineno, const RectF *bounds, INT *underlined_indexes,
902 INT underlined_index_count, void *priv)
903 {
904 static const MAT2 identity = { {0,1}, {0,0}, {0,0}, {0,1} };
905 struct format_string_args *args = priv;
906 GpPath *path = args->path;
907 GpStatus status = Ok;
908 float x = rect->X + (bounds->X - rect->X) * args->scale;
909 float y = rect->Y + (bounds->Y - rect->Y) * args->scale;
910 int i;
911
912 if (underlined_index_count)
913 FIXME("hotkey underlines not drawn yet\n");
914
915 if (y + bounds->Height * args->scale > args->maxY)
916 args->maxY = y + bounds->Height * args->scale;
917
918 for (i = index; i < length; ++i)
919 {
920 GLYPHMETRICS gm;
921 TTPOLYGONHEADER *ph = NULL, *origph;
922 char *start;
923 DWORD len, ofs = 0;
924 len = GetGlyphOutlineW(dc, string[i], GGO_BEZIER, &gm, 0, NULL, &identity);
925 if (len == GDI_ERROR)
926 {
927 status = GenericError;
928 break;
929 }
930 origph = ph = heap_alloc_zero(len);
931 start = (char *)ph;
932 if (!ph || !lengthen_path(path, len / sizeof(POINTFX)))
933 {
934 heap_free(ph);
935 status = OutOfMemory;
936 break;
937 }
938 GetGlyphOutlineW(dc, string[i], GGO_BEZIER, &gm, len, start, &identity);
939
940 ofs = 0;
941 while (ofs < len)
942 {
943 DWORD ofs_start = ofs;
944 ph = (TTPOLYGONHEADER*)&start[ofs];
945 path->pathdata.Types[path->pathdata.Count] = PathPointTypeStart;
946 path->pathdata.Points[path->pathdata.Count].X = x + fromfixedpoint(ph->pfxStart.x) * args->scale;
947 path->pathdata.Points[path->pathdata.Count++].Y = y + args->ascent - fromfixedpoint(ph->pfxStart.y) * args->scale;
948 TRACE("Starting at count %i with pos %f, %f)\n", path->pathdata.Count, x, y);
949 ofs += sizeof(*ph);
950 while (ofs - ofs_start < ph->cb)
951 {
952 TTPOLYCURVE *curve = (TTPOLYCURVE*)&start[ofs];
953 int j;
954 ofs += sizeof(TTPOLYCURVE) + (curve->cpfx - 1) * sizeof(POINTFX);
955
956 switch (curve->wType)
957 {
958 case TT_PRIM_LINE:
959 for (j = 0; j < curve->cpfx; ++j)
960 {
961 path->pathdata.Types[path->pathdata.Count] = PathPointTypeLine;
962 path->pathdata.Points[path->pathdata.Count].X = x + fromfixedpoint(curve->apfx[j].x) * args->scale;
963 path->pathdata.Points[path->pathdata.Count++].Y = y + args->ascent - fromfixedpoint(curve->apfx[j].y) * args->scale;
964 }
965 break;
966 case TT_PRIM_CSPLINE:
967 for (j = 0; j < curve->cpfx; ++j)
968 {
969 path->pathdata.Types[path->pathdata.Count] = PathPointTypeBezier;
970 path->pathdata.Points[path->pathdata.Count].X = x + fromfixedpoint(curve->apfx[j].x) * args->scale;
971 path->pathdata.Points[path->pathdata.Count++].Y = y + args->ascent - fromfixedpoint(curve->apfx[j].y) * args->scale;
972 }
973 break;
974 default:
975 ERR("Unhandled type: %u\n", curve->wType);
976 status = GenericError;
977 }
978 }
979 path->pathdata.Types[path->pathdata.Count - 1] |= PathPointTypeCloseSubpath;
980 }
981 path->newfigure = TRUE;
982 x += gm.gmCellIncX * args->scale;
983 y += gm.gmCellIncY * args->scale;
984
985 heap_free(origph);
986 if (status != Ok)
987 break;
988 }
989
990 return status;
991 }
992
993 GpStatus WINGDIPAPI GdipAddPathString(GpPath* path, GDIPCONST WCHAR* string, INT length, GDIPCONST GpFontFamily* family, INT style, REAL emSize, GDIPCONST RectF* layoutRect, GDIPCONST GpStringFormat* format)
994 {
995 GpFont *font;
996 GpStatus status;
997 LOGFONTW lfw;
998 HANDLE hfont;
999 HDC dc;
1000 GpGraphics *graphics;
1001 GpPath *backup;
1002 struct format_string_args args;
1003 int i;
1004 UINT16 native_height;
1005 RectF scaled_layout_rect;
1006 TEXTMETRICW textmetric;
1007
1008 TRACE("(%p, %s, %d, %p, %d, %f, %p, %p)\n", path, debugstr_w(string), length, family, style, emSize, layoutRect, format);
1009 if (!path || !string || !family || !emSize || !layoutRect || !format)
1010 return InvalidParameter;
1011
1012 status = GdipGetEmHeight(family, style, &native_height);
1013 if (status != Ok)
1014 return status;
1015
1016 scaled_layout_rect.X = layoutRect->X;
1017 scaled_layout_rect.Y = layoutRect->Y;
1018 scaled_layout_rect.Width = layoutRect->Width * native_height / emSize;
1019 scaled_layout_rect.Height = layoutRect->Height * native_height / emSize;
1020
1021 if ((status = GdipClonePath(path, &backup)) != Ok)
1022 return status;
1023
1024 dc = CreateCompatibleDC(0);
1025 status = GdipCreateFromHDC(dc, &graphics);
1026 if (status != Ok)
1027 {
1028 DeleteDC(dc);
1029 GdipDeletePath(backup);
1030 return status;
1031 }
1032
1033 status = GdipCreateFont(family, native_height, style, UnitPixel, &font);
1034 if (status != Ok)
1035 {
1036 GdipDeleteGraphics(graphics);
1037 DeleteDC(dc);
1038 GdipDeletePath(backup);
1039 return status;
1040 }
1041
1042 get_log_fontW(font, graphics, &lfw);
1043 GdipDeleteFont(font);
1044 GdipDeleteGraphics(graphics);
1045
1046 hfont = CreateFontIndirectW(&lfw);
1047 if (!hfont)
1048 {
1049 WARN("Failed to create font\n");
1050 DeleteDC(dc);
1051 GdipDeletePath(backup);
1052 return GenericError;
1053 }
1054
1055 SelectObject(dc, hfont);
1056
1057 GetTextMetricsW(dc, &textmetric);
1058
1059 args.path = path;
1060 args.maxY = 0;
1061 args.scale = emSize / native_height;
1062 args.ascent = textmetric.tmAscent * args.scale;
1063 status = gdip_format_string(dc, string, length, NULL, &scaled_layout_rect,
1064 format, TRUE, format_string_callback, &args);
1065
1066 DeleteDC(dc);
1067 DeleteObject(hfont);
1068
1069 if (status != Ok) /* free backup */
1070 {
1071 heap_free(path->pathdata.Points);
1072 heap_free(path->pathdata.Types);
1073 *path = *backup;
1074 heap_free(backup);
1075 return status;
1076 }
1077 if (format && format->line_align == StringAlignmentCenter && layoutRect->Y + args.maxY < layoutRect->Height)
1078 {
1079 float inc = layoutRect->Height + layoutRect->Y - args.maxY;
1080 inc /= 2;
1081 for (i = backup->pathdata.Count; i < path->pathdata.Count; ++i)
1082 path->pathdata.Points[i].Y += inc;
1083 } else if (format && format->line_align == StringAlignmentFar) {
1084 float inc = layoutRect->Height + layoutRect->Y - args.maxY;
1085 for (i = backup->pathdata.Count; i < path->pathdata.Count; ++i)
1086 path->pathdata.Points[i].Y += inc;
1087 }
1088 GdipDeletePath(backup);
1089 return status;
1090 }
1091
1092 GpStatus WINGDIPAPI GdipAddPathStringI(GpPath* path, GDIPCONST WCHAR* string, INT length, GDIPCONST GpFontFamily* family, INT style, REAL emSize, GDIPCONST Rect* layoutRect, GDIPCONST GpStringFormat* format)
1093 {
1094 if (layoutRect)
1095 {
1096 RectF layoutRectF = {
1097 (REAL)layoutRect->X,
1098 (REAL)layoutRect->Y,
1099 (REAL)layoutRect->Width,
1100 (REAL)layoutRect->Height
1101 };
1102 return GdipAddPathString(path, string, length, family, style, emSize, &layoutRectF, format);
1103 }
1104 return InvalidParameter;
1105 }
1106
1107 /*************************************************************************
1108 * GdipClonePath [GDIPLUS.53]
1109 *
1110 * Duplicate the given path in memory.
1111 *
1112 * PARAMS
1113 * path [I] The path to be duplicated
1114 * clone [O] Pointer to the new path
1115 *
1116 * RETURNS
1117 * InvalidParameter If the input path is invalid
1118 * OutOfMemory If allocation of needed memory fails
1119 * Ok If everything works out as expected
1120 */
1121 GpStatus WINGDIPAPI GdipClonePath(GpPath* path, GpPath **clone)
1122 {
1123 TRACE("(%p, %p)\n", path, clone);
1124
1125 if(!path || !clone)
1126 return InvalidParameter;
1127
1128 *clone = heap_alloc_zero(sizeof(GpPath));
1129 if(!*clone) return OutOfMemory;
1130
1131 **clone = *path;
1132
1133 (*clone)->pathdata.Points = heap_alloc_zero(path->datalen * sizeof(PointF));
1134 (*clone)->pathdata.Types = heap_alloc_zero(path->datalen);
1135 if(!(*clone)->pathdata.Points || !(*clone)->pathdata.Types){
1136 heap_free((*clone)->pathdata.Points);
1137 heap_free((*clone)->pathdata.Types);
1138 heap_free(*clone);
1139 return OutOfMemory;
1140 }
1141
1142 memcpy((*clone)->pathdata.Points, path->pathdata.Points,
1143 path->datalen * sizeof(PointF));
1144 memcpy((*clone)->pathdata.Types, path->pathdata.Types, path->datalen);
1145
1146 return Ok;
1147 }
1148
1149 GpStatus WINGDIPAPI GdipClosePathFigure(GpPath* path)
1150 {
1151 TRACE("(%p)\n", path);
1152
1153 if(!path)
1154 return InvalidParameter;
1155
1156 if(path->pathdata.Count > 0){
1157 path->pathdata.Types[path->pathdata.Count - 1] |= PathPointTypeCloseSubpath;
1158 path->newfigure = TRUE;
1159 }
1160
1161 return Ok;
1162 }
1163
1164 GpStatus WINGDIPAPI GdipClosePathFigures(GpPath* path)
1165 {
1166 INT i;
1167
1168 TRACE("(%p)\n", path);
1169
1170 if(!path)
1171 return InvalidParameter;
1172
1173 for(i = 1; i < path->pathdata.Count; i++){
1174 if(path->pathdata.Types[i] == PathPointTypeStart)
1175 path->pathdata.Types[i-1] |= PathPointTypeCloseSubpath;
1176 }
1177
1178 path->newfigure = TRUE;
1179
1180 return Ok;
1181 }
1182
1183 GpStatus WINGDIPAPI GdipCreatePath(GpFillMode fill, GpPath **path)
1184 {
1185 TRACE("(%d, %p)\n", fill, path);
1186
1187 if(!path)
1188 return InvalidParameter;
1189
1190 *path = heap_alloc_zero(sizeof(GpPath));
1191 if(!*path) return OutOfMemory;
1192
1193 (*path)->fill = fill;
1194 (*path)->newfigure = TRUE;
1195
1196 return Ok;
1197 }
1198
1199 GpStatus WINGDIPAPI GdipCreatePath2(GDIPCONST GpPointF* points,
1200 GDIPCONST BYTE* types, INT count, GpFillMode fill, GpPath **path)
1201 {
1202 TRACE("(%p, %p, %d, %d, %p)\n", points, types, count, fill, path);
1203
1204 if(!path)
1205 return InvalidParameter;
1206
1207 *path = heap_alloc_zero(sizeof(GpPath));
1208 if(!*path) return OutOfMemory;
1209
1210 (*path)->pathdata.Points = heap_alloc_zero(count * sizeof(PointF));
1211 (*path)->pathdata.Types = heap_alloc_zero(count);
1212
1213 if(!(*path)->pathdata.Points || !(*path)->pathdata.Types){
1214 heap_free((*path)->pathdata.Points);
1215 heap_free((*path)->pathdata.Types);
1216 heap_free(*path);
1217 return OutOfMemory;
1218 }
1219
1220 memcpy((*path)->pathdata.Points, points, count * sizeof(PointF));
1221 memcpy((*path)->pathdata.Types, types, count);
1222 (*path)->pathdata.Count = count;
1223 (*path)->datalen = count;
1224
1225 (*path)->fill = fill;
1226 (*path)->newfigure = TRUE;
1227
1228 return Ok;
1229 }
1230
1231 GpStatus WINGDIPAPI GdipCreatePath2I(GDIPCONST GpPoint* points,
1232 GDIPCONST BYTE* types, INT count, GpFillMode fill, GpPath **path)
1233 {
1234 GpPointF *ptF;
1235 GpStatus ret;
1236 INT i;
1237
1238 TRACE("(%p, %p, %d, %d, %p)\n", points, types, count, fill, path);
1239
1240 ptF = heap_alloc_zero(sizeof(GpPointF)*count);
1241
1242 for(i = 0;i < count; i++){
1243 ptF[i].X = (REAL)points[i].X;
1244 ptF[i].Y = (REAL)points[i].Y;
1245 }
1246
1247 ret = GdipCreatePath2(ptF, types, count, fill, path);
1248
1249 heap_free(ptF);
1250
1251 return ret;
1252 }
1253
1254 GpStatus WINGDIPAPI GdipDeletePath(GpPath *path)
1255 {
1256 TRACE("(%p)\n", path);
1257
1258 if(!path)
1259 return InvalidParameter;
1260
1261 heap_free(path->pathdata.Points);
1262 heap_free(path->pathdata.Types);
1263 heap_free(path);
1264
1265 return Ok;
1266 }
1267
1268 GpStatus WINGDIPAPI GdipFlattenPath(GpPath *path, GpMatrix* matrix, REAL flatness)
1269 {
1270 path_list_node_t *list, *node;
1271 GpPointF pt;
1272 INT i = 1;
1273 INT startidx = 0;
1274 GpStatus stat;
1275
1276 TRACE("(%p, %p, %.2f)\n", path, matrix, flatness);
1277
1278 if(!path)
1279 return InvalidParameter;
1280
1281 if(path->pathdata.Count == 0)
1282 return Ok;
1283
1284 stat = GdipTransformPath(path, matrix);
1285 if(stat != Ok)
1286 return stat;
1287
1288 pt = path->pathdata.Points[0];
1289 if(!init_path_list(&list, pt.X, pt.Y))
1290 return OutOfMemory;
1291
1292 node = list;
1293
1294 while(i < path->pathdata.Count){
1295
1296 BYTE type = path->pathdata.Types[i] & PathPointTypePathTypeMask;
1297 path_list_node_t *start;
1298
1299 pt = path->pathdata.Points[i];
1300
1301 /* save last start point index */
1302 if(type == PathPointTypeStart)
1303 startidx = i;
1304
1305 /* always add line points and start points */
1306 if((type == PathPointTypeStart) || (type == PathPointTypeLine)){
1307 if(!add_path_list_node(node, pt.X, pt.Y, path->pathdata.Types[i]))
1308 goto memout;
1309
1310 node = node->next;
1311 ++i;
1312 continue;
1313 }
1314
1315 /* Bezier curve */
1316
1317 /* test for closed figure */
1318 if(path->pathdata.Types[i+1] & PathPointTypeCloseSubpath){
1319 pt = path->pathdata.Points[startidx];
1320 ++i;
1321 }
1322 else
1323 {
1324 i += 2;
1325 pt = path->pathdata.Points[i];
1326 };
1327
1328 start = node;
1329 /* add Bezier end point */
1330 type = (path->pathdata.Types[i] & ~PathPointTypePathTypeMask) | PathPointTypeLine;
1331 if(!add_path_list_node(node, pt.X, pt.Y, type))
1332 goto memout;
1333 node = node->next;
1334
1335 /* flatten curve */
1336 if(!flatten_bezier(start, path->pathdata.Points[i-2].X, path->pathdata.Points[i-2].Y,
1337 path->pathdata.Points[i-1].X, path->pathdata.Points[i-1].Y,
1338 node, flatness))
1339 goto memout;
1340
1341 ++i;
1342 }/* while */
1343
1344 /* store path data back */
1345 i = path_list_count(list);
1346 if(!lengthen_path(path, i))
1347 goto memout;
1348 path->pathdata.Count = i;
1349
1350 node = list;
1351 for(i = 0; i < path->pathdata.Count; i++){
1352 path->pathdata.Points[i] = node->pt;
1353 path->pathdata.Types[i] = node->type;
1354 node = node->next;
1355 }
1356
1357 free_path_list(list);
1358 return Ok;
1359
1360 memout:
1361 free_path_list(list);
1362 return OutOfMemory;
1363 }
1364
1365 GpStatus WINGDIPAPI GdipGetPathData(GpPath *path, GpPathData* pathData)
1366 {
1367 TRACE("(%p, %p)\n", path, pathData);
1368
1369 if(!path || !pathData)
1370 return InvalidParameter;
1371
1372 /* Only copy data. pathData allocation/freeing controlled by wrapper class.
1373 Assumed that pathData is enough wide to get all data - controlled by wrapper too. */
1374 memcpy(pathData->Points, path->pathdata.Points, sizeof(PointF) * pathData->Count);
1375 memcpy(pathData->Types , path->pathdata.Types , pathData->Count);
1376
1377 return Ok;
1378 }
1379
1380 GpStatus WINGDIPAPI GdipGetPathFillMode(GpPath *path, GpFillMode *fillmode)
1381 {
1382 TRACE("(%p, %p)\n", path, fillmode);
1383
1384 if(!path || !fillmode)
1385 return InvalidParameter;
1386
1387 *fillmode = path->fill;
1388
1389 return Ok;
1390 }
1391
1392 GpStatus WINGDIPAPI GdipGetPathLastPoint(GpPath* path, GpPointF* lastPoint)
1393 {
1394 INT count;
1395
1396 TRACE("(%p, %p)\n", path, lastPoint);
1397
1398 if(!path || !lastPoint)
1399 return InvalidParameter;
1400
1401 count = path->pathdata.Count;
1402 if(count > 0)
1403 *lastPoint = path->pathdata.Points[count-1];
1404
1405 return Ok;
1406 }
1407
1408 GpStatus WINGDIPAPI GdipGetPathPoints(GpPath *path, GpPointF* points, INT count)
1409 {
1410 TRACE("(%p, %p, %d)\n", path, points, count);
1411
1412 if(!path)
1413 return InvalidParameter;
1414
1415 if(count < path->pathdata.Count)
1416 return InsufficientBuffer;
1417
1418 memcpy(points, path->pathdata.Points, path->pathdata.Count * sizeof(GpPointF));
1419
1420 return Ok;
1421 }
1422
1423 GpStatus WINGDIPAPI GdipGetPathPointsI(GpPath *path, GpPoint* points, INT count)
1424 {
1425 GpStatus ret;
1426 GpPointF *ptf;
1427 INT i;
1428
1429 TRACE("(%p, %p, %d)\n", path, points, count);
1430
1431 if(count <= 0)
1432 return InvalidParameter;
1433
1434 ptf = heap_alloc_zero(sizeof(GpPointF)*count);
1435 if(!ptf) return OutOfMemory;
1436
1437 ret = GdipGetPathPoints(path,ptf,count);
1438 if(ret == Ok)
1439 for(i = 0;i < count;i++){
1440 points[i].X = gdip_round(ptf[i].X);
1441 points[i].Y = gdip_round(ptf[i].Y);
1442 };
1443 heap_free(ptf);
1444
1445 return ret;
1446 }
1447
1448 GpStatus WINGDIPAPI GdipGetPathTypes(GpPath *path, BYTE* types, INT count)
1449 {
1450 TRACE("(%p, %p, %d)\n", path, types, count);
1451
1452 if(!path)
1453 return InvalidParameter;
1454
1455 if(count < path->pathdata.Count)
1456 return InsufficientBuffer;
1457
1458 memcpy(types, path->pathdata.Types, path->pathdata.Count);
1459
1460 return Ok;
1461 }
1462
1463 /* Windows expands the bounding box to the maximum possible bounding box
1464 * for a given pen. For example, if a line join can extend past the point
1465 * it's joining by x units, the bounding box is extended by x units in every
1466 * direction (even though this is too conservative for most cases). */
1467 GpStatus WINGDIPAPI GdipGetPathWorldBounds(GpPath* path, GpRectF* bounds,
1468 GDIPCONST GpMatrix *matrix, GDIPCONST GpPen *pen)
1469 {
1470 GpPointF * points, temp_pts[4];
1471 INT count, i;
1472 REAL path_width = 1.0, width, height, temp, low_x, low_y, high_x, high_y;
1473
1474 TRACE("(%p, %p, %p, %p)\n", path, bounds, matrix, pen);
1475
1476 /* Matrix and pen can be null. */
1477 if(!path || !bounds)
1478 return InvalidParameter;
1479
1480 /* If path is empty just return. */
1481 count = path->pathdata.Count;
1482 if(count == 0){
1483 bounds->X = bounds->Y = bounds->Width = bounds->Height = 0.0;
1484 return Ok;
1485 }
1486
1487 points = path->pathdata.Points;
1488
1489 low_x = high_x = points[0].X;
1490 low_y = high_y = points[0].Y;
1491
1492 for(i = 1; i < count; i++){
1493 low_x = min(low_x, points[i].X);
1494 low_y = min(low_y, points[i].Y);
1495 high_x = max(high_x, points[i].X);
1496 high_y = max(high_y, points[i].Y);
1497 }
1498
1499 width = high_x - low_x;
1500 height = high_y - low_y;
1501
1502 /* This looks unusual but it's the only way I can imitate windows. */
1503 if(matrix){
1504 temp_pts[0].X = low_x;
1505 temp_pts[0].Y = low_y;
1506 temp_pts[1].X = low_x;
1507 temp_pts[1].Y = high_y;
1508 temp_pts[2].X = high_x;
1509 temp_pts[2].Y = high_y;
1510 temp_pts[3].X = high_x;
1511 temp_pts[3].Y = low_y;
1512
1513 GdipTransformMatrixPoints((GpMatrix*)matrix, temp_pts, 4);
1514 low_x = temp_pts[0].X;
1515 low_y = temp_pts[0].Y;
1516
1517 for(i = 1; i < 4; i++){
1518 low_x = min(low_x, temp_pts[i].X);
1519 low_y = min(low_y, temp_pts[i].Y);
1520 }
1521
1522 temp = width;
1523 width = height * fabs(matrix->matrix[2]) + width * fabs(matrix->matrix[0]);
1524 height = height * fabs(matrix->matrix[3]) + temp * fabs(matrix->matrix[1]);
1525 }
1526
1527 if(pen){
1528 path_width = pen->width / 2.0;
1529
1530 if(count > 2)
1531 path_width = max(path_width, pen->width * pen->miterlimit / 2.0);
1532 /* FIXME: this should probably also check for the startcap */
1533 if(pen->endcap & LineCapNoAnchor)
1534 path_width = max(path_width, pen->width * 2.2);
1535
1536 low_x -= path_width;
1537 low_y -= path_width;
1538 width += 2.0 * path_width;
1539 height += 2.0 * path_width;
1540 }
1541
1542 bounds->X = low_x;
1543 bounds->Y = low_y;
1544 bounds->Width = width;
1545 bounds->Height = height;
1546
1547 return Ok;
1548 }
1549
1550 GpStatus WINGDIPAPI GdipGetPathWorldBoundsI(GpPath* path, GpRect* bounds,
1551 GDIPCONST GpMatrix *matrix, GDIPCONST GpPen *pen)
1552 {
1553 GpStatus ret;
1554 GpRectF boundsF;
1555
1556 TRACE("(%p, %p, %p, %p)\n", path, bounds, matrix, pen);
1557
1558 ret = GdipGetPathWorldBounds(path,&boundsF,matrix,pen);
1559
1560 if(ret == Ok){
1561 bounds->X = gdip_round(boundsF.X);
1562 bounds->Y = gdip_round(boundsF.Y);
1563 bounds->Width = gdip_round(boundsF.Width);
1564 bounds->Height = gdip_round(boundsF.Height);
1565 }
1566
1567 return ret;
1568 }
1569
1570 GpStatus WINGDIPAPI GdipGetPointCount(GpPath *path, INT *count)
1571 {
1572 TRACE("(%p, %p)\n", path, count);
1573
1574 if(!path)
1575 return InvalidParameter;
1576
1577 *count = path->pathdata.Count;
1578
1579 return Ok;
1580 }
1581
1582 GpStatus WINGDIPAPI GdipReversePath(GpPath* path)
1583 {
1584 INT i, count;
1585 INT start = 0; /* position in reversed path */
1586 GpPathData revpath;
1587
1588 TRACE("(%p)\n", path);
1589
1590 if(!path)
1591 return InvalidParameter;
1592
1593 count = path->pathdata.Count;
1594
1595 if(count == 0) return Ok;
1596
1597 revpath.Points = heap_alloc_zero(sizeof(GpPointF)*count);
1598 revpath.Types = heap_alloc_zero(sizeof(BYTE)*count);
1599 revpath.Count = count;
1600 if(!revpath.Points || !revpath.Types){
1601 heap_free(revpath.Points);
1602 heap_free(revpath.Types);
1603 return OutOfMemory;
1604 }
1605
1606 for(i = 0; i < count; i++){
1607
1608 /* find next start point */
1609 if(path->pathdata.Types[count-i-1] == PathPointTypeStart){
1610 INT j;
1611 for(j = start; j <= i; j++){
1612 revpath.Points[j] = path->pathdata.Points[count-j-1];
1613 revpath.Types[j] = path->pathdata.Types[count-j-1];
1614 }
1615 /* mark start point */
1616 revpath.Types[start] = PathPointTypeStart;
1617 /* set 'figure' endpoint type */
1618 if(i-start > 1){
1619 revpath.Types[i] = path->pathdata.Types[count-start-1] & ~PathPointTypePathTypeMask;
1620 revpath.Types[i] |= revpath.Types[i-1];
1621 }
1622 else
1623 revpath.Types[i] = path->pathdata.Types[start];
1624
1625 start = i+1;
1626 }
1627 }
1628
1629 memcpy(path->pathdata.Points, revpath.Points, sizeof(GpPointF)*count);
1630 memcpy(path->pathdata.Types, revpath.Types, sizeof(BYTE)*count);
1631
1632 heap_free(revpath.Points);
1633 heap_free(revpath.Types);
1634
1635 return Ok;
1636 }
1637
1638 GpStatus WINGDIPAPI GdipIsOutlineVisiblePathPointI(GpPath* path, INT x, INT y,
1639 GpPen *pen, GpGraphics *graphics, BOOL *result)
1640 {
1641 TRACE("(%p, %d, %d, %p, %p, %p)\n", path, x, y, pen, graphics, result);
1642
1643 return GdipIsOutlineVisiblePathPoint(path, x, y, pen, graphics, result);
1644 }
1645
1646 GpStatus WINGDIPAPI GdipIsOutlineVisiblePathPoint(GpPath* path, REAL x, REAL y,
1647 GpPen *pen, GpGraphics *graphics, BOOL *result)
1648 {
1649 GpStatus stat;
1650 GpPath *wide_path;
1651 GpMatrix *transform = NULL;
1652
1653 TRACE("(%p,%0.2f,%0.2f,%p,%p,%p)\n", path, x, y, pen, graphics, result);
1654
1655 if(!path || !pen)
1656 return InvalidParameter;
1657
1658 stat = GdipClonePath(path, &wide_path);
1659
1660 if (stat != Ok)
1661 return stat;
1662
1663 if (pen->unit == UnitPixel && graphics != NULL)
1664 {
1665 stat = GdipCreateMatrix(&transform);
1666
1667 if (stat == Ok)
1668 stat = get_graphics_transform(graphics, CoordinateSpaceDevice,
1669 CoordinateSpaceWorld, transform);
1670 }
1671
1672 if (stat == Ok)
1673 stat = GdipWidenPath(wide_path, pen, transform, 1.0);
1674
1675 if (pen->unit == UnitPixel && graphics != NULL)
1676 {
1677 if (stat == Ok)
1678 stat = GdipInvertMatrix(transform);
1679
1680 if (stat == Ok)
1681 stat = GdipTransformPath(wide_path, transform);
1682 }
1683
1684 if (stat == Ok)
1685 stat = GdipIsVisiblePathPoint(wide_path, x, y, graphics, result);
1686
1687 GdipDeleteMatrix(transform);
1688
1689 GdipDeletePath(wide_path);
1690
1691 return stat;
1692 }
1693
1694 GpStatus WINGDIPAPI GdipIsVisiblePathPointI(GpPath* path, INT x, INT y, GpGraphics *graphics, BOOL *result)
1695 {
1696 TRACE("(%p, %d, %d, %p, %p)\n", path, x, y, graphics, result);
1697
1698 return GdipIsVisiblePathPoint(path, x, y, graphics, result);
1699 }
1700
1701 /*****************************************************************************
1702 * GdipIsVisiblePathPoint [GDIPLUS.@]
1703 */
1704 GpStatus WINGDIPAPI GdipIsVisiblePathPoint(GpPath* path, REAL x, REAL y, GpGraphics *graphics, BOOL *result)
1705 {
1706 GpRegion *region;
1707 HRGN hrgn;
1708 GpStatus status;
1709
1710 if(!path || !result) return InvalidParameter;
1711
1712 status = GdipCreateRegionPath(path, &region);
1713 if(status != Ok)
1714 return status;
1715
1716 status = GdipGetRegionHRgn(region, graphics, &hrgn);
1717 if(status != Ok){
1718 GdipDeleteRegion(region);
1719 return status;
1720 }
1721
1722 *result = PtInRegion(hrgn, gdip_round(x), gdip_round(y));
1723
1724 DeleteObject(hrgn);
1725 GdipDeleteRegion(region);
1726
1727 return Ok;
1728 }
1729
1730 GpStatus WINGDIPAPI GdipStartPathFigure(GpPath *path)
1731 {
1732 TRACE("(%p)\n", path);
1733
1734 if(!path)
1735 return InvalidParameter;
1736
1737 path->newfigure = TRUE;
1738
1739 return Ok;
1740 }
1741
1742 GpStatus WINGDIPAPI GdipResetPath(GpPath *path)
1743 {
1744 TRACE("(%p)\n", path);
1745
1746 if(!path)
1747 return InvalidParameter;
1748
1749 path->pathdata.Count = 0;
1750 path->newfigure = TRUE;
1751 path->fill = FillModeAlternate;
1752
1753 return Ok;
1754 }
1755
1756 GpStatus WINGDIPAPI GdipSetPathFillMode(GpPath *path, GpFillMode fill)
1757 {
1758 TRACE("(%p, %d)\n", path, fill);
1759
1760 if(!path)
1761 return InvalidParameter;
1762
1763 path->fill = fill;
1764
1765 return Ok;
1766 }
1767
1768 GpStatus WINGDIPAPI GdipTransformPath(GpPath *path, GpMatrix *matrix)
1769 {
1770 TRACE("(%p, %p)\n", path, matrix);
1771
1772 if(!path)
1773 return InvalidParameter;
1774
1775 if(path->pathdata.Count == 0 || !matrix)
1776 return Ok;
1777
1778 return GdipTransformMatrixPoints(matrix, path->pathdata.Points,
1779 path->pathdata.Count);
1780 }
1781
1782 GpStatus WINGDIPAPI GdipWarpPath(GpPath *path, GpMatrix* matrix,
1783 GDIPCONST GpPointF *points, INT count, REAL x, REAL y, REAL width,
1784 REAL height, WarpMode warpmode, REAL flatness)
1785 {
1786 FIXME("(%p,%p,%p,%i,%0.2f,%0.2f,%0.2f,%0.2f,%i,%0.2f)\n", path, matrix,
1787 points, count, x, y, width, height, warpmode, flatness);
1788
1789 return NotImplemented;
1790 }
1791
1792 static void add_bevel_point(const GpPointF *endpoint, const GpPointF *nextpoint,
1793 GpPen *pen, int right_side, path_list_node_t **last_point)
1794 {
1795 REAL segment_dy = nextpoint->Y-endpoint->Y;
1796 REAL segment_dx = nextpoint->X-endpoint->X;
1797 REAL segment_length = sqrtf(segment_dy*segment_dy + segment_dx*segment_dx);
1798 REAL distance = pen->width/2.0;
1799 REAL bevel_dx, bevel_dy;
1800
1801 if (segment_length == 0.0)
1802 {
1803 *last_point = add_path_list_node(*last_point, endpoint->X,
1804 endpoint->Y, PathPointTypeLine);
1805 return;
1806 }
1807
1808 if (right_side)
1809 {
1810 bevel_dx = -distance * segment_dy / segment_length;
1811 bevel_dy = distance * segment_dx / segment_length;
1812 }
1813 else
1814 {
1815 bevel_dx = distance * segment_dy / segment_length;
1816 bevel_dy = -distance * segment_dx / segment_length;
1817 }
1818
1819 *last_point = add_path_list_node(*last_point, endpoint->X + bevel_dx,
1820 endpoint->Y + bevel_dy, PathPointTypeLine);
1821 }
1822
1823 static void widen_joint(const GpPointF *p1, const GpPointF *p2, const GpPointF *p3,
1824 GpPen* pen, path_list_node_t **last_point)
1825 {
1826 switch (pen->join)
1827 {
1828 case LineJoinMiter:
1829 case LineJoinMiterClipped:
1830 if ((p2->X - p1->X) * (p3->Y - p1->Y) > (p2->Y - p1->Y) * (p3->X - p1->X))
1831 {
1832 float distance = pen->width/2.0;
1833 float length_0 = sqrtf((p2->X-p1->X)*(p2->X-p1->X)+(p2->Y-p1->Y)*(p2->Y-p1->Y));
1834 float length_1 = sqrtf((p3->X-p2->X)*(p3->X-p2->X)+(p3->Y-p2->Y)*(p3->Y-p2->Y));
1835 float dx0 = distance * (p2->X - p1->X) / length_0;
1836 float dy0 = distance * (p2->Y - p1->Y) / length_0;
1837 float dx1 = distance * (p3->X - p2->X) / length_1;
1838 float dy1 = distance * (p3->Y - p2->Y) / length_1;
1839 float det = (dy0*dx1 - dx0*dy1);
1840 float dx = (dx0*dx1*(dx0-dx1) + dy0*dy0*dx1 - dy1*dy1*dx0)/det;
1841 float dy = (dy0*dy1*(dy0-dy1) + dx0*dx0*dy1 - dx1*dx1*dy0)/det;
1842 if (dx*dx + dy*dy < pen->miterlimit*pen->miterlimit * distance*distance)
1843 {
1844 *last_point = add_path_list_node(*last_point, p2->X + dx,
1845 p2->Y + dy, PathPointTypeLine);
1846 break;
1847 }
1848 else if (pen->join == LineJoinMiter)
1849 {
1850 static int once;
1851 if (!once++)
1852 FIXME("should add a clipped corner\n");
1853 }
1854 /* else fall-through */
1855 }
1856 /* else fall-through */
1857 default:
1858 case LineJoinBevel:
1859 add_bevel_point(p2, p1, pen, 1, last_point);
1860 add_bevel_point(p2, p3, pen, 0, last_point);
1861 break;
1862 }
1863 }
1864
1865 static void widen_cap(const GpPointF *endpoint, const GpPointF *nextpoint,
1866 GpPen *pen, GpLineCap cap, GpCustomLineCap *custom, int add_first_points,
1867 int add_last_point, path_list_node_t **last_point)
1868 {
1869 switch (cap)
1870 {
1871 default:
1872 case LineCapFlat:
1873 if (add_first_points)
1874 add_bevel_point(endpoint, nextpoint, pen, 1, last_point);
1875 if (add_last_point)
1876 add_bevel_point(endpoint, nextpoint, pen, 0, last_point);
1877 break;
1878 case LineCapSquare:
1879 {
1880 REAL segment_dy = nextpoint->Y-endpoint->Y;
1881 REAL segment_dx = nextpoint->X-endpoint->X;
1882 REAL segment_length = sqrtf(segment_dy*segment_dy + segment_dx*segment_dx);
1883 REAL distance = pen->width/2.0;
1884 REAL bevel_dx, bevel_dy;
1885 REAL extend_dx, extend_dy;
1886
1887 extend_dx = -distance * segment_dx / segment_length;
1888 extend_dy = -distance * segment_dy / segment_length;
1889
1890 bevel_dx = -distance * segment_dy / segment_length;
1891 bevel_dy = distance * segment_dx / segment_length;
1892
1893 if (add_first_points)
1894 *last_point = add_path_list_node(*last_point, endpoint->X + extend_dx + bevel_dx,
1895 endpoint->Y + extend_dy + bevel_dy, PathPointTypeLine);
1896
1897 if (add_last_point)
1898 *last_point = add_path_list_node(*last_point, endpoint->X + extend_dx - bevel_dx,
1899 endpoint->Y + extend_dy - bevel_dy, PathPointTypeLine);
1900
1901 break;
1902 }
1903 case LineCapRound:
1904 {
1905 REAL segment_dy = nextpoint->Y-endpoint->Y;
1906 REAL segment_dx = nextpoint->X-endpoint->X;
1907 REAL segment_length = sqrtf(segment_dy*segment_dy + segment_dx*segment_dx);
1908 REAL distance = pen->width/2.0;
1909 REAL dx, dy, dx2, dy2;
1910 const REAL control_point_distance = 0.5522847498307935; /* 4/3 * (sqrt(2) - 1) */
1911
1912 if (add_first_points)
1913 {
1914 dx = -distance * segment_dx / segment_length;
1915 dy = -distance * segment_dy / segment_length;
1916
1917 dx2 = dx * control_point_distance;
1918 dy2 = dy * control_point_distance;
1919
1920 /* first 90-degree arc */
1921 *last_point = add_path_list_node(*last_point, endpoint->X + dy,
1922 endpoint->Y - dx, PathPointTypeLine);
1923
1924 *last_point = add_path_list_node(*last_point, endpoint->X + dy + dx2,
1925 endpoint->Y - dx + dy2, PathPointTypeBezier);
1926
1927 *last_point = add_path_list_node(*last_point, endpoint->X + dx + dy2,
1928 endpoint->Y + dy - dx2, PathPointTypeBezier);
1929
1930 /* midpoint */
1931 *last_point = add_path_list_node(*last_point, endpoint->X + dx,
1932 endpoint->Y + dy, PathPointTypeBezier);
1933
1934 /* second 90-degree arc */
1935 *last_point = add_path_list_node(*last_point, endpoint->X + dx - dy2,
1936 endpoint->Y + dy + dx2, PathPointTypeBezier);
1937
1938 *last_point = add_path_list_node(*last_point, endpoint->X - dy + dx2,
1939 endpoint->Y + dx + dy2, PathPointTypeBezier);
1940
1941 *last_point = add_path_list_node(*last_point, endpoint->X - dy,
1942 endpoint->Y + dx, PathPointTypeBezier);
1943 }
1944 break;
1945 }
1946 case LineCapTriangle:
1947 {
1948 REAL segment_dy = nextpoint->Y-endpoint->Y;
1949 REAL segment_dx = nextpoint->X-endpoint->X;
1950 REAL segment_length = sqrtf(segment_dy*segment_dy + segment_dx*segment_dx);
1951 REAL distance = pen->width/2.0;
1952 REAL dx, dy;
1953
1954 dx = distance * segment_dx / segment_length;
1955 dy = distance * segment_dy / segment_length;
1956
1957 if (add_first_points) {
1958 add_bevel_point(endpoint, nextpoint, pen, 1, last_point);
1959
1960 *last_point = add_path_list_node(*last_point, endpoint->X - dx,
1961 endpoint->Y - dy, PathPointTypeLine);
1962 }
1963 if (add_last_point)
1964 add_bevel_point(endpoint, nextpoint, pen, 0, last_point);
1965 break;
1966 }
1967 }
1968 }
1969
1970 static void widen_open_figure(const GpPointF *points, GpPen *pen, int start, int end,
1971 GpLineCap start_cap, GpCustomLineCap *start_custom, GpLineCap end_cap,
1972 GpCustomLineCap *end_custom, path_list_node_t **last_point)
1973 {
1974 int i;
1975 path_list_node_t *prev_point;
1976
1977 if (end <= start)
1978 return;
1979
1980 prev_point = *last_point;
1981
1982 widen_cap(&points[start], &points[start+1],
1983 pen, start_cap, start_custom, FALSE, TRUE, last_point);
1984
1985 for (i=start+1; i<end; i++)
1986 widen_joint(&points[i-1], &points[i],
1987 &points[i+1], pen, last_point);
1988
1989 widen_cap(&points[end], &points[end-1],
1990 pen, end_cap, end_custom, TRUE, TRUE, last_point);
1991
1992 for (i=end-1; i>start; i--)
1993 widen_joint(&points[i+1], &points[i],
1994 &points[i-1], pen, last_point);
1995
1996 widen_cap(&points[start], &points[start+1],
1997 pen, start_cap, start_custom, TRUE, FALSE, last_point);
1998
1999 prev_point->next->type = PathPointTypeStart;
2000 (*last_point)->type |= PathPointTypeCloseSubpath;
2001 }
2002
2003 static void widen_closed_figure(GpPath *path, GpPen *pen, int start, int end,
2004 path_list_node_t **last_point)
2005 {
2006 int i;
2007 path_list_node_t *prev_point;
2008
2009 if (end <= start)
2010 return;
2011
2012 /* left outline */
2013 prev_point = *last_point;
2014
2015 widen_joint(&path->pathdata.Points[end], &path->pathdata.Points[start],
2016 &path->pathdata.Points[start+1], pen, last_point);
2017
2018 for (i=start+1; i<end; i++)
2019 widen_joint(&path->pathdata.Points[i-1], &path->pathdata.Points[i],
2020 &path->pathdata.Points[i+1], pen, last_point);
2021
2022 widen_joint(&path->pathdata.Points[end-1], &path->pathdata.Points[end],
2023 &path->pathdata.Points[start], pen, last_point);
2024
2025 prev_point->next->type = PathPointTypeStart;
2026 (*last_point)->type |= PathPointTypeCloseSubpath;
2027
2028 /* right outline */
2029 prev_point = *last_point;
2030
2031 widen_joint(&path->pathdata.Points[start], &path->pathdata.Points[end],
2032 &path->pathdata.Points[end-1], pen, last_point);
2033
2034 for (i=end-1; i>start; i--)
2035 widen_joint(&path->pathdata.Points[i+1], &path->pathdata.Points[i],
2036 &path->pathdata.Points[i-1], pen, last_point);
2037
2038 widen_joint(&path->pathdata.Points[start+1], &path->pathdata.Points[start],
2039 &path->pathdata.Points[end], pen, last_point);
2040
2041 prev_point->next->type = PathPointTypeStart;
2042 (*last_point)->type |= PathPointTypeCloseSubpath;
2043 }
2044
2045 static void widen_dashed_figure(GpPath *path, GpPen *pen, int start, int end,
2046 int closed, path_list_node_t **last_point)
2047 {
2048 int i, j;
2049 REAL dash_pos=0.0;
2050 int dash_index=0;
2051 const REAL *dash_pattern;
2052 REAL *dash_pattern_scaled;
2053 int dash_count;
2054 GpPointF *tmp_points;
2055 REAL segment_dy;
2056 REAL segment_dx;
2057 REAL segment_length;
2058 REAL segment_pos;
2059 int num_tmp_points=0;
2060 int draw_start_cap=0;
2061 static const REAL dash_dot_dot[6] = { 3.0, 1.0, 1.0, 1.0, 1.0, 1.0 };
2062
2063 if (end <= start)
2064 return;
2065
2066 switch (pen->dash)
2067 {
2068 case DashStyleDash:
2069 default:
2070 dash_pattern = dash_dot_dot;
2071 dash_count = 2;
2072 break;
2073 case DashStyleDot:
2074 dash_pattern = &dash_dot_dot[2];
2075 dash_count = 2;
2076 break;
2077 case DashStyleDashDot:
2078 dash_pattern = dash_dot_dot;
2079 dash_count = 4;
2080 break;
2081 case DashStyleDashDotDot:
2082 dash_pattern = dash_dot_dot;
2083 dash_count = 6;
2084 break;
2085 case DashStyleCustom:
2086 dash_pattern = pen->dashes;
2087 dash_count = pen->numdashes;
2088 break;
2089 }
2090
2091 dash_pattern_scaled = heap_alloc(dash_count * sizeof(REAL));
2092 if (!dash_pattern_scaled) return;
2093
2094 for (i = 0; i < dash_count; i++)
2095 dash_pattern_scaled[i] = pen->width * dash_pattern[i];
2096
2097 tmp_points = heap_alloc_zero((end - start + 2) * sizeof(GpPoint));
2098 if (!tmp_points) {
2099 heap_free(dash_pattern_scaled);
2100 return; /* FIXME */
2101 }
2102
2103 if (!closed)
2104 draw_start_cap = 1;
2105
2106 for (j=start; j <= end; j++)
2107 {
2108 if (j == start)
2109 {
2110 if (closed)
2111 i = end;
2112 else
2113 continue;
2114 }
2115 else
2116 i = j-1;
2117
2118 segment_dy = path->pathdata.Points[j].Y - path->pathdata.Points[i].Y;
2119 segment_dx = path->pathdata.Points[j].X - path->pathdata.Points[i].X;
2120 segment_length = sqrtf(segment_dy*segment_dy + segment_dx*segment_dx);
2121 segment_pos = 0.0;
2122
2123 while (1)
2124 {
2125 if (dash_pos == 0.0)
2126 {
2127 if ((dash_index % 2) == 0)
2128 {
2129 /* start dash */
2130 num_tmp_points = 1;
2131 tmp_points[0].X = path->pathdata.Points[i].X + segment_dx * segment_pos / segment_length;
2132 tmp_points[0].Y = path->pathdata.Points[i].Y + segment_dy * segment_pos / segment_length;
2133 }
2134 else
2135 {
2136 /* end dash */
2137 tmp_points[num_tmp_points].X = path->pathdata.Points[i].X + segment_dx * segment_pos / segment_length;
2138 tmp_points[num_tmp_points].Y = path->pathdata.Points[i].Y + segment_dy * segment_pos / segment_length;
2139
2140 widen_open_figure(tmp_points, pen, 0, num_tmp_points,
2141 draw_start_cap ? pen->startcap : LineCapFlat, pen->customstart,
2142 LineCapFlat, NULL, last_point);
2143 draw_start_cap = 0;
2144 num_tmp_points = 0;
2145 }
2146 }
2147
2148 if (dash_pattern_scaled[dash_index] - dash_pos > segment_length - segment_pos)
2149 {
2150 /* advance to next segment */
2151 if ((dash_index % 2) == 0)
2152 {
2153 tmp_points[num_tmp_points] = path->pathdata.Points[j];
2154 num_tmp_points++;
2155 }
2156 dash_pos += segment_length - segment_pos;
2157 break;
2158 }
2159 else
2160 {
2161 /* advance to next dash in pattern */
2162 segment_pos += dash_pattern_scaled[dash_index] - dash_pos;
2163 dash_pos = 0.0;
2164 if (++dash_index == dash_count)
2165 dash_index = 0;
2166 continue;
2167 }
2168 }
2169 }
2170
2171 if (dash_index % 2 == 0 && num_tmp_points != 0)
2172 {
2173 /* last dash overflows last segment */
2174 widen_open_figure(tmp_points, pen, 0, num_tmp_points-1,
2175 draw_start_cap ? pen->startcap : LineCapFlat, pen->customstart,
2176 closed ? LineCapFlat : pen->endcap, pen->customend, last_point);
2177 }
2178
2179 heap_free(dash_pattern_scaled);
2180 heap_free(tmp_points);
2181 }
2182
2183 GpStatus WINGDIPAPI GdipWidenPath(GpPath *path, GpPen *pen, GpMatrix *matrix,
2184 REAL flatness)
2185 {
2186 GpPath *flat_path=NULL;
2187 GpStatus status;
2188 path_list_node_t *points=NULL, *last_point=NULL;
2189 int i, subpath_start=0, new_length;
2190 BYTE type;
2191
2192 TRACE("(%p,%p,%p,%0.2f)\n", path, pen, matrix, flatness);
2193
2194 if (!path || !pen)
2195 return InvalidParameter;
2196
2197 if (path->pathdata.Count <= 1)
2198 return OutOfMemory;
2199
2200 status = GdipClonePath(path, &flat_path);
2201
2202 if (status == Ok)
2203 status = GdipFlattenPath(flat_path, pen->unit == UnitPixel ? matrix : NULL, flatness);
2204
2205 if (status == Ok && !init_path_list(&points, 314.0, 22.0))
2206 status = OutOfMemory;
2207
2208 if (status == Ok)
2209 {
2210 last_point = points;
2211
2212 if (pen->endcap > LineCapTriangle)
2213 FIXME("unimplemented end cap %x\n", pen->endcap);
2214
2215 if (pen->startcap > LineCapTriangle)
2216 FIXME("unimplemented start cap %x\n", pen->startcap);
2217
2218 if (pen->dashcap != DashCapFlat)
2219 FIXME("unimplemented dash cap %d\n", pen->dashcap);
2220
2221 if (pen->join == LineJoinRound)
2222 FIXME("unimplemented line join %d\n", pen->join);
2223
2224 if (pen->align != PenAlignmentCenter)
2225 FIXME("unimplemented pen alignment %d\n", pen->align);
2226
2227 for (i=0; i < flat_path->pathdata.Count; i++)
2228 {
2229 type = flat_path->pathdata.Types[i];
2230
2231 if ((type&PathPointTypePathTypeMask) == PathPointTypeStart)
2232 subpath_start = i;
2233
2234 if ((type&PathPointTypeCloseSubpath) == PathPointTypeCloseSubpath)
2235 {
2236 if (pen->dash != DashStyleSolid)
2237 widen_dashed_figure(flat_path, pen, subpath_start, i, 1, &last_point);
2238 else
2239 widen_closed_figure(flat_path, pen, subpath_start, i, &last_point);
2240 }
2241 else if (i == flat_path->pathdata.Count-1 ||
2242 (flat_path->pathdata.Types[i+1]&PathPointTypePathTypeMask) == PathPointTypeStart)
2243 {
2244 if (pen->dash != DashStyleSolid)
2245 widen_dashed_figure(flat_path, pen, subpath_start, i, 0, &last_point);
2246 else
2247 widen_open_figure(flat_path->pathdata.Points, pen, subpath_start, i, pen->startcap, pen->customstart, pen->endcap, pen->customend, &last_point);
2248 }
2249 }
2250
2251 new_length = path_list_count(points)-1;
2252
2253 if (!lengthen_path(path, new_length))
2254 status = OutOfMemory;
2255 }
2256
2257 if (status == Ok)
2258 {
2259 path->pathdata.Count = new_length;
2260
2261 last_point = points->next;
2262 for (i = 0; i < new_length; i++)
2263 {
2264 path->pathdata.Points[i] = last_point->pt;
2265 path->pathdata.Types[i] = last_point->type;
2266 last_point = last_point->next;
2267 }
2268
2269 path->fill = FillModeWinding;
2270 }
2271
2272 free_path_list(points);
2273
2274 GdipDeletePath(flat_path);
2275
2276 if (status == Ok && pen->unit != UnitPixel)
2277 status = GdipTransformPath(path, matrix);
2278
2279 return status;
2280 }
2281
2282 GpStatus WINGDIPAPI GdipAddPathRectangle(GpPath *path, REAL x, REAL y,
2283 REAL width, REAL height)
2284 {
2285 GpPath *backup;
2286 GpPointF ptf[2];
2287 GpStatus retstat;
2288 BOOL old_new;
2289
2290 TRACE("(%p, %.2f, %.2f, %.2f, %.2f)\n", path, x, y, width, height);
2291
2292 if(!path)
2293 return InvalidParameter;
2294
2295 /* make a backup copy of path data */
2296 if((retstat = GdipClonePath(path, &backup)) != Ok)
2297 return retstat;
2298
2299 /* rectangle should start as new path */
2300 old_new = path->newfigure;
2301 path->newfigure = TRUE;
2302 if((retstat = GdipAddPathLine(path,x,y,x+width,y)) != Ok){
2303 path->newfigure = old_new;
2304 goto fail;
2305 }
2306
2307 ptf[0].X = x+width;
2308 ptf[0].Y = y+height;
2309 ptf[1].X = x;
2310 ptf[1].Y = y+height;
2311
2312 if((retstat = GdipAddPathLine2(path, ptf, 2)) != Ok) goto fail;
2313 path->pathdata.Types[path->pathdata.Count-1] |= PathPointTypeCloseSubpath;
2314
2315 /* free backup */
2316 GdipDeletePath(backup);
2317 return Ok;
2318
2319 fail:
2320 /* reverting */
2321 heap_free(path->pathdata.Points);
2322 heap_free(path->pathdata.Types);
2323 memcpy(path, backup, sizeof(*path));
2324 heap_free(backup);
2325
2326 return retstat;
2327 }
2328
2329 GpStatus WINGDIPAPI GdipAddPathRectangleI(GpPath *path, INT x, INT y,
2330 INT width, INT height)
2331 {
2332 TRACE("(%p, %d, %d, %d, %d)\n", path, x, y, width, height);
2333
2334 return GdipAddPathRectangle(path,(REAL)x,(REAL)y,(REAL)width,(REAL)height);
2335 }
2336
2337 GpStatus WINGDIPAPI GdipAddPathRectangles(GpPath *path, GDIPCONST GpRectF *rects, INT count)
2338 {
2339 GpPath *backup;
2340 GpStatus retstat;
2341 INT i;
2342
2343 TRACE("(%p, %p, %d)\n", path, rects, count);
2344
2345 /* count == 0 - verified condition */
2346 if(!path || !rects || count == 0)
2347 return InvalidParameter;
2348
2349 if(count < 0)
2350 return OutOfMemory;
2351
2352 /* make a backup copy */
2353 if((retstat = GdipClonePath(path, &backup)) != Ok)
2354 return retstat;
2355
2356 for(i = 0; i < count; i++){
2357 if((retstat = GdipAddPathRectangle(path,rects[i].X,rects[i].Y,rects[i].Width,rects[i].Height)) != Ok)
2358 goto fail;
2359 }
2360
2361 /* free backup */
2362 GdipDeletePath(backup);
2363 return Ok;
2364
2365 fail:
2366 /* reverting */
2367 heap_free(path->pathdata.Points);
2368 heap_free(path->pathdata.Types);
2369 memcpy(path, backup, sizeof(*path));
2370 heap_free(backup);
2371
2372 return retstat;
2373 }
2374
2375 GpStatus WINGDIPAPI GdipAddPathRectanglesI(GpPath *path, GDIPCONST GpRect *rects, INT count)
2376 {
2377 GpRectF *rectsF;
2378 GpStatus retstat;
2379 INT i;
2380
2381 TRACE("(%p, %p, %d)\n", path, rects, count);
2382
2383 if(!rects || count == 0)
2384 return InvalidParameter;
2385
2386 if(count < 0)
2387 return OutOfMemory;
2388
2389 rectsF = heap_alloc_zero(sizeof(GpRectF)*count);
2390
2391 for(i = 0;i < count;i++){
2392 rectsF[i].X = (REAL)rects[i].X;
2393 rectsF[i].Y = (REAL)rects[i].Y;
2394 rectsF[i].Width = (REAL)rects[i].Width;
2395 rectsF[i].Height = (REAL)rects[i].Height;
2396 }
2397
2398 retstat = GdipAddPathRectangles(path, rectsF, count);
2399 heap_free(rectsF);
2400
2401 return retstat;
2402 }
2403
2404 GpStatus WINGDIPAPI GdipSetPathMarker(GpPath* path)
2405 {
2406 INT count;
2407
2408 TRACE("(%p)\n", path);
2409
2410 if(!path)
2411 return InvalidParameter;
2412
2413 count = path->pathdata.Count;
2414
2415 /* set marker flag */
2416 if(count > 0)
2417 path->pathdata.Types[count-1] |= PathPointTypePathMarker;
2418
2419 return Ok;
2420 }
2421
2422 GpStatus WINGDIPAPI GdipClearPathMarkers(GpPath* path)
2423 {
2424 INT count;
2425 INT i;
2426
2427 TRACE("(%p)\n", path);
2428
2429 if(!path)
2430 return InvalidParameter;
2431
2432 count = path->pathdata.Count;
2433
2434 for(i = 0; i < count - 1; i++){
2435 path->pathdata.Types[i] &= ~PathPointTypePathMarker;
2436 }
2437
2438 return Ok;
2439 }
2440
2441 GpStatus WINGDIPAPI GdipWindingModeOutline(GpPath *path, GpMatrix *matrix, REAL flatness)
2442 {
2443 FIXME("stub: %p, %p, %.2f\n", path, matrix, flatness);
2444 return NotImplemented;
2445 }
2446
2447 #define FLAGS_INTPATH 0x4000
2448
2449 struct path_header
2450 {
2451 DWORD version;
2452 DWORD count;
2453 DWORD flags;
2454 };
2455
2456 /* Test to see if the path could be stored as an array of shorts */
2457 static BOOL is_integer_path(const GpPath *path)
2458 {
2459 int i;
2460
2461 if (!path->pathdata.Count) return FALSE;
2462
2463 for (i = 0; i < path->pathdata.Count; i++)
2464 {
2465 short x, y;
2466 x = gdip_round(path->pathdata.Points[i].X);
2467 y = gdip_round(path->pathdata.Points[i].Y);
2468 if (path->pathdata.Points[i].X != (REAL)x || path->pathdata.Points[i].Y != (REAL)y)
2469 return FALSE;
2470 }
2471 return TRUE;
2472 }
2473
2474 DWORD write_path_data(GpPath *path, void *data)
2475 {
2476 struct path_header *header = data;
2477 BOOL integer_path = is_integer_path(path);
2478 DWORD i, size;
2479 BYTE *types;
2480
2481 size = sizeof(struct path_header) + path->pathdata.Count;
2482 if (integer_path)
2483 size += sizeof(short[2]) * path->pathdata.Count;
2484 else
2485 size += sizeof(float[2]) * path->pathdata.Count;
2486 size = (size + 3) & ~3;
2487
2488 if (!data) return size;
2489
2490 header->version = VERSION_MAGIC2;
2491 header->count = path->pathdata.Count;
2492 header->flags = integer_path ? FLAGS_INTPATH : 0;
2493
2494 if (integer_path)
2495 {
2496 short *points = (short*)(header + 1);
2497 for (i = 0; i < path->pathdata.Count; i++)
2498 {
2499 points[2*i] = path->pathdata.Points[i].X;
2500 points[2*i + 1] = path->pathdata.Points[i].Y;
2501 }
2502 types = (BYTE*)(points + 2*i);
2503 }
2504 else
2505 {
2506 float *points = (float*)(header + 1);
2507 for (i = 0; i < path->pathdata.Count; i++)
2508 {
2509 points[2*i] = path->pathdata.Points[i].X;
2510 points[2*i + 1] = path->pathdata.Points[i].Y;
2511 }
2512 types = (BYTE*)(points + 2*i);
2513 }
2514
2515 for (i=0; i<path->pathdata.Count; i++)
2516 types[i] = path->pathdata.Types[i];
2517 memset(types + i, 0, ((path->pathdata.Count + 3) & ~3) - path->pathdata.Count);
2518 return size;
2519 }