FreeRDP
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 
71 struct S_REGION16_DATA
72 {
73  long size;
74  long nbRects;
75 };
76 
77 static REGION16_DATA empty_region = { 0, 0 };
78 
79 void region16_init(REGION16* region)
80 {
81  WINPR_ASSERT(region);
82  ZeroMemory(region, sizeof(REGION16));
83  region->data = &empty_region;
84 }
85 
86 int region16_n_rects(const REGION16* region)
87 {
88  WINPR_ASSERT(region);
89  WINPR_ASSERT(region->data);
90  WINPR_ASSERT(region->data->nbRects <= INT32_MAX);
91  return (int)region->data->nbRects;
92 }
93 
94 const RECTANGLE_16* region16_rects(const REGION16* region, UINT32* nbRects)
95 {
96  REGION16_DATA* data = NULL;
97 
98  if (nbRects)
99  *nbRects = 0;
100 
101  if (!region)
102  return NULL;
103 
104  data = region->data;
105 
106  if (!data)
107  return NULL;
108 
109  if (nbRects)
110  {
111  WINPR_ASSERT(data->nbRects <= UINT32_MAX);
112  *nbRects = (UINT32)data->nbRects;
113  }
114 
115  return (RECTANGLE_16*)(data + 1);
116 }
117 
118 static INLINE RECTANGLE_16* region16_rects_noconst(REGION16* region)
119 {
120  REGION16_DATA* data = NULL;
121  data = region->data;
122 
123  if (!data)
124  return NULL;
125 
126  return (RECTANGLE_16*)(&data[1]);
127 }
128 
129 const RECTANGLE_16* region16_extents(const REGION16* region)
130 {
131  if (!region)
132  return NULL;
133 
134  return &region->extents;
135 }
136 
137 static RECTANGLE_16* region16_extents_noconst(REGION16* region)
138 {
139  if (!region)
140  return NULL;
141 
142  return &region->extents;
143 }
144 
145 BOOL rectangle_is_empty(const RECTANGLE_16* rect)
146 {
147  /* A rectangle with width <= 0 or height <= 0 should be regarded
148  * as empty.
149  */
150  return ((rect->left >= rect->right) || (rect->top >= rect->bottom)) ? TRUE : FALSE;
151 }
152 
153 BOOL region16_is_empty(const REGION16* region)
154 {
155  WINPR_ASSERT(region);
156  WINPR_ASSERT(region->data);
157  return (region->data->nbRects == 0);
158 }
159 
160 BOOL rectangles_equal(const RECTANGLE_16* r1, const RECTANGLE_16* r2)
161 {
162  return ((r1->left == r2->left) && (r1->top == r2->top) && (r1->right == r2->right) &&
163  (r1->bottom == r2->bottom))
164  ? TRUE
165  : FALSE;
166 }
167 
168 BOOL rectangles_intersects(const RECTANGLE_16* r1, const RECTANGLE_16* r2)
169 {
170  RECTANGLE_16 tmp = { 0 };
171  return rectangles_intersection(r1, r2, &tmp);
172 }
173 
174 BOOL rectangles_intersection(const RECTANGLE_16* r1, const RECTANGLE_16* r2, RECTANGLE_16* dst)
175 {
176  dst->left = MAX(r1->left, r2->left);
177  dst->right = MIN(r1->right, r2->right);
178  dst->top = MAX(r1->top, r2->top);
179  dst->bottom = MIN(r1->bottom, r2->bottom);
180  return (dst->left < dst->right) && (dst->top < dst->bottom);
181 }
182 
183 void region16_clear(REGION16* region)
184 {
185  WINPR_ASSERT(region);
186  WINPR_ASSERT(region->data);
187 
188  if ((region->data->size > 0) && (region->data != &empty_region))
189  free(region->data);
190 
191  region->data = &empty_region;
192  ZeroMemory(&region->extents, sizeof(region->extents));
193 }
194 
195 static INLINE REGION16_DATA* allocateRegion(long nbItems)
196 {
197  size_t allocSize =
198  sizeof(REGION16_DATA) + (WINPR_ASSERTING_INT_CAST(size_t, nbItems) * sizeof(RECTANGLE_16));
199  REGION16_DATA* ret = (REGION16_DATA*)malloc(allocSize);
200 
201  if (!ret)
202  return ret;
203 
204  ret->size = WINPR_ASSERTING_INT_CAST(long, allocSize);
205  ret->nbRects = nbItems;
206  return ret;
207 }
208 
209 BOOL region16_copy(REGION16* dst, const REGION16* src)
210 {
211  WINPR_ASSERT(dst);
212  WINPR_ASSERT(dst->data);
213  WINPR_ASSERT(src);
214  WINPR_ASSERT(src->data);
215 
216  if (dst == src)
217  return TRUE;
218 
219  dst->extents = src->extents;
220 
221  if ((dst->data->size > 0) && (dst->data != &empty_region))
222  free(dst->data);
223 
224  if (src->data->size == 0)
225  dst->data = &empty_region;
226  else
227  {
228  dst->data = allocateRegion(src->data->nbRects);
229 
230  if (!dst->data)
231  return FALSE;
232 
233  CopyMemory(dst->data, src->data, WINPR_ASSERTING_INT_CAST(size_t, src->data->size));
234  }
235 
236  return TRUE;
237 }
238 
239 void region16_print(const REGION16* region)
240 {
241  const RECTANGLE_16* rects = NULL;
242  UINT32 nbRects = 0;
243  int currentBandY = -1;
244  rects = region16_rects(region, &nbRects);
245  WLog_DBG(TAG, "nrects=%" PRIu32 "", nbRects);
246 
247  for (UINT32 i = 0; i < nbRects; i++, rects++)
248  {
249  if (rects->top != currentBandY)
250  {
251  currentBandY = rects->top;
252  WLog_DBG(TAG, "band %d: ", currentBandY);
253  }
254 
255  WLog_DBG(TAG, "(%" PRIu16 ",%" PRIu16 "-%" PRIu16 ",%" PRIu16 ")", rects->left, rects->top,
256  rects->right, rects->bottom);
257  }
258 }
259 
260 static void region16_copy_band_with_union(RECTANGLE_16* dst, const RECTANGLE_16* src,
261  const RECTANGLE_16* end, UINT16 newTop, UINT16 newBottom,
262  const RECTANGLE_16* unionRect, UINT32* dstCounter,
263  const RECTANGLE_16** srcPtr, RECTANGLE_16** dstPtr)
264 {
265  UINT16 refY = src->top;
266  const RECTANGLE_16* startOverlap = NULL;
267  const RECTANGLE_16* endOverlap = NULL;
268 
269  /* merges a band with the given rect
270  * Input:
271  * unionRect
272  * | |
273  * | |
274  * ==============+===============+================================
275  * |Item1| |Item2| |Item3| |Item4| |Item5| Band
276  * ==============+===============+================================
277  * before | overlap | after
278  *
279  * Resulting band:
280  * +-----+ +----------------------+ +-----+
281  * |Item1| | Item2 | |Item3|
282  * +-----+ +----------------------+ +-----+
283  *
284  * We first copy as-is items that are before Item2, the first overlapping
285  * item.
286  * Then we find the last one that overlap unionRect to aggregate Item2, Item3
287  * and Item4 to create Item2.
288  * Finally Item5 is copied as Item3.
289  *
290  * When no unionRect is provided, we skip the two first steps to just copy items
291  */
292 
293  if (unionRect)
294  {
295  /* items before unionRect */
296  while ((src < end) && (src->top == refY) && (src->right < unionRect->left))
297  {
298  dst->top = newTop;
299  dst->bottom = newBottom;
300  dst->right = src->right;
301  dst->left = src->left;
302  src++;
303  dst++;
304  *dstCounter += 1;
305  }
306 
307  /* treat items overlapping with unionRect */
308  startOverlap = unionRect;
309  endOverlap = unionRect;
310 
311  if ((src < end) && (src->top == refY) && (src->left < unionRect->left))
312  startOverlap = src;
313 
314  while ((src < end) && (src->top == refY) && (src->right < unionRect->right))
315  {
316  src++;
317  }
318 
319  if ((src < end) && (src->top == refY) && (src->left < unionRect->right))
320  {
321  endOverlap = src;
322  src++;
323  }
324 
325  dst->bottom = newBottom;
326  dst->top = newTop;
327  dst->left = startOverlap->left;
328  dst->right = endOverlap->right;
329  dst++;
330  *dstCounter += 1;
331  }
332 
333  /* treat remaining items on the same band */
334  while ((src < end) && (src->top == refY))
335  {
336  dst->top = newTop;
337  dst->bottom = newBottom;
338  dst->right = src->right;
339  dst->left = src->left;
340  src++;
341  dst++;
342  *dstCounter += 1;
343  }
344 
345  if (srcPtr)
346  *srcPtr = src;
347 
348  *dstPtr = dst;
349 }
350 
351 static RECTANGLE_16* next_band(RECTANGLE_16* band1, RECTANGLE_16* endPtr, int* nbItems)
352 {
353  UINT16 refY = band1->top;
354  *nbItems = 0;
355 
356  while ((band1 < endPtr) && (band1->top == refY))
357  {
358  band1++;
359  *nbItems += 1;
360  }
361 
362  return band1;
363 }
364 
365 static BOOL band_match(const RECTANGLE_16* band1, const RECTANGLE_16* band2,
366  const RECTANGLE_16* endPtr)
367 {
368  int refBand2 = band2->top;
369  const RECTANGLE_16* band2Start = band2;
370 
371  while ((band1 < band2Start) && (band2 < endPtr) && (band2->top == refBand2))
372  {
373  if ((band1->left != band2->left) || (band1->right != band2->right))
374  return FALSE;
375 
376  band1++;
377  band2++;
378  }
379 
380  if (band1 != band2Start)
381  return FALSE;
382 
383  return (band2 == endPtr) || (band2->top != refBand2);
384 }
385 
392 static BOOL rectangle_contained_in_band(const RECTANGLE_16* band, const RECTANGLE_16* endPtr,
393  const RECTANGLE_16* rect)
394 {
395  UINT16 refY = band->top;
396 
397  if ((band->top > rect->top) || (rect->bottom > band->bottom))
398  return FALSE;
399 
400  /* note: as the band is sorted from left to right, once we've seen an item
401  * that is after rect->left we're sure that the result is False.
402  */
403  while ((band < endPtr) && (band->top == refY) && (band->left <= rect->left))
404  {
405  if (rect->right <= band->right)
406  return TRUE;
407 
408  band++;
409  }
410 
411  return FALSE;
412 }
413 
414 static BOOL region16_simplify_bands(REGION16* region)
415 {
427  RECTANGLE_16* endBand = NULL;
428  int nbRects = 0;
429  int finalNbRects = 0;
430  int bandItems = 0;
431  finalNbRects = nbRects = region16_n_rects(region);
432 
433  if (nbRects < 2)
434  return TRUE;
435 
436  RECTANGLE_16* band1 = region16_rects_noconst(region);
437  RECTANGLE_16* endPtr = band1 + nbRects;
438 
439  do
440  {
441  RECTANGLE_16* band2 = next_band(band1, endPtr, &bandItems);
442 
443  if (band2 == endPtr)
444  break;
445 
446  if ((band1->bottom == band2->top) && band_match(band1, band2, endPtr))
447  {
448  /* adjust the bottom of band1 items */
449  RECTANGLE_16* tmp = band1;
450 
451  while (tmp < band2)
452  {
453  tmp->bottom = band2->bottom;
454  tmp++;
455  }
456 
457  /* override band2, we don't move band1 pointer as the band after band2
458  * may be merged too */
459  endBand = band2 + bandItems;
460  const size_t toMove =
461  WINPR_ASSERTING_INT_CAST(size_t, (endPtr - endBand)) * sizeof(RECTANGLE_16);
462 
463  if (toMove)
464  MoveMemory(band2, endBand, toMove);
465 
466  finalNbRects -= bandItems;
467  endPtr -= bandItems;
468  }
469  else
470  {
471  band1 = band2;
472  }
473  } while (TRUE);
474 
475  if (finalNbRects != nbRects)
476  {
477  size_t allocSize = sizeof(REGION16_DATA) +
478  (WINPR_ASSERTING_INT_CAST(size_t, finalNbRects) * sizeof(RECTANGLE_16));
479  REGION16_DATA* data = realloc(region->data, allocSize);
480  if (!data)
481  free(region->data);
482  region->data = data;
483 
484  if (!region->data)
485  {
486  region->data = &empty_region;
487  return FALSE;
488  }
489 
490  region->data->nbRects = finalNbRects;
491  region->data->size = WINPR_ASSERTING_INT_CAST(long, allocSize);
492  }
493 
494  return TRUE;
495 }
496 
497 BOOL region16_union_rect(REGION16* dst, const REGION16* src, const RECTANGLE_16* rect)
498 {
499  const RECTANGLE_16* srcExtents = NULL;
500  RECTANGLE_16* dstExtents = NULL;
501  const RECTANGLE_16* currentBand = NULL;
502  const RECTANGLE_16* endSrcRect = NULL;
503  const RECTANGLE_16* nextBand = NULL;
504  REGION16_DATA* newItems = NULL;
505  REGION16_DATA* tmpItems = NULL;
506  RECTANGLE_16* dstRect = NULL;
507  UINT32 usedRects = 0;
508  UINT32 srcNbRects = 0;
509  UINT16 topInterBand = 0;
510  WINPR_ASSERT(src);
511  WINPR_ASSERT(dst);
512  srcExtents = region16_extents(src);
513  dstExtents = region16_extents_noconst(dst);
514 
515  if (!region16_n_rects(src))
516  {
517  /* source is empty, so the union is rect */
518  dst->extents = *rect;
519  dst->data = allocateRegion(1);
520 
521  if (!dst->data)
522  return FALSE;
523 
524  dstRect = region16_rects_noconst(dst);
525  dstRect->top = rect->top;
526  dstRect->left = rect->left;
527  dstRect->right = rect->right;
528  dstRect->bottom = rect->bottom;
529  return TRUE;
530  }
531 
532  newItems = allocateRegion((1L + 4L * region16_n_rects(src)));
533 
534  if (!newItems)
535  return FALSE;
536 
537  dstRect = (RECTANGLE_16*)(&newItems[1]);
538  usedRects = 0;
539 
540  /* adds the piece of rect that is on the top of src */
541  if (rect->top < srcExtents->top)
542  {
543  dstRect->top = rect->top;
544  dstRect->left = rect->left;
545  dstRect->right = rect->right;
546  dstRect->bottom = MIN(srcExtents->top, rect->bottom);
547  usedRects++;
548  dstRect++;
549  }
550 
551  /* treat possibly overlapping region */
552  currentBand = region16_rects(src, &srcNbRects);
553  endSrcRect = currentBand + srcNbRects;
554 
555  while (currentBand < endSrcRect)
556  {
557  if ((currentBand->bottom <= rect->top) || (rect->bottom <= currentBand->top) ||
558  rectangle_contained_in_band(currentBand, endSrcRect, rect))
559  {
560  /* no overlap between rect and the band, rect is totally below or totally above
561  * the current band, or rect is already covered by an item of the band.
562  * let's copy all the rectangles from this band
563  +----+
564  | | rect (case 1)
565  +----+
566 
567  =================
568  band of srcRect
569  =================
570  +----+
571  | | rect (case 2)
572  +----+
573  */
574  region16_copy_band_with_union(dstRect, currentBand, endSrcRect, currentBand->top,
575  currentBand->bottom, NULL, &usedRects, &nextBand,
576  &dstRect);
577  topInterBand = rect->top;
578  }
579  else
580  {
581  /* rect overlaps the band:
582  | | | |
583  ====^=================| |==| |=========================== band
584  | top split | | | |
585  v | 1 | | 2 |
586  ^ | | | | +----+ +----+
587  | merge zone | | | | | | | 4 |
588  v +----+ | | | | +----+
589  ^ | | | 3 |
590  | bottom split | | | |
591  ====v=========================| |==| |===================
592  | | | |
593 
594  possible cases:
595  1) no top split, merge zone then a bottom split. The band will be split
596  in two
597  2) not band split, only the merge zone, band merged with rect but not split
598  3) a top split, the merge zone and no bottom split. The band will be split
599  in two
600  4) a top split, the merge zone and also a bottom split. The band will be
601  split in 3, but the coalesce algorithm may merge the created bands
602  */
603  UINT16 mergeTop = currentBand->top;
604  UINT16 mergeBottom = currentBand->bottom;
605 
606  /* test if we need a top split, case 3 and 4 */
607  if (rect->top > currentBand->top)
608  {
609  region16_copy_band_with_union(dstRect, currentBand, endSrcRect, currentBand->top,
610  rect->top, NULL, &usedRects, &nextBand, &dstRect);
611  mergeTop = rect->top;
612  }
613 
614  /* do the merge zone (all cases) */
615  if (rect->bottom < currentBand->bottom)
616  mergeBottom = rect->bottom;
617 
618  region16_copy_band_with_union(dstRect, currentBand, endSrcRect, mergeTop, mergeBottom,
619  rect, &usedRects, &nextBand, &dstRect);
620 
621  /* test if we need a bottom split, case 1 and 4 */
622  if (rect->bottom < currentBand->bottom)
623  {
624  region16_copy_band_with_union(dstRect, currentBand, endSrcRect, mergeBottom,
625  currentBand->bottom, NULL, &usedRects, &nextBand,
626  &dstRect);
627  }
628 
629  topInterBand = currentBand->bottom;
630  }
631 
632  /* test if a piece of rect should be inserted as a new band between
633  * the current band and the next one. band n and n+1 shouldn't touch.
634  *
635  * ==============================================================
636  * band n
637  * +------+ +------+
638  * ===========| rect |====================| |===============
639  * | | +------+ | |
640  * +------+ | rect | | rect |
641  * +------+ | |
642  * =======================================| |================
643  * +------+ band n+1
644  * ===============================================================
645  *
646  */
647  if ((nextBand < endSrcRect) && (nextBand->top != currentBand->bottom) &&
648  (rect->bottom > currentBand->bottom) && (rect->top < nextBand->top))
649  {
650  dstRect->right = rect->right;
651  dstRect->left = rect->left;
652  dstRect->top = topInterBand;
653  dstRect->bottom = MIN(nextBand->top, rect->bottom);
654  dstRect++;
655  usedRects++;
656  }
657 
658  currentBand = nextBand;
659  }
660 
661  /* adds the piece of rect that is below src */
662  if (srcExtents->bottom < rect->bottom)
663  {
664  dstRect->top = MAX(srcExtents->bottom, rect->top);
665  dstRect->left = rect->left;
666  dstRect->right = rect->right;
667  dstRect->bottom = rect->bottom;
668  usedRects++;
669  dstRect++;
670  }
671 
672  if ((src == dst) && (dst->data != &empty_region))
673  free(dst->data);
674 
675  dstExtents->top = MIN(rect->top, srcExtents->top);
676  dstExtents->left = MIN(rect->left, srcExtents->left);
677  dstExtents->bottom = MAX(rect->bottom, srcExtents->bottom);
678  dstExtents->right = MAX(rect->right, srcExtents->right);
679  newItems->size =
680  WINPR_ASSERTING_INT_CAST(long, sizeof(REGION16_DATA) + (usedRects * sizeof(RECTANGLE_16)));
681  if (newItems->size != 0)
682  tmpItems = realloc(newItems, WINPR_ASSERTING_INT_CAST(size_t, newItems->size));
683  if (!tmpItems)
684  free(newItems);
685 
686  newItems = tmpItems;
687  dst->data = newItems;
688 
689  if (!dst->data)
690  return FALSE;
691 
692  dst->data->nbRects = usedRects;
693  return region16_simplify_bands(dst);
694 }
695 
696 BOOL region16_intersects_rect(const REGION16* src, const RECTANGLE_16* arg2)
697 {
698  const RECTANGLE_16* rect = NULL;
699  const RECTANGLE_16* endPtr = NULL;
700  const RECTANGLE_16* srcExtents = NULL;
701  UINT32 nbRects = 0;
702 
703  if (!src || !src->data || !arg2)
704  return FALSE;
705 
706  rect = region16_rects(src, &nbRects);
707 
708  if (!nbRects)
709  return FALSE;
710 
711  srcExtents = region16_extents(src);
712 
713  if (nbRects == 1)
714  return rectangles_intersects(srcExtents, arg2);
715 
716  if (!rectangles_intersects(srcExtents, arg2))
717  return FALSE;
718 
719  for (endPtr = rect + nbRects; (rect < endPtr) && (arg2->bottom > rect->top); rect++)
720  {
721  if (rectangles_intersects(rect, arg2))
722  return TRUE;
723  }
724 
725  return FALSE;
726 }
727 
728 BOOL region16_intersect_rect(REGION16* dst, const REGION16* src, const RECTANGLE_16* rect)
729 {
730  REGION16_DATA* newItems = NULL;
731  const RECTANGLE_16* srcPtr = NULL;
732  const RECTANGLE_16* endPtr = NULL;
733  const RECTANGLE_16* srcExtents = NULL;
734  RECTANGLE_16* dstPtr = NULL;
735  UINT32 nbRects = 0;
736  UINT32 usedRects = 0;
737  RECTANGLE_16 common;
738  RECTANGLE_16 newExtents;
739  WINPR_ASSERT(src);
740  WINPR_ASSERT(src->data);
741  srcPtr = region16_rects(src, &nbRects);
742 
743  if (!nbRects)
744  {
745  region16_clear(dst);
746  return TRUE;
747  }
748 
749  srcExtents = region16_extents(src);
750 
751  if (nbRects == 1)
752  {
753  BOOL intersects = rectangles_intersection(srcExtents, rect, &common);
754  region16_clear(dst);
755 
756  if (intersects)
757  return region16_union_rect(dst, dst, &common);
758 
759  return TRUE;
760  }
761 
762  newItems = allocateRegion(nbRects);
763 
764  if (!newItems)
765  return FALSE;
766 
767  dstPtr = (RECTANGLE_16*)(&newItems[1]);
768  usedRects = 0;
769  ZeroMemory(&newExtents, sizeof(newExtents));
770 
771  /* accumulate intersecting rectangles, the final region16_simplify_bands() will
772  * do all the bad job to recreate correct rectangles
773  */
774  for (endPtr = srcPtr + nbRects; (srcPtr < endPtr) && (rect->bottom > srcPtr->top); srcPtr++)
775  {
776  if (rectangles_intersection(srcPtr, rect, &common))
777  {
778  *dstPtr = common;
779  usedRects++;
780  dstPtr++;
781 
782  if (rectangle_is_empty(&newExtents))
783  {
784  /* Check if the existing newExtents is empty. If it is empty, use
785  * new common directly. We do not need to check common rectangle
786  * because the rectangles_intersection() ensures that it is not empty.
787  */
788  newExtents = common;
789  }
790  else
791  {
792  newExtents.top = MIN(common.top, newExtents.top);
793  newExtents.left = MIN(common.left, newExtents.left);
794  newExtents.bottom = MAX(common.bottom, newExtents.bottom);
795  newExtents.right = MAX(common.right, newExtents.right);
796  }
797  }
798  }
799 
800  newItems->nbRects = usedRects;
801  newItems->size =
802  WINPR_ASSERTING_INT_CAST(long, sizeof(REGION16_DATA) + (usedRects * sizeof(RECTANGLE_16)));
803 
804  if ((dst->data->size > 0) && (dst->data != &empty_region))
805  free(dst->data);
806 
807  dst->data = realloc(newItems, WINPR_ASSERTING_INT_CAST(size_t, newItems->size));
808 
809  if (!dst->data)
810  {
811  free(newItems);
812  return FALSE;
813  }
814 
815  dst->extents = newExtents;
816  return region16_simplify_bands(dst);
817 }
818 
819 void region16_uninit(REGION16* region)
820 {
821  WINPR_ASSERT(region);
822 
823  if (region->data)
824  {
825  if ((region->data->size > 0) && (region->data != &empty_region))
826  free(region->data);
827 
828  region->data = NULL;
829  }
830 }