Coverage Report

Created: 2023-09-25 06:56

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