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