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
186static void freeRegion(REGION16_DATA* data)
187{
188 if (data)
189 free(data->rects);
190 free(data);
191}
192
193void region16_clear(REGION16* region)
194{
195 WINPR_ASSERT(region);
196
197 freeRegion(region->data);
198 region->data = nullptr;
199
200 const RECTANGLE_16 empty = WINPR_C_ARRAY_INIT;
201 region->extents = empty;
202}
203
204WINPR_ATTR_MALLOC(freeRegion, 1)
205WINPR_ATTR_NODISCARD
206static REGION16_DATA* allocateRegion(size_t nbItems)
207{
208 REGION16_DATA* data = calloc(1, sizeof(REGION16_DATA));
209 if (!data)
210 return nullptr;
211
212 if (nbItems > 0)
213 {
214 data->rects = calloc(nbItems, sizeof(RECTANGLE_16));
215 if (!data->rects)
216 {
217 free(data);
218 return nullptr;
219 }
220 }
221
222 data->nbRects = nbItems;
223 return data;
224}
225
226static inline RECTANGLE_16* nextRect(REGION16_DATA* data, size_t index)
227{
228 WINPR_ASSERT(data);
229 if (index + 1 > data->nbRects)
230 {
231 RECTANGLE_16* rects = realloc(data->rects, (index + 1) * sizeof(RECTANGLE_16));
232 if (!rects)
233 {
234 freeRegion(data);
235 return nullptr;
236 }
237
238 const RECTANGLE_16 empty = WINPR_C_ARRAY_INIT;
239 rects[index] = empty;
240 data->nbRects = index + 1;
241 data->rects = rects;
242 }
243 return &data->rects[index];
244}
245
246static BOOL resizeRegion(REGION16* region, size_t nbItems)
247{
248 WINPR_ASSERT(region);
249 if (nbItems == 0)
250 {
251 freeRegion(region->data);
252 region->data = nullptr;
253 return TRUE;
254 }
255
256 if (!region->data)
257 {
258 region->data = allocateRegion(nbItems);
259 return region->data != nullptr;
260 }
261
262 RECTANGLE_16* rects = realloc(region->data->rects, nbItems * sizeof(RECTANGLE_16));
263 if (!rects)
264 {
265 free(region->data->rects);
266 region->data->nbRects = 0;
267 region->data->rects = nullptr;
268 return FALSE;
269 }
270
271 for (size_t x = region->data->nbRects; x < nbItems; x++)
272 {
273 const RECTANGLE_16 empty = WINPR_C_ARRAY_INIT;
274 rects[x] = empty;
275 }
276 region->data->rects = rects;
277 region->data->nbRects = nbItems;
278 return TRUE;
279}
280
281static inline BOOL region16_copy_data(REGION16* dst, const REGION16* src)
282{
283 WINPR_ASSERT(dst);
284 WINPR_ASSERT(src);
285
286 freeRegion(dst->data);
287 dst->data = nullptr;
288
289 if (src->data && (src->data->nbRects > 0))
290 {
291 dst->data = allocateRegion(src->data->nbRects);
292 if (!dst->data || !dst->data->rects)
293 return FALSE;
294 memcpy(dst->data->rects, src->data->rects, dst->data->nbRects * sizeof(RECTANGLE_16));
295 }
296 return TRUE;
297}
298
299BOOL region16_copy(REGION16* dst, const REGION16* src)
300{
301 if (dst == src)
302 return TRUE;
303
304 WINPR_ASSERT(dst);
305 WINPR_ASSERT(src);
306
307 dst->extents = src->extents;
308
309 return region16_copy_data(dst, src);
310}
311
312void region16_print(const REGION16* region)
313{
314 UINT32 nbRects = 0;
315 int currentBandY = -1;
316 const RECTANGLE_16* rects = region16_rects(region, &nbRects);
317
318 WLog_DBG(TAG, "nrects=%" PRIu32 "", nbRects);
319
320 for (UINT32 i = 0; i < nbRects; i++)
321 {
322 const RECTANGLE_16* rect = &rects[i];
323
324 if (rect->top != currentBandY)
325 {
326 currentBandY = rect->top;
327 WLog_DBG(TAG, "band %d: ", currentBandY);
328 }
329
330 WLog_DBG(TAG, "(%" PRIu16 ",%" PRIu16 "-%" PRIu16 ",%" PRIu16 ")", rect->left, rect->top,
331 rect->right, rect->bottom);
332 }
333}
334
335static BOOL region16_copy_band_with_union(REGION16_DATA* region, const RECTANGLE_16* src,
336 const RECTANGLE_16* end, UINT16 newTop, UINT16 newBottom,
337 const RECTANGLE_16* unionRect, UINT32* dstCounter,
338 const RECTANGLE_16** srcPtr)
339{
340 WINPR_ASSERT(region);
341 WINPR_ASSERT(src);
342 WINPR_ASSERT(end);
343 WINPR_ASSERT(dstCounter);
344
345 UINT16 refY = src->top;
346
347 /* merges a band with the given rect
348 * Input:
349 * unionRect
350 * | |
351 * | |
352 * ==============+===============+================================
353 * |Item1| |Item2| |Item3| |Item4| |Item5| Band
354 * ==============+===============+================================
355 * before | overlap | after
356 *
357 * Resulting band:
358 * +-----+ +----------------------+ +-----+
359 * |Item1| | Item2 | |Item3|
360 * +-----+ +----------------------+ +-----+
361 *
362 * We first copy as-is items that are before Item2, the first overlapping
363 * item.
364 * Then we find the last one that overlap unionRect to aggregate Item2, Item3
365 * and Item4 to create Item2.
366 * Finally Item5 is copied as Item3.
367 *
368 * When no unionRect is provided, we skip the two first steps to just copy items
369 */
370
371 if (unionRect)
372 {
373 /* items before unionRect */
374 while ((src < end) && (src->top == refY) && (src->right < unionRect->left))
375 {
376 RECTANGLE_16* dst = nextRect(region, (*dstCounter)++);
377 if (!dst)
378 return FALSE;
379 dst->top = newTop;
380 dst->bottom = newBottom;
381 dst->right = src->right;
382 dst->left = src->left;
383 src++;
384 }
385
386 /* treat items overlapping with unionRect */
387 const RECTANGLE_16* startOverlap = unionRect;
388 const RECTANGLE_16* endOverlap = unionRect;
389
390 if ((src < end) && (src->top == refY) && (src->left < unionRect->left))
391 startOverlap = src;
392
393 while ((src < end) && (src->top == refY) && (src->right < unionRect->right))
394 {
395 src++;
396 }
397
398 if ((src < end) && (src->top == refY) && (src->left < unionRect->right))
399 {
400 endOverlap = src;
401 src++;
402 }
403
404 {
405 RECTANGLE_16* dst = nextRect(region, (*dstCounter)++);
406 if (!dst)
407 return FALSE;
408 dst->bottom = newBottom;
409 dst->top = newTop;
410 dst->left = startOverlap->left;
411 dst->right = endOverlap->right;
412 }
413 }
414
415 /* treat remaining items on the same band */
416 while ((src < end) && (src->top == refY))
417 {
418 RECTANGLE_16* dst = nextRect(region, (*dstCounter)++);
419 if (!dst)
420 return FALSE;
421
422 dst->top = newTop;
423 dst->bottom = newBottom;
424 dst->right = src->right;
425 dst->left = src->left;
426 src++;
427 }
428
429 if (srcPtr)
430 *srcPtr = src;
431
432 return TRUE;
433}
434
435static RECTANGLE_16* next_band(RECTANGLE_16* band1, RECTANGLE_16* endPtr, int* nbItems)
436{
437 WINPR_ASSERT(band1);
438 WINPR_ASSERT(endPtr);
439 WINPR_ASSERT(nbItems);
440
441 UINT16 refY = band1->top;
442 *nbItems = 0;
443
444 while ((band1 < endPtr) && (band1->top == refY))
445 {
446 band1++;
447 *nbItems += 1;
448 }
449
450 return band1;
451}
452
453static BOOL band_match(const RECTANGLE_16* band1, const RECTANGLE_16* band2,
454 const RECTANGLE_16* endPtr)
455{
456 int refBand2 = band2->top;
457 const RECTANGLE_16* band2Start = band2;
458
459 while ((band1 < band2Start) && (band2 < endPtr) && (band2->top == refBand2))
460 {
461 if ((band1->left != band2->left) || (band1->right != band2->right))
462 return FALSE;
463
464 band1++;
465 band2++;
466 }
467
468 if (band1 != band2Start)
469 return FALSE;
470
471 return (band2 == endPtr) || (band2->top != refBand2);
472}
473
480static BOOL rectangle_contained_in_band(const RECTANGLE_16* band, const RECTANGLE_16* endPtr,
481 const RECTANGLE_16* rect)
482{
483 WINPR_ASSERT(band);
484 WINPR_ASSERT(endPtr);
485 WINPR_ASSERT(rect);
486
487 UINT16 refY = band->top;
488
489 if ((band->top > rect->top) || (rect->bottom > band->bottom))
490 return FALSE;
491
492 /* note: as the band is sorted from left to right, once we've seen an item
493 * that is after rect->left we're sure that the result is False.
494 */
495 while ((band < endPtr) && (band->top == refY) && (band->left <= rect->left))
496 {
497 if (rect->right <= band->right)
498 return TRUE;
499
500 band++;
501 }
502
503 return FALSE;
504}
505
506static BOOL region16_simplify_bands(REGION16* region)
507{
520 const int nbRects = region16_n_rects(region);
521 int finalNbRects = nbRects;
522
523 if (nbRects < 2)
524 return TRUE;
525
526 RECTANGLE_16* band1 = region16_rects_noconst(region);
527 RECTANGLE_16* endPtr = band1 + nbRects;
528
529 do
530 {
531 int bandItems = 0;
532 RECTANGLE_16* band2 = next_band(band1, endPtr, &bandItems);
533
534 if (band2 == endPtr)
535 break;
536
537 if ((band1->bottom == band2->top) && band_match(band1, band2, endPtr))
538 {
539 /* adjust the bottom of band1 items */
540 RECTANGLE_16* tmp = band1;
541
542 while (tmp < band2)
543 {
544 tmp->bottom = band2->bottom;
545 tmp++;
546 }
547
548 /* override band2, we don't move band1 pointer as the band after band2
549 * may be merged too */
550 const RECTANGLE_16* endBand = band2 + bandItems;
551 const size_t toMove =
552 WINPR_ASSERTING_INT_CAST(size_t, (endPtr - endBand)) * sizeof(RECTANGLE_16);
553
554 if (toMove)
555 MoveMemory(band2, endBand, toMove);
556
557 finalNbRects -= bandItems;
558 endPtr -= bandItems;
559 }
560 else
561 {
562 band1 = band2;
563 }
564 } while (TRUE);
565
566 if (finalNbRects != nbRects)
567 {
568 if (!resizeRegion(region, WINPR_ASSERTING_INT_CAST(size_t, finalNbRects)))
569 return FALSE;
570 }
571
572 return TRUE;
573}
574
575BOOL region16_union_rect(REGION16* dst, const REGION16* src, const RECTANGLE_16* rect)
576{
577 const RECTANGLE_16* nextBand = nullptr;
578 UINT32 srcNbRects = 0;
579 UINT16 topInterBand = 0;
580 WINPR_ASSERT(src);
581 WINPR_ASSERT(dst);
582
583 const RECTANGLE_16* srcExtents = region16_extents(src);
584 RECTANGLE_16* dstExtents = region16_extents_noconst(dst);
585
586 const int nrSrcRects = region16_n_rects(src);
587 if (nrSrcRects == 0)
588 {
589 /* source is empty, so the union is rect */
590 dst->extents = *rect;
591
592 if (!resizeRegion(dst, 1))
593 return FALSE;
594
595 RECTANGLE_16* dstRect = region16_rects_noconst(dst);
596 WINPR_ASSERT(dstRect);
597
598 dstRect->top = rect->top;
599 dstRect->left = rect->left;
600 dstRect->right = rect->right;
601 dstRect->bottom = rect->bottom;
602 dst->data->nbRects = 1;
603 return TRUE;
604 }
605
606 REGION16_DATA* newItems = allocateRegion(WINPR_ASSERTING_INT_CAST(size_t, nrSrcRects + 1));
607
608 if (!newItems)
609 return FALSE;
610
611 UINT32 usedRects = 0;
612
613 /* adds the piece of rect that is on the top of src */
614 if (rect->top < srcExtents->top)
615 {
616 RECTANGLE_16* dstRect = nextRect(newItems, usedRects++);
617 if (!dstRect)
618 return FALSE;
619
620 dstRect->top = rect->top;
621 dstRect->left = rect->left;
622 dstRect->right = rect->right;
623 dstRect->bottom = MIN(srcExtents->top, rect->bottom);
624 }
625
626 /* treat possibly overlapping region */
627 const RECTANGLE_16* currentBand = region16_rects(src, &srcNbRects);
628 const RECTANGLE_16* endSrcRect = currentBand + srcNbRects;
629
630 while (currentBand < endSrcRect)
631 {
632 if ((currentBand->bottom <= rect->top) || (rect->bottom <= currentBand->top) ||
633 rectangle_contained_in_band(currentBand, endSrcRect, rect))
634 {
635 /* no overlap between rect and the band, rect is totally below or totally above
636 * the current band, or rect is already covered by an item of the band.
637 * let's copy all the rectangles from this band
638 +----+
639 | | rect (case 1)
640 +----+
641
642 =================
643 band of srcRect
644 =================
645 +----+
646 | | rect (case 2)
647 +----+
648 */
649 if (!region16_copy_band_with_union(newItems, currentBand, endSrcRect, currentBand->top,
650 currentBand->bottom, nullptr, &usedRects, &nextBand))
651 return FALSE;
652 topInterBand = rect->top;
653 }
654 else
655 {
656 /* rect overlaps the band:
657 | | | |
658 ====^=================| |==| |=========================== band
659 | top split | | | |
660 v | 1 | | 2 |
661 ^ | | | | +----+ +----+
662 | merge zone | | | | | | | 4 |
663 v +----+ | | | | +----+
664 ^ | | | 3 |
665 | bottom split | | | |
666 ====v=========================| |==| |===================
667 | | | |
668
669 possible cases:
670 1) no top split, merge zone then a bottom split. The band will be split
671 in two
672 2) not band split, only the merge zone, band merged with rect but not split
673 3) a top split, the merge zone and no bottom split. The band will be split
674 in two
675 4) a top split, the merge zone and also a bottom split. The band will be
676 split in 3, but the coalesce algorithm may merge the created bands
677 */
678 UINT16 mergeTop = currentBand->top;
679 UINT16 mergeBottom = currentBand->bottom;
680
681 /* test if we need a top split, case 3 and 4 */
682 if (rect->top > currentBand->top)
683 {
684 if (!region16_copy_band_with_union(newItems, currentBand, endSrcRect,
685 currentBand->top, rect->top, nullptr, &usedRects,
686 &nextBand))
687 return FALSE;
688 mergeTop = rect->top;
689 }
690
691 /* do the merge zone (all cases) */
692 if (rect->bottom < currentBand->bottom)
693 mergeBottom = rect->bottom;
694
695 if (!region16_copy_band_with_union(newItems, currentBand, endSrcRect, mergeTop,
696 mergeBottom, rect, &usedRects, &nextBand))
697 return FALSE;
698
699 /* test if we need a bottom split, case 1 and 4 */
700 if (rect->bottom < currentBand->bottom)
701 {
702 if (!region16_copy_band_with_union(newItems, currentBand, endSrcRect, mergeBottom,
703 currentBand->bottom, nullptr, &usedRects,
704 &nextBand))
705 return FALSE;
706 }
707
708 topInterBand = currentBand->bottom;
709 }
710
711 /* test if a piece of rect should be inserted as a new band between
712 * the current band and the next one. band n and n+1 shouldn't touch.
713 *
714 * ==============================================================
715 * band n
716 * +------+ +------+
717 * ===========| rect |====================| |===============
718 * | | +------+ | |
719 * +------+ | rect | | rect |
720 * +------+ | |
721 * =======================================| |================
722 * +------+ band n+1
723 * ===============================================================
724 *
725 */
726 if ((nextBand < endSrcRect) && (nextBand->top != currentBand->bottom) &&
727 (rect->bottom > currentBand->bottom) && (rect->top < nextBand->top))
728 {
729 RECTANGLE_16* dstRect = nextRect(newItems, usedRects++);
730 if (!dstRect)
731 return FALSE;
732
733 dstRect->right = rect->right;
734 dstRect->left = rect->left;
735 dstRect->top = topInterBand;
736 dstRect->bottom = MIN(nextBand->top, rect->bottom);
737 }
738
739 currentBand = nextBand;
740 }
741
742 /* adds the piece of rect that is below src */
743 if (srcExtents->bottom < rect->bottom)
744 {
745 RECTANGLE_16* dstRect = nextRect(newItems, usedRects++);
746 if (!dstRect)
747 return FALSE;
748
749 dstRect->top = MAX(srcExtents->bottom, rect->top);
750 dstRect->left = rect->left;
751 dstRect->right = rect->right;
752 dstRect->bottom = rect->bottom;
753 }
754
755 dstExtents->top = MIN(rect->top, srcExtents->top);
756 dstExtents->left = MIN(rect->left, srcExtents->left);
757 dstExtents->bottom = MAX(rect->bottom, srcExtents->bottom);
758 dstExtents->right = MAX(rect->right, srcExtents->right);
759
760 newItems->nbRects = usedRects;
761 freeRegion(dst->data);
762 dst->data = newItems;
763
764 return region16_simplify_bands(dst);
765}
766
767BOOL region16_intersects_rect(const REGION16* src, const RECTANGLE_16* arg2)
768{
769 const RECTANGLE_16* endPtr = nullptr;
770 UINT32 nbRects = 0;
771
772 if (!src || !src->data || !arg2)
773 return FALSE;
774
775 const RECTANGLE_16* rect = region16_rects(src, &nbRects);
776
777 if (!nbRects)
778 return FALSE;
779
780 const RECTANGLE_16* srcExtents = region16_extents(src);
781
782 if (nbRects == 1)
783 return rectangles_intersects(srcExtents, arg2);
784
785 if (!rectangles_intersects(srcExtents, arg2))
786 return FALSE;
787
788 for (endPtr = rect + nbRects; (rect < endPtr) && (arg2->bottom > rect->top); rect++)
789 {
790 if (rectangles_intersects(rect, arg2))
791 return TRUE;
792 }
793
794 return FALSE;
795}
796
797BOOL region16_intersect_rect(REGION16* dst, const REGION16* src, const RECTANGLE_16* rect)
798{
799 const RECTANGLE_16* endPtr = nullptr;
800 UINT32 nbRects = 0;
801 RECTANGLE_16 common = WINPR_C_ARRAY_INIT;
802
803 WINPR_ASSERT(dst);
804 WINPR_ASSERT(src);
805
806 const RECTANGLE_16* srcPtr = region16_rects(src, &nbRects);
807
808 if (!nbRects)
809 {
810 region16_clear(dst);
811 return TRUE;
812 }
813
814 const RECTANGLE_16* srcExtents = region16_extents(src);
815
816 if (nbRects == 1)
817 {
818 BOOL intersects = rectangles_intersection(srcExtents, rect, &common);
819 region16_clear(dst);
820
821 if (intersects)
822 return region16_union_rect(dst, dst, &common);
823
824 return TRUE;
825 }
826
827 REGION16_DATA* newItems = allocateRegion(nbRects);
828
829 if (!newItems)
830 return FALSE;
831
832 RECTANGLE_16* dstPtr = newItems->rects;
833 UINT32 usedRects = 0;
834 RECTANGLE_16 newExtents = WINPR_C_ARRAY_INIT;
835
836 /* accumulate intersecting rectangles, the final region16_simplify_bands() will
837 * do all the bad job to recreate correct rectangles
838 */
839 for (endPtr = srcPtr + nbRects; (srcPtr < endPtr) && (rect->bottom > srcPtr->top); srcPtr++)
840 {
841 if (usedRects > nbRects)
842 {
843 freeRegion(newItems);
844 return FALSE;
845 }
846
847 if (rectangles_intersection(srcPtr, rect, &common))
848 {
849 *dstPtr = common;
850 usedRects++;
851 dstPtr++;
852
853 if (rectangle_is_empty(&newExtents))
854 {
855 /* Check if the existing newExtents is empty. If it is empty, use
856 * new common directly. We do not need to check common rectangle
857 * because the rectangles_intersection() ensures that it is not empty.
858 */
859 newExtents = common;
860 }
861 else
862 {
863 newExtents.top = MIN(common.top, newExtents.top);
864 newExtents.left = MIN(common.left, newExtents.left);
865 newExtents.bottom = MAX(common.bottom, newExtents.bottom);
866 newExtents.right = MAX(common.right, newExtents.right);
867 }
868 }
869 }
870
871 newItems->nbRects = usedRects;
872
873 freeRegion(dst->data);
874 dst->data = newItems;
875 dst->extents = newExtents;
876 return region16_simplify_bands(dst);
877}
878
879void region16_uninit(REGION16* region)
880{
881 WINPR_ASSERT(region);
882
883 freeRegion(region->data);
884 region->data = nullptr;
885}