Coverage Report

Created: 2026-07-16 07:10

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