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, ®ion->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 (®ion->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, ®ion->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 (®ion->rects, ®ion->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, ¤t, &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 (¤t, recttrg) == FALSE
887 || gdip_equals (¤t, 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 (¤t, 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, ¤t)) {
967 gdip_add_rect_to_array_notcontained (&rects, &cnt, &cap, ¤t);
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 (®src, 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 (®union, &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 (®ion->rects, ®ion->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 (®ion->rects, ®ion->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 (®ion->rects, ®ion->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 (®ion->rects, ®ion->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, ®ion->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, ®ion->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, ®ion1->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