1 /*
2 * graphics-path.c
3 *
4 * Copyright (C) 2003-2007, Novell Inc. (http://www.novell.com)
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
7 * and associated documentation files (the "Software"), to deal in the Software without restriction,
8 * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
10 * subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in all copies or substantial
13 * portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
16 * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
18 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
19 * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20 *
21 * Authors:
22 * Duncan Mak (duncan@ximian.com)
23 * Ravindra (rkumar@novell.com)
24 * Sebastien Pouliot <sebastien@ximian.com>
25 */
26
27 #include "graphics-path-private.h"
28 #include "matrix-private.h"
29 #include "font-private.h"
30 #include "graphics-cairo-private.h"
31 #include "fontfamily.h"
32
33 #ifdef USE_PANGO_RENDERING
34 #include "text-pango-private.h"
35 #endif
36
37 BOOL
gdip_path_ensure_size(GpPath * path,int size)38 gdip_path_ensure_size (GpPath *path, int size)
39 {
40 BYTE *new_types;
41 GpPointF *new_points;
42
43 if (path->size < size) {
44 if (size < path->size + 64)
45 size = path->size + 64;
46 new_types = gdip_realloc (path->types, size * sizeof (BYTE));
47 if (!new_types)
48 return FALSE;
49 path->types = new_types;
50 new_points = gdip_realloc (path->points, size * sizeof (GpPointF));
51 if (!new_points)
52 return FALSE;
53 path->points = new_points;
54 path->size = size;
55 }
56
57 return TRUE;
58 }
59
60 /* return TRUE if the specified path has (at least one) curves, FALSE otherwise */
61 BOOL
gdip_path_has_curve(GpPath * path)62 gdip_path_has_curve (GpPath *path)
63 {
64 int i;
65
66 if (!path)
67 return FALSE;
68
69 for (i = 0; i < path->count; i++) {
70 if (path->types[i] == PathPointTypeBezier)
71 return TRUE;
72 }
73
74 return FALSE;
75 }
76
77 BOOL
gdip_path_closed(GpPath * path)78 gdip_path_closed (GpPath *path)
79 {
80 if (path->count < 1) {
81 return FALSE;
82 }
83
84 PathPointType last_type = path->types[path->count - 1];
85 return (last_type & PathPointTypeCloseSubpath) == PathPointTypeCloseSubpath;
86 }
87
88 /*
89 * Return the correct point type when adding a new shape to the path.
90 */
91 static PathPointType
gdip_get_first_point_type(GpPath * path)92 gdip_get_first_point_type (GpPath *path)
93 {
94 PathPointType type;
95
96 /* check for a new figure flag or an empty path */
97 if (path->start_new_fig || (path->count == 0))
98 return PathPointTypeStart;
99
100 /* check if the previous point is a closure */
101 type = path->types[path->count - 1];
102 if (type & PathPointTypeCloseSubpath)
103 return PathPointTypeStart;
104 else
105 return PathPointTypeLine;
106 }
107
108 static VOID
append(GpPath * path,float x,float y,PathPointType type,BOOL compress)109 append (GpPath *path, float x, float y, PathPointType type, BOOL compress)
110 {
111 BYTE t = (BYTE) type;
112 GpPointF pt;
113
114 if (path->start_new_fig) {
115 t = PathPointTypeStart;
116 } else if (path->count > 0) {
117 /* in some case we're allowed to compress identical points */
118 if (compress) {
119 /* points (X, Y) must be identical */
120 GpPointF lastPoint = path->points[path->count - 1];
121 if ((lastPoint.X == x) && (lastPoint.Y == y)) {
122 /* types need not be identical but must handle closed subpaths */
123 if (!gdip_path_closed (path)) {
124 if ((type & PathPointTypeCloseSubpath) == PathPointTypeCloseSubpath)
125 path->types[path->count - 1] |= PathPointTypeCloseSubpath;
126 return;
127 }
128 }
129 }
130
131 /* if we closed a subpath, then start new figure and append */
132 type = path->types[path->count - 1];
133 if (type & PathPointTypeCloseSubpath)
134 t = PathPointTypeStart;
135 }
136
137 pt.X = x;
138 pt.Y = y;
139
140 /* all external APIs resize the buffers beforehand and fail gracefully */
141 if (!gdip_path_ensure_size (path, path->count + 1))
142 g_assert(FALSE);
143
144 path->points[path->count] = pt;
145 path->types[path->count] = t;
146 path->count++;
147 path->start_new_fig = FALSE;
148 }
149
150 static VOID
append_point(GpPath * path,GpPointF pt,PathPointType type,BOOL compress)151 append_point (GpPath *path, GpPointF pt, PathPointType type, BOOL compress)
152 {
153 append (path, pt.X, pt.Y, type, compress);
154 }
155
156 static VOID
append_bezier(GpPath * path,float x1,float y1,float x2,float y2,float x3,float y3)157 append_bezier (GpPath *path, float x1, float y1, float x2, float y2, float x3, float y3)
158 {
159 append (path, x1, y1, PathPointTypeBezier3, FALSE);
160 append (path, x2, y2, PathPointTypeBezier3, FALSE);
161 append (path, x3, y3, PathPointTypeBezier3, FALSE);
162 }
163
164 static VOID
append_curve(GpPath * path,const GpPointF * points,GpPointF * tangents,int offset,int length,_CurveType type)165 append_curve (GpPath *path, const GpPointF *points, GpPointF *tangents, int offset, int length, _CurveType type)
166 {
167 int i;
168 PathPointType ptype = ((type == CURVE_CLOSE) || (path->count == 0)) ? PathPointTypeStart : PathPointTypeLine;
169
170 append_point (path, points [offset], ptype, TRUE);
171 for (i = offset; i < offset + length; i++) {
172 int j = i + 1;
173
174 double x1 = points [i].X + tangents [i].X;
175 double y1 = points [i].Y + tangents [i].Y;
176
177 double x2 = points [j].X - tangents [j].X;
178 double y2 = points [j].Y - tangents [j].Y;
179
180 double x3 = points [j].X;
181 double y3 = points [j].Y;
182
183 append_bezier (path, x1, y1, x2, y2, x3, y3);
184 }
185
186 if (type == CURVE_CLOSE) {
187 /* complete (close) the curve using the first point */
188 double x1 = points [i].X + tangents [i].X;
189 double y1 = points [i].Y + tangents [i].Y;
190
191 double x2 = points [0].X - tangents [0].X;
192 double y2 = points [0].Y - tangents [0].Y;
193
194 double x3 = points [0].X;
195 double y3 = points [0].Y;
196
197 append_bezier (path, x1, y1, x2, y2, x3, y3);
198 GdipClosePathFigure (path);
199 }
200 }
201
202 static BOOL
gdip_validate_path_types(const BYTE * types,INT count)203 gdip_validate_path_types (const BYTE *types, INT count)
204 {
205 const BYTE *currentType = types;
206 INT remaining = count;
207
208 if (count == 1)
209 return TRUE;
210
211 while (TRUE) {
212 remaining--;
213 currentType++;
214
215 // No more.
216 if (remaining == 0)
217 return FALSE;
218
219 // Already started.
220 if ((*currentType & PathPointTypePathTypeMask) == PathPointTypeStart)
221 return FALSE;
222
223 do {
224 if ((*currentType & PathPointTypePathTypeMask) == PathPointTypeLine) {
225 // No validation needed for lines. Move to the next type.
226 } else if ((*currentType & PathPointTypePathTypeMask) == PathPointTypeBezier) {
227 // A bezier path requires a start point (already handled) followed by 3 bezier
228 // points.
229 remaining--;
230 currentType++;
231 if (remaining == 0 || (*currentType & PathPointTypePathTypeMask) != PathPointTypeBezier)
232 return FALSE;
233
234 remaining--;
235 currentType++;
236 if (remaining == 0 || (*currentType & PathPointTypePathTypeMask) != PathPointTypeBezier)
237 return FALSE;
238 } else {
239 // Unknown type.
240 return FALSE;
241 }
242
243 BOOL endOfPath = (*currentType & PathPointTypeCloseSubpath) != 0;
244 remaining--;
245 currentType++;
246
247 // End of an open path. There are no more subpaths.
248 if (remaining == 0)
249 return TRUE;
250 // End of a closed path. There may be additional subpaths.
251 if (endOfPath)
252 break;
253 } while ((*currentType & PathPointTypePathTypeMask) != PathPointTypeStart);
254 }
255
256 return TRUE;
257 }
258
259 /* coverity[+alloc : arg-*1] */
260 GpStatus WINGDIPAPI
GdipCreatePath(FillMode fillMode,GpPath ** path)261 GdipCreatePath (FillMode fillMode, GpPath **path)
262 {
263 GpPath *result;
264
265 if (!gdiplusInitialized)
266 return GdiplusNotInitialized;
267
268 if (!path)
269 return InvalidParameter;
270
271 result = (GpPath *) GdipAlloc (sizeof (GpPath));
272 if (!result)
273 return OutOfMemory;
274
275 result->fill_mode = fillMode;
276 result->size = 0;
277 result->points = NULL;
278 result->types = NULL;
279 result->count = 0;
280 result->start_new_fig = TRUE;
281
282 *path = result;
283 return Ok;
284 }
285
286 /* coverity[+alloc : arg-*4] */
287 GpStatus WINGDIPAPI
GdipCreatePath2(GDIPCONST GpPointF * points,GDIPCONST BYTE * types,INT count,FillMode fillMode,GpPath ** path)288 GdipCreatePath2 (GDIPCONST GpPointF *points, GDIPCONST BYTE *types, INT count, FillMode fillMode, GpPath **path)
289 {
290 GpPath *result;
291
292 if (!gdiplusInitialized)
293 return GdiplusNotInitialized;
294
295 if (!path || !points || !types)
296 return InvalidParameter;
297 if (count <= 0 || fillMode > FillModeWinding)
298 return OutOfMemory;
299
300 // Match GDI+ behaviour and set the path to empty if it is invalid.
301 if (!gdip_validate_path_types (types, count))
302 return GdipCreatePath (fillMode, path);
303
304 result = (GpPath *) GdipAlloc (sizeof (GpPath));
305 if (!result)
306 return OutOfMemory;
307
308 result->fill_mode = fillMode;
309 result->count = count;
310 result->size = (count + 63) & ~63;
311 result->points = GdipAlloc (sizeof (GpPointF) * result->size);
312 if (!result->points) {
313 GdipFree (result);
314 return OutOfMemory;
315 }
316
317 result->types = GdipAlloc (sizeof (BYTE) * result->size);
318 if (!result->types) {
319 GdipFree (result->points);
320 GdipFree (result);
321 return OutOfMemory;
322 }
323
324 memcpy (result->points, points, sizeof (GpPointF) * count);
325 memcpy (result->types, types, sizeof (BYTE) * count);
326
327 // Match GDI+ behaviour by normalizing the start of the type array.
328 result->types[0] = PathPointTypeStart;
329
330 *path = result;
331 return Ok;
332 }
333
334 /* coverity[+alloc : arg-*4] */
335 GpStatus WINGDIPAPI
GdipCreatePath2I(GDIPCONST GpPoint * points,GDIPCONST BYTE * types,INT count,FillMode fillMode,GpPath ** path)336 GdipCreatePath2I (GDIPCONST GpPoint *points, GDIPCONST BYTE *types, INT count, FillMode fillMode, GpPath **path)
337 {
338 GpPointF *pt;
339 GpStatus s;
340
341 if (!gdiplusInitialized)
342 return GdiplusNotInitialized;
343
344 if (!points || !types || !path)
345 return InvalidParameter;
346 if (count < 0)
347 return OutOfMemory;
348
349 pt = convert_points (points, count);
350 if (!pt)
351 return OutOfMemory;
352
353 s = GdipCreatePath2 (pt, types, count, fillMode, path);
354
355 GdipFree (pt);
356
357 return s;
358 }
359
360 /* coverity[+alloc : arg-*1] */
361 GpStatus WINGDIPAPI
GdipClonePath(GpPath * path,GpPath ** clonePath)362 GdipClonePath (GpPath *path, GpPath **clonePath)
363 {
364 GpPath *result;
365
366 if (!path || !clonePath)
367 return InvalidParameter;
368
369 result = (GpPath *) GdipAlloc (sizeof (GpPath));
370 if (!result)
371 return OutOfMemory;
372
373 result->fill_mode = path->fill_mode;
374 result->count = path->count;
375 result->size = path->size;
376 result->points = GdipAlloc (sizeof (GpPointF) * result->size);
377 if (!result->points) {
378 GdipFree (result);
379 return OutOfMemory;
380 }
381
382 result->types = GdipAlloc (sizeof (BYTE) * result->size);
383 if (!result->types) {
384 GdipFree (result->points);
385 GdipFree (result);
386 return OutOfMemory;
387 }
388
389 memcpy (result->points, path->points, sizeof (GpPointF) * path->count);
390 memcpy (result->types, path->types, sizeof (BYTE) * path->count);
391
392 result->start_new_fig = path->start_new_fig;
393
394 *clonePath = result;
395 return Ok;
396 }
397
398 GpStatus WINGDIPAPI
GdipDeletePath(GpPath * path)399 GdipDeletePath (GpPath *path)
400 {
401 if (path == NULL)
402 return InvalidParameter;
403
404 if (path->points != NULL)
405 GdipFree (path->points);
406 path->points = NULL;
407
408 if (path->types != NULL)
409 GdipFree (path->types);
410 path->types = NULL;
411
412 GdipFree (path);
413 return Ok;
414 }
415
416 GpStatus WINGDIPAPI
GdipResetPath(GpPath * path)417 GdipResetPath (GpPath *path)
418 {
419 if (path == NULL)
420 return InvalidParameter;
421
422 path->count = 0;
423 path->fill_mode = FillModeAlternate;
424 path->start_new_fig = TRUE;
425 path->size = 0;
426 if (path->points != NULL)
427 GdipFree (path->points);
428 if (path->types != NULL)
429 GdipFree (path->types);
430 path->points = NULL;
431 path->types = NULL;
432
433 return Ok;
434 }
435
436 GpStatus WINGDIPAPI
GdipGetPointCount(GpPath * path,int * count)437 GdipGetPointCount (GpPath *path, int *count)
438 {
439 if (!path || !count)
440 return InvalidParameter;
441
442 *count = path->count;
443 return Ok;
444 }
445
446 GpStatus WINGDIPAPI
GdipGetPathTypes(GpPath * path,BYTE * types,INT count)447 GdipGetPathTypes (GpPath *path, BYTE *types, INT count)
448 {
449 if (!path || !types || count <= 0)
450 return InvalidParameter;
451 if (count < path->count)
452 return InsufficientBuffer;
453
454 memcpy (types, path->types, path->count * sizeof (BYTE));
455 return Ok;
456 }
457
458 GpStatus WINGDIPAPI
GdipGetPathPoints(GpPath * path,GpPointF * points,int count)459 GdipGetPathPoints (GpPath *path, GpPointF *points, int count)
460 {
461 if (!path || !points || count <= 0)
462 return InvalidParameter;
463 if (count < path->count)
464 return InsufficientBuffer;
465
466 memcpy (points, path->points, path->count * sizeof (GpPointF));
467 return Ok;
468 }
469
470 GpStatus WINGDIPAPI
GdipGetPathPointsI(GpPath * path,GpPoint * points,int count)471 GdipGetPathPointsI (GpPath *path, GpPoint *points, int count)
472 {
473 if (!path || !points || count <= 0)
474 return InvalidParameter;
475 if (count < path->count)
476 return InsufficientBuffer;
477
478 for (int i = 0; i < path->count; i++)
479 gdip_Point_from_PointF (path->points + i, points + i);
480
481 return Ok;
482 }
483
484 GpStatus WINGDIPAPI
GdipGetPathFillMode(GpPath * path,FillMode * fillMode)485 GdipGetPathFillMode (GpPath *path, FillMode *fillMode)
486 {
487 if (!path || !fillMode)
488 return InvalidParameter;
489
490 *fillMode = path->fill_mode;
491
492 return Ok;
493 }
494
495 GpStatus WINGDIPAPI
GdipSetPathFillMode(GpPath * path,FillMode fillMode)496 GdipSetPathFillMode (GpPath *path, FillMode fillMode)
497 {
498 if (!path)
499 return InvalidParameter;
500
501 path->fill_mode = fillMode;
502
503 return Ok;
504 }
505
506 GpStatus WINGDIPAPI
GdipGetPathData(GpPath * path,GpPathData * pathData)507 GdipGetPathData (GpPath *path, GpPathData *pathData)
508 {
509 if (!path || !pathData || !pathData->Points || !pathData->Types || pathData->Count < 0)
510 return InvalidParameter;
511 if (pathData->Count < path->count)
512 return OutOfMemory;
513
514 memcpy (pathData->Points, path->points, path->count * sizeof (GpPointF));
515 memcpy (pathData->Types, path->types, path->count * sizeof (BYTE));
516 pathData->Count = path->count;
517
518 return Ok;
519 }
520
521 GpStatus WINGDIPAPI
GdipStartPathFigure(GpPath * path)522 GdipStartPathFigure (GpPath *path)
523 {
524 if (!path)
525 return InvalidParameter;
526
527 path->start_new_fig = TRUE;
528 return Ok;
529 }
530
531 GpStatus WINGDIPAPI
GdipClosePathFigure(GpPath * path)532 GdipClosePathFigure (GpPath *path)
533 {
534 if (!path)
535 return InvalidParameter;
536
537 // Close the last figure.
538 if (path->count > 1)
539 path->types[path->count - 1] |= PathPointTypeCloseSubpath;
540
541 // Start a new figure.
542 path->start_new_fig = TRUE;
543 return Ok;
544 }
545
546 GpStatus WINGDIPAPI
GdipClosePathFigures(GpPath * path)547 GdipClosePathFigures (GpPath *path)
548 {
549 if (!path)
550 return InvalidParameter;
551
552 if (path->count > 1) {
553 // Close the last figure.
554 path->types[path->count - 1] |= PathPointTypeCloseSubpath;
555
556 // Close each open figure.
557 for (int index = 1; index < path->count; index++) {
558 if (path->types[index] == PathPointTypeStart) {
559 path->types[index - 1] |= PathPointTypeCloseSubpath;
560 }
561 }
562 }
563
564 // Start a new figure.
565 path->start_new_fig = TRUE;
566 return Ok;
567 }
568
569 GpStatus WINGDIPAPI
GdipSetPathMarker(GpPath * path)570 GdipSetPathMarker (GpPath *path)
571 {
572 if (!path)
573 return InvalidParameter;
574
575 if (path->count > 1)
576 path->types[path->count - 1] |= PathPointTypePathMarker;
577
578 return Ok;
579 }
580
581 GpStatus WINGDIPAPI
GdipClearPathMarkers(GpPath * path)582 GdipClearPathMarkers (GpPath *path)
583 {
584 if (!path)
585 return InvalidParameter;
586
587 for (int i = 0; i < path->count; i++)
588 path->types[i] &= ~PathPointTypePathMarker;
589
590 return Ok;
591 }
592
593 /*
594 * Append old_types[start, end] to new_types, adjusting flags.
595 */
596 static void
reverse_subpath_adjust_flags(int start,int end,BYTE * types,BOOL * prev_had_marker)597 reverse_subpath_adjust_flags (int start, int end, BYTE *types, BOOL *prev_had_marker)
598 {
599 BYTE prev_last;
600
601 /* Copy all but PathPointTypeStart */
602 if (end != start)
603 memmove (types + start, types + start + 1, (end - start) * sizeof (BYTE));
604
605 /* Append PathPointTypeStart */
606 prev_last = types[end];
607 types[end] = PathPointTypeStart;
608
609 /* Remove potential flags from our future start point */
610 if (end != start)
611 types[end - 1] &= PathPointTypePathTypeMask;
612 /* Set the flags on our to-be-last point */
613 types[start] |= prev_last & (PathPointTypeDashMode | PathPointTypeCloseSubpath);
614
615 /* If the last point of the previous subpath had a marker, we inherit it */
616 if (*prev_had_marker)
617 types[start] |= PathPointTypePathMarker;
618 else
619 types[start] &= ~PathPointTypePathMarker;
620
621 *prev_had_marker = ((prev_last & PathPointTypePathMarker) == PathPointTypePathMarker);
622 }
623
624 GpStatus WINGDIPAPI
GdipReversePath(GpPath * path)625 GdipReversePath (GpPath *path)
626 {
627 int length, i;
628 int start = 0;
629 BOOL prev_had_marker = FALSE;
630
631 if (!path)
632 return InvalidParameter;
633
634 length = path->count;
635 /* shortcut */
636 if (length <= 1)
637 return Ok;
638
639 /* PathTypes reversal */
640
641 /* First adjust the flags for each subpath */
642 for (i = 1; i < length; i++) {
643 BYTE t = path->types[i];
644 if ((t & PathPointTypePathTypeMask) == PathPointTypeStart) {
645 reverse_subpath_adjust_flags (start, i - 1, path->types, &prev_had_marker);
646 start = i;
647 }
648 }
649 if (start < length - 1)
650 reverse_subpath_adjust_flags (start, length - 1, path->types, &prev_had_marker);
651
652 /* Then reverse the resulting array */
653 for (i = 0; i < (length >> 1); i++) {
654 BYTE *a = path->types + i;
655 BYTE *b = path->types + length - i - 1;
656 BYTE temp = *a;
657 *a = *b;
658 *b = temp;
659 }
660
661 /* PathPoints reversal
662 * note: if length is odd then the middle point doesn't need to switch side
663 */
664 for (i = 0; i < (length >> 1); i++) {
665 GpPointF *first = path->points + i;
666 GpPointF *last = path->points + length - i - 1;
667
668 GpPointF temp;
669 temp.X = first->X;
670 temp.Y = first->Y;
671 first->X = last->X;
672 first->Y = last->Y;
673 last->X = temp.X;
674 last->Y = temp.Y;
675 }
676
677 return Ok;
678 }
679
680 GpStatus WINGDIPAPI
GdipGetPathLastPoint(GpPath * path,GpPointF * lastPoint)681 GdipGetPathLastPoint (GpPath *path, GpPointF *lastPoint)
682 {
683 if (!path || !lastPoint || (path->count <= 0))
684 return InvalidParameter;
685
686 *lastPoint = path->points[path->count - 1];
687 return Ok;
688 }
689
690 GpStatus WINGDIPAPI
GdipAddPathLine(GpPath * path,float x1,float y1,float x2,float y2)691 GdipAddPathLine (GpPath *path, float x1, float y1, float x2, float y2)
692 {
693 if (!path)
694 return InvalidParameter;
695
696 if (!gdip_path_ensure_size (path, path->count + 2))
697 return OutOfMemory;
698
699 /* only the first point can be compressed (i.e. removed if identical to previous) */
700 append (path, x1, y1, PathPointTypeLine, TRUE);
701 append (path, x2, y2, PathPointTypeLine, FALSE);
702
703 return Ok;
704 }
705
706 GpStatus WINGDIPAPI
GdipAddPathLine2(GpPath * path,const GpPointF * points,int count)707 GdipAddPathLine2 (GpPath *path, const GpPointF *points, int count)
708 {
709 int i;
710 GpPointF *tmp;
711
712 if (!path || !points || (count < 0))
713 return InvalidParameter;
714
715 if (!gdip_path_ensure_size (path, path->count + count))
716 return OutOfMemory;
717
718 /* only the first point can be compressed (i.e. removed if identical to previous) */
719 for (i = 0, tmp = (GpPointF*) points; i < count; i++, tmp++)
720 append (path, tmp->X, tmp->Y, PathPointTypeLine, (i == 0));
721
722 return Ok;
723 }
724
725 static VOID
append_arc(GpPath * path,BOOL start,float x,float y,float width,float height,float startAngle,float endAngle)726 append_arc (GpPath *path, BOOL start, float x, float y, float width, float height, float startAngle, float endAngle)
727 {
728 float delta, bcp;
729 double sin_alpha, sin_beta, cos_alpha, cos_beta;
730
731 float rx = width / 2;
732 float ry = height / 2;
733
734 /* center */
735 float cx = x + rx;
736 float cy = y + ry;
737
738 /* angles in radians */
739 float alpha = startAngle * PI / 180;
740 float beta = endAngle * PI / 180;
741
742 /* adjust angles for ellipses */
743 alpha = atan2 (rx * sin (alpha), ry * cos (alpha));
744 beta = atan2 (rx * sin (beta), ry * cos (beta));
745
746 if (fabs (beta - alpha) > M_PI){
747 if (beta > alpha)
748 beta -= 2 * PI;
749 else
750 alpha -= 2 * PI;
751 }
752
753 delta = beta - alpha;
754 // http://www.stillhq.com/ctpfaq/2001/comp.text.pdf-faq-2001-04.txt (section 2.13)
755 bcp = 4.0 / 3 * (1 - cos (delta / 2)) / sin (delta / 2);
756
757 sin_alpha = sin (alpha);
758 sin_beta = sin (beta);
759 cos_alpha = cos (alpha);
760 cos_beta = cos (beta);
761
762 /* move to the starting point if we're not continuing a curve */
763 if (start) {
764 /* starting point */
765 double sx = cx + rx * cos_alpha;
766 double sy = cy + ry * sin_alpha;
767 append (path, sx, sy, PathPointTypeLine, FALSE);
768 }
769
770 append_bezier (path,
771 cx + rx * (cos_alpha - bcp * sin_alpha),
772 cy + ry * (sin_alpha + bcp * cos_alpha),
773 cx + rx * (cos_beta + bcp * sin_beta),
774 cy + ry * (sin_beta - bcp * cos_beta),
775 cx + rx * cos_beta,
776 cy + ry * sin_beta);
777 }
778
779 static int
count_arcs_points(GpPath * path,float x,float y,float width,float height,float startAngle,float sweepAngle)780 count_arcs_points (GpPath *path, float x, float y, float width, float height, float startAngle, float sweepAngle)
781 {
782 int i;
783 float drawn = 0;
784 int increment;
785 float endAngle;
786 int count = 1;
787
788 if (fabs (sweepAngle) >= 360)
789 return 13;
790
791 endAngle = startAngle + sweepAngle;
792 increment = (endAngle < startAngle) ? -90 : 90;
793
794 /* i is the number of sub-arcs drawn, each sub-arc can be at most 90 degrees.*/
795 /* there can be no more then 4 subarcs, ie. 90 + 90 + 90 + (something less than 90) */
796 for (i = 0; i < 4; i++) {
797 float current = startAngle + drawn;
798 float additional;
799
800 additional = endAngle - current; /* otherwise, add the remainder */
801 if (fabs (additional) > 90) {
802 additional = increment;
803 } else {
804 /* a near zero value will introduce bad artefact in the drawing (#78999) */
805 if (gdip_near_zero (additional))
806 return count;
807 count += 3;
808 return count;
809 }
810
811 count += 3;
812 drawn += additional;
813 }
814
815 return count;
816 }
817
818
819 static VOID
append_arcs(GpPath * path,float x,float y,float width,float height,float startAngle,float sweepAngle)820 append_arcs (GpPath *path, float x, float y, float width, float height, float startAngle, float sweepAngle)
821 {
822 int i;
823 float drawn = 0;
824 int increment;
825 float endAngle;
826 BOOL enough = FALSE;
827
828 if (fabs (sweepAngle) >= 360) {
829 GdipAddPathEllipse (path, x, y, width, height);
830 return;
831 }
832
833 endAngle = startAngle + sweepAngle;
834 increment = (endAngle < startAngle) ? -90 : 90;
835
836 /* i is the number of sub-arcs drawn, each sub-arc can be at most 90 degrees.*/
837 /* there can be no more then 4 subarcs, ie. 90 + 90 + 90 + (something less than 90) */
838 for (i = 0; i < 4; i++) {
839 float current = startAngle + drawn;
840 float additional;
841
842 if (enough)
843 return;
844
845 additional = endAngle - current; /* otherwise, add the remainder */
846 if (fabs (additional) > 90) {
847 additional = increment;
848 } else {
849 /* a near zero value will introduce bad artefact in the drawing (#78999) */
850 if (gdip_near_zero (additional))
851 return;
852
853 enough = TRUE;
854 }
855
856 append_arc (path, (i == 0), /* only move to the starting pt in the 1st iteration */
857 x, y, width, height, /* bounding rectangle */
858 current, current + additional);
859 drawn += additional;
860 }
861 }
862
863 GpStatus WINGDIPAPI
GdipAddPathArc(GpPath * path,float x,float y,float width,float height,float startAngle,float sweepAngle)864 GdipAddPathArc (GpPath *path, float x, float y,
865 float width, float height, float startAngle, float sweepAngle)
866 {
867 int point_count;
868
869 if (!path)
870 return InvalidParameter;
871
872 if (width == 0 || height == 0)
873 return InvalidParameter;
874
875 point_count = count_arcs_points (path, x, y, width, height, startAngle, sweepAngle);
876 if (!gdip_path_ensure_size (path, path->count + point_count))
877 return OutOfMemory;
878
879 /* draw the arcs */
880 append_arcs (path, x, y, width, height, startAngle, sweepAngle);
881
882 return Ok;
883 }
884
885 GpStatus WINGDIPAPI
GdipAddPathBezier(GpPath * path,float x1,float y1,float x2,float y2,float x3,float y3,float x4,float y4)886 GdipAddPathBezier (GpPath *path,
887 float x1, float y1, float x2, float y2,
888 float x3, float y3, float x4, float y4)
889 {
890 if (!path)
891 return InvalidParameter;
892
893 if (!gdip_path_ensure_size (path, path->count + 4))
894 return OutOfMemory;
895
896 append (path, x1, y1, PathPointTypeLine, TRUE);
897 append_bezier (path, x2, y2, x3, y3, x4, y4);
898
899 return Ok;
900 }
901
902 GpStatus WINGDIPAPI
GdipAddPathBeziers(GpPath * path,const GpPointF * points,int count)903 GdipAddPathBeziers (GpPath *path, const GpPointF *points, int count)
904 {
905 int i;
906 GpPointF *tmp = (GpPointF *) points;
907
908 if (!path || !points)
909 return InvalidParameter;
910
911 /* first bezier requires 4 points, other 3 more points */
912 if ((count < 4) || ((count % 3) != 1))
913 return InvalidParameter;
914
915 if (!gdip_path_ensure_size (path, path->count + count))
916 return OutOfMemory;
917
918 append_point (path, *tmp, PathPointTypeLine, TRUE);
919 tmp++;
920
921 for (i = 1; i < count; i++, tmp++)
922 append_point (path, *tmp, PathPointTypeBezier3, FALSE);
923
924 return Ok;
925 }
926
927 GpStatus WINGDIPAPI
GdipAddPathCurve(GpPath * path,const GpPointF * points,int count)928 GdipAddPathCurve (GpPath *path, const GpPointF *points, int count)
929 {
930 return GdipAddPathCurve2 (path, points, count, 0.5);
931 }
932
933 GpStatus WINGDIPAPI
GdipAddPathCurve2(GpPath * path,const GpPointF * points,int count,float tension)934 GdipAddPathCurve2 (GpPath *path, const GpPointF *points, int count, float tension)
935 {
936 GpPointF *tangents;
937
938 /* special case, here we support a curve with 2 points */
939 if (!path || !points || (count < 2))
940 return InvalidParameter;
941
942 tangents = gdip_open_curve_tangents (CURVE_MIN_TERMS, points, count, tension);
943 if (!tangents)
944 return OutOfMemory;
945
946 if (!gdip_path_ensure_size (path, path->count + (3 * (count - 1)) + 1))
947 return OutOfMemory;
948
949 append_curve (path, points, tangents, 0, count - 1, CURVE_OPEN);
950
951 GdipFree (tangents);
952
953 return Ok;
954 }
955
956 GpStatus WINGDIPAPI
GdipAddPathCurve3(GpPath * path,const GpPointF * points,int count,int offset,int numberOfSegments,float tension)957 GdipAddPathCurve3 (GpPath *path, const GpPointF *points, int count,
958 int offset, int numberOfSegments, float tension)
959 {
960 GpPointF *tangents;
961
962 if (!path || !points || (numberOfSegments < 1))
963 return InvalidParameter;
964
965 /* we need 3 points for the first curve, 2 more for each curves */
966 /* and it's possible to use a point prior to the offset (to calculate) */
967 if ((offset == 0) && (numberOfSegments == 1) && (count < 3))
968 return InvalidParameter;
969 else if (numberOfSegments >= count - offset)
970 return InvalidParameter;
971
972 tangents = gdip_open_curve_tangents (CURVE_MIN_TERMS, points, count, tension);
973 if (!tangents)
974 return OutOfMemory;
975
976 if (!gdip_path_ensure_size (path, path->count + (3 * numberOfSegments) + 1))
977 return OutOfMemory;
978
979 append_curve (path, points, tangents, offset, numberOfSegments, CURVE_OPEN);
980
981 GdipFree (tangents);
982
983 return Ok;
984 }
985
986 GpStatus WINGDIPAPI
GdipAddPathClosedCurve(GpPath * path,const GpPointF * points,int count)987 GdipAddPathClosedCurve (GpPath *path, const GpPointF *points, int count)
988 {
989 return GdipAddPathClosedCurve2 (path, points, count, 0.5);
990 }
991
992 GpStatus WINGDIPAPI
GdipAddPathClosedCurve2(GpPath * path,const GpPointF * points,int count,float tension)993 GdipAddPathClosedCurve2 (GpPath *path, const GpPointF *points, int count, float tension)
994 {
995 GpPointF *tangents;
996
997 if (!path || !points || (count < 3))
998 return InvalidParameter;
999
1000 tangents = gdip_closed_curve_tangents (CURVE_MIN_TERMS, points, count, tension);
1001 if (!tangents)
1002 return OutOfMemory;
1003
1004 if (!gdip_path_ensure_size (path, path->count + (3 * count) + 1))
1005 return OutOfMemory;
1006
1007 append_curve (path, points, tangents, 0, count - 1, CURVE_CLOSE);
1008
1009 /* close the path */
1010 GdipClosePathFigure (path);
1011 GdipFree (tangents);
1012
1013 return Ok;
1014 }
1015
1016 GpStatus WINGDIPAPI
GdipAddPathRectangle(GpPath * path,float x,float y,float width,float height)1017 GdipAddPathRectangle (GpPath *path, float x, float y, float width, float height)
1018 {
1019 if (!path)
1020 return InvalidParameter;
1021
1022 if ((width <= 0.0) || (height <= 0.0))
1023 return Ok;
1024
1025 if (!gdip_path_ensure_size (path, path->count + 4))
1026 return OutOfMemory;
1027
1028 append (path, x, y, PathPointTypeStart, FALSE);
1029 append (path, x + width, y, PathPointTypeLine, FALSE);
1030 append (path, x + width, y + height, PathPointTypeLine, FALSE);
1031 append (path, x, y + height, PathPointTypeLine | PathPointTypeCloseSubpath, FALSE);
1032
1033 return Ok;
1034 }
1035
1036 GpStatus WINGDIPAPI
GdipAddPathRectangles(GpPath * path,const GpRectF * rects,int count)1037 GdipAddPathRectangles (GpPath *path, const GpRectF *rects, int count)
1038 {
1039 int i;
1040
1041 if (!path || !rects)
1042 return InvalidParameter;
1043
1044 if (!gdip_path_ensure_size (path, path->count + (4 * count)))
1045 return OutOfMemory;
1046
1047 for (i = 0; i < count; i++) {
1048 float x = rects[i].X;
1049 float y = rects[i].Y;
1050 float width = rects[i].Width;
1051 float height = rects[i].Height;
1052 GdipAddPathRectangle (path, x, y, width, height);
1053 }
1054
1055 return Ok;
1056 }
1057
1058 GpStatus WINGDIPAPI
GdipAddPathEllipse(GpPath * path,float x,float y,float width,float height)1059 GdipAddPathEllipse (GpPath *path, float x, float y, float width, float height)
1060 {
1061 double rx = width / 2;
1062 double ry = height / 2;
1063 double cx = x + rx;
1064 double cy = y + ry;
1065
1066 if (!path)
1067 return InvalidParameter;
1068
1069 if (!gdip_path_ensure_size (path, path->count + 13))
1070 return OutOfMemory;
1071
1072 /* origin */
1073 append (path, cx + rx, cy, PathPointTypeStart, FALSE);
1074
1075 /* quadrant I */
1076 append_bezier (path,
1077 cx + rx, cy - C1 * ry,
1078 cx + C1 * rx, cy - ry,
1079 cx, cy - ry);
1080
1081 /* quadrant II */
1082 append_bezier (path,
1083 cx - C1 * rx, cy - ry,
1084 cx - rx, cy - C1 * ry,
1085 cx - rx, cy);
1086
1087 /* quadrant III */
1088 append_bezier (path,
1089 cx - rx, cy + C1 * ry,
1090 cx - C1 * rx, cy + ry,
1091 cx, cy + ry);
1092
1093 /* quadrant IV */
1094 append_bezier (path,
1095 cx + C1 * rx, cy + ry,
1096 cx + rx, cy + C1 * ry,
1097 cx + rx, cy);
1098
1099 /* close the path */
1100 GdipClosePathFigure (path);
1101
1102 return Ok;
1103 }
1104
1105 GpStatus WINGDIPAPI
GdipAddPathPie(GpPath * path,float x,float y,float width,float height,float startAngle,float sweepAngle)1106 GdipAddPathPie (GpPath *path, float x, float y, float width, float height, float startAngle, float sweepAngle)
1107 {
1108 int point_count;
1109
1110 float sin_alpha, cos_alpha;
1111
1112 if (width == 0 || height == 0)
1113 return InvalidParameter;
1114
1115 float rx = width / 2;
1116 float ry = height / 2;
1117
1118 /* center */
1119 int cx = x + rx;
1120 int cy = y + ry;
1121
1122 /* angles in radians */
1123 float alpha = startAngle * PI / 180;
1124
1125 /* adjust angle for ellipses */
1126 alpha = atan2 (rx * sin (alpha), ry * cos (alpha));
1127
1128 sin_alpha = sin (alpha);
1129 cos_alpha = cos (alpha);
1130
1131 if (!path)
1132 return InvalidParameter;
1133
1134 point_count = count_arcs_points (path, x, y, width, height, startAngle, sweepAngle) + 1;
1135 if (fabs (sweepAngle) < 360)
1136 point_count += 2;
1137 if (!gdip_path_ensure_size (path, path->count + point_count))
1138 return OutOfMemory;
1139
1140 /* move to center */
1141 append (path, cx, cy, PathPointTypeStart, FALSE);
1142
1143 /* draw pie edge */
1144 if (fabs (sweepAngle) < 360)
1145 append (path, cx + rx * cos_alpha, cy + ry * sin_alpha, PathPointTypeLine, FALSE);
1146
1147 /* draw the arcs */
1148 append_arcs (path, x, y, width, height, startAngle, sweepAngle);
1149
1150 /* draw pie edge */
1151 if (fabs (sweepAngle) < 360)
1152 append (path, cx, cy, PathPointTypeLine, FALSE);
1153
1154 /* close the path */
1155 return GdipClosePathFigure (path);
1156 }
1157
1158 GpStatus WINGDIPAPI
GdipAddPathPolygon(GpPath * path,const GpPointF * points,int count)1159 GdipAddPathPolygon (GpPath *path, const GpPointF *points, int count)
1160 {
1161 int i;
1162 GpPointF *tmp = (GpPointF *) points;
1163
1164 if (!path || !points || (count < 3))
1165 return InvalidParameter;
1166
1167 if (!gdip_path_ensure_size (path, path->count + count + 1))
1168 return OutOfMemory;
1169
1170 /* note: polygon points are never compressed (i.e. removed if identical) */
1171
1172 append_point (path, *tmp, PathPointTypeStart, FALSE);
1173 tmp ++;
1174
1175 for (i = 1; i < count; i++, tmp++)
1176 append_point (path, *tmp, PathPointTypeLine, FALSE);
1177
1178 /*
1179 * Add a line from the last point back to the first point if
1180 * they're not the same
1181 */
1182 if (points [0].X != points [count - 1].X && points [0].Y != points [count - 1].Y)
1183 append_point (path, points [0], PathPointTypeLine, FALSE);
1184
1185 /* close the path */
1186 return GdipClosePathFigure (path);
1187 }
1188
1189 GpStatus WINGDIPAPI
GdipAddPathPath(GpPath * path,GDIPCONST GpPath * addingPath,BOOL connect)1190 GdipAddPathPath (GpPath *path, GDIPCONST GpPath *addingPath, BOOL connect)
1191 {
1192 if (!path || !addingPath)
1193 return InvalidParameter;
1194
1195 if (!gdip_path_ensure_size (path, path->count + addingPath->count))
1196 return OutOfMemory;
1197
1198 memcpy (path->types + path->count, addingPath->types, sizeof (BYTE) * addingPath->count);
1199 memcpy (path->points + path->count, addingPath->points, sizeof (GpPointF) * addingPath->count);
1200
1201 /* We can connect only open figures. If first figure is closed
1202 * it can't be connected.
1203 */
1204 path->types[path->count] = connect ? gdip_get_first_point_type (path) : PathPointTypeStart;
1205 path->start_new_fig = FALSE;
1206 path->count += addingPath->count;
1207
1208 return Ok;
1209 }
1210
1211 /* MonoTODO - deal with layoutRect, format... */
1212 GpStatus WINGDIPAPI
GdipAddPathString(GpPath * path,GDIPCONST WCHAR * string,int length,GDIPCONST GpFontFamily * family,int style,float emSize,GDIPCONST GpRectF * layoutRect,GDIPCONST GpStringFormat * format)1213 GdipAddPathString (GpPath *path, GDIPCONST WCHAR *string, int length,
1214 GDIPCONST GpFontFamily *family, int style, float emSize,
1215 GDIPCONST GpRectF *layoutRect, GDIPCONST GpStringFormat *format)
1216 {
1217 cairo_surface_t *cs;
1218 cairo_t *cr;
1219 cairo_path_t *cp;
1220 GpFont *font = NULL;
1221 GpStatus status;
1222 BYTE *utf8 = NULL;
1223
1224 if (!path || !string || length < -1 || !family || !layoutRect)
1225 return InvalidParameter;
1226
1227 if (length == 0) {
1228 return Ok;
1229 } else if (length == -1) {
1230 const WCHAR * ptr = string;
1231 length = 0;
1232 while (*ptr != 0) {
1233 length++;
1234 ptr++;
1235 }
1236 }
1237
1238 if (emSize == 0)
1239 return GenericError;
1240
1241 cs = cairo_recording_surface_create (CAIRO_CONTENT_COLOR_ALPHA, NULL);
1242 if (cairo_surface_status (cs) != CAIRO_STATUS_SUCCESS) {
1243 cairo_surface_destroy (cs);
1244 return OutOfMemory;
1245 }
1246
1247 cr = cairo_create (cs);
1248 if (cairo_status (cr) != CAIRO_STATUS_SUCCESS) {
1249 cairo_destroy (cr);
1250 cairo_surface_destroy (cs);
1251 return OutOfMemory;
1252 }
1253
1254 status = gdip_create_font_without_validation (family, fabsf (emSize), style, UnitPixel, &font);
1255 if (status != Ok) {
1256 GdipFree (utf8);
1257 cairo_destroy (cr);
1258 cairo_surface_destroy (cs);
1259 return status;
1260 }
1261
1262 #ifdef USE_PANGO_RENDERING
1263 {
1264 GpRectF box;
1265 GpPointF box_offset;
1266 PangoLayout* layout;
1267 GpStringFormat *string_format;
1268
1269 if (format == NULL) {
1270 status = GdipCreateStringFormat (StringFormatFlagsNoClip, 0, &string_format);
1271 }
1272 else if (!(format->formatFlags & StringFormatFlagsNoClip)) {
1273 status = GdipCloneStringFormat (format, &string_format);
1274 if (status == Ok)
1275 string_format->formatFlags |= StringFormatFlagsNoClip;
1276 } else {
1277 status = Ok;
1278 string_format = (GpStringFormat *) format;
1279 }
1280
1281 if (status != Ok) {
1282 GdipDeleteFont (font);
1283 GdipFree (utf8);
1284 cairo_destroy (cr);
1285 cairo_surface_destroy (cs);
1286 return status;
1287 }
1288
1289 layout = gdip_pango_setup_layout (cr, string, length, font, layoutRect, &box, &box_offset, string_format, NULL);
1290 cairo_move_to (cr, layoutRect->X + box_offset.X, layoutRect->Y + box_offset.Y);
1291 pango_cairo_layout_path (cr, layout);
1292 g_object_unref (layout);
1293
1294 if (string_format != format)
1295 GdipDeleteStringFormat (string_format);
1296
1297 // If our Cairo context had a current point before laying out the path, Pango will have moved us back there.
1298 // We don't want that when we process the path below, so clear it if set.
1299 if (cairo_has_current_point(cr))
1300 cairo_new_sub_path(cr);
1301
1302 }
1303 #else
1304 {
1305 BYTE *utf8 = (BYTE*) utf16_to_utf8 (string, length);
1306 if (!utf8) {
1307 GdipDeleteFont (font);
1308 cairo_destroy (cr);
1309 cairo_surface_destroy (cs);
1310 return OutOfMemory;
1311 }
1312
1313 cairo_move_to (cr, layoutRect->X, layoutRect->Y + font->sizeInPixels);
1314
1315 cairo_set_font_face (cr, gdip_get_cairo_font_face (font));
1316 cairo_set_font_size (cr, font->sizeInPixels);
1317 /* TODO - deal with layoutRect, format... ideally we would be calling a subset
1318 of GdipDrawString that already does everything *and* preserve the whole path */
1319 cairo_text_path (cr, (const char *) utf8);
1320
1321 GdipFree (utf8);
1322 }
1323 #endif
1324
1325 /* get the font data from the cairo path and translate it as a gdi+ path */
1326 status = Ok;
1327 cp = cairo_copy_path (cr);
1328 if (cp) {
1329 int i;
1330 int count = 0;
1331 for (i=0; i < cp->num_data; i += cp->data[i].header.length) {
1332 cairo_path_data_t *data = &cp->data[i];
1333 switch (data->header.type) {
1334 case CAIRO_PATH_MOVE_TO: count++; break;
1335 case CAIRO_PATH_LINE_TO: count++; break;
1336 case CAIRO_PATH_CURVE_TO: count += 3; break;
1337 case CAIRO_PATH_CLOSE_PATH: break;
1338 }
1339 }
1340
1341 if (gdip_path_ensure_size (path, path->count + count)) {
1342 for (i=0; i < cp->num_data; i += cp->data[i].header.length) {
1343 PathPointType type = PathPointTypeStart;
1344 int dataLength = cp->data[i].header.length;
1345 cairo_path_data_t *data = &cp->data[i];
1346
1347 if ((i < cp->num_data - dataLength) && (cp->data[i + dataLength].header.type == CAIRO_PATH_CLOSE_PATH))
1348 type |= PathPointTypeCloseSubpath;
1349
1350 switch (data->header.type) {
1351 case CAIRO_PATH_MOVE_TO:
1352 append (path, data[1].point.x, data[1].point.y, type, FALSE);
1353 break;
1354 case CAIRO_PATH_LINE_TO:
1355 append (path, data[1].point.x, data[1].point.y, type | PathPointTypeLine, FALSE);
1356 break;
1357 case CAIRO_PATH_CURVE_TO:
1358 append (path, data[1].point.x, data[1].point.y, PathPointTypeBezier, FALSE);
1359 append (path, data[2].point.x, data[2].point.y, PathPointTypeBezier, FALSE);
1360 append (path, data[3].point.x, data[3].point.y, type | PathPointTypeBezier, FALSE);
1361 break;
1362 case CAIRO_PATH_CLOSE_PATH:
1363 break;
1364 }
1365 }
1366 } else {
1367 status = OutOfMemory;
1368 }
1369
1370 cairo_path_destroy (cp);
1371 }
1372
1373 if (font) {
1374 GdipDeleteFont (font);
1375 }
1376 GdipFree (utf8);
1377 cairo_destroy (cr);
1378 cairo_surface_destroy (cs);
1379 return status;
1380 }
1381
1382 GpStatus WINGDIPAPI
GdipAddPathStringI(GpPath * path,GDIPCONST WCHAR * string,int length,GDIPCONST GpFontFamily * family,int style,float emSize,GDIPCONST GpRect * layoutRect,GDIPCONST GpStringFormat * format)1383 GdipAddPathStringI (GpPath *path, GDIPCONST WCHAR *string, int length,
1384 GDIPCONST GpFontFamily *family, int style, float emSize,
1385 GDIPCONST GpRect *layoutRect, GDIPCONST GpStringFormat *format)
1386 {
1387 GpRectF rect;
1388
1389 if (!layoutRect)
1390 return InvalidParameter;
1391
1392 gdip_RectF_from_Rect (layoutRect, &rect);
1393 return GdipAddPathString (path, string, length, family, style, emSize, &rect, format);
1394 }
1395
1396 GpStatus WINGDIPAPI
GdipAddPathLineI(GpPath * path,int x1,int y1,int x2,int y2)1397 GdipAddPathLineI (GpPath *path, int x1, int y1, int x2, int y2)
1398 {
1399 return GdipAddPathLine (path, x1, y1, x2, y2);
1400 }
1401
1402 GpStatus WINGDIPAPI
GdipAddPathLine2I(GpPath * path,const GpPoint * points,int count)1403 GdipAddPathLine2I (GpPath* path, const GpPoint *points, int count)
1404 {
1405 GpPoint *tmp;
1406 int i;
1407
1408 if (!path || !points || (count < 0))
1409 return InvalidParameter;
1410
1411 if (!gdip_path_ensure_size (path, path->count + count))
1412 return OutOfMemory;
1413
1414 /* only the first point can be compressed (i.e. removed if identical to previous) */
1415 for (i = 0, tmp = (GpPoint*) points; i < count; i++, tmp++)
1416 append (path, tmp->X, tmp->Y, PathPointTypeLine, (i == 0));
1417
1418 return Ok;
1419 }
1420
1421 GpStatus WINGDIPAPI
GdipAddPathArcI(GpPath * path,int x,int y,int width,int height,float startAngle,float sweepAngle)1422 GdipAddPathArcI (GpPath *path, int x, int y, int width, int height, float startAngle, float sweepAngle)
1423 {
1424 return GdipAddPathArc (path, x, y, width, height, startAngle, sweepAngle);
1425 }
1426
1427 GpStatus WINGDIPAPI
GdipAddPathBezierI(GpPath * path,int x1,int y1,int x2,int y2,int x3,int y3,int x4,int y4)1428 GdipAddPathBezierI (GpPath *path, int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4)
1429 {
1430 return GdipAddPathBezier (path, x1, y1, x2, y2, x3, y3, x4, y4);
1431 }
1432
1433 GpStatus WINGDIPAPI
GdipAddPathBeziersI(GpPath * path,const GpPoint * points,int count)1434 GdipAddPathBeziersI (GpPath *path, const GpPoint *points, int count)
1435 {
1436 GpPoint *tmp;
1437 int i;
1438
1439 if (!path || !points)
1440 return InvalidParameter;
1441
1442 /* first bezier requires 4 points, other 3 more points */
1443 if ((count < 4) || ((count % 3) != 1))
1444 return InvalidParameter;
1445
1446 if (!gdip_path_ensure_size (path, path->count + count))
1447 return OutOfMemory;
1448
1449 tmp = (GpPoint*) points;
1450 append (path, tmp->X, tmp->Y, PathPointTypeLine, TRUE);
1451 tmp++;
1452
1453 for (i = 1; i < count; i++, tmp++)
1454 append (path, tmp->X, tmp->Y, PathPointTypeBezier3, FALSE);
1455
1456 return Ok;
1457 }
1458
1459 GpStatus WINGDIPAPI
GdipAddPathCurveI(GpPath * path,const GpPoint * points,int count)1460 GdipAddPathCurveI (GpPath *path, const GpPoint *points, int count)
1461 {
1462 return GdipAddPathCurve2I (path, points, count, 0.5);
1463 }
1464
1465 GpStatus WINGDIPAPI
GdipAddPathCurve2I(GpPath * path,const GpPoint * points,int count,float tension)1466 GdipAddPathCurve2I (GpPath *path, const GpPoint *points, int count, float tension)
1467 {
1468 GpPointF *pt;
1469 GpStatus s;
1470
1471 if (!points)
1472 return InvalidParameter;
1473
1474 pt = convert_points (points, count);
1475 if (!pt)
1476 return OutOfMemory;
1477
1478 /* here we must deal/accept curve with 2 points, GdipAddPathCurve3I doesn't */
1479 s = GdipAddPathCurve2 (path, pt, count, tension);
1480
1481 GdipFree (pt);
1482 return s;
1483 }
1484
1485 GpStatus WINGDIPAPI
GdipAddPathCurve3I(GpPath * path,const GpPoint * points,int count,int offset,int numberOfSegments,float tension)1486 GdipAddPathCurve3I (GpPath *path, const GpPoint *points, int count, int offset, int numberOfSegments, float tension)
1487 {
1488 GpPointF *pt;
1489 GpStatus s;
1490
1491 if (!points)
1492 return InvalidParameter;
1493
1494 pt = convert_points (points, count);
1495 if (!pt)
1496 return OutOfMemory;
1497
1498 s = GdipAddPathCurve3 (path, pt, count, offset, numberOfSegments, tension);
1499
1500 GdipFree (pt);
1501 return s;
1502 }
1503
1504 GpStatus WINGDIPAPI
GdipAddPathClosedCurveI(GpPath * path,const GpPoint * points,int count)1505 GdipAddPathClosedCurveI (GpPath *path, const GpPoint *points, int count)
1506 {
1507 return GdipAddPathClosedCurve2I (path, points, count, 0.5);
1508 }
1509
1510 GpStatus WINGDIPAPI
GdipAddPathClosedCurve2I(GpPath * path,const GpPoint * points,int count,float tension)1511 GdipAddPathClosedCurve2I (GpPath *path, const GpPoint *points, int count, float tension)
1512 {
1513 GpPointF *pt;
1514 GpStatus s;
1515
1516 if (!path || !points)
1517 return InvalidParameter;
1518
1519 pt = convert_points (points, count);
1520 if (!pt)
1521 return OutOfMemory;
1522
1523 s = GdipAddPathClosedCurve2 (path, pt, count, tension);
1524
1525 GdipFree (pt);
1526
1527 return s;
1528 }
1529
1530 GpStatus WINGDIPAPI
GdipAddPathRectangleI(GpPath * path,int x,int y,int width,int height)1531 GdipAddPathRectangleI (GpPath *path, int x, int y, int width, int height)
1532 {
1533 return GdipAddPathRectangle (path, x, y, width, height);
1534 }
1535
1536 GpStatus WINGDIPAPI
GdipAddPathRectanglesI(GpPath * path,const GpRect * rects,int count)1537 GdipAddPathRectanglesI (GpPath *path, const GpRect *rects, int count)
1538 {
1539 int i;
1540
1541 if (!path || !rects)
1542 return InvalidParameter;
1543
1544 for (i = 0; i < count; i++) {
1545 float x = (float) rects[i].X;
1546 float y = (float) rects[i].Y;
1547 float width = (float) rects[i].Width;
1548 float height = (float) rects[i].Height;
1549 GdipAddPathRectangle (path, x, y, width, height);
1550 }
1551
1552 return Ok;
1553 }
1554
1555 GpStatus WINGDIPAPI
GdipAddPathEllipseI(GpPath * path,int x,int y,int width,int height)1556 GdipAddPathEllipseI (GpPath *path, int x, int y, int width, int height)
1557 {
1558 return GdipAddPathEllipse (path, x, y, width, height);
1559 }
1560
1561 GpStatus WINGDIPAPI
GdipAddPathPieI(GpPath * path,int x,int y,int width,int height,float startAngle,float sweepAngle)1562 GdipAddPathPieI (GpPath *path, int x, int y, int width, int height, float startAngle, float sweepAngle)
1563 {
1564 return GdipAddPathPie (path, x, y, width, height, startAngle, sweepAngle);
1565 }
1566
1567 GpStatus WINGDIPAPI
GdipAddPathPolygonI(GpPath * path,const GpPoint * points,int count)1568 GdipAddPathPolygonI (GpPath *path, const GpPoint *points, int count)
1569 {
1570 int i;
1571 GpPoint *tmp;
1572
1573 if (!path || !points || (count < 3))
1574 return InvalidParameter;
1575
1576 if (!gdip_path_ensure_size (path, path->count + count + 1))
1577 return OutOfMemory;
1578
1579 /* note: polygon points are never compressed (i.e. removed if identical) */
1580
1581 tmp = (GpPoint *) points;
1582 append (path, tmp->X, tmp->Y, PathPointTypeStart, FALSE);
1583 tmp++;
1584
1585 for (i = 1; i < count; i++, tmp++)
1586 append (path, tmp->X, tmp->Y, PathPointTypeLine, FALSE);
1587
1588 /*
1589 * Add a line from the last point back to the first point if
1590 * they're not the same
1591 */
1592 if (points [0].X != points [count - 1].X && points [0].Y != points [count - 1].Y)
1593 append (path, points [0].X, points [0].Y, PathPointTypeLine, FALSE);
1594
1595 /* close the path */
1596 return GdipClosePathFigure (path);
1597 }
1598
1599 /* nr_curve_flatten comes from Sodipodi's libnr (public domain) available from http://www.sodipodi.com/ */
1600 /* Mono changes: converted to float (from double), added recursion limit, use GArray */
1601 static BOOL
nr_curve_flatten(float x0,float y0,float x1,float y1,float x2,float y2,float x3,float y3,float flatness,int level,GpPath * flat_path)1602 nr_curve_flatten (float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3, float flatness, int level, GpPath *flat_path)
1603 {
1604 float dx1_0, dy1_0, dx2_0, dy2_0, dx3_0, dy3_0, dx2_3, dy2_3, d3_0_2;
1605 float s1_q, t1_q, s2_q, t2_q, v2_q;
1606 float f2, f2_q;
1607 float x00t, y00t, x0tt, y0tt, xttt, yttt, x1tt, y1tt, x11t, y11t;
1608
1609 dx1_0 = x1 - x0;
1610 dy1_0 = y1 - y0;
1611 dx2_0 = x2 - x0;
1612 dy2_0 = y2 - y0;
1613 dx3_0 = x3 - x0;
1614 dy3_0 = y3 - y0;
1615 dx2_3 = x3 - x2;
1616 dy2_3 = y3 - y2;
1617 f2 = flatness;
1618 d3_0_2 = dx3_0 * dx3_0 + dy3_0 * dy3_0;
1619 if (d3_0_2 < f2) {
1620 float d1_0_2, d2_0_2;
1621 d1_0_2 = dx1_0 * dx1_0 + dy1_0 * dy1_0;
1622 d2_0_2 = dx2_0 * dx2_0 + dy2_0 * dy2_0;
1623 if ((d1_0_2 < f2) && (d2_0_2 < f2)) {
1624 goto nosubdivide;
1625 } else {
1626 goto subdivide;
1627 }
1628 }
1629 f2_q = f2 * d3_0_2;
1630 s1_q = dx1_0 * dx3_0 + dy1_0 * dy3_0;
1631 t1_q = dy1_0 * dx3_0 - dx1_0 * dy3_0;
1632 s2_q = dx2_0 * dx3_0 + dy2_0 * dy3_0;
1633 t2_q = dy2_0 * dx3_0 - dx2_0 * dy3_0;
1634 v2_q = dx2_3 * dx3_0 + dy2_3 * dy3_0;
1635 if ((t1_q * t1_q) > f2_q) goto subdivide;
1636 if ((t2_q * t2_q) > f2_q) goto subdivide;
1637 if ((s1_q < 0.0) && ((s1_q * s1_q) > f2_q)) goto subdivide;
1638 if ((v2_q < 0.0) && ((v2_q * v2_q) > f2_q)) goto subdivide;
1639 if (s1_q >= s2_q) goto subdivide;
1640
1641 nosubdivide:
1642 {
1643 append (flat_path, x3, y3, PathPointTypeLine, FALSE);
1644 return TRUE;
1645 }
1646 subdivide:
1647 /* things gets *VERY* memory intensive without a limit */
1648 if (level >= FLATTEN_RECURSION_LIMIT)
1649 return FALSE;
1650
1651 x00t = (x0 + x1) * 0.5;
1652 y00t = (y0 + y1) * 0.5;
1653 x0tt = (x0 + 2 * x1 + x2) * 0.25;
1654 y0tt = (y0 + 2 * y1 + y2) * 0.25;
1655 x1tt = (x1 + 2 * x2 + x3) * 0.25;
1656 y1tt = (y1 + 2 * y2 + y3) * 0.25;
1657 x11t = (x2 + x3) * 0.5;
1658 y11t = (y2 + y3) * 0.5;
1659 xttt = (x0tt + x1tt) * 0.5;
1660 yttt = (y0tt + y1tt) * 0.5;
1661
1662 if (!nr_curve_flatten (x0, y0, x00t, y00t, x0tt, y0tt, xttt, yttt, flatness, level+1, flat_path)) return FALSE;
1663 if (!nr_curve_flatten (xttt, yttt, x1tt, y1tt, x11t, y11t, x3, y3, flatness, level+1, flat_path)) return FALSE;
1664 return TRUE;
1665 }
1666
1667 static BOOL
gdip_convert_bezier_to_lines(GpPath * path,int index,float flatness,GpPath * flat_path)1668 gdip_convert_bezier_to_lines (GpPath *path, int index, float flatness, GpPath *flat_path)
1669 {
1670 GpPointF start, first, second, end;
1671 int saved_count;
1672
1673 if ((index <= 0) || (index + 2 >= path->count))
1674 return FALSE; /* bad path data */
1675
1676 start = path->points[index - 1];
1677 first = path->points[index];
1678 second = path->points[index + 1];
1679 end = path->points[index + 2];
1680
1681 if (!gdip_path_ensure_size (path, path->count + (1 << FLATTEN_RECURSION_LIMIT)))
1682 return FALSE;
1683
1684 saved_count = flat_path->count;
1685 if (!nr_curve_flatten (start.X, start.Y, first.X, first.Y, second.X, second.Y, end.X, end.Y, flatness, 0, flat_path)) {
1686 /* curved path is too complex (i.e. would result in too many points) to render as a polygon */
1687 flat_path->count = saved_count;
1688 return FALSE;
1689 }
1690
1691 return TRUE;
1692 }
1693
1694 GpStatus WINGDIPAPI
GdipFlattenPath(GpPath * path,GpMatrix * matrix,float flatness)1695 GdipFlattenPath (GpPath *path, GpMatrix *matrix, float flatness)
1696 {
1697 GpStatus status = Ok;
1698 GpPath *flat_path;
1699 int i;
1700
1701 if (!path)
1702 return InvalidParameter;
1703
1704 /* apply matrix before flattening (as there's less points at this stage) */
1705 if (matrix) {
1706 status = GdipTransformPath (path, matrix);
1707 if (status != Ok)
1708 return status;
1709 }
1710
1711 /* if no bezier are present then the path doesn't need to be flattened */
1712 if (!gdip_path_has_curve (path))
1713 return status;
1714
1715 status = GdipCreatePath (path->fill_mode, &flat_path);
1716 if (status != Ok)
1717 return status;
1718
1719 /* Iterate the current path and replace each bezier with multiple lines */
1720 for (i = 0; i < path->count; i++) {
1721 GpPointF point = path->points[i];
1722 BYTE type = path->types[i];
1723
1724 /* PathPointTypeBezier3 has the same value as PathPointTypeBezier */
1725 if ((type & PathPointTypeBezier) == PathPointTypeBezier) {
1726 if (!gdip_convert_bezier_to_lines (path, i, fabs (flatness), flat_path)) {
1727 /* uho, too much recursion - do not pass go, do not collect 200$ */
1728
1729 /* free the the partial flat */
1730 GdipResetPath (flat_path);
1731
1732 if (!gdip_path_ensure_size (flat_path, flat_path->count + 4)) {
1733 GdipDeletePath (flat_path);
1734 return OutOfMemory;
1735 }
1736
1737 /* mimic MS behaviour when recursion becomes a problem */
1738 /* note: it's not really an empty rectangle as the last point isn't closing */
1739
1740 append (flat_path, 0, 0, PathPointTypeStart, FALSE);
1741 append (flat_path, 0, 0, PathPointTypeLine, FALSE);
1742 append (flat_path, 0, 0, PathPointTypeLine, FALSE);
1743 append (flat_path, 0, 0, PathPointTypeLine, FALSE);
1744 break;
1745 }
1746 /* beziers have 4 points: the previous one, the current and the next two */
1747 i += 2;
1748 } else {
1749 if (!gdip_path_ensure_size (flat_path, flat_path->count + 1)) {
1750 GdipDeletePath (flat_path);
1751 return OutOfMemory;
1752 }
1753
1754 /* no change required, just copy the point */
1755 append_point(flat_path, point, type, FALSE);
1756 }
1757 }
1758
1759 /* free original path points and types */
1760 if (path->points != NULL)
1761 GdipFree (path->points);
1762 if (path->types != NULL)
1763 GdipFree (path->types);
1764
1765 /* transfer new path informations */
1766 path->points = flat_path->points;
1767 path->types = flat_path->types;
1768 path->count = flat_path->count;
1769 path->size = flat_path->size;
1770 GdipFree (flat_path);
1771
1772 /* note: no error code is given for excessive recursion */
1773 return Ok;
1774 }
1775
1776 static GpStatus
gdip_prepare_path(GpPath * path,GpMatrix * matrix,float flatness)1777 gdip_prepare_path (GpPath *path, GpMatrix *matrix, float flatness)
1778 {
1779 /* convert any curve into lines */
1780 if (gdip_path_has_curve (path)) {
1781 /* this will apply the matrix too (before flattening) */
1782 return GdipFlattenPath (path, matrix, flatness);
1783 } else if (!gdip_is_matrix_empty (matrix)) {
1784 /* no curve, but we still have a matrix to apply... */
1785 return GdipTransformPath (path, matrix);
1786 }
1787
1788 /* no preparation required */
1789 return Ok;
1790 }
1791
1792 /* MonoTODO - doesn't seems to be exposed in System.Drawing.dll */
1793 GpStatus WINGDIPAPI
GdipWindingModeOutline(GpPath * path,GpMatrix * matrix,float flatness)1794 GdipWindingModeOutline (GpPath *path, GpMatrix *matrix, float flatness)
1795 {
1796 GpStatus status;
1797
1798 if (!path)
1799 return InvalidParameter;
1800
1801 /* quick out */
1802 if (path->count == 0)
1803 return Ok;
1804
1805 status = gdip_prepare_path (path, matrix, flatness);
1806 if (status != Ok)
1807 return status;
1808
1809 /* TODO */
1810
1811 return NotImplemented;
1812 }
1813
1814 /* MonoTODO */
1815 GpStatus WINGDIPAPI
GdipWidenPath(GpPath * nativePath,GpPen * pen,GpMatrix * matrix,float flatness)1816 GdipWidenPath (GpPath *nativePath, GpPen *pen, GpMatrix *matrix, float flatness)
1817 {
1818 static int called = 0;
1819 GpStatus status;
1820
1821 if (!nativePath || !pen)
1822 return InvalidParameter;
1823
1824 /* (0) is deal within System.Drawing */
1825 /* (1) for compatibility with MS GDI+ (reported as FDBK49685) */
1826 if (nativePath->count <= 1)
1827 return OutOfMemory;
1828
1829 status = gdip_prepare_path (nativePath, matrix, flatness);
1830 if (status != Ok)
1831 return status;
1832
1833 /* TODO inner path (same number of points as the prepared path) */
1834
1835 /* TODO outer path (twice the number of points as the prepared path) */
1836
1837 if (!called) {
1838 g_warning ("NOT IMPLEMENTED: GdipWidenPath");
1839 called = 1;
1840 }
1841 return Ok;
1842 }
1843
1844 /* MonoTODO */
1845 GpStatus WINGDIPAPI
GdipWarpPath(GpPath * path,GpMatrix * matrix,const GpPointF * points,int count,float srcx,float srcy,float srcwidth,float srcheight,WarpMode warpMode,float flatness)1846 GdipWarpPath (GpPath *path, GpMatrix *matrix, const GpPointF *points, int count,
1847 float srcx, float srcy, float srcwidth, float srcheight,
1848 WarpMode warpMode, float flatness)
1849 {
1850 static int called = 0;
1851 GpStatus status;
1852
1853 if (!path || !points || (count < 1))
1854 return InvalidParameter;
1855
1856 /* quick out */
1857 if (path->count == 0)
1858 return Ok;
1859
1860 /* an invalid warp mode resets the current path */
1861 /* a path with a single point will reset it too */
1862 if (((warpMode != WarpModePerspective) && (warpMode != WarpModeBilinear)) || (path->count == 1))
1863 return GdipResetPath (path);
1864
1865 status = gdip_prepare_path (path, matrix, flatness);
1866 if (status != Ok)
1867 return status;
1868
1869 /* TODO */
1870
1871 if (!called) {
1872 g_warning ("NOT IMPLEMENTED: GdipWarpPath");
1873 called = 1;
1874 }
1875 return Ok;
1876 }
1877
1878 GpStatus WINGDIPAPI
GdipTransformPath(GpPath * path,GpMatrix * matrix)1879 GdipTransformPath (GpPath* path, GpMatrix *matrix)
1880 {
1881 if (!path)
1882 return InvalidParameter;
1883
1884 if (path->count == 0)
1885 return Ok; /* GdipTransformMatrixPoints would fail */
1886
1887 /* avoid allocation/free/calculation for null/identity matrix */
1888 if (gdip_is_matrix_empty (matrix))
1889 return Ok;
1890
1891 return GdipTransformMatrixPoints (matrix, path->points, path->count);
1892 }
1893
1894 GpStatus WINGDIPAPI
GdipGetPathWorldBounds(GpPath * path,GpRectF * bounds,const GpMatrix * matrix,const GpPen * pen)1895 GdipGetPathWorldBounds (GpPath *path, GpRectF *bounds, const GpMatrix *matrix, const GpPen *pen)
1896 {
1897 GpStatus status;
1898 GpPath *workpath;
1899 GpPointF points;
1900
1901 if (!path || !bounds)
1902 return InvalidParameter;
1903
1904 // Paths with zero or one points are empty.
1905 if (path->count <= 1) {
1906 bounds->X = 0.0f;
1907 bounds->Y = 0.0f;
1908 bounds->Width = 0.0f;
1909 bounds->Height = 0.0f;
1910 return Ok;
1911 }
1912
1913 status = GdipClonePath (path, &workpath);
1914 if (status != Ok)
1915 return status;
1916
1917 /* We don't need a very precise flat value to get the bounds (GDI+ isn't, big time) -
1918 * however flattening helps by removing curves, making the rest of the algorithm a
1919 * lot simpler.
1920 */
1921
1922 /* note: only the matrix is applied if no curves are present in the path */
1923 status = GdipFlattenPath (workpath, (GpMatrix*)matrix, 25.0f);
1924 if (status != Ok) {
1925 GdipDeletePath (workpath);
1926 return status;
1927 }
1928
1929 points = workpath->points[0];
1930 bounds->X = points.X; /* keep minimum X here */
1931 bounds->Y = points.Y; /* keep minimum Y here */
1932 if (workpath->count == 1) {
1933 /* special case #2 - Only one element */
1934 bounds->Width = 0.0f;
1935 bounds->Height = 0.0f;
1936 GdipDeletePath (workpath);
1937 return Ok;
1938 }
1939
1940 bounds->Width = points.X; /* keep maximum X here */
1941 bounds->Height = points.Y; /* keep maximum Y here */
1942
1943 for (int i = 1; i < workpath->count; i++) {
1944 points = workpath->points[i];
1945 if (points.X < bounds->X)
1946 bounds->X = points.X;
1947 if (points.Y < bounds->Y)
1948 bounds->Y = points.Y;
1949 if (points.X > bounds->Width)
1950 bounds->Width = points.X;
1951 if (points.Y > bounds->Height)
1952 bounds->Height = points.Y;
1953 }
1954
1955 /* convert maximum values (width/height) as length */
1956 bounds->Width -= bounds->X;
1957 bounds->Height -= bounds->Y;
1958
1959 if (pen) {
1960 /* in calculation the pen's width is at least 1.0 */
1961 float width = (pen->width < 1.0f) ? 1.0f : pen->width;
1962 float halfw = (width / 2);
1963
1964 bounds->X -= halfw;
1965 bounds->Y -= halfw;
1966 bounds->Width += width;
1967 bounds->Height += width;
1968 }
1969
1970 GdipDeletePath (workpath);
1971 return Ok;
1972 }
1973
1974 GpStatus WINGDIPAPI
GdipGetPathWorldBoundsI(GpPath * path,GpRect * bounds,const GpMatrix * matrix,const GpPen * pen)1975 GdipGetPathWorldBoundsI (GpPath *path, GpRect *bounds, const GpMatrix *matrix, const GpPen *pen)
1976 {
1977 GpRectF rect;
1978 GpStatus status;
1979
1980 if (!path || !bounds)
1981 return InvalidParameter;
1982
1983 status = GdipGetPathWorldBounds (path, &rect, matrix, pen);
1984 if (status != Ok)
1985 return status;
1986
1987 bounds->X = (int) rect.X;
1988 bounds->Y = (int) rect.Y;
1989 bounds->Width = (int) rect.Width;
1990 bounds->Height = (int) rect.Height;
1991 return Ok;
1992 }
1993
1994 GpStatus WINGDIPAPI
GdipIsVisiblePathPoint(GpPath * path,float x,float y,GpGraphics * graphics,BOOL * result)1995 GdipIsVisiblePathPoint (GpPath *path, float x, float y, GpGraphics *graphics, BOOL *result)
1996 {
1997 GpStatus status = Ok;
1998 cairo_surface_t* s = NULL;
1999 GpGraphics *g;
2000 GpUnit page_unit = UnitPixel;
2001
2002 if (!path || !result)
2003 return InvalidParameter;
2004
2005 if (graphics) {
2006 g = graphics;
2007 cairo_save (g->ct);
2008 page_unit = g->page_unit;
2009 } else {
2010 /* create a temporary context */
2011 s = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 1, 1);
2012 g = gdip_graphics_new (s);
2013 }
2014
2015 cairo_new_path (g->ct);
2016 /* unit tests shows that PageUnit isn't consireded (well x, y are probably considered to be the same unit format ) */
2017 g->page_unit = UnitPixel;
2018 status = gdip_plot_path (g, path, FALSE);
2019 if (status == Ok) {
2020 cairo_set_fill_rule (g->ct, gdip_convert_fill_mode (path->fill_mode));
2021 cairo_set_antialias (g->ct, CAIRO_ANTIALIAS_NONE);
2022 *result = cairo_in_fill (g->ct, x + 1.0 /* CAIRO_AA_OFFSET_X */, y + CAIRO_AA_OFFSET_Y);
2023 } else {
2024 *result = FALSE;
2025 }
2026
2027 if (graphics) {
2028 /* restore GpGraphics to original state */
2029 cairo_restore (graphics->ct);
2030 g->page_unit = page_unit;
2031 } else {
2032 /* delete temporary context */
2033 cairo_surface_destroy (s);
2034 GdipDeleteGraphics (g);
2035 }
2036
2037 return status;
2038 }
2039
2040 GpStatus WINGDIPAPI
GdipIsVisiblePathPointI(GpPath * path,int x,int y,GpGraphics * graphics,BOOL * result)2041 GdipIsVisiblePathPointI (GpPath *path, int x, int y, GpGraphics *graphics, BOOL *result)
2042 {
2043 return GdipIsVisiblePathPoint (path, x, y, graphics, result);
2044 }
2045
2046 GpStatus WINGDIPAPI
GdipIsOutlineVisiblePathPoint(GpPath * path,float x,float y,GpPen * pen,GpGraphics * graphics,BOOL * result)2047 GdipIsOutlineVisiblePathPoint (GpPath *path, float x, float y, GpPen *pen, GpGraphics *graphics, BOOL *result)
2048 {
2049 GpStatus status = Ok;
2050 cairo_surface_t* s = NULL;
2051 GpGraphics *g;
2052 GpUnit page_unit = UnitPixel;
2053
2054 if (!path || !pen || !result)
2055 return InvalidParameter;
2056
2057 if (graphics) {
2058 g = graphics;
2059 cairo_save (graphics->ct);
2060 page_unit = g->page_unit;
2061 } else {
2062 /* create a temporary context */
2063 s = cairo_image_surface_create (CAIRO_FORMAT_A1, 1, 1);
2064 g = gdip_graphics_new (s);
2065 }
2066
2067 cairo_new_path (g->ct);
2068 /* unit tests shows that PageUnit isn't consireded (well x, y are probably considered to be the same unit format ) */
2069 g->page_unit = UnitPixel;
2070 status = gdip_plot_path (g, path, FALSE);
2071 if (status == Ok) {
2072 /* we must fight around cairo AA */
2073 cairo_set_antialias (g->ct, CAIRO_ANTIALIAS_NONE);
2074 cairo_set_line_width (g->ct, pen->width - CAIRO_AA_OFFSET_Y);
2075 *result = cairo_in_stroke (g->ct, x, y);
2076 } else {
2077 *result = FALSE;
2078 }
2079
2080 if (graphics) {
2081 /* restore GpGraphics to original state */
2082 cairo_restore (graphics->ct);
2083 g->page_unit = page_unit;
2084 } else {
2085 /* delete temporary context */
2086 cairo_surface_destroy (s);
2087 GdipDeleteGraphics (g);
2088 }
2089
2090 return status;
2091 }
2092
2093 GpStatus WINGDIPAPI
GdipIsOutlineVisiblePathPointI(GpPath * path,int x,int y,GpPen * pen,GpGraphics * graphics,BOOL * result)2094 GdipIsOutlineVisiblePathPointI (GpPath *path, int x, int y, GpPen *pen, GpGraphics *graphics, BOOL *result)
2095 {
2096 return GdipIsOutlineVisiblePathPoint (path, x, y, pen, graphics, result);
2097 }
2098