Coverage Report

Created: 2026-03-04 06:14

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