1 /*
2  * Copyright (c) 2004-2005 Ximian
3  * Copyright (C) 2006-2007 Novell, Inc (http://www.novell.com)
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
6  * and associated documentation files (the "Software"), to deal in the Software without restriction,
7  * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
9  * subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in all copies or substantial
12  * portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
15  * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
16  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
17  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
18  * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19  *
20  * Authors:
21  *          Jordi Mas i Hernandez <jordi@ximian.com>, 2004-2005
22  *          Sebastien Pouliot  <sebastien@ximian.com>
23  *
24  */
25 
26 #include "region-private.h"
27 #include "general-private.h"
28 #include "graphics-path-private.h"
29 
30 /*
31 	Helper functions
32 */
33 
34 void
gdip_region_init(GpRegion * result)35 gdip_region_init (GpRegion *result)
36 {
37 	result->type = RegionTypeInfinite;
38 	result->cnt = 0;
39 	result->rects = NULL;
40 	result->tree = NULL;
41 	result->bitmap = NULL;
42 }
43 
44 GpRegion *
gdip_region_new()45 gdip_region_new ()
46 {
47 	GpRegion *result;
48 
49 	result = (GpRegion *) GdipAlloc (sizeof (GpRegion));
50 	if (result)
51 		gdip_region_init (result);
52 
53 	return result;
54 }
55 
56 static int
gdip_compare_rectf(const void * a,const void * b)57 gdip_compare_rectf (const void *a, const void *b) {
58 	const GpRectF *r1 = (GpRectF*)a;
59 	const GpRectF *r2 = (GpRectF*)b;
60 	if (r1->Y == r2->Y && r1->X == r2->X)
61 		return 0;
62 	if (r1->Y > r2->Y || (r1->Y == r2->Y && r1->X > r2->X))
63 		return 1;
64 	return -1;
65 }
66 
67 static void
gdip_sort_rect_array(GpRectF * array,int length)68 gdip_sort_rect_array (GpRectF* array, int length) {
69 	qsort (array, length, sizeof (GpRectF), gdip_compare_rectf);
70 }
71 
72 // Not a mistake in the name, it is for re-sorting nearly-sorted data.
73 // Insertion sort.
74 static void
gdip_sort_rect_array_sorted(GpRectF * array,int length)75 gdip_sort_rect_array_sorted (GpRectF* array, int length) {
76 	GpRectF rect;
77 	GpRectF *i, *j;
78 
79 	for (i = array + 1; i < array + length; i++) {
80 		rect = *i;
81 		for (j = i - 1; j >= array && gdip_compare_rectf (j, &rect) > 0; j--) {
82 			*(j + 1) = *j;
83 		}
84 		*(j + 1) = rect;
85 	}
86 }
87 
88 static GpStatus
gdip_extend_rect_array(GpRectF ** srcarray,int * elements,int * capacity)89 gdip_extend_rect_array (GpRectF** srcarray, int* elements, int* capacity) {
90 	GpRectF *array;
91 	int newCapacity = -1;
92 
93 	if (capacity) {
94 		if (*srcarray == NULL) {
95 			if (*capacity < 1)
96 				*capacity = 5; // starting capacity if we're given a size of zero
97 			newCapacity = *capacity;
98 		} else if (*elements == *capacity) {
99 			newCapacity = *elements * 2;
100 		}
101 	} else {
102 		newCapacity = *elements + 1;
103 	}
104 
105 	if (newCapacity > 0) {
106 		array = GdipAlloc (sizeof (GpRectF) * newCapacity);
107 		if (!array)
108 			return OutOfMemory;
109 
110 		memcpy (array, *srcarray, sizeof (GpRectF) * (*elements));
111 
112 		if (*srcarray)
113 			GdipFree (*srcarray);
114 
115 		*srcarray = array;
116 		if (capacity)
117 			*capacity = newCapacity;
118 	}
119 	return Ok;
120 }
121 
122 static GpStatus
gdip_trim_rect_array(GpRectF ** srcarray,int elements)123 gdip_trim_rect_array (GpRectF** srcarray, int elements) {
124 	GpRectF *array;
125 
126 	array = GdipAlloc (sizeof (GpRectF) * elements);
127 	if (!array)
128 		return OutOfMemory;
129 
130 	memcpy (array, *srcarray, sizeof (GpRectF) * elements);
131 
132 	if (*srcarray)
133 		GdipFree (*srcarray);
134 
135 	*srcarray = array;
136 	return Ok;
137 }
138 
139 static GpStatus
gdip_add_rect_to_array(GpRectF ** srcarray,int * elements,int * capacity,const GpRectF * rect)140 gdip_add_rect_to_array (GpRectF** srcarray, int* elements, int* capacity, const GpRectF* rect)
141 {
142 	GpRectF *next;
143 	GpStatus status;
144 
145 	status = gdip_extend_rect_array (srcarray, elements, capacity);
146 	if (status != Ok)
147 		return status;
148 
149 	next = *srcarray;
150 	next += (*elements);
151 	memcpy (next, rect, sizeof (GpRectF));
152 
153 	*elements = *elements + 1;
154 
155 	return Ok;
156 }
157 
158 static GpRectF*
gdip_binsearch_rect_array(GpRectF * array,int elements,const GpRectF * search,int * index)159 gdip_binsearch_rect_array (GpRectF* array, int elements, const GpRectF* search, int* index)
160 {
161 	GpRectF *next;
162 	int upper = elements, lower = 0, mid;
163 
164 	while (upper > lower) {
165 		mid = (upper + lower) / 2;
166 		next = array + mid;
167 		if (gdip_compare_rectf (search, next) > 0) {
168 			lower = mid + 1;
169 		} else {
170 			upper = mid;
171 		}
172 	}
173 	next = array + lower;
174 	if (index)
175 		*index = lower;
176 	return next;
177 }
178 
179 static GpStatus
gdip_add_rect_to_array_sorted(GpRectF ** srcarray,int * elements,int * capacity,const GpRectF * rect)180 gdip_add_rect_to_array_sorted (GpRectF** srcarray, int* elements, int* capacity, const GpRectF* rect)
181 {
182 	GpRectF *next;
183 	GpStatus status;
184 	int insertAt;
185 
186 	status = gdip_extend_rect_array (srcarray, elements, capacity);
187 	if (status != Ok)
188 		return status;
189 
190 	next = gdip_binsearch_rect_array (*srcarray, *elements, rect, &insertAt);
191 	memmove (next + 1, next, sizeof (GpRectF) * (*elements - insertAt));
192 	memcpy (next, rect, sizeof (GpRectF));
193 
194 	*elements = *elements + 1;
195 
196 	return Ok;
197 }
198 
199 static BOOL
gdip_is_Point_in_RectF_Visible(float x,float y,GpRectF * rect)200 gdip_is_Point_in_RectF_Visible (float x, float y, GpRectF* rect)
201 {
202 	if ((x >= rect->X && x < (rect->X + rect->Width))
203 		&& (y >= rect->Y && y < (rect->Y + rect->Height)))
204 		return TRUE;
205 	else
206 		return FALSE;
207 }
208 
209 static BOOL
gdip_is_Point_in_RectFs_Visible(float x,float y,GpRectF * r,int cnt)210 gdip_is_Point_in_RectFs_Visible (float x, float y, GpRectF* r, int cnt)
211 {
212 	GpRectF* rect = r;
213 	int i;
214 
215 	for (i = 0; i < cnt; i++, rect++) {
216 		if (gdip_is_Point_in_RectF_Visible (x, y, rect)) {
217 			return TRUE;
218 		}
219 	}
220 
221 	return FALSE;
222 }
223 
224 static BOOL
gdip_is_Rect_in_RectF_Visible(float x,float y,float width,float height,GpRectF * rect)225 gdip_is_Rect_in_RectF_Visible (float x, float y, float width, float height, GpRectF* rect)
226 {
227 	if (rect->Width == 0 || rect->Height == 0)
228 		return FALSE;
229 
230 	return x < rect->X + rect->Width && x + width > rect->X && y < rect->Y + rect->Height && y + height > rect->Y;
231 }
232 
233 static BOOL
gdip_is_Rect_in_RectFs_Visible(float x,float y,float width,float height,GpRectF * r,int cnt)234 gdip_is_Rect_in_RectFs_Visible (float x, float y, float width, float height, GpRectF* r, int cnt)
235 {
236 	GpRectF* rect = r;
237 	int i;
238 
239 	for (i = 0; i < cnt; i++, rect++) {
240 		if (gdip_is_Rect_in_RectF_Visible (x, y, width, height, rect))
241 			return TRUE;
242 	}
243 
244 	return FALSE;
245 }
246 
247 static void
gdip_get_bounds(GpRectF * allrects,int allcnt,GpRectF * bound)248 gdip_get_bounds (GpRectF *allrects, int allcnt, GpRectF *bound)
249 {
250 	float nx, ny, fx, fy;
251 	int i;
252 	GpRectF *rect;
253 
254 	if (allrects == NULL || allcnt == 0) {
255 		bound->X = bound->Y = bound->Width =  bound->Height = 0;
256 		return;
257 	}
258 
259 	/* Build a rect that contains all the rects inside. Smallest x,y and biggest x,y*/
260 	nx = allrects->X; ny = allrects->Y;
261 	fx = allrects->X + allrects->Width; fy = allrects->Y + allrects->Height;
262 
263 	for (i = 0, rect = allrects; i < allcnt; i++, rect++) {
264 
265 		if (rect->X < nx)
266 			nx = rect->X;
267 
268 		if (rect->Y < ny)
269 			ny = rect->Y;
270 
271 		if (rect->X + rect->Width  > fx)
272 			fx = rect->X + rect->Width;
273 
274 		if (rect->Y + rect->Height > fy)
275 			fy = rect->Y + rect->Height;
276 	}
277 
278 	bound->X = nx; bound->Y = ny;
279 	bound->Width = fx - nx; bound->Height = fy - ny;
280 }
281 
282 static BOOL
gdip_is_region_empty(const GpRegion * region,BOOL allowNegative)283 gdip_is_region_empty (const GpRegion *region, BOOL allowNegative)
284 {
285 	GpRectF rect;
286 
287 	if (!region)
288 		return FALSE;
289 
290 	switch (region->type) {
291 	case RegionTypeRect:
292 		if (!region->rects || (region->cnt == 0))
293 			return TRUE;
294 
295 		gdip_get_bounds (region->rects, region->cnt, &rect);
296 		return gdip_is_rectF_empty (&rect, allowNegative);
297 	case RegionTypeInfinite:
298 		return FALSE;
299 	case RegionTypePath:
300 		if (!region->tree)
301 			return TRUE;
302 		if (region->tree->path) {
303 			if (region->tree->path->count == 0)
304 				return TRUE;
305 
306 			// Open paths are empty.
307 			if (!gdip_path_closed (region->tree->path))
308 				return TRUE;
309 		}
310 		if (region->bitmap && (region->bitmap->Width == 0 || region->bitmap->Height == 0))
311 			return TRUE;
312 
313 		return FALSE;
314 	default:
315 		g_warning ("unknown type 0x%08X", region->type);
316 		return FALSE;
317 	}
318 }
319 
320 static BOOL
gdip_is_rect_infinite(const GpRectF * rect)321 gdip_is_rect_infinite (const GpRectF *rect)
322 {
323 	if (!rect)
324 		return FALSE;
325 
326 	if (gdip_is_rectF_empty (rect, /* allowNegative */ TRUE))
327 		return FALSE;
328 
329 	if (rect->Width >= REGION_INFINITE_LENGTH || rect->Height >= REGION_INFINITE_LENGTH)
330 		return TRUE;
331 
332 	return FALSE;
333 }
334 
335 BOOL
gdip_is_InfiniteRegion(const GpRegion * region)336 gdip_is_InfiniteRegion (const GpRegion *region)
337 {
338 	switch (region->type) {
339 	case RegionTypeRect:
340 		if (region->cnt != 1)
341 		      return FALSE;
342 		return gdip_is_rect_infinite (region->rects);
343 	case RegionTypePath:
344 		/* FIXME: incomplete and not 100% accurate (curves) - but cover the most common case */
345 		if (!region->tree || !region->tree->path)
346 			return FALSE;
347 
348 		if (gdip_path_closed (region->tree->path) && region->tree->path->count == 4) {
349 			GpRectF bounds;
350 			if (GdipGetPathWorldBounds (region->tree->path, &bounds, NULL, NULL) == Ok)
351 				return gdip_is_rect_infinite (&bounds);
352 		}
353 		break;
354 	case RegionTypeInfinite:
355 		return TRUE;
356 	default:
357 		g_warning ("unknown type 0x%08X", region->type);
358 		break;
359 	}
360 	return FALSE;
361 }
362 
363 static BOOL
gdip_intersects(const GpRectF * rect1,const GpRectF * rect2)364 gdip_intersects (const GpRectF *rect1, const GpRectF *rect2)
365 {
366 	return (rect1->X < rect2->X + rect2->Width &&
367 		rect1->X + rect1->Width > rect2->X &&
368 		rect1->Y < rect2->Y + rect2->Height &&
369 		rect1->Y + rect1->Height > rect2->Y);
370 }
371 
372 static BOOL
gdip_intersects_or_touches(GpRectF * rect1,GpRectF * rect2)373 gdip_intersects_or_touches (GpRectF *rect1, GpRectF *rect2)
374 {
375 	return (rect1->X <= rect2->X + rect2->Width &&
376 		rect1->X + rect1->Width >= rect2->X &&
377 		rect1->Y <= rect2->Y + rect2->Height &&
378 		rect1->Y + rect1->Height >= rect2->Y);
379 }
380 
381 /* Is source contained in target ? */
382 static BOOL
gdip_contains(GpRectF * rect1,GpRectF * rect2)383 gdip_contains (GpRectF *rect1, GpRectF *rect2)
384 {
385 	return (rect1->X >= rect2->X &&
386 		rect1->X + rect1->Width <= rect2->X + rect2->Width &&
387 		rect1->Y >= rect2->Y &&
388 		rect1->Y + rect1->Height <= rect2->Y + rect2->Height);
389 }
390 
391 static BOOL
gdip_add_rect_to_array_notcontained(GpRectF ** srcarray,int * elements,int * capacity,GpRectF * rect)392 gdip_add_rect_to_array_notcontained (GpRectF** srcarray, int* elements, int* capacity,  GpRectF* rect)
393 {
394 	int i;
395 	GpRectF* rectarray = *srcarray;
396 
397 	if (rect->Height <= 0 || rect->Width <= 0)
398 		return FALSE;
399 
400 	for (i = 0; i < *elements; i++, rectarray++) {
401 		if (gdip_contains (rect, rectarray) == TRUE) {
402 			return FALSE;
403 		}
404 	}
405 
406 	gdip_add_rect_to_array (srcarray, elements, capacity, rect);
407 	return TRUE;
408 }
409 
410 
411 static BOOL
gdip_equals(GpRectF * rect1,GpRectF * rect2)412 gdip_equals (GpRectF *rect1, GpRectF *rect2)
413 {
414 	if (!rect1)
415 		return (rect2 == NULL);
416 
417 	return (rect1->X == rect2->X &&
418 		rect1->Width == rect2->Width &&
419 		rect1->Y == rect2->Y &&
420 		rect1->Height == rect2->Height);
421 }
422 
423 BOOL
gdip_is_Point_in_RectF_inclusive(float x,float y,GpRectF * rect)424 gdip_is_Point_in_RectF_inclusive (float x, float y, GpRectF* rect)
425 {
426 	if ((x >= rect->X && x <= (rect->X + rect->Width))
427 		&& (y >= rect->Y && y <= (rect->Y + rect->Height)))
428 		return TRUE;
429 	else
430 		return FALSE;
431 }
432 
433 /* Finds a rect that has the lowest x and y after the src rect provided */
434 static BOOL
gdip_getlowestrect(GpRectF * rects,int cnt,GpRectF * src,GpRectF * rslt)435 gdip_getlowestrect (GpRectF *rects, int cnt, GpRectF* src, GpRectF* rslt)
436 {
437 	int i;
438 	GpRectF *current;
439 	GpRectF *lowest = NULL;
440 
441 	for (i = 0, current = rects; i < cnt; i++, current++) {
442 		if (current->Width <= 0 || current->Height <= 0)
443 			continue;
444 
445 		if (current->Y > src->Y ||
446 			(current->Y == src->Y && current->X > src->X)) {
447 			if (lowest == NULL) {
448 				lowest = current;
449 			}
450 			else {
451 				if (current->Y < lowest->Y ||
452 					(current->Y == lowest->Y && current->X < lowest->X)) {
453 						lowest = current;
454 				}
455 			}
456 		}
457 	}
458 
459 	if (lowest == NULL) {
460 		return FALSE;
461 	}
462 
463 	rslt->X = lowest->X; rslt->Y = lowest->Y;
464 	rslt->Width = lowest->Width; rslt->Height = lowest->Height;
465 	return TRUE;
466 }
467 
468 void
gdip_clear_region(GpRegion * region)469 gdip_clear_region (GpRegion *region)
470 {
471 	region->type = RegionTypeInfinite;
472 
473 	if (region->rects) {
474 		GdipFree (region->rects);
475 		region->rects = NULL;
476 	}
477 
478 	if (region->tree) {
479 		gdip_region_clear_tree (region->tree);
480 		GdipFree (region->tree);
481 		region->tree = NULL;
482 	}
483 
484 	if (region->bitmap) {
485 		gdip_region_bitmap_free (region->bitmap);
486 		region->bitmap = NULL;
487 	}
488 
489 	region->cnt = 0;
490 }
491 
492 GpStatus
gdip_copy_region(GpRegion * source,GpRegion * dest)493 gdip_copy_region (GpRegion *source, GpRegion *dest)
494 {
495 	GpStatus status;
496 
497 	dest->type = source->type;
498 
499 	if (source->rects) {
500 		dest->cnt = source->cnt;
501 		dest->rects = (GpRectF *) GdipAlloc (sizeof (GpRectF) * source->cnt);
502 		if (!dest->rects)
503 			return OutOfMemory;
504 
505 		memcpy (dest->rects, source->rects, sizeof (GpRectF) * source->cnt);
506 	} else {
507 		dest->cnt = 0;
508 		dest->rects = NULL;
509 	}
510 
511 	if (source->tree) {
512 		dest->tree = (GpPathTree *) GdipAlloc (sizeof (GpPathTree));
513 		if (!dest->tree)
514 			return OutOfMemory;
515 
516 		status = gdip_region_copy_tree (source->tree, dest->tree);
517 		if (status != Ok)
518 			return status;
519 	} else {
520 		dest->tree = NULL;
521 	}
522 
523 	if (source->bitmap) {
524 		dest->bitmap = gdip_region_bitmap_clone (source->bitmap);
525 	} else {
526 		dest->bitmap = NULL;
527 	}
528 
529 	return Ok;
530 }
531 
532 /* convert a rectangle-based region to a path based region */
533 static GpStatus
gdip_region_convert_to_path(GpRegion * region)534 gdip_region_convert_to_path (GpRegion *region)
535 {
536 	GpStatus status;
537 
538 	/* no conversion is required for complex regions */
539 	if (!region || (region->type == RegionTypePath))
540 		return Ok;
541 
542 	region->tree = (GpPathTree *) GdipAlloc (sizeof (GpPathTree));
543 	if (!region->tree)
544 		return OutOfMemory;
545 
546 	status = GdipCreatePath (FillModeAlternate, &region->tree->path);
547 	if (status != Ok)
548 		return status;
549 
550 	switch (region->type) {
551 	case RegionTypeRect:
552 	case RegionTypeInfinite: {
553 		/* all rectangles are converted into a single path */
554 		for (int i = 0; i < region->cnt; i++) {
555 			RectF normalized;
556 			gdip_normalize_rectangle (&region->rects[i], &normalized);
557 			GdipAddPathRectangle (region->tree->path, normalized.X, normalized.Y, normalized.Width, normalized.Height);
558 		}
559 
560 		break;
561 	}
562 	default:
563 		g_warning ("unknown type 0x%08X", region->type);
564 		return NotImplemented;
565 	}
566 
567 	if (region->rects) {
568 		GdipFree (region->rects);
569 		region->cnt = 0;
570 		region->rects = NULL;
571 	}
572 
573 	region->type = RegionTypePath;
574 	return Ok;
575 }
576 
577 /*
578  * Create a region (path-tree) from a path.
579  */
580 static GpStatus
gdip_region_create_from_path(GpRegion * region,GpPath * path)581 gdip_region_create_from_path (GpRegion *region, GpPath *path)
582 {
583 	region->type = RegionTypePath;
584 	region->tree = (GpPathTree *) GdipAlloc (sizeof (GpPathTree));
585 	if (!region->tree)
586 		return OutOfMemory;
587 
588 	return GdipClonePath (path, &region->tree->path);
589 }
590 
591 /*
592 	API implementation
593 */
594 
595 // coverity[+alloc : arg-*0]
596 GpStatus WINGDIPAPI
GdipCreateRegion(GpRegion ** region)597 GdipCreateRegion (GpRegion **region)
598 {
599 	GpRegion *result;
600 	GpStatus status;
601 
602 	if (!gdiplusInitialized)
603 		return GdiplusNotInitialized;
604 
605 	if (!region)
606 		return InvalidParameter;
607 
608 	result = gdip_region_new ();
609 	if (!result)
610 		return OutOfMemory;
611 
612 	/* GdipSetInfinite handles setting region->type. */
613 	status = GdipSetInfinite (result);
614 	if (status != Ok) {
615 		GdipDeleteRegion (result);
616 		return status;
617 	}
618 
619 	*region = result;
620 	return Ok;
621 }
622 
623 // coverity[+alloc : arg-*1]
624 GpStatus WINGDIPAPI
GdipCreateRegionRect(GDIPCONST GpRectF * rect,GpRegion ** region)625 GdipCreateRegionRect (GDIPCONST GpRectF *rect, GpRegion **region)
626 {
627 	GpRegion *result;
628 	GpStatus status;
629 
630 	if (!gdiplusInitialized)
631 		return GdiplusNotInitialized;
632 
633 	if (!region || !rect)
634 		return InvalidParameter;
635 
636 	result = gdip_region_new ();
637 	if (!result)
638 		return OutOfMemory;
639 
640 	result->type = RegionTypeRect;
641 	status = gdip_add_rect_to_array (&result->rects, &result->cnt, NULL, rect);
642 	if (status != Ok) {
643 		GdipDeleteRegion (result);
644 		return status;
645 	}
646 
647 	*region = result;
648 	return Ok;
649 }
650 
651 // coverity[+alloc : arg-*1]
652 GpStatus WINGDIPAPI
GdipCreateRegionRectI(GDIPCONST GpRect * rect,GpRegion ** region)653 GdipCreateRegionRectI (GDIPCONST GpRect *rect, GpRegion **region)
654 {
655 	GpRectF rectF;
656 
657 	if (!gdiplusInitialized)
658 		return GdiplusNotInitialized;
659 
660 	if (!region || !rect)
661 		return InvalidParameter;
662 
663 	gdip_RectF_from_Rect (rect, &rectF);
664 	return GdipCreateRegionRect (&rectF, region);
665 }
666 
667 // coverity[+alloc : arg-*2]
668 GpStatus WINGDIPAPI
GdipCreateRegionRgnData(GDIPCONST BYTE * regionData,INT size,GpRegion ** region)669 GdipCreateRegionRgnData (GDIPCONST BYTE *regionData, INT size, GpRegion **region)
670 {
671 	GpRegion *result;
672 	RegionHeader header;
673 	DWORD type;
674 
675 	if (!gdiplusInitialized)
676 		return GdiplusNotInitialized;
677 
678 	if (!region || !regionData || size < 0)
679 		return InvalidParameter;
680 
681 	/* Read and validate the region data header. */
682 	if (size < sizeof (RegionHeader))
683 		return GenericError;
684 
685 	memcpy (&header, regionData, sizeof (RegionHeader));
686 	if (header.size < 8 || header.checksum != gdip_crc32 (regionData + 8, size - 8) || (header.magic & 0xfffff000) != 0xdbc01000) {
687 		return GenericError;
688 	}
689 
690 	regionData += sizeof (RegionHeader);
691 	size -= sizeof (RegionHeader);
692 
693 	/* Now read the rest of the data. */
694 	result = gdip_region_new ();
695 	if (!result)
696 		return OutOfMemory;
697 
698 	result->cnt = 0;
699 	result->rects = NULL;
700 	result->tree = NULL;
701 	result->bitmap = NULL;
702 
703 	// Read the type.
704 	memcpy (&type, regionData, sizeof (DWORD));
705 	regionData += sizeof (DWORD);
706 	size -= sizeof (DWORD);
707 
708 	switch (type) {
709 	case RegionDataRect:
710 		result->type = RegionTypeRect;
711 		if (header.size < sizeof (DWORD) * 3 + sizeof (GpRectF)) {
712 			GdipFree (result);
713 			return GenericError;
714 		}
715 
716 		GpRectF rect;
717 		memcpy (&rect, regionData, sizeof (GpRectF));
718 		gdip_add_rect_to_array (&result->rects, &result->cnt, NULL, &rect);
719 
720 		break;
721 	case RegionDataPath:
722 		result->type = RegionTypePath;
723 		if (size < 16) {
724 			GdipFree (result);
725 			return InvalidParameter;
726 		}
727 
728 		result->tree = (GpPathTree*) GdipAlloc (sizeof (GpPathTree));
729 		if (!result->tree) {
730 			GdipFree (result);
731 			return OutOfMemory;
732 		}
733 
734 		if (!gdip_region_deserialize_tree ((BYTE *) regionData, size, result->tree)) {
735 			GdipFree (result);
736 			return InvalidParameter;
737 		}
738 		break;
739 	case RegionDataEmptyRect: {
740 		result->type = RegionTypeRect;
741 
742 		break;
743 	}
744 	case RegionDataInfiniteRect: {
745 		result->type = RegionTypeInfinite;
746 
747 		GpRectF rect = {REGION_INFINITE_POSITION, REGION_INFINITE_POSITION, REGION_INFINITE_LENGTH, REGION_INFINITE_LENGTH};
748 		gdip_add_rect_to_array (&result->rects, &result->cnt, NULL, &rect);
749 
750 		break;
751 	}
752 	default:
753 		g_warning ("unknown type 0x%08X", result->type);
754 		GdipFree (result);
755 		return NotImplemented;
756 	}
757 
758 	*region = result;
759 	return Ok;
760 }
761 
762 // coverity[+alloc : arg-*1]
763 GpStatus WINGDIPAPI
GdipCloneRegion(GpRegion * region,GpRegion ** cloneRegion)764 GdipCloneRegion (GpRegion *region, GpRegion **cloneRegion)
765 {
766 	GpRegion *result;
767 	GpStatus status;
768 
769 	if (!gdiplusInitialized)
770 		return GdiplusNotInitialized;
771 
772 	if (!region || !cloneRegion)
773 		return InvalidParameter;
774 
775 	result = (GpRegion *) GdipAlloc (sizeof (GpRegion));
776 	if (!result)
777 		return OutOfMemory;
778 
779 	status = gdip_copy_region (region, result);
780 	if (status != Ok) {
781 		GdipFree (result);
782 		return status;
783 	}
784 
785 	*cloneRegion = result;
786 	return Ok;
787 }
788 
789 GpStatus WINGDIPAPI
GdipDeleteRegion(GpRegion * region)790 GdipDeleteRegion (GpRegion *region)
791 {
792 	if (!region)
793 		return InvalidParameter;
794 
795 	gdip_clear_region (region);
796 	GdipFree (region);
797 
798 	return Ok;
799 }
800 
801 
802 GpStatus WINGDIPAPI
GdipSetInfinite(GpRegion * region)803 GdipSetInfinite (GpRegion *region)
804 {
805 	GpRectF rect;
806 
807 	if (!region)
808 		return InvalidParameter;
809 
810 	gdip_clear_region (region);
811 	region->type = RegionTypeInfinite;
812 
813 	rect.X = rect.Y = REGION_INFINITE_POSITION;
814 	rect.Width = rect.Height = REGION_INFINITE_LENGTH;
815 
816 	return gdip_add_rect_to_array (&region->rects, &region->cnt, NULL, &rect);
817 }
818 
819 
820 GpStatus WINGDIPAPI
GdipSetEmpty(GpRegion * region)821 GdipSetEmpty (GpRegion *region)
822 {
823 	if (!region)
824 		return InvalidParameter;
825 
826 	gdip_clear_region (region);
827 	region->type = RegionTypeRect;
828 
829 	return Ok;
830 }
831 
832 /* Exclude */
833 static GpStatus
gdip_combine_exclude(GpRegion * region,GpRectF * rtrg,int cntt)834 gdip_combine_exclude (GpRegion *region, GpRectF *rtrg, int cntt)
835 {
836 	GpRectF *allsrcrects = NULL, *rects = NULL;
837 	GpRectF *alltrgrects = NULL, *rect, *rectop, *recttrg;
838 	int allsrccnt = 0, allsrccap, cnt = 0, cap, i, n, alltrgcnt = 0, alltrgcap;
839 	GpRectF current, rslt, newrect;
840 	BOOL storecomplete;
841 	GpStatus status;
842 
843 	/* Create the list of source rectangles to process, it will contain splitted ones later */
844 	allsrccap = region->cnt * 2;
845 	cap = allsrccap;
846 	for (i = 0, rect = region->rects; i < region->cnt; i++, rect++) {
847 		status = gdip_add_rect_to_array (&allsrcrects, &allsrccnt, &allsrccap, rect);
848 		if (status != Ok) {
849 			if (allsrcrects) {
850 				GdipFree (allsrcrects);
851 			}
852 
853 			return status;
854 		}
855 	}
856 
857 	/* Create the list of target rectangles to process, it will contain splitted ones later */
858 	alltrgcap = cntt;
859 	for (i = 0, rect = rtrg; i < cntt; i++, rect++) {
860 		/* normalize */
861 		GpRectF normal;
862 		gdip_normalize_rectangle (rect, &normal);
863 		status = gdip_add_rect_to_array (&alltrgrects, &alltrgcnt, &alltrgcap, &normal);
864 		if (status != Ok) {
865 			if (alltrgrects) {
866 				GdipFree (alltrgrects);
867 			}
868 
869 			return status;
870 		}
871 	}
872 
873 	/* Init current with the first element in the array */
874 	current.X = REGION_INFINITE_POSITION - 1;
875 	current.Y = REGION_INFINITE_POSITION - 1;
876 	current.Width = 0; current.Height = 0;
877 
878 	while (gdip_getlowestrect (allsrcrects, allsrccnt, &current, &rslt)) {
879 		current.X = rslt.X; current.Y = rslt.Y;
880 		current.Width = rslt.Width; current.Height = rslt.Height;
881 		storecomplete = TRUE;
882 
883 		/* Current rect with lowest y and X against the target ones */
884 		for (i = 0, recttrg = alltrgrects; i < alltrgcnt; i++, recttrg++) {
885 
886 			if (gdip_intersects (&current, recttrg) == FALSE
887 				|| gdip_equals (&current, recttrg) == TRUE ||
888 				recttrg->Height < 0 || recttrg->Width < 0) {
889 				continue;
890 			}
891 
892 			/* Once a rect is splitted, we do not want to take into account anymore */
893 			for (rectop = allsrcrects, n = 0; n < allsrccnt; n++, rectop++) {
894 				if (gdip_equals (&current, rectop)) {
895 					rectop->X = 0; rectop->Y = 0;
896 					rectop->Width = 0; rectop->Height = 0;
897 					break;
898 				}
899 			}
900 
901 			/* Result rect */
902 			newrect.Y = current.Y;
903 			if (current.Y >= recttrg->Y) {  /* Our rect intersects in the upper part with another rect */
904 				newrect.Height = MIN (recttrg->Y + recttrg->Height - current.Y, current.Height);
905 				if (newrect.Height < 0)
906 					newrect.Height = current.Height;
907 
908 				if (current.X >= recttrg->X) { /* Hit from behind */
909 					newrect.X = recttrg->X + recttrg->Width;
910 					newrect.Width = MAX (current.X + current.Width - newrect.X, 0);
911 				}
912 				else {
913 					newrect.X = current.X;
914 					newrect.Width = MAX (recttrg->X - current.X, 0);
915 				}
916 			}
917 			else {
918 				newrect.Height = MIN (recttrg->Y - current.Y, current.Height);
919 				newrect.X = current.X;
920 				newrect.Width = current.Width;
921 			}
922 
923 			gdip_add_rect_to_array_notcontained (&rects, &cnt, &cap, &newrect);
924 
925 			/* What's left to process from the source region */
926 			if (current.Y >= recttrg->Y) {  /* Our rect intersects in the upper part with another rect */
927 				/* A whole part from the top has been taken*/
928 				if (recttrg->X <= current.X && recttrg->X + recttrg->Width  >= current.X + current.Width)
929 					rslt.Y = recttrg->Y + recttrg->Height;
930 				else
931 					rslt.Y = newrect.Y + newrect.Height;
932 
933 				rslt.Height = current.Y  + current.Height - rslt.Y;
934 			}
935 			else {
936 				rslt.Y = recttrg->Y;
937 				rslt.Height = current.Y + current.Height - recttrg->Y;
938 			}
939 
940 			rslt.X = current.X;
941 			rslt.Width = current.Width;
942 
943 			if (rslt.Height > 0 && rslt.Width > 0) {
944 				status = gdip_add_rect_to_array (&allsrcrects, &allsrccnt, &allsrccap, &rslt);
945 				if (status != Ok) {
946 					GdipFree (allsrcrects);
947 					GdipFree (alltrgrects);
948 
949 					return status;
950 				}
951 			}
952 
953 			/* Special case where our rect is hit and split in two parts IIIUIII */
954 			if (recttrg->X >= current.X && recttrg->X + recttrg->Width  <= current.X + current.Width) {
955 				/* Generate extra right rect, keep previous values of Y and Height */
956 				newrect.Width = current.X + current.Width - (recttrg->X + recttrg->Width);
957 				newrect.X = recttrg->X + recttrg->Width;
958 				gdip_add_rect_to_array_notcontained (&rects, &cnt, &cap, &newrect);
959 			}
960 
961 			storecomplete = FALSE;
962 			break;
963 		}
964 
965 		/* don't include a rectangle identical to the excluded one! */
966 		if (storecomplete && !gdip_equals (rtrg, &current)) {
967 			gdip_add_rect_to_array_notcontained (&rects, &cnt, &cap, &current);
968 		}
969 	}
970 
971 	gdip_trim_rect_array (&rects, cnt);
972 
973 	GdipFree (allsrcrects);
974 	GdipFree (alltrgrects);
975 	if (region->rects)
976 		GdipFree (region->rects);
977 
978 	region->rects = rects;
979 	region->cnt = cnt;
980 
981 	return Ok;
982 }
983 
984 
985 /*
986 	Complement: the part of the second region not shared with the first region.
987 	Scans the region to be combined and store the rects not present in the region
988 */
989 static GpStatus
gdip_combine_complement(GpRegion * region,GpRectF * rtrg,int cntt)990 gdip_combine_complement (GpRegion *region, GpRectF *rtrg, int cntt)
991 {
992 	GpRegion regsrc;
993 	GpRectF* trg, *rect;
994 	GpRectF* allsrcrects = NULL;
995 	int allsrccnt = 0, i,  trgcnt, allsrccap;
996 	GpStatus status;
997 
998 	/* Create the list of source rectangles to process */
999 	allsrccap = cntt;
1000 	for (i = 0, rect = rtrg; i < cntt; i++, rect++) {
1001 		/* normalize */
1002 		GpRectF normal;
1003 		gdip_normalize_rectangle (rect, &normal);
1004 		status = gdip_add_rect_to_array (&allsrcrects, &allsrccnt, &allsrccap, &normal);
1005 		if (status != Ok) {
1006 			goto error;
1007 		}
1008 	}
1009 
1010 	regsrc.rects = allsrcrects;
1011 	regsrc.cnt = allsrccnt;
1012 	trg = region->rects;
1013 	trgcnt = region->cnt;
1014 
1015 	status = gdip_combine_exclude (&regsrc, trg, trgcnt);
1016 	if (status != Ok) {
1017 		goto error;
1018 	}
1019 
1020 	if ((regsrc.rects != allsrcrects) || (regsrc.cnt != allsrccnt)) {
1021 		if (region->rects)
1022 			GdipFree (region->rects);
1023 
1024 		region->rects = regsrc.rects;
1025 		region->cnt = regsrc.cnt;
1026 	}
1027 
1028 	return Ok;
1029 
1030 error:
1031 	if (allsrcrects)
1032 		GdipFree (allsrcrects);
1033 
1034 	return status;
1035 }
1036 
1037 
1038 /* Union */
1039 static GpStatus
gdip_combine_union(GpRegion * region,GpRectF * rtrg,int cnttrg)1040 gdip_combine_union (GpRegion *region, GpRectF *rtrg, int cnttrg)
1041 {
1042 	GpRectF *allrects = NULL, *rects = NULL;
1043 	GpRectF *recttrg, *rect, *rectop, *current;
1044 	int allcnt = 0, allcap, cnt = 0, cap = 0, currentIndex = -1, i, n;
1045 	GpRectF rslt, newrect;
1046 	BOOL storecomplete, contained, needsort;
1047 	GpStatus status;
1048 
1049 	/* All the src and trg rects in a single array*/
1050 	allcap = (region->cnt + cnttrg) * 2;
1051 	cap = allcap;
1052 	for (i = 0, rect = region->rects; i < region->cnt; i++, rect++) {
1053 		status = gdip_add_rect_to_array (&allrects, &allcnt, &allcap, rect);
1054 		if (status != Ok) {
1055 			if (allrects)
1056 				GdipFree (allrects);
1057 
1058 			return status;
1059 		}
1060 	}
1061 
1062 	for (i = 0, rect = rtrg; i < cnttrg; i++, rect++) {
1063 		/* normalize */
1064 		GpRectF normal;
1065 		gdip_normalize_rectangle (rect, &normal);
1066 		gdip_add_rect_to_array (&allrects, &allcnt, &allcap, &normal);
1067 	}
1068 
1069 	if (allcnt == 0) {
1070 		GdipFree (allrects);
1071 		return Ok;
1072 	}
1073 
1074 	gdip_sort_rect_array(allrects, allcnt);
1075 
1076 	for (currentIndex = 0; currentIndex < allcnt; currentIndex++) {
1077 		current = allrects + currentIndex;
1078 
1079 		if (current->Width <= 0 || current->Height <= 0) {
1080 			continue;
1081 		}
1082 
1083 		storecomplete = TRUE;
1084 
1085 		/* Current rect with lowest y and X againt the stored ones */
1086 		for (i = currentIndex + 1; i < allcnt; i++) {
1087 			recttrg = allrects + i;
1088 
1089 			needsort = FALSE;
1090 
1091 			// If it is positioned after the bottom-right corner of current, no useful rectangles can be found (due to sorting).
1092 			if (recttrg->Y > current->Y + current->Height ||
1093 				(recttrg->Y == current->Y + current->Height && recttrg->X > current->X + current->Width)) {
1094 				break;
1095 			}
1096 
1097 			/* If it has lower coordinates or negative / zero size it has been already processed */
1098 			if (recttrg->Height <= 0 || recttrg->Width <= 0 ||
1099 				current->Y > recttrg->Y ||
1100 				(current->Y == recttrg->Y && current->X > recttrg->X)) {
1101 				continue;
1102 			}
1103 
1104 			if (gdip_intersects_or_touches (current, recttrg) == FALSE
1105 				|| gdip_equals (current, recttrg) == TRUE) {
1106 				continue;
1107 			}
1108 
1109 			if (gdip_contains  (recttrg, current) == TRUE) {
1110 				continue;
1111 			}
1112 
1113 			/* Our rect intersects in the lower part with another rect */
1114 			newrect.Y = current->Y;
1115 			newrect.X = current->X;
1116 			if (current->Y == recttrg->Y) {
1117 				newrect.Width = MAX (current->X + current->Width, recttrg->X + recttrg->Width) - newrect.X;
1118 				newrect.Height = MIN (current->Height, recttrg->Height);
1119 			}
1120 			else {
1121 				newrect.Width = current->Width;
1122 				newrect.Height = recttrg->Y - current->Y;
1123 			}
1124 
1125 			/* If it's contained inside, get the > height */
1126 			if (recttrg->X == current->X && (recttrg->Width == current->Width ||
1127 				(recttrg->Y == current->Y && recttrg->Width > current->Width))) {
1128 
1129 				newrect.Height = recttrg->Y + recttrg->Height - current->Y;
1130 			} else if (recttrg->X >= current->X && recttrg->X + recttrg->Width <= current->X + current->Width) {
1131 				newrect.Height = current->Height;
1132 			}
1133 
1134 			gdip_add_rect_to_array_notcontained (&rects, &cnt, &cap, &newrect);
1135 
1136 			/* Push what's left from the current the rect in the list of rects to process
1137 			 if it's already not contained in other rects except the current (we just split from there) */
1138 			rslt.X = current->X;
1139 			rslt.Y = newrect.Y + newrect.Height;
1140 			rslt.Width = current->Width;
1141 			rslt.Height = current->Height - newrect.Height;
1142 
1143 			if (rslt.Height > 0 && rslt.Width > 0) {
1144 				contained = FALSE;
1145 				for (rectop = allrects + currentIndex + 1, n = currentIndex + 1; n < allcnt; n++, rectop++) {
1146 					// Rectangles before currentIndex have been processed and will be empty. They cannot contain anything.
1147 					if (gdip_contains (&rslt, rectop)) {
1148 						contained = TRUE;
1149 						break;
1150 					} else if (gdip_compare_rectf (rectop, &rslt) > 0) {
1151 						break; // Not going to find one containing it after this.
1152 					}
1153 				}
1154 
1155 				if (contained == FALSE) {
1156 					status = gdip_add_rect_to_array_sorted (&allrects, &allcnt, &allcap,  &rslt);
1157 					if (status != Ok) {
1158 						GdipFree (allrects);
1159 						return status;
1160 					}
1161 
1162 					// Must get recttrg in the new array in case adding rslt above had to increase the array capacity.
1163 					recttrg = allrects + i;
1164 				}
1165 			}
1166 
1167 			/* If both we at the same Y when take into account the X also to process the following
1168 			   that exceeds the X also */
1169 			if (recttrg->Y == current->Y) {
1170 				recttrg->Height -= newrect.Height;
1171 				if (recttrg->Height > 0) {
1172 					recttrg->Y += newrect.Height;
1173 					needsort = TRUE; // Modified Y, re-sort.
1174 				}
1175 			} else if (recttrg->X >= current->X && recttrg->X + recttrg->Width <= current->X + current->Width) {
1176 				/* If it's contained inside, get the > height  */
1177 				recttrg->Height = recttrg->Y + recttrg->Height - (newrect.Y + newrect.Height);
1178 				if (recttrg->Height > 0) {
1179 					recttrg->Y = newrect.Y + newrect.Height;
1180 					needsort = TRUE; // Modified Y, re-sort.
1181 				}
1182 			}
1183 
1184 			if (needsort == TRUE)
1185 				gdip_sort_rect_array_sorted (allrects, allcnt);
1186 
1187 			storecomplete = FALSE;
1188 			break;
1189 		}
1190 
1191 		if (storecomplete) {
1192 			gdip_add_rect_to_array_notcontained (&rects, &cnt, &cap, current);
1193 		}
1194 	}
1195 
1196 	GdipFree (allrects);
1197 	if (region->rects)
1198 		GdipFree (region->rects);
1199 
1200 	gdip_trim_rect_array (&rects, cnt);
1201 	region->rects = rects;
1202 	region->cnt = cnt;
1203 
1204 	return Ok;
1205 }
1206 
1207 /* Intersect */
1208 static GpStatus
gdip_combine_intersect(GpRegion * region,GpRectF * rtrg,int cnttrg)1209 gdip_combine_intersect (GpRegion *region, GpRectF *rtrg, int cnttrg)
1210 {
1211 	GpRectF *rectsrc;
1212 	int src, trg;
1213 	GpRectF rectcur;
1214 	GpRegion regunion;
1215 	GpRectF *recttrg;
1216 	GpStatus status;
1217 
1218 	regunion.rects = NULL;
1219 	regunion.cnt = 0;
1220 
1221 	for (rectsrc = region->rects, src = 0; src < region->cnt; src++, rectsrc++) {
1222 		for (recttrg = rtrg, trg = 0; trg < cnttrg; trg++, recttrg++) {
1223 			/* normalize */
1224 			GpRectF normal;
1225 			gdip_normalize_rectangle (recttrg, &normal);
1226 
1227 			/* Intersects With */
1228 			if ((rectsrc->X >= normal.X + normal.Width) || (rectsrc->X + rectsrc->Width <= normal.X) ||
1229 				(rectsrc->Y >= normal.Y + normal.Height) || (rectsrc->Y + rectsrc->Height <= normal.Y)) {
1230 				continue;
1231 			}
1232 			/* Area that intersects */
1233 			rectcur.X = rectsrc->X > normal.X ? rectsrc->X : normal.X;
1234 			rectcur.Y = rectsrc->Y > normal.Y ? rectsrc->Y : normal.Y;
1235 			rectcur.Width = rectsrc->X + rectsrc->Width < normal.X + normal.Width ?
1236 				rectsrc->X + rectsrc->Width - rectcur.X : normal.X + normal.Width - rectcur.X;
1237 
1238 			rectcur.Height = rectsrc->Y + rectsrc->Height < normal.Y + normal.Height ?
1239 				rectsrc->Y + rectsrc->Height - rectcur.Y : normal.Y + normal.Height - rectcur.Y;
1240 
1241 			/* Combine with previous areas that intersect with rect */
1242 			status = gdip_combine_union (&regunion, &rectcur, 1);
1243 			if (status != Ok)
1244 				return status;
1245 		}
1246 	}
1247 
1248 	if (region->rects)
1249 		GdipFree (region->rects);
1250 
1251 	region->rects = regunion.rects;
1252 	region->cnt = regunion.cnt;
1253 
1254 	return Ok;
1255 }
1256 
1257 /* Xor */
1258 static GpStatus
gdip_combine_xor(GpRegion * region,GpRectF * recttrg,int cnttrg)1259 gdip_combine_xor (GpRegion *region, GpRectF *recttrg, int cnttrg)
1260 {
1261 	GpRegion *rgnsrc = NULL;  /* All rectangles of both regions*/
1262 	GpRegion *rgntrg = NULL;  /* Only the ones that intersect*/
1263 	GpRectF *allrects = NULL, *rect;
1264 	int allcnt = 0, allcap, i;
1265 	GpStatus status;
1266 
1267 	/* All the src and trg rects in a single array*/
1268 	allcap = region->cnt + cnttrg;
1269 	for (i = 0, rect = region->rects; i < region->cnt; i++, rect++) {
1270 		status = gdip_add_rect_to_array (&allrects, &allcnt, &allcap, rect);
1271 		if (status != Ok)
1272 			goto error;
1273 	}
1274 
1275 	for (i = 0, rect = recttrg; i < cnttrg; i++, rect++) {
1276 		/* normalize */
1277 		GpRectF normal;
1278 		gdip_normalize_rectangle (rect, &normal);
1279 		gdip_add_rect_to_array (&allrects, &allcnt, &allcap, &normal);
1280 	}
1281 
1282 	rgnsrc = (GpRegion *) GdipAlloc (sizeof (GpRegion));
1283 	if (!rgnsrc) {
1284 		status = OutOfMemory;
1285 		goto error;
1286 	}
1287 
1288 	rgnsrc->type = RegionTypeRect;
1289 	rgnsrc->cnt = allcnt;
1290 	rgnsrc->rects = allrects;
1291 
1292 	status = GdipCloneRegion (region, &rgntrg);
1293 	if (status != Ok)
1294 		goto error;
1295 
1296 	status = gdip_combine_intersect (rgntrg, recttrg, cnttrg);
1297 	if (status != Ok)
1298 		goto error;
1299 
1300 	/* exclude the intersecting rectangles (if any) */
1301 	if (rgntrg->cnt > 0) {
1302 		status = gdip_combine_exclude (rgnsrc, rgntrg->rects, rgntrg->cnt);
1303 		if (status != Ok)
1304 			goto error;
1305 	}
1306 
1307 	if (region->rects)
1308 		GdipFree (region->rects);
1309 
1310 	region->rects = rgnsrc->rects;
1311 	region->cnt = rgnsrc->cnt;
1312 
1313 	GdipFree (rgnsrc);
1314 	GdipDeleteRegion (rgntrg);
1315 
1316 	return Ok;
1317 
1318 error:
1319 	if (allrects)
1320 		GdipFree (allrects);
1321 
1322 	GdipFree (rgnsrc);
1323 	GdipDeleteRegion (rgntrg);
1324 
1325 	return status;
1326 }
1327 
1328 GpStatus WINGDIPAPI
GdipCombineRegionRect(GpRegion * region,GDIPCONST GpRectF * rect,CombineMode combineMode)1329 GdipCombineRegionRect (GpRegion *region, GDIPCONST GpRectF *rect, CombineMode combineMode)
1330 {
1331 	if (!region || !rect)
1332 		return InvalidParameter;
1333 
1334 	if (combineMode == CombineModeReplace) {
1335 		GdipSetEmpty (region);
1336 		return gdip_add_rect_to_array (&region->rects, &region->cnt, NULL, (GpRectF *)rect);
1337 	}
1338 
1339 	GpRectF normalized;
1340 	gdip_normalize_rectangle (rect, &normalized);
1341 
1342 	BOOL infinite = gdip_is_InfiniteRegion (region);
1343 	BOOL empty = gdip_is_region_empty (region, /* allowNegative */ TRUE);
1344 	BOOL rectEmpty = gdip_is_rectF_empty (&normalized, /* allowNegative */ FALSE);
1345 
1346 	if (rectEmpty) {
1347 		switch (combineMode) {
1348 		case CombineModeUnion:
1349 		case CombineModeXor:
1350 		case CombineModeExclude:
1351 			/* The union of the empty region and X is X */
1352 			/* The xor of the empty region and X is X */
1353 			/* Everything is outside the empty region */
1354 			if (empty)
1355 				return GdipSetEmpty (region);
1356 			if (infinite)
1357 				return GdipSetInfinite (region);
1358 
1359 			return Ok;
1360 		case CombineModeIntersect:
1361 		case CombineModeComplement:
1362 			/* The empty region does not intersect with anything */
1363 			/* Nothing is inside the empty region */
1364 			return GdipSetEmpty (region);
1365 		default:
1366 			break;
1367 		}
1368 	}
1369 
1370 	if (infinite) {
1371 		switch (combineMode) {
1372 		case CombineModeIntersect: {
1373 			/* The intersection of the infinite region with X is X */
1374 			GdipSetEmpty (region);
1375 			return gdip_add_rect_to_array (&region->rects, &region->cnt, NULL, &normalized);
1376 		}
1377 		case CombineModeUnion:
1378 			/* The union of the infinite region and X is the infinite region */
1379 			return GdipSetInfinite (region);
1380 		case CombineModeComplement:
1381 			/* Nothing is outside the infinite region */
1382 			return GdipSetEmpty (region);
1383 		default:
1384 			break;
1385 		}
1386 	} else if (empty) {
1387 		switch (combineMode) {
1388 		case CombineModeIntersect:
1389 		case CombineModeExclude:
1390 			/* The empty region does not intersect with anything */
1391 			/* Nothing to exclude */
1392 			return GdipSetEmpty (region);
1393 		case CombineModeUnion:
1394 		case CombineModeXor:
1395 		case CombineModeComplement:
1396 			/* The union of the empty region and X is X */
1397 			/* The XOR of the empty region and X is X */
1398 			/* Everything is outside the empty region */
1399 			GdipSetEmpty (region);
1400 			return gdip_add_rect_to_array (&region->rects, &region->cnt, NULL, &normalized);
1401 		default:
1402 			break;
1403 		}
1404 	}
1405 
1406 	switch (region->type) {
1407 	case RegionTypeRect:
1408 	case RegionTypeInfinite: {
1409 		region->type = RegionTypeRect;
1410 		switch (combineMode) {
1411 		case CombineModeExclude:
1412 			return gdip_combine_exclude (region, &normalized, 1);
1413 		case CombineModeComplement:
1414 			return gdip_combine_complement (region, &normalized, 1);
1415 		case CombineModeIntersect:
1416 			return gdip_combine_intersect (region, &normalized, 1);
1417 		case CombineModeUnion:
1418 			return gdip_combine_union (region, &normalized, 1);
1419 		case CombineModeXor:
1420 			return gdip_combine_xor (region, &normalized, 1);
1421 		case CombineModeReplace: /* Used by Graphics clipping */
1422 			return gdip_add_rect_to_array (&region->rects, &region->cnt, NULL, &normalized);
1423 		default:
1424 			return NotImplemented;
1425 		}
1426 	}
1427 	case RegionTypePath: {
1428 		/* Convert GpRectF to GpPath and use GdipCombineRegionPath */
1429 		GpPath *path;
1430 		GpStatus status = GdipCreatePath (FillModeAlternate, &path);
1431 		if (status != Ok)
1432 			return status;
1433 
1434 		status = GdipAddPathRectangle (path, normalized.X, normalized.Y, normalized.Width, normalized.Height);
1435 		if (status != Ok) {
1436 			GdipDeletePath (path);
1437 			return status;
1438 		}
1439 
1440 		status = GdipCombineRegionPath (region, path, combineMode);
1441 		GdipDeletePath (path);
1442 		return status;
1443 	}
1444 	default:
1445 		g_warning ("unknown type 0x%08X", region->type);
1446 		return NotImplemented;
1447 	}
1448 }
1449 
1450 
1451 GpStatus WINGDIPAPI
GdipCombineRegionRectI(GpRegion * region,GDIPCONST GpRect * recti,CombineMode combineMode)1452 GdipCombineRegionRectI (GpRegion *region, GDIPCONST GpRect *recti, CombineMode combineMode)
1453 {
1454 	GpRectF rect;
1455 
1456 	if (!region || !recti)
1457 		return InvalidParameter;
1458 
1459 	gdip_RectF_from_Rect ((GpRect *) recti, &rect);
1460 
1461 	return GdipCombineRegionRect (region, (GDIPCONST GpRectF *) &rect, combineMode);
1462 }
1463 
1464 /* Exclude path from infinite region */
1465 static BOOL
gdip_combine_exclude_from_infinite(GpRegion * region,GpPath * path)1466 gdip_combine_exclude_from_infinite (GpRegion *region, GpPath *path)
1467 {
1468 	/*
1469 	 * We combine the path with the infinite region's, then reverse it.
1470 	 */
1471 	GpPath *region_path;
1472 	GpStatus status;
1473 
1474 	if (path->count == 0)
1475 		return TRUE;
1476 
1477 	if (region->type != RegionTypePath) {
1478 		status = gdip_region_convert_to_path (region);
1479 		if (status != Ok)
1480 			return FALSE;
1481 	}
1482 
1483 	g_assert (region->tree->path);
1484 	region_path = region->tree->path;
1485 	status = GdipClonePath (path, &region->tree->path);
1486 	if (status != Ok) {
1487 		region->tree->path = region_path;
1488 		return FALSE;
1489 	}
1490 	status = GdipAddPathPath (region->tree->path, region_path, FALSE);
1491 	if (status != Ok) {
1492 		GdipDeletePath (region->tree->path);
1493 		region->tree->path = region_path;
1494 		return FALSE;
1495 	}
1496 	status = GdipReversePath (region->tree->path);
1497 	if (status != Ok) {
1498 		GdipDeletePath (region->tree->path);
1499 		region->tree->path = region_path;
1500 		return FALSE;
1501 	}
1502 	return TRUE;
1503 }
1504 
1505 GpStatus WINGDIPAPI
GdipCombineRegionPath(GpRegion * region,GpPath * path,CombineMode combineMode)1506 GdipCombineRegionPath (GpRegion *region, GpPath *path, CombineMode combineMode)
1507 {
1508 	GpRegionBitmap *path_bitmap, *result;
1509 	GpStatus status;
1510 
1511 	if (!region || !path)
1512 		return InvalidParameter;
1513 
1514 	if (combineMode == CombineModeReplace) {
1515 		gdip_clear_region (region);
1516 		return gdip_region_create_from_path (region, path);
1517 	}
1518 
1519 	BOOL infinite = gdip_is_InfiniteRegion (region);
1520 	BOOL empty = gdip_is_region_empty (region, /* allowNegative */ TRUE);
1521 	BOOL pathEmpty = path->count == 0;
1522 
1523 	if (pathEmpty) {
1524 		switch (combineMode) {
1525 		case CombineModeUnion:
1526 		case CombineModeXor:
1527 		case CombineModeExclude:
1528 			/* The union of the empty region and X is X */
1529 			/* The xor of the empty region and X is X */
1530 			/* Everything is outside the empty region */
1531 			if (empty)
1532 				return GdipSetEmpty (region);
1533 
1534 			return Ok;
1535 		case CombineModeIntersect:
1536 		case CombineModeComplement:
1537 			/* The empty region does not intersect with anything */
1538 			/* Nothing is inside the empty region */
1539 			return GdipSetEmpty (region);
1540 		default:
1541 			break;
1542 		}
1543 	}
1544 
1545 	if (infinite) {
1546 		switch (combineMode) {
1547 		case CombineModeIntersect:
1548 			/* The intersection of the infinite region with X is X */
1549 			GdipSetEmpty (region);
1550 			return gdip_region_create_from_path (region, path);
1551 		case CombineModeUnion:
1552 			/* The union of the infinite region and X is the infinite region */
1553 			return GdipSetInfinite (region);
1554 		case CombineModeComplement:
1555 			/* Nothing is outside the infinite region */
1556 			return GdipSetEmpty (region);
1557 		case CombineModeExclude:
1558 			if (gdip_combine_exclude_from_infinite (region, path))
1559 				return Ok;
1560 
1561 			break;
1562 		default:
1563 			break;
1564 		}
1565 	} else if (empty) {
1566 		switch (combineMode) {
1567 		case CombineModeIntersect:
1568 		case CombineModeExclude:
1569 			/* The empty region does not intersect with anything */
1570 			/* Nothing to exclude */
1571 			return GdipSetEmpty (region);
1572 		case CombineModeUnion:
1573 		case CombineModeXor:
1574 		case CombineModeComplement:
1575 			/* The union of the empty region and X is X */
1576 			/* The XOR of the empty region and X is X */
1577 			/* Everything is outside the empty region */
1578 			GdipSetEmpty (region);
1579 			return gdip_region_create_from_path (region, path);
1580 		default:
1581 			break;
1582 		}
1583 	}
1584 
1585 	if (region->type != RegionTypePath) {
1586 		status = gdip_region_convert_to_path (region);
1587 		if (status != Ok)
1588 			return status;
1589 	}
1590 
1591 	/* make sure the region's bitmap is available */
1592 	gdip_region_bitmap_ensure (region);
1593 	if (!region->bitmap)
1594 		return OutOfMemory;
1595 
1596 	/* create a bitmap for the path to combine into the region */
1597 	path_bitmap = gdip_region_bitmap_from_path (path);
1598 
1599 	result = gdip_region_bitmap_combine (region->bitmap, path_bitmap, combineMode);
1600 	gdip_region_bitmap_free (path_bitmap);
1601 	if (!result)
1602 		return NotImplemented;
1603 
1604 	gdip_region_bitmap_free (region->bitmap);
1605 	region->bitmap = result;
1606 
1607 	/* add a copy of path into region1 tree */
1608 	if (region->tree->path) {
1609 		/* move the existing path into a new tree (branch1) ... */
1610 		region->tree->branch1 = (GpPathTree*) GdipAlloc (sizeof (GpPathTree));
1611 		if (!region->tree->branch1)
1612 			return OutOfMemory;
1613 
1614 		region->tree->branch1->path = region->tree->path;
1615 		region->tree->branch2 = (GpPathTree*) GdipAlloc (sizeof (GpPathTree));
1616 		if (!region->tree->branch2)
1617 			return OutOfMemory;
1618 	} else {
1619 		/* move the current base tree into branch1 of a new tree ... */
1620 		GpPathTree* tmp = (GpPathTree*) GdipAlloc (sizeof (GpPathTree));
1621 		if (!tmp)
1622 			return OutOfMemory;
1623 
1624 		tmp->branch1 = region->tree;
1625 		tmp->branch2 = (GpPathTree*) GdipAlloc (sizeof (GpPathTree));
1626 		if (!tmp->branch2) {
1627 			GdipFree (tmp);
1628 			return OutOfMemory;
1629 		}
1630 
1631 		region->tree = tmp;
1632 	}
1633 	/* ... and clone the specified path into branch2 */
1634 	region->tree->mode = combineMode;
1635 	region->tree->path = NULL;
1636 
1637 	return GdipClonePath (path, &region->tree->branch2->path);
1638 }
1639 
1640 
1641 static GpStatus
gdip_combine_pathbased_region(GpRegion * region1,GpRegion * region2,CombineMode combineMode)1642 gdip_combine_pathbased_region (GpRegion *region1, GpRegion *region2, CombineMode combineMode)
1643 {
1644 	GpRegionBitmap *result;
1645 
1646 	/* if not available, construct the bitmaps for both regions */
1647 	gdip_region_bitmap_ensure (region1);
1648 	gdip_region_bitmap_ensure (region2);
1649 	if (!region1->bitmap || !region2->bitmap)
1650 		return OutOfMemory;
1651 
1652 	result = gdip_region_bitmap_combine (region1->bitmap, region2->bitmap, combineMode);
1653 	if (!result)
1654 		return NotImplemented;
1655 	gdip_region_bitmap_free (region1->bitmap);
1656 	region1->bitmap = result;
1657 
1658 	/* re-structure region1 to allow adding a copy of region2 inside it */
1659 	if (region1->tree->path) {
1660 		region1->tree->branch1 = (GpPathTree*) GdipAlloc (sizeof (GpPathTree));
1661 		if (!region1->tree->branch1)
1662 			return OutOfMemory;
1663 
1664 		region1->tree->branch1->path = region1->tree->path;
1665 		region1->tree->branch2 = (GpPathTree*) GdipAlloc (sizeof (GpPathTree));
1666 		if (!region1->tree->branch2)
1667 			return OutOfMemory;
1668 	} else {
1669 		/* move the current base tree into branch1 of a new tree ... */
1670 		GpPathTree* tmp = (GpPathTree*) GdipAlloc (sizeof (GpPathTree));
1671 		if (!tmp)
1672 			return OutOfMemory;
1673 
1674 		tmp->branch1 = region1->tree;
1675 		tmp->branch2 = (GpPathTree*) GdipAlloc (sizeof (GpPathTree));
1676 		if (!tmp->branch2) {
1677 			GdipFree (tmp);
1678 			return OutOfMemory;
1679 		}
1680 
1681 		region1->tree = tmp;
1682 	}
1683 
1684 	region1->tree->mode = combineMode;
1685 	region1->tree->path = NULL;
1686 
1687 	/* add a copy of region2 tree into region1 tree */
1688 	if (region2->tree->path) {
1689 		return GdipClonePath (region2->tree->path, &region1->tree->branch2->path);
1690 	} else {
1691 		return gdip_region_copy_tree (region2->tree, region1->tree->branch2);
1692 	}
1693 }
1694 
1695 
1696 GpStatus WINGDIPAPI
GdipCombineRegionRegion(GpRegion * region,GpRegion * region2,CombineMode combineMode)1697 GdipCombineRegionRegion (GpRegion *region, GpRegion *region2, CombineMode combineMode)
1698 {
1699 	GpStatus status;
1700 
1701 	if (!region || !region2)
1702 		return InvalidParameter;
1703 
1704 	if (combineMode == CombineModeReplace) {
1705 		GdipSetEmpty (region);
1706 		return gdip_copy_region (region2, region);
1707 	}
1708 
1709 	BOOL region1Empty = gdip_is_region_empty (region, /* allowNegative */ TRUE);
1710 	BOOL region1Infinite = gdip_is_InfiniteRegion (region);
1711 	BOOL region2Empty = gdip_is_region_empty (region2, /* allowNegative */ combineMode != CombineModeIntersect || region->type != RegionTypeInfinite);
1712 	BOOL region2Infinite = gdip_is_InfiniteRegion (region2);
1713 
1714 	switch (combineMode) {
1715 	case CombineModeUnion:
1716 		if (region1Infinite || region2Infinite) {
1717 			/* The union of X with the infinite region is infinite */
1718 			return GdipSetInfinite (region);
1719 		}
1720 		if (region1Empty) {
1721 			/* The union of the empty region and X is X */
1722 			GdipSetEmpty (region);
1723 			if (!region2Empty)
1724 				return gdip_copy_region (region2, region);
1725 
1726 			return Ok;
1727 		}
1728 		if (region2Empty) {
1729 			/* The union of the empty region and X is X */
1730 			return Ok;
1731 		}
1732 
1733 		break;
1734 	case CombineModeIntersect:
1735 		if (region1Empty || region2Empty) {
1736 			/* Nothing intersects with the empty region */
1737 			return GdipSetEmpty (region);
1738 		}
1739 		if (region1Infinite) {
1740 			/* Everything intersects with the infinite region */
1741 			GdipSetEmpty (region);
1742 			return gdip_copy_region (region2, region);
1743 		}
1744 		if (region2Infinite) {
1745 			/* Everything intersects with the infinite region */
1746 			return Ok;
1747 		}
1748 
1749 		break;
1750 	case CombineModeExclude:
1751 		if (region1Empty) {
1752 			/* Nothing is outside the empty region */
1753 			return GdipSetEmpty (region);
1754 		}
1755 		if (region2Empty) {
1756 			/* Everything is outside the empty region */
1757 			return Ok;
1758 		}
1759 		if (region1Infinite) {
1760 			if ((region2->type == RegionTypePath) && region2->tree && region2->tree->path &&
1761 				gdip_combine_exclude_from_infinite (region, region2->tree->path))
1762 				return Ok;
1763 		}
1764 
1765 		break;
1766 	case CombineModeXor:
1767 		if (region2Empty) {
1768 			/* The XOR of the empty region and X is X */
1769 			if (region1Empty) {
1770 				return GdipSetEmpty (region);
1771 			}
1772 
1773 			return Ok;
1774 		}
1775 		if (region1Empty) {
1776 			/* The XOR of the empty region and X is X */
1777 			GdipSetEmpty (region);
1778 			return gdip_copy_region (region2, region);
1779 		}
1780 		if (region1Infinite && region2Infinite) {
1781 			/* The XOR of the infinite region and the infinite region is X */
1782 			return GdipSetEmpty (region);
1783 		}
1784 
1785 		break;
1786 	case CombineModeComplement:
1787 		if (region1Infinite || region2Empty) {
1788 			/* Nothing is outside the infinite region */
1789 			/* Nothing is inside the empty region */
1790 			return GdipSetEmpty (region);
1791 		}
1792 		if (region1Empty) {
1793 			/* Anything is outside of the empty region */
1794 			if (region2Infinite) {
1795 				return GdipSetInfinite (region);
1796 			}
1797 
1798 			GdipSetEmpty (region);
1799 			return gdip_copy_region (region2, region);
1800 		}
1801 
1802 		break;
1803 	default:
1804 		break;
1805 	}
1806 
1807 	if (region->type == RegionTypePath) {
1808 		status = gdip_region_convert_to_path (region2);
1809 		if (status != Ok)
1810 			return status;
1811 
1812 		return gdip_combine_pathbased_region (region, region2, combineMode);
1813 	} else if (region2->type == RegionTypePath) {
1814 		status = gdip_region_convert_to_path (region);
1815 		if (status != Ok)
1816 			return status;
1817 
1818 		return gdip_combine_pathbased_region (region, region2, combineMode);
1819 	}
1820 
1821 	/* at this stage we are sure that BOTH region and region2 are rectangle
1822 	 * based, so we can use the old rectangle-based code to combine regions
1823 	 */
1824 	region->type = RegionTypeRect;
1825 	switch (combineMode) {
1826 	case CombineModeExclude:
1827 		return gdip_combine_exclude (region, region2->rects, region2->cnt);
1828 	case CombineModeComplement:
1829 		return gdip_combine_complement (region, region2->rects, region2->cnt);
1830 	case CombineModeIntersect:
1831 		return gdip_combine_intersect (region, region2->rects, region2->cnt);
1832 	case CombineModeUnion:
1833 		return gdip_combine_union (region, region2->rects, region2->cnt);
1834 	case CombineModeXor:
1835 		return gdip_combine_xor (region, region2->rects, region2->cnt);
1836 	default:
1837 		return NotImplemented;
1838 	}
1839 }
1840 
1841 GpStatus WINGDIPAPI
GdipGetRegionBounds(GpRegion * region,GpGraphics * graphics,GpRectF * rect)1842 GdipGetRegionBounds (GpRegion *region, GpGraphics *graphics, GpRectF *rect)
1843 {
1844 	if (!region || !graphics || !rect)
1845 		return InvalidParameter;
1846 
1847 	switch (region->type) {
1848 	case RegionTypeRect:
1849 	case RegionTypeInfinite:
1850 		gdip_get_bounds (region->rects , region->cnt, rect);
1851 		break;
1852 	case RegionTypePath: {
1853 		GpRect bounds;
1854 
1855 		/* optimisation for simple path */
1856 		if (region->tree->path)
1857 			return GdipGetPathWorldBounds (region->tree->path, rect, NULL, NULL);
1858 
1859 		gdip_region_bitmap_ensure (region);
1860 		if (!region->bitmap)
1861 			return OutOfMemory;
1862 
1863 		/* base the bounds on the reduced bitmap of the paths */
1864 		gdip_region_bitmap_get_smallest_rect (region->bitmap, &bounds);
1865 
1866 		/* small loss of precision when converting the bitmap coord to float */
1867 		rect->X = bounds.X;
1868 		rect->Y = bounds.Y;
1869 		rect->Width = bounds.Width;
1870 		rect->Height = bounds.Height;
1871 		break;
1872 	}
1873 	default:
1874 		g_warning ("unknown type 0x%08X", region->type);
1875 		return NotImplemented;
1876 	}
1877 
1878 	return Ok;
1879 }
1880 
1881 
1882 GpStatus WINGDIPAPI
GdipIsEmptyRegion(GpRegion * region,GpGraphics * graphics,BOOL * result)1883 GdipIsEmptyRegion (GpRegion *region, GpGraphics *graphics, BOOL *result)
1884 {
1885 	if (!region || !graphics || !result)
1886 		return InvalidParameter;
1887 
1888 	*result = gdip_is_region_empty (region, /* allowNegative */ TRUE);
1889 	return Ok;
1890 }
1891 
1892 
1893 GpStatus WINGDIPAPI
GdipIsInfiniteRegion(GpRegion * region,GpGraphics * graphics,BOOL * result)1894 GdipIsInfiniteRegion (GpRegion *region, GpGraphics *graphics, BOOL *result)
1895 {
1896 	if (!region || !graphics || !result)
1897 		return InvalidParameter;
1898 
1899 	*result = gdip_is_InfiniteRegion (region);
1900 	return Ok;
1901 }
1902 
1903 
1904 GpStatus WINGDIPAPI
GdipIsVisibleRegionPoint(GpRegion * region,float x,float y,GpGraphics * graphics,BOOL * result)1905 GdipIsVisibleRegionPoint (GpRegion *region, float x, float y, GpGraphics *graphics, BOOL *result)
1906 {
1907 	if (!region || !result)
1908 		return InvalidParameter;
1909 
1910 	switch (region->type) {
1911 	case RegionTypeRect:
1912 	case RegionTypeInfinite:
1913 		*result = gdip_is_Point_in_RectFs_Visible (x, y, region->rects, region->cnt);
1914 		break;
1915 	case RegionTypePath:
1916 		gdip_region_bitmap_ensure (region);
1917 		g_assert (region->bitmap);
1918 
1919 		*result = gdip_region_bitmap_is_point_visible (region->bitmap, x, y);
1920 		break;
1921 	default:
1922 		g_warning ("unknown type 0x%08X", region->type);
1923 		return NotImplemented;
1924 	}
1925 
1926 	return Ok;
1927 }
1928 
1929 
1930 GpStatus WINGDIPAPI
GdipIsVisibleRegionPointI(GpRegion * region,int x,int y,GpGraphics * graphics,BOOL * result)1931 GdipIsVisibleRegionPointI (GpRegion *region, int x, int y, GpGraphics *graphics, BOOL *result)
1932 {
1933 	return GdipIsVisibleRegionPoint (region, x, y, graphics, result);
1934 }
1935 
1936 
1937 GpStatus WINGDIPAPI
GdipIsVisibleRegionRect(GpRegion * region,float x,float y,float width,float height,GpGraphics * graphics,BOOL * result)1938 GdipIsVisibleRegionRect (GpRegion *region, float x, float y, float width, float height, GpGraphics *graphics, BOOL *result)
1939 {
1940 	if (!region || !result)
1941 		return InvalidParameter;
1942 
1943 	if (width == 0 || height == 0) {
1944 		*result = FALSE;
1945 		return Ok;
1946 	}
1947 
1948 	switch (region->type) {
1949 	case RegionTypeRect:
1950 	case RegionTypeInfinite:
1951 		*result = gdip_is_Rect_in_RectFs_Visible (x, y, width, height, region->rects, region->cnt);
1952 		break;
1953 	case RegionTypePath: {
1954 		GpRect rect = {x, y, width, height};
1955 
1956 		gdip_region_bitmap_ensure (region);
1957 		g_assert (region->bitmap);
1958 
1959 		*result = gdip_region_bitmap_is_rect_visible (region->bitmap, &rect);
1960 		break;
1961 	}
1962 	default:
1963 		g_warning ("unknown type 0x%08X", region->type);
1964 		return NotImplemented;
1965 	}
1966 
1967 	return Ok;
1968 }
1969 
1970 
1971 GpStatus WINGDIPAPI
GdipIsVisibleRegionRectI(GpRegion * region,int x,int y,int width,int height,GpGraphics * graphics,BOOL * result)1972 GdipIsVisibleRegionRectI (GpRegion *region, int x, int y, int width, int height, GpGraphics *graphics, BOOL *result)
1973 {
1974 	return GdipIsVisibleRegionRect (region, x, y, width, height, graphics, result);
1975 }
1976 
1977 
1978 static GpStatus
get_transformed_region(GpRegion * region,GpMatrix * matrix,GpRegion ** result)1979 get_transformed_region (GpRegion *region, GpMatrix *matrix, GpRegion **result)
1980 {
1981 	GpStatus status;
1982 	GpRegion *work;
1983 
1984 	if (gdip_is_matrix_empty (matrix)) {
1985 		*result = region;
1986 		return Ok;
1987 	}
1988 
1989 	/* The matrix doesn't affect the original region - only the result */
1990 	status = GdipCloneRegion (region, &work);
1991 	if (status != Ok)
1992 		return status;
1993 
1994 	/* If required convert into a path-based region */
1995 	if (work->type != RegionTypePath) {
1996 		status = gdip_region_convert_to_path (work);
1997 		if (status != Ok) {
1998 			GdipDeleteRegion (work);
1999 			return status;
2000 		}
2001 	}
2002 
2003 	/* Transform all the paths */
2004 	status = gdip_region_transform_tree (work->tree, matrix);
2005 	if (status != Ok) {
2006 		GdipDeleteRegion (work);
2007 		return status;
2008 	}
2009 
2010 	/* Any existing bitmap has been invalidated */
2011 	gdip_region_bitmap_invalidate (work);
2012 
2013 	*result = work;
2014 	return Ok;
2015 }
2016 
2017 GpStatus WINGDIPAPI
GdipGetRegionScansCount(GpRegion * region,UINT * count,GpMatrix * matrix)2018 GdipGetRegionScansCount (GpRegion *region, UINT *count, GpMatrix *matrix)
2019 {
2020 	GpStatus status;
2021 	INT countResult;
2022 
2023 	if (!region || !matrix || !count)
2024 		return InvalidParameter;
2025 
2026 	status = GdipGetRegionScans (region, NULL, &countResult, matrix);
2027 	if (status != Ok)
2028 		return status;
2029 
2030 	*count = countResult;
2031 	return Ok;
2032 }
2033 
2034 GpStatus WINGDIPAPI
GdipGetRegionScans(GpRegion * region,GpRectF * rects,INT * count,GpMatrix * matrix)2035 GdipGetRegionScans (GpRegion *region, GpRectF* rects, INT* count, GpMatrix* matrix)
2036 {
2037 	GpStatus status;
2038 	GpRegion *work;
2039 
2040 	if (!region || !matrix || !count)
2041 		return InvalidParameter;
2042 
2043 	status = get_transformed_region (region, matrix, &work);
2044 	if (status != Ok)
2045 		return status;
2046 
2047 	if (gdip_is_region_empty (work, /* allowNegative */ TRUE)) {
2048 		*count = 0;
2049 	} else if (gdip_is_InfiniteRegion (work)) {
2050 		if (rects) {
2051 			rects->X = REGION_INFINITE_POSITION;
2052 			rects->Y = REGION_INFINITE_POSITION;
2053 			rects->Width = REGION_INFINITE_LENGTH;
2054 			rects->Height = REGION_INFINITE_LENGTH;
2055 		}
2056 
2057 		*count = 1;
2058 	} else {
2059 		switch (work->type) {
2060 		case RegionTypeRect:
2061 			if (rects) {
2062 				for (int i = 0; i < work->cnt; i++) {
2063 					GpRectF rect = work->rects[i];
2064 
2065 					INT origX = iround ((rect.X * 16.0f));
2066 					INT origY = iround ((rect.Y * 16.0f));
2067 					INT origMaxX = iround (((rect.Width + rect.X) * 16.0f));
2068 					INT origMaxY = iround (((rect.Height + rect.Y) * 16.0f));
2069 
2070 					INT x = (origX + 15) >> 4;
2071 					INT y = (origY + 15) >> 4;
2072 					INT maxX = (origMaxX + 15) >> 4;
2073 					INT maxY = (origMaxY + 15) >> 4;
2074 
2075 					rects[i].X = x;
2076 					rects[i].Y = y;
2077 					rects[i].Width = maxX - x;
2078 					rects[i].Height = maxY - y;
2079 				}
2080 			}
2081 
2082 			*count = work->cnt;
2083 			break;
2084 		case RegionTypePath:
2085 			/* ensure the bitmap is usable */
2086 			gdip_region_bitmap_ensure (work);
2087 			*count = gdip_region_bitmap_get_scans (work->bitmap, rects);
2088 			break;
2089 		default:
2090 			g_warning ("unknown type 0x%08X", region->type);
2091 			if (work != region)
2092 				GdipDeleteRegion (work);
2093 
2094 			return NotImplemented;
2095 		}
2096 	}
2097 
2098 	/* Delete the clone */
2099 	if (work != region)
2100 		GdipDeleteRegion (work);
2101 	return Ok;
2102 }
2103 
2104 GpStatus WINGDIPAPI
GdipGetRegionScansI(GpRegion * region,GpRect * rects,INT * count,GpMatrix * matrix)2105 GdipGetRegionScansI (GpRegion *region, GpRect *rects, INT *count, GpMatrix *matrix)
2106 {
2107 	GpStatus status;
2108 	GpRectF *rectsF;
2109 	UINT scansCount;
2110 
2111 	if (!region || !count || !matrix)
2112 		return InvalidParameter;
2113 
2114 	if (rects) {
2115 		status = GdipGetRegionScansCount (region, &scansCount, matrix);
2116 		if (status != Ok)
2117 			return status;
2118 
2119 		rectsF = malloc (scansCount * sizeof (GpRectF));
2120 		if (!rectsF)
2121 			return OutOfMemory;
2122 	} else {
2123 		rectsF = NULL;
2124 	}
2125 
2126 	status = GdipGetRegionScans (region, rectsF, count, matrix);
2127 	if (status != Ok)
2128 		return status;
2129 
2130 	if (rects) {
2131 		for (int i = 0; i < scansCount; i++)
2132 			gdip_Rect_from_RectF (&rectsF[i], &rects[i]);
2133 	}
2134 
2135 	return Ok;
2136 }
2137 
2138 
2139 GpStatus WINGDIPAPI
GdipIsEqualRegion(GpRegion * region,GpRegion * region2,GpGraphics * graphics,BOOL * result)2140 GdipIsEqualRegion (GpRegion *region, GpRegion *region2, GpGraphics *graphics, BOOL *result)
2141 {
2142 	int i;
2143 	GpRectF *rectsrc, *recttrg;
2144 	GpStatus status;
2145 
2146 	if (!region || !region2 || !graphics || !result)
2147 		return InvalidParameter;
2148 
2149 	/* quick case: same pointer == same region == equals */
2150 	if (region == region2) {
2151 		*result = TRUE;
2152 		return Ok;
2153 	}
2154 
2155 	BOOL region1Infinite = gdip_is_InfiniteRegion (region);
2156 	BOOL region1Empty = gdip_is_region_empty (region, /* allowNegative */ TRUE);
2157 	BOOL region2Infinite = gdip_is_InfiniteRegion (region2);
2158 	BOOL region2Empty = gdip_is_region_empty (region2, /* allowNegative */ TRUE);
2159 
2160 	if (region1Infinite || region2Infinite) {
2161 		*result = region1Infinite == region2Infinite;
2162 		return Ok;
2163 	}
2164 	if (region1Empty || region2Empty) {
2165 		*result = region1Empty == region2Empty;
2166 		return Ok;
2167 	}
2168 
2169 	if ((region->type == RegionTypePath) || (region2->type == RegionTypePath)) {
2170 		/* if required convert one region to a path based region */
2171 		if (region->type != RegionTypePath) {
2172 			status = gdip_region_convert_to_path (region);
2173 			if (status != Ok)
2174 				return status;
2175 		}
2176 
2177 		gdip_region_bitmap_ensure (region);
2178 		g_assert (region->bitmap);
2179 
2180 		if (region2->type != RegionTypePath) {
2181 			status = gdip_region_convert_to_path (region2);
2182 			if (status != Ok)
2183 				return status;
2184 		}
2185 
2186 		gdip_region_bitmap_ensure (region2);
2187 		g_assert (region2->bitmap);
2188 
2189 		*result = gdip_region_bitmap_compare (region->bitmap, region2->bitmap);
2190 		return Ok;
2191 	}
2192 
2193 	/* rectangular-based region quality test */
2194 	if (region->cnt != region2->cnt) {
2195 		*result = FALSE;
2196 		return Ok;
2197 	}
2198 
2199 	for (i = 0, rectsrc = region->rects, recttrg = region2->rects; i < region->cnt; i++, rectsrc++, recttrg++) {
2200 		if (rectsrc->X != recttrg->X || rectsrc->Y != recttrg->Y ||
2201 				rectsrc->Width != recttrg->Width || rectsrc->Height != recttrg->Height) {
2202 			*result = FALSE;
2203 			return Ok;
2204 		}
2205 	}
2206 
2207 	*result = TRUE;
2208 	return Ok;
2209 }
2210 
2211 GpStatus WINGDIPAPI
GdipTranslateRegion(GpRegion * region,float dx,float dy)2212 GdipTranslateRegion (GpRegion *region, float dx, float dy)
2213 {
2214 	if (!region)
2215 		return InvalidParameter;
2216 
2217 	// Infinite regions cannot be transformed.
2218 	if (region->type == RegionTypeInfinite)
2219 		return Ok;
2220 
2221 	switch (region->type) {
2222 	case RegionTypeRect: {
2223 		int i;
2224 		GpRectF *rect;
2225 		for (i = 0, rect = region->rects ; i < region->cnt; i++, rect++) {
2226 			rect->X += dx;
2227 			rect->Y += dy;
2228 		}
2229 
2230 		break;
2231 	}
2232 	case RegionTypePath:
2233 		gdip_region_translate_tree (region->tree, dx, dy);
2234 		if (region->bitmap) {
2235 			region->bitmap->X += dx;
2236 			region->bitmap->Y += dy;
2237 		}
2238 
2239 		break;
2240 	default:
2241 		g_warning ("unknown type 0x%08X", region->type);
2242 		return NotImplemented;
2243 	}
2244 
2245 	return Ok;
2246 }
2247 
2248 GpStatus WINGDIPAPI
GdipTranslateRegionI(GpRegion * region,int dx,int dy)2249 GdipTranslateRegionI (GpRegion *region, int dx, int dy)
2250 {
2251 	return GdipTranslateRegion (region, dx, dy);
2252 }
2253 
2254 /* this call doesn't exists in GDI+ */
2255 static GpStatus
ScaleRegion(GpRegion * region,float sx,float sy)2256 ScaleRegion (GpRegion *region, float sx, float sy)
2257 {
2258 	g_assert (region);
2259 	g_assert (region->type == RegionTypeRect && region->rects);
2260 
2261 	for (int i = 0; i < region->cnt; i++) {
2262 		region->rects[i].X *= sx;
2263 		region->rects[i].Y *= sy;
2264 		region->rects[i].Width *= sx;
2265 		region->rects[i].Height *= sy;
2266 	}
2267 
2268 	return Ok;
2269 }
2270 
2271 GpStatus WINGDIPAPI
GdipTransformRegion(GpRegion * region,GpMatrix * matrix)2272 GdipTransformRegion (GpRegion *region, GpMatrix *matrix)
2273 {
2274 	GpStatus status = Ok;
2275 
2276 	if (!region || !matrix)
2277 		return InvalidParameter;
2278 
2279 	// Infinite and empty regions cannot be transformed.
2280 	if (region->type == RegionTypeInfinite || ((region->cnt == 0) && (region->type == RegionTypeRect)))
2281 		return Ok;
2282 
2283 	// Nothing to do.
2284 	if (gdip_is_matrix_empty (matrix))
2285 		return Ok;
2286 
2287 	BOOL isSimpleMatrix = (matrix->xy == 0) && (matrix->yx == 0);
2288 	BOOL matrixHasTranslate = (matrix->x0 != 0) || (matrix->y0 != 0);
2289 	BOOL matrixHasScale = (matrix->xx != 1) || (matrix->yy != 1);
2290 
2291 	/* try to avoid heavy stuff (e.g. conversion to path, invalidating
2292 	 * bitmap...) if the transform is:
2293 	 * - a translation + scale operations (for rectangle ebased region)
2294 	 * - only to do a scale operation (for a rectangle based region)
2295 	 * - only to do a simple translation (for both rectangular and bitmap based regions)
2296 	 */
2297 	if (region->type == RegionTypeRect) {
2298 		if (isSimpleMatrix) {
2299 			if (matrixHasScale)
2300 				ScaleRegion (region, matrix->xx, matrix->yy);
2301 			if (matrixHasTranslate)
2302 				GdipTranslateRegion (region, matrix->x0, matrix->y0);
2303 
2304 			return Ok;
2305 		}
2306 	} else if (isSimpleMatrix && !matrixHasScale) {
2307 		GdipTranslateRegion (region, matrix->x0, matrix->y0);
2308 		return Ok;
2309 	}
2310 
2311 	/* most matrix operations would change the rectangles into path so we always preempt this */
2312 	if (region->type != RegionTypePath) {
2313 		status = gdip_region_convert_to_path (region);
2314 		if (status != Ok) {
2315 			gdip_region_bitmap_invalidate (region);
2316 
2317 			return status;
2318 		}
2319 	}
2320 
2321 	/* apply the same transformation matrix to all paths */
2322 	status = gdip_region_transform_tree (region->tree, matrix);
2323 
2324 	/* invalidate the bitmap so it will get re-created on the next gdip_region_bitmap_ensure call */
2325 	gdip_region_bitmap_invalidate (region);
2326 
2327 	return status;
2328 }
2329 
2330 // coverity[+alloc : arg-*1]
2331 GpStatus WINGDIPAPI
GdipCreateRegionPath(GpPath * path,GpRegion ** region)2332 GdipCreateRegionPath (GpPath *path, GpRegion **region)
2333 {
2334 	GpRegion *result;
2335 	GpStatus status;
2336 
2337 	if (!gdiplusInitialized)
2338 		return GdiplusNotInitialized;
2339 
2340 	if (!region || !path)
2341 		return InvalidParameter;
2342 
2343 	result = gdip_region_new ();
2344 	if (!result)
2345 		return OutOfMemory;
2346 
2347 	status = gdip_region_create_from_path (result, path);
2348 	if (status != Ok) {
2349 		GdipDeleteRegion (result);
2350 		return status;
2351 	}
2352 
2353 	*region = result;
2354 	return Ok;
2355 }
2356 
2357 
2358 /*
2359  * The internal data representation for RegionData depends on the type of region.
2360  *
2361  * Type 1 (RegionTypeRect), variable size
2362  *	guint32 RegionType	Always 0x10000000
2363  *	guint32 Count		0-2^32
2364  *	GpRectF[Count] Points
2365  *
2366  * Type 2 (RegionTypePath), variable size
2367  *	guint32 RegionType	Always 0x10000001
2368  *	GpPathTree tree
2369  *
2370  * Type 3 (RegionTypeInfinite)
2371  *	guint32 RegionType	Always 0x10000003.
2372  *
2373  * where GpPathTree is
2374  *	guint32 Tag		1 = Path, 2 = Tree
2375  *	data[n]
2376  *
2377  *	where data is for tag 1 (Path)
2378  *		guint32 Count		0-2^32
2379  *		GpFillMode FillMode
2380  *		guint8[Count] Types
2381  *		GpPointF[Count] Points
2382  *	or
2383  *	where data is for tag 2 (Tree)
2384  *		guint32 Operation	see CombineMode
2385  *		guint32 Size1		0-2^32
2386  *		byte[Size1]		branch #1
2387  *		guint32 Size2		0-2^32
2388  *		byte[Size2]		branch #2
2389  */
2390 
2391 GpStatus WINGDIPAPI
GdipGetRegionDataSize(GpRegion * region,UINT * bufferSize)2392 GdipGetRegionDataSize (GpRegion *region, UINT *bufferSize)
2393 {
2394 	if (!region || !bufferSize)
2395 		return InvalidParameter;
2396 
2397 	*bufferSize = sizeof (RegionHeader);
2398 
2399 	switch (region->type) {
2400 	case RegionTypeRect:
2401 		*bufferSize += sizeof (DWORD) + region->cnt * sizeof (GpRectF);
2402 		break;
2403 	case RegionTypePath:
2404 		/* regiontype, tree */
2405 		*bufferSize += sizeof (DWORD) + gdip_region_get_tree_size (region->tree);
2406 		break;
2407 	case RegionTypeInfinite:
2408 		// Only one DWORD.
2409 		*bufferSize += sizeof (DWORD);
2410 		break;
2411 	default:
2412 		g_warning ("unknown type 0x%08X", region->type);
2413 		return NotImplemented;
2414 	}
2415 	return Ok;
2416 }
2417 
2418 
2419 GpStatus WINGDIPAPI
GdipGetRegionData(GpRegion * region,BYTE * buffer,UINT bufferSize,UINT * sizeFilled)2420 GdipGetRegionData (GpRegion *region, BYTE *buffer, UINT bufferSize, UINT *sizeFilled)
2421 {
2422 	GpStatus status;
2423 	UINT size;
2424 	UINT filled = 0;
2425 	RegionHeader header;
2426 	header.combiningOps = 0;
2427 
2428 	if (!region || !buffer || !bufferSize)
2429 		return InvalidParameter;
2430 
2431 	status = GdipGetRegionDataSize (region, &size);
2432 	if (status != Ok)
2433 		return status;
2434 	if (size > bufferSize)
2435 		return InsufficientBuffer;
2436 
2437 	/* Write the region header at the end, as we need to calculate a checksum based off all the data. */
2438 	filled += sizeof (RegionHeader);
2439 
2440 	switch (region->type) {
2441 	case RegionTypeRect: {
2442 		DWORD type;
2443 
2444 		if (region->cnt) {
2445 			type = RegionDataRect;
2446 			memcpy (buffer + filled, &type, sizeof (DWORD));
2447 			filled += sizeof (DWORD);
2448 
2449 			memcpy (buffer + filled, region->rects, region->cnt * sizeof (GpRectF));
2450 			filled += region->cnt * sizeof (GpRectF);
2451 		} else {
2452 			type = RegionDataEmptyRect;
2453 			memcpy (buffer + filled, &type, sizeof (DWORD));
2454 
2455 			filled += sizeof (DWORD);
2456 		}
2457 
2458 		break;
2459 	}
2460 	case RegionTypePath: {
2461 		DWORD type = RegionDataPath;
2462 		memcpy (buffer + filled, &type, sizeof (DWORD));
2463 		filled += sizeof (DWORD);
2464 
2465 		if (!gdip_region_serialize_tree (region->tree, buffer + filled, bufferSize - filled, &filled))
2466 			return InsufficientBuffer;
2467 		break;
2468 	}
2469 	case RegionTypeInfinite: {
2470 		DWORD type = RegionDataInfiniteRect;
2471 		memcpy (buffer + filled, &type, sizeof (DWORD));
2472 		filled += sizeof (DWORD);
2473 		break;
2474 	}
2475 	default:
2476 		g_warning ("unknown type 0x%08X", region->type);
2477 		return NotImplemented;
2478 	}
2479 
2480 	/* Write the header at the start of the buffer. */
2481 	header.size = filled - 8;
2482 	header.magic = 0xdbc01002;
2483 	header.combiningOps = 0;
2484 	memcpy (buffer, &header, sizeof (RegionHeader));
2485 
2486 	/* Finally, write the checksum. */
2487 	header.checksum = gdip_crc32 (buffer + 8, filled - 8);
2488 	memcpy (buffer + 4, &header.checksum, sizeof (DWORD));
2489 
2490 	if (sizeFilled)
2491 		*sizeFilled = filled;
2492 
2493 	return Ok;
2494 }
2495 
2496 GpStatus WINGDIPAPI
GdipGetRegionHRgn(GpRegion * region,GpGraphics * graphics,HRGN * hRgn)2497 GdipGetRegionHRgn (GpRegion *region, GpGraphics *graphics, HRGN *hRgn)
2498 {
2499 	if (!region || !graphics || !hRgn)
2500 		return InvalidParameter;
2501 
2502 	/* infinite region returns NULL */
2503 	if (gdip_is_InfiniteRegion (region)) {
2504 		*hRgn = NULL;
2505 		return Ok;
2506 	}
2507 
2508 	/* calling GdipGetRegionHRgn multiple times returns a different HRNG value
2509 	   (i.e. each to be freed separately) */
2510 	return GdipCloneRegion (region, (GpRegion**)hRgn);
2511 }
2512 
2513 // coverity[+alloc : arg-*1]
2514 GpStatus WINGDIPAPI
GdipCreateRegionHrgn(HRGN hRgn,GpRegion ** region)2515 GdipCreateRegionHrgn (HRGN hRgn, GpRegion **region)
2516 {
2517 	if (!hRgn || !region)
2518 		return InvalidParameter;
2519 
2520 	return GdipCloneRegion ((GpRegion*) hRgn, region);
2521 }
2522