Coverage Report

Created: 2026-02-26 06:50

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