FreeRDP
Loading...
Searching...
No Matches
codec/region.c
1
22#include <winpr/assert.h>
23#include <winpr/memory.h>
24#include <freerdp/log.h>
25#include <freerdp/codec/region.h>
26
27#define TAG FREERDP_TAG("codec")
28
29/*
30 * The functions in this file implement the Region abstraction largely inspired from
31 * pixman library. The following comment is taken from the pixman code.
32 *
33 * A Region is simply a set of disjoint(non-overlapping) rectangles, plus an
34 * "extent" rectangle which is the smallest single rectangle that contains all
35 * the non-overlapping rectangles.
36 *
37 * A Region is implemented as a "y-x-banded" array of rectangles. This array
38 * imposes two degrees of order. First, all rectangles are sorted by top side
39 * y coordinate first (y1), and then by left side x coordinate (x1).
40 *
41 * Furthermore, the rectangles are grouped into "bands". Each rectangle in a
42 * band has the same top y coordinate (y1), and each has the same bottom y
43 * coordinate (y2). Thus all rectangles in a band differ only in their left
44 * and right side (x1 and x2). Bands are implicit in the array of rectangles:
45 * there is no separate list of band start pointers.
46 *
47 * The y-x band representation does not minimize rectangles. In particular,
48 * if a rectangle vertically crosses a band (the rectangle has scanlines in
49 * the y1 to y2 area spanned by the band), then the rectangle may be broken
50 * down into two or more smaller rectangles stacked one atop the other.
51 *
52 * ----------- -----------
53 * | | | | band 0
54 * | | -------- ----------- --------
55 * | | | | in y-x banded | | | | band 1
56 * | | | | form is | | | |
57 * ----------- | | ----------- --------
58 * | | | | band 2
59 * -------- --------
60 *
61 * An added constraint on the rectangles is that they must cover as much
62 * horizontal area as possible: no two rectangles within a band are allowed
63 * to touch.
64 *
65 * Whenever possible, bands will be merged together to cover a greater vertical
66 * distance (and thus reduce the number of rectangles). Two bands can be merged
67 * only if the bottom of one touches the top of the other and they have
68 * rectangles in the same places (of the same width, of course).
69 */
70
71struct S_REGION16_DATA
72{
73 size_t nbRects;
74 RECTANGLE_16* rects;
75};
76
77void region16_init(REGION16* region)
78{
79 WINPR_ASSERT(region);
80
81 const REGION16 empty = WINPR_C_ARRAY_INIT;
82 *region = empty;
83}
84
85int region16_n_rects(const REGION16* region)
86{
87 WINPR_ASSERT(region);
88 if (!region->data)
89 return 0;
90
91 return WINPR_ASSERTING_INT_CAST(int, region->data->nbRects);
92}
93
94const RECTANGLE_16* region16_rects(const REGION16* region, UINT32* nbRects)
95{
96 if (nbRects)
97 *nbRects = 0;
98
99 if (!region)
100 return nullptr;
101
102 REGION16_DATA* data = region->data;
103 if (!data)
104 return nullptr;
105
106 if (nbRects)
107 *nbRects = WINPR_ASSERTING_INT_CAST(UINT32, data->nbRects);
108
109 return data->rects;
110}
111
112static inline RECTANGLE_16* region16_rects_noconst(REGION16* region)
113{
114 WINPR_ASSERT(region);
115
116 REGION16_DATA* data = region->data;
117
118 if (!data)
119 return nullptr;
120
121 return data->rects;
122}
123
124const RECTANGLE_16* region16_extents(const REGION16* region)
125{
126 if (!region)
127 return nullptr;
128
129 return &region->extents;
130}
131
132static RECTANGLE_16* region16_extents_noconst(REGION16* region)
133{
134 if (!region)
135 return nullptr;
136
137 return &region->extents;
138}
139
140BOOL rectangle_is_empty(const RECTANGLE_16* rect)
141{
142 WINPR_ASSERT(rect);
143
144 /* A rectangle with width <= 0 or height <= 0 should be regarded
145 * as empty.
146 */
147 return ((rect->left >= rect->right) || (rect->top >= rect->bottom));
148}
149
150BOOL region16_is_empty(const REGION16* region)
151{
152 WINPR_ASSERT(region);
153 if (!region->data)
154 return TRUE;
155 return (region->data->nbRects == 0);
156}
157
158BOOL rectangles_equal(const RECTANGLE_16* r1, const RECTANGLE_16* r2)
159{
160 WINPR_ASSERT(r1);
161 WINPR_ASSERT(r2);
162
163 return ((r1->left == r2->left) && (r1->top == r2->top) && (r1->right == r2->right) &&
164 (r1->bottom == r2->bottom));
165}
166
167BOOL rectangles_intersects(const RECTANGLE_16* r1, const RECTANGLE_16* r2)
168{
169 RECTANGLE_16 tmp = WINPR_C_ARRAY_INIT;
170 return rectangles_intersection(r1, r2, &tmp);
171}
172
173BOOL rectangles_intersection(const RECTANGLE_16* r1, const RECTANGLE_16* r2, RECTANGLE_16* dst)
174{
175 WINPR_ASSERT(r1);
176 WINPR_ASSERT(r2);
177 WINPR_ASSERT(dst);
178
179 dst->left = MAX(r1->left, r2->left);
180 dst->right = MIN(r1->right, r2->right);
181 dst->top = MAX(r1->top, r2->top);
182 dst->bottom = MIN(r1->bottom, r2->bottom);
183 return (dst->left < dst->right) && (dst->top < dst->bottom);
184}
185
186const char* rectangle_to_string(const RECTANGLE_16* rect, char* buffer, size_t length)
187{
188 if (!buffer || (length < 2))
189 return nullptr;
190 if (!rect)
191 (void)_snprintf(buffer, length - 1, "{ nullptr }");
192 else
193 (void)_snprintf(buffer, length - 1, "{%" PRIu16 "x%" PRIu16 "-%" PRIu16 "x%" PRIu16 "}",
194 rect->left, rect->top, rect->right, rect->bottom);
195 return buffer;
196}
197
198RECTANGLE_16* rectangles_clone(const RECTANGLE_16* rects, size_t count)
199{
200 if (count == 0)
201 return nullptr;
202 if (count > SIZE_MAX / sizeof(RECTANGLE_16))
203 return nullptr;
204
205 WINPR_ASSERT(rects);
206
207 RECTANGLE_16* clone = calloc(count, sizeof(RECTANGLE_16));
208 if (!clone)
209 return nullptr;
210
211 memcpy(clone, rects, sizeof(RECTANGLE_16) * count);
212 return clone;
213}
214
215static void freeRegion(REGION16_DATA* data)
216{
217 if (data)
218 free(data->rects);
219 free(data);
220}
221
222void region16_clear(REGION16* region)
223{
224 WINPR_ASSERT(region);
225
226 freeRegion(region->data);
227 region->data = nullptr;
228
229 const RECTANGLE_16 empty = WINPR_C_ARRAY_INIT;
230 region->extents = empty;
231}
232
233WINPR_ATTR_MALLOC(freeRegion, 1)
234WINPR_ATTR_NODISCARD
235static REGION16_DATA* allocateRegion(size_t nbItems)
236{
237 REGION16_DATA* data = calloc(1, sizeof(REGION16_DATA));
238 if (!data)
239 return nullptr;
240
241 if (nbItems > 0)
242 {
243 data->rects = calloc(nbItems, sizeof(RECTANGLE_16));
244 if (!data->rects)
245 {
246 free(data);
247 return nullptr;
248 }
249 }
250
251 data->nbRects = nbItems;
252 return data;
253}
254
255static inline RECTANGLE_16* nextRect(REGION16_DATA* data, size_t index)
256{
257 WINPR_ASSERT(data);
258 if (index + 1 > data->nbRects)
259 {
260 RECTANGLE_16* rects = realloc(data->rects, (index + 1) * sizeof(RECTANGLE_16));
261 if (!rects)
262 {
263 freeRegion(data);
264 return nullptr;
265 }
266
267 const RECTANGLE_16 empty = WINPR_C_ARRAY_INIT;
268 rects[index] = empty;
269 data->nbRects = index + 1;
270 data->rects = rects;
271 }
272 return &data->rects[index];
273}
274
275static BOOL resizeRegion(REGION16* region, size_t nbItems)
276{
277 WINPR_ASSERT(region);
278 if (nbItems == 0)
279 {
280 freeRegion(region->data);
281 region->data = nullptr;
282 return TRUE;
283 }
284
285 if (!region->data)
286 {
287 region->data = allocateRegion(nbItems);
288 return region->data != nullptr;
289 }
290
291 RECTANGLE_16* rects = realloc(region->data->rects, nbItems * sizeof(RECTANGLE_16));
292 if (!rects)
293 {
294 free(region->data->rects);
295 region->data->nbRects = 0;
296 region->data->rects = nullptr;
297 return FALSE;
298 }
299
300 for (size_t x = region->data->nbRects; x < nbItems; x++)
301 {
302 const RECTANGLE_16 empty = WINPR_C_ARRAY_INIT;
303 rects[x] = empty;
304 }
305 region->data->rects = rects;
306 region->data->nbRects = nbItems;
307 return TRUE;
308}
309
310static inline BOOL region16_copy_data(REGION16* dst, const REGION16* src)
311{
312 WINPR_ASSERT(dst);
313 WINPR_ASSERT(src);
314
315 freeRegion(dst->data);
316 dst->data = nullptr;
317
318 if (src->data && (src->data->nbRects > 0))
319 {
320 dst->data = allocateRegion(src->data->nbRects);
321 if (!dst->data || !dst->data->rects)
322 return FALSE;
323 memcpy(dst->data->rects, src->data->rects, dst->data->nbRects * sizeof(RECTANGLE_16));
324 }
325 return TRUE;
326}
327
328BOOL region16_copy(REGION16* dst, const REGION16* src)
329{
330 if (dst == src)
331 return TRUE;
332
333 WINPR_ASSERT(dst);
334 WINPR_ASSERT(src);
335
336 dst->extents = src->extents;
337
338 return region16_copy_data(dst, src);
339}
340
341void region16_print(const REGION16* region)
342{
343 UINT32 nbRects = 0;
344 int currentBandY = -1;
345 const RECTANGLE_16* rects = region16_rects(region, &nbRects);
346
347 WLog_DBG(TAG, "nrects=%" PRIu32 "", nbRects);
348
349 for (UINT32 i = 0; i < nbRects; i++)
350 {
351 const RECTANGLE_16* rect = &rects[i];
352
353 if (rect->top != currentBandY)
354 {
355 currentBandY = rect->top;
356 WLog_DBG(TAG, "band %d: ", currentBandY);
357 }
358
359 WLog_DBG(TAG, "(%" PRIu16 ",%" PRIu16 "-%" PRIu16 ",%" PRIu16 ")", rect->left, rect->top,
360 rect->right, rect->bottom);
361 }
362}
363
364char* region16_to_string(const REGION16* region)
365{
366 if (!region)
367 return _strdup("REGION16{nullptr}");
368
369 UINT32 nbRects = 0;
370 const RECTANGLE_16* rects = region16_rects(region, &nbRects);
371
372 char* str = nullptr;
373 size_t slen = 0;
374 winpr_asprintf(&str, &slen, "REGION16{nrects=%" PRIu32 " [", nbRects);
375
376 UINT16 currentBandY = 0;
377 for (UINT32 i = 0; i < nbRects; i++)
378 {
379 const RECTANGLE_16* rect = &rects[i];
380
381 if (rect->top != currentBandY)
382 {
383 currentBandY = rect->top;
384 char* tmp = nullptr;
385 winpr_asprintf(&tmp, &slen, "%sband %" PRIu16 ":", str, currentBandY);
386 free(str);
387 str = tmp;
388 }
389
390 char buffer[64] = WINPR_C_ARRAY_INIT;
391 char* tmp = nullptr;
392 winpr_asprintf(&tmp, &slen, "%s, %s", str,
393 rectangle_to_string(rect, buffer, sizeof(buffer)));
394 free(str);
395 str = tmp;
396 }
397
398 char* tmp = nullptr;
399 winpr_asprintf(&tmp, &slen, "%s]}", str);
400 free(str);
401 return tmp;
402}
403
404static BOOL region16_copy_band_with_union(REGION16_DATA* region, const RECTANGLE_16* src,
405 const RECTANGLE_16* end, UINT16 newTop, UINT16 newBottom,
406 const RECTANGLE_16* unionRect, UINT32* dstCounter,
407 const RECTANGLE_16** srcPtr)
408{
409 WINPR_ASSERT(region);
410 WINPR_ASSERT(src);
411 WINPR_ASSERT(end);
412 WINPR_ASSERT(dstCounter);
413
414 UINT16 refY = src->top;
415
416 /* merges a band with the given rect
417 * Input:
418 * unionRect
419 * | |
420 * | |
421 * ==============+===============+================================
422 * |Item1| |Item2| |Item3| |Item4| |Item5| Band
423 * ==============+===============+================================
424 * before | overlap | after
425 *
426 * Resulting band:
427 * +-----+ +----------------------+ +-----+
428 * |Item1| | Item2 | |Item3|
429 * +-----+ +----------------------+ +-----+
430 *
431 * We first copy as-is items that are before Item2, the first overlapping
432 * item.
433 * Then we find the last one that overlap unionRect to aggregate Item2, Item3
434 * and Item4 to create Item2.
435 * Finally Item5 is copied as Item3.
436 *
437 * When no unionRect is provided, we skip the two first steps to just copy items
438 */
439
440 if (unionRect)
441 {
442 /* items before unionRect */
443 while ((src < end) && (src->top == refY) && (src->right < unionRect->left))
444 {
445 RECTANGLE_16* dst = nextRect(region, (*dstCounter)++);
446 if (!dst)
447 return FALSE;
448 dst->top = newTop;
449 dst->bottom = newBottom;
450 dst->right = src->right;
451 dst->left = src->left;
452 src++;
453 }
454
455 /* treat items overlapping with unionRect */
456 const RECTANGLE_16* startOverlap = unionRect;
457 const RECTANGLE_16* endOverlap = unionRect;
458
459 if ((src < end) && (src->top == refY) && (src->left < unionRect->left))
460 startOverlap = src;
461
462 while ((src < end) && (src->top == refY) && (src->right < unionRect->right))
463 {
464 src++;
465 }
466
467 if ((src < end) && (src->top == refY) && (src->left < unionRect->right))
468 {
469 endOverlap = src;
470 src++;
471 }
472
473 {
474 RECTANGLE_16* dst = nextRect(region, (*dstCounter)++);
475 if (!dst)
476 return FALSE;
477 dst->bottom = newBottom;
478 dst->top = newTop;
479 dst->left = startOverlap->left;
480 dst->right = endOverlap->right;
481 }
482 }
483
484 /* treat remaining items on the same band */
485 while ((src < end) && (src->top == refY))
486 {
487 RECTANGLE_16* dst = nextRect(region, (*dstCounter)++);
488 if (!dst)
489 return FALSE;
490
491 dst->top = newTop;
492 dst->bottom = newBottom;
493 dst->right = src->right;
494 dst->left = src->left;
495 src++;
496 }
497
498 if (srcPtr)
499 *srcPtr = src;
500
501 return TRUE;
502}
503
504static RECTANGLE_16* next_band(RECTANGLE_16* band1, RECTANGLE_16* endPtr, int* nbItems)
505{
506 WINPR_ASSERT(band1);
507 WINPR_ASSERT(endPtr);
508 WINPR_ASSERT(nbItems);
509
510 UINT16 refY = band1->top;
511 *nbItems = 0;
512
513 while ((band1 < endPtr) && (band1->top == refY))
514 {
515 band1++;
516 *nbItems += 1;
517 }
518
519 return band1;
520}
521
522static BOOL band_match(const RECTANGLE_16* band1, const RECTANGLE_16* band2,
523 const RECTANGLE_16* endPtr)
524{
525 int refBand2 = band2->top;
526 const RECTANGLE_16* band2Start = band2;
527
528 while ((band1 < band2Start) && (band2 < endPtr) && (band2->top == refBand2))
529 {
530 if ((band1->left != band2->left) || (band1->right != band2->right))
531 return FALSE;
532
533 band1++;
534 band2++;
535 }
536
537 if (band1 != band2Start)
538 return FALSE;
539
540 return (band2 == endPtr) || (band2->top != refBand2);
541}
542
549static BOOL rectangle_contained_in_band(const RECTANGLE_16* band, const RECTANGLE_16* endPtr,
550 const RECTANGLE_16* rect)
551{
552 WINPR_ASSERT(band);
553 WINPR_ASSERT(endPtr);
554 WINPR_ASSERT(rect);
555
556 UINT16 refY = band->top;
557
558 if ((band->top > rect->top) || (rect->bottom > band->bottom))
559 return FALSE;
560
561 /* note: as the band is sorted from left to right, once we've seen an item
562 * that is after rect->left we're sure that the result is False.
563 */
564 while ((band < endPtr) && (band->top == refY) && (band->left <= rect->left))
565 {
566 if (rect->right <= band->right)
567 return TRUE;
568
569 band++;
570 }
571
572 return FALSE;
573}
574
575static BOOL region16_simplify_bands(REGION16* region)
576{
589 const int nbRects = region16_n_rects(region);
590 int finalNbRects = nbRects;
591
592 if (nbRects < 2)
593 return TRUE;
594
595 RECTANGLE_16* band1 = region16_rects_noconst(region);
596 RECTANGLE_16* endPtr = band1 + nbRects;
597
598 do
599 {
600 int bandItems = 0;
601 RECTANGLE_16* band2 = next_band(band1, endPtr, &bandItems);
602
603 if (band2 == endPtr)
604 break;
605
606 if ((band1->bottom == band2->top) && band_match(band1, band2, endPtr))
607 {
608 /* adjust the bottom of band1 items */
609 RECTANGLE_16* tmp = band1;
610
611 while (tmp < band2)
612 {
613 tmp->bottom = band2->bottom;
614 tmp++;
615 }
616
617 /* override band2, we don't move band1 pointer as the band after band2
618 * may be merged too */
619 const RECTANGLE_16* endBand = band2 + bandItems;
620 const size_t toMove =
621 WINPR_ASSERTING_INT_CAST(size_t, (endPtr - endBand)) * sizeof(RECTANGLE_16);
622
623 if (toMove)
624 MoveMemory(band2, endBand, toMove);
625
626 finalNbRects -= bandItems;
627 endPtr -= bandItems;
628 }
629 else
630 {
631 band1 = band2;
632 }
633 } while (TRUE);
634
635 if (finalNbRects != nbRects)
636 {
637 if (!resizeRegion(region, WINPR_ASSERTING_INT_CAST(size_t, finalNbRects)))
638 return FALSE;
639 }
640
641 return TRUE;
642}
643
644BOOL region16_union_rect(REGION16* dst, const REGION16* src, const RECTANGLE_16* rect)
645{
646 const RECTANGLE_16* nextBand = nullptr;
647 UINT32 srcNbRects = 0;
648 UINT16 topInterBand = 0;
649 WINPR_ASSERT(src);
650 WINPR_ASSERT(dst);
651
652 const RECTANGLE_16* srcExtents = region16_extents(src);
653 RECTANGLE_16* dstExtents = region16_extents_noconst(dst);
654
655 const int nrSrcRects = region16_n_rects(src);
656 if (nrSrcRects == 0)
657 {
658 /* source is empty, so the union is rect */
659 dst->extents = *rect;
660
661 if (!resizeRegion(dst, 1))
662 return FALSE;
663
664 RECTANGLE_16* dstRect = region16_rects_noconst(dst);
665 WINPR_ASSERT(dstRect);
666
667 dstRect->top = rect->top;
668 dstRect->left = rect->left;
669 dstRect->right = rect->right;
670 dstRect->bottom = rect->bottom;
671 dst->data->nbRects = 1;
672 return TRUE;
673 }
674
675 REGION16_DATA* newItems = allocateRegion(WINPR_ASSERTING_INT_CAST(size_t, nrSrcRects + 1));
676
677 if (!newItems)
678 return FALSE;
679
680 UINT32 usedRects = 0;
681
682 /* adds the piece of rect that is on the top of src */
683 if (rect->top < srcExtents->top)
684 {
685 RECTANGLE_16* dstRect = nextRect(newItems, usedRects++);
686 if (!dstRect)
687 return FALSE;
688
689 dstRect->top = rect->top;
690 dstRect->left = rect->left;
691 dstRect->right = rect->right;
692 dstRect->bottom = MIN(srcExtents->top, rect->bottom);
693 }
694
695 /* treat possibly overlapping region */
696 const RECTANGLE_16* currentBand = region16_rects(src, &srcNbRects);
697 const RECTANGLE_16* endSrcRect = currentBand + srcNbRects;
698
699 while (currentBand < endSrcRect)
700 {
701 if ((currentBand->bottom <= rect->top) || (rect->bottom <= currentBand->top) ||
702 rectangle_contained_in_band(currentBand, endSrcRect, rect))
703 {
704 /* no overlap between rect and the band, rect is totally below or totally above
705 * the current band, or rect is already covered by an item of the band.
706 * let's copy all the rectangles from this band
707 +----+
708 | | rect (case 1)
709 +----+
710
711 =================
712 band of srcRect
713 =================
714 +----+
715 | | rect (case 2)
716 +----+
717 */
718 if (!region16_copy_band_with_union(newItems, currentBand, endSrcRect, currentBand->top,
719 currentBand->bottom, nullptr, &usedRects, &nextBand))
720 return FALSE;
721 topInterBand = rect->top;
722 }
723 else
724 {
725 /* rect overlaps the band:
726 | | | |
727 ====^=================| |==| |=========================== band
728 | top split | | | |
729 v | 1 | | 2 |
730 ^ | | | | +----+ +----+
731 | merge zone | | | | | | | 4 |
732 v +----+ | | | | +----+
733 ^ | | | 3 |
734 | bottom split | | | |
735 ====v=========================| |==| |===================
736 | | | |
737
738 possible cases:
739 1) no top split, merge zone then a bottom split. The band will be split
740 in two
741 2) not band split, only the merge zone, band merged with rect but not split
742 3) a top split, the merge zone and no bottom split. The band will be split
743 in two
744 4) a top split, the merge zone and also a bottom split. The band will be
745 split in 3, but the coalesce algorithm may merge the created bands
746 */
747 UINT16 mergeTop = currentBand->top;
748 UINT16 mergeBottom = currentBand->bottom;
749
750 /* test if we need a top split, case 3 and 4 */
751 if (rect->top > currentBand->top)
752 {
753 if (!region16_copy_band_with_union(newItems, currentBand, endSrcRect,
754 currentBand->top, rect->top, nullptr, &usedRects,
755 &nextBand))
756 return FALSE;
757 mergeTop = rect->top;
758 }
759
760 /* do the merge zone (all cases) */
761 if (rect->bottom < currentBand->bottom)
762 mergeBottom = rect->bottom;
763
764 if (!region16_copy_band_with_union(newItems, currentBand, endSrcRect, mergeTop,
765 mergeBottom, rect, &usedRects, &nextBand))
766 return FALSE;
767
768 /* test if we need a bottom split, case 1 and 4 */
769 if (rect->bottom < currentBand->bottom)
770 {
771 if (!region16_copy_band_with_union(newItems, currentBand, endSrcRect, mergeBottom,
772 currentBand->bottom, nullptr, &usedRects,
773 &nextBand))
774 return FALSE;
775 }
776
777 topInterBand = currentBand->bottom;
778 }
779
780 /* test if a piece of rect should be inserted as a new band between
781 * the current band and the next one. band n and n+1 shouldn't touch.
782 *
783 * ==============================================================
784 * band n
785 * +------+ +------+
786 * ===========| rect |====================| |===============
787 * | | +------+ | |
788 * +------+ | rect | | rect |
789 * +------+ | |
790 * =======================================| |================
791 * +------+ band n+1
792 * ===============================================================
793 *
794 */
795 if ((nextBand < endSrcRect) && (nextBand->top != currentBand->bottom) &&
796 (rect->bottom > currentBand->bottom) && (rect->top < nextBand->top))
797 {
798 RECTANGLE_16* dstRect = nextRect(newItems, usedRects++);
799 if (!dstRect)
800 return FALSE;
801
802 dstRect->right = rect->right;
803 dstRect->left = rect->left;
804 dstRect->top = topInterBand;
805 dstRect->bottom = MIN(nextBand->top, rect->bottom);
806 }
807
808 currentBand = nextBand;
809 }
810
811 /* adds the piece of rect that is below src */
812 if (srcExtents->bottom < rect->bottom)
813 {
814 RECTANGLE_16* dstRect = nextRect(newItems, usedRects++);
815 if (!dstRect)
816 return FALSE;
817
818 dstRect->top = MAX(srcExtents->bottom, rect->top);
819 dstRect->left = rect->left;
820 dstRect->right = rect->right;
821 dstRect->bottom = rect->bottom;
822 }
823
824 dstExtents->top = MIN(rect->top, srcExtents->top);
825 dstExtents->left = MIN(rect->left, srcExtents->left);
826 dstExtents->bottom = MAX(rect->bottom, srcExtents->bottom);
827 dstExtents->right = MAX(rect->right, srcExtents->right);
828
829 newItems->nbRects = usedRects;
830 freeRegion(dst->data);
831 dst->data = newItems;
832
833 return region16_simplify_bands(dst);
834}
835
836BOOL region16_intersects_rect(const REGION16* src, const RECTANGLE_16* arg2)
837{
838 const RECTANGLE_16* endPtr = nullptr;
839 UINT32 nbRects = 0;
840
841 if (!src || !src->data || !arg2)
842 return FALSE;
843
844 const RECTANGLE_16* rect = region16_rects(src, &nbRects);
845
846 if (!nbRects)
847 return FALSE;
848
849 const RECTANGLE_16* srcExtents = region16_extents(src);
850
851 if (nbRects == 1)
852 return rectangles_intersects(srcExtents, arg2);
853
854 if (!rectangles_intersects(srcExtents, arg2))
855 return FALSE;
856
857 for (endPtr = rect + nbRects; (rect < endPtr) && (arg2->bottom > rect->top); rect++)
858 {
859 if (rectangles_intersects(rect, arg2))
860 return TRUE;
861 }
862
863 return FALSE;
864}
865
866BOOL region16_intersect_rect(REGION16* dst, const REGION16* src, const RECTANGLE_16* rect)
867{
868 const RECTANGLE_16* endPtr = nullptr;
869 UINT32 nbRects = 0;
870 RECTANGLE_16 common = WINPR_C_ARRAY_INIT;
871
872 WINPR_ASSERT(dst);
873 WINPR_ASSERT(src);
874
875 const RECTANGLE_16* srcPtr = region16_rects(src, &nbRects);
876
877 if (!nbRects)
878 {
879 region16_clear(dst);
880 return TRUE;
881 }
882
883 const RECTANGLE_16* srcExtents = region16_extents(src);
884
885 if (nbRects == 1)
886 {
887 BOOL intersects = rectangles_intersection(srcExtents, rect, &common);
888 region16_clear(dst);
889
890 if (intersects)
891 return region16_union_rect(dst, dst, &common);
892
893 return TRUE;
894 }
895
896 REGION16_DATA* newItems = allocateRegion(nbRects);
897
898 if (!newItems)
899 return FALSE;
900
901 RECTANGLE_16* dstPtr = newItems->rects;
902 UINT32 usedRects = 0;
903 RECTANGLE_16 newExtents = WINPR_C_ARRAY_INIT;
904
905 /* accumulate intersecting rectangles, the final region16_simplify_bands() will
906 * do all the bad job to recreate correct rectangles
907 */
908 for (endPtr = srcPtr + nbRects; (srcPtr < endPtr) && (rect->bottom > srcPtr->top); srcPtr++)
909 {
910 if (usedRects > nbRects)
911 {
912 freeRegion(newItems);
913 return FALSE;
914 }
915
916 if (rectangles_intersection(srcPtr, rect, &common))
917 {
918 *dstPtr = common;
919 usedRects++;
920 dstPtr++;
921
922 if (rectangle_is_empty(&newExtents))
923 {
924 /* Check if the existing newExtents is empty. If it is empty, use
925 * new common directly. We do not need to check common rectangle
926 * because the rectangles_intersection() ensures that it is not empty.
927 */
928 newExtents = common;
929 }
930 else
931 {
932 newExtents.top = MIN(common.top, newExtents.top);
933 newExtents.left = MIN(common.left, newExtents.left);
934 newExtents.bottom = MAX(common.bottom, newExtents.bottom);
935 newExtents.right = MAX(common.right, newExtents.right);
936 }
937 }
938 }
939
940 newItems->nbRects = usedRects;
941
942 freeRegion(dst->data);
943 dst->data = newItems;
944 dst->extents = newExtents;
945 return region16_simplify_bands(dst);
946}
947
948void region16_uninit(REGION16* region)
949{
950 WINPR_ASSERT(region);
951
952 freeRegion(region->data);
953 region->data = nullptr;
954}