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