Coverage Report

Created: 2025-07-01 06:46

/src/FreeRDP/libfreerdp/gdi/region.c
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * FreeRDP: A Remote Desktop Protocol Implementation
3
 * GDI Region Functions
4
 *
5
 * Copyright 2010-2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
6
 * Copyright 2016 Armin Novak <armin.novak@thincast.com>
7
 * Copyright 2016 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 <freerdp/config.h>
23
24
#include <stdio.h>
25
#include <string.h>
26
#include <stdlib.h>
27
28
#include <winpr/wtypes.h>
29
30
#include <freerdp/api.h>
31
#include <freerdp/freerdp.h>
32
#include <freerdp/gdi/gdi.h>
33
34
#include <freerdp/gdi/region.h>
35
36
#include <freerdp/log.h>
37
38
#define TAG FREERDP_TAG("gdi.region")
39
40
static char* gdi_rect_str(char* buffer, size_t size, const GDI_RECT* rect)
41
0
{
42
0
  if (!buffer || (size < 1) || !rect)
43
0
    return NULL;
44
45
0
  (void)_snprintf(buffer, size - 1,
46
0
                  "[top/left=%" PRId32 "x%" PRId32 "-bottom/right%" PRId32 "x%" PRId32 "]",
47
0
                  rect->top, rect->left, rect->bottom, rect->right);
48
0
  buffer[size - 1] = '\0';
49
50
0
  return buffer;
51
0
}
52
53
static char* gdi_regn_str(char* buffer, size_t size, const GDI_RGN* rgn)
54
0
{
55
0
  if (!buffer || (size < 1) || !rgn)
56
0
    return NULL;
57
58
0
  (void)_snprintf(buffer, size - 1, "[%" PRId32 "x%" PRId32 "-%" PRId32 "x%" PRId32 "]", rgn->x,
59
0
                  rgn->y, rgn->w, rgn->h);
60
0
  buffer[size - 1] = '\0';
61
62
0
  return buffer;
63
0
}
64
65
/**
66
 * Create a region from rectangular coordinates.
67
 * msdn{dd183514}
68
 *
69
 * @param nLeftRect x1
70
 * @param nTopRect y1
71
 * @param nRightRect x2
72
 * @param nBottomRect y2
73
 *
74
 * @return new region
75
 */
76
77
GDI_RGN* gdi_CreateRectRgn(INT32 nLeftRect, INT32 nTopRect, INT32 nRightRect, INT32 nBottomRect)
78
0
{
79
0
  INT64 w = 0;
80
0
  INT64 h = 0;
81
0
  GDI_RGN* hRgn = NULL;
82
83
0
  w = nRightRect - nLeftRect + 1ll;
84
0
  h = nBottomRect - nTopRect + 1ll;
85
0
  if ((w < 0) || (h < 0) || (w > INT32_MAX) || (h > INT32_MAX))
86
0
  {
87
0
    WLog_ERR(TAG,
88
0
             "Can not create region top/left=%" PRId32 "x%" PRId32 "-bottom/right=%" PRId32
89
0
             "x%" PRId32,
90
0
             nTopRect, nLeftRect, nBottomRect, nRightRect);
91
0
    return NULL;
92
0
  }
93
0
  hRgn = (GDI_RGN*)calloc(1, sizeof(GDI_RGN));
94
95
0
  if (!hRgn)
96
0
    return NULL;
97
98
0
  hRgn->objectType = GDIOBJECT_REGION;
99
0
  hRgn->x = nLeftRect;
100
0
  hRgn->y = nTopRect;
101
0
  hRgn->w = (INT32)w;
102
0
  hRgn->h = (INT32)h;
103
0
  hRgn->null = FALSE;
104
0
  return hRgn;
105
0
}
106
107
/**
108
 * Create a new rectangle.
109
 * @param xLeft x1
110
 * @param yTop y1
111
 * @param xRight x2
112
 * @param yBottom y2
113
 * @return new rectangle
114
 */
115
116
GDI_RECT* gdi_CreateRect(INT32 xLeft, INT32 yTop, INT32 xRight, INT32 yBottom)
117
0
{
118
0
  GDI_RECT* hRect = NULL;
119
120
0
  if (xLeft > xRight)
121
0
    return NULL;
122
0
  if (yTop > yBottom)
123
0
    return NULL;
124
125
0
  hRect = (GDI_RECT*)calloc(1, sizeof(GDI_RECT));
126
127
0
  if (!hRect)
128
0
    return NULL;
129
130
0
  hRect->objectType = GDIOBJECT_RECT;
131
0
  hRect->left = xLeft;
132
0
  hRect->top = yTop;
133
0
  hRect->right = xRight;
134
0
  hRect->bottom = yBottom;
135
0
  return hRect;
136
0
}
137
138
/**
139
 * Convert a rectangle to a region.
140
 * @param rect source rectangle
141
 * @param rgn destination region
142
 */
143
144
BOOL gdi_RectToRgn(const GDI_RECT* rect, GDI_RGN* rgn)
145
0
{
146
0
  BOOL rc = TRUE;
147
0
  INT64 w = 0;
148
0
  INT64 h = 0;
149
0
  w = rect->right - rect->left + 1ll;
150
0
  h = rect->bottom - rect->top + 1ll;
151
152
0
  if ((w < 0) || (h < 0) || (w > INT32_MAX) || (h > INT32_MAX))
153
0
  {
154
0
    WLog_ERR(TAG,
155
0
             "Can not create region top/left=%" PRId32 "x%" PRId32 "-bottom/right=%" PRId32
156
0
             "x%" PRId32,
157
0
             rect->top, rect->left, rect->bottom, rect->right);
158
0
    w = 0;
159
0
    h = 0;
160
0
    rc = FALSE;
161
0
  }
162
163
0
  rgn->x = rect->left;
164
0
  rgn->y = rect->top;
165
0
  rgn->w = (INT32)w;
166
0
  rgn->h = (INT32)h;
167
168
0
  return rc;
169
0
}
170
171
/**
172
 * Convert rectangular coordinates to a region.
173
 * @param left x1
174
 * @param top y1
175
 * @param right x2
176
 * @param bottom y2
177
 * @param rgn destination region
178
 */
179
180
BOOL gdi_CRectToRgn(INT32 left, INT32 top, INT32 right, INT32 bottom, GDI_RGN* rgn)
181
0
{
182
0
  BOOL rc = TRUE;
183
0
  INT64 w = 0;
184
0
  INT64 h = 0;
185
0
  w = right - left + 1ll;
186
0
  h = bottom - top + 1ll;
187
188
0
  if (!rgn)
189
0
    return FALSE;
190
191
0
  if ((w < 0) || (h < 0) || (w > INT32_MAX) || (h > INT32_MAX))
192
0
  {
193
0
    WLog_ERR(TAG,
194
0
             "Can not create region top/left=%" PRId32 "x%" PRId32 "-bottom/right=%" PRId32
195
0
             "x%" PRId32,
196
0
             top, left, bottom, right);
197
0
    w = 0;
198
0
    h = 0;
199
0
    rc = FALSE;
200
0
  }
201
202
0
  rgn->x = left;
203
0
  rgn->y = top;
204
0
  rgn->w = (INT32)w;
205
0
  rgn->h = (INT32)h;
206
0
  return rc;
207
0
}
208
209
/**
210
 * Convert a rectangle to region coordinates.
211
 * @param rect source rectangle
212
 * @param x x1
213
 * @param y y1
214
 * @param w width
215
 * @param h height
216
 */
217
218
BOOL gdi_RectToCRgn(const GDI_RECT* rect, INT32* x, INT32* y, INT32* w, INT32* h)
219
0
{
220
0
  BOOL rc = TRUE;
221
0
  *x = rect->left;
222
0
  *y = rect->top;
223
0
  INT64 tmp = rect->right - rect->left + 1;
224
0
  if ((tmp < 0) || (tmp > INT32_MAX))
225
0
  {
226
0
    char buffer[256];
227
0
    WLog_ERR(TAG, "rectangle invalid %s", gdi_rect_str(buffer, sizeof(buffer), rect));
228
0
    *w = 0;
229
0
    rc = FALSE;
230
0
  }
231
0
  else
232
0
    *w = (INT32)tmp;
233
0
  tmp = rect->bottom - rect->top + 1;
234
0
  if ((tmp < 0) || (tmp > INT32_MAX))
235
0
  {
236
0
    char buffer[256];
237
0
    WLog_ERR(TAG, "rectangle invalid %s", gdi_rect_str(buffer, sizeof(buffer), rect));
238
0
    *h = 0;
239
0
    rc = FALSE;
240
0
  }
241
0
  else
242
0
    *h = (INT32)tmp;
243
0
  return rc;
244
0
}
245
246
/**
247
 * Convert rectangular coordinates to region coordinates.
248
 * @param left x1
249
 * @param top y1
250
 * @param right x2
251
 * @param bottom y2
252
 * @param x x1
253
 * @param y y1
254
 * @param w width
255
 * @param h height
256
 */
257
258
BOOL gdi_CRectToCRgn(INT32 left, INT32 top, INT32 right, INT32 bottom, INT32* x, INT32* y, INT32* w,
259
                     INT32* h)
260
0
{
261
0
  INT64 wl = 0;
262
0
  INT64 hl = 0;
263
0
  BOOL rc = TRUE;
264
0
  wl = right - left + 1ll;
265
0
  hl = bottom - top + 1ll;
266
267
0
  if ((left > right) || (top > bottom) || (wl <= 0) || (hl <= 0) || (wl > INT32_MAX) ||
268
0
      (hl > INT32_MAX))
269
0
  {
270
0
    WLog_ERR(TAG,
271
0
             "Can not create region top/left=%" PRId32 "x%" PRId32 "-bottom/right=%" PRId32
272
0
             "x%" PRId32,
273
0
             top, left, bottom, right);
274
0
    wl = 0;
275
0
    hl = 0;
276
0
    rc = FALSE;
277
0
  }
278
279
0
  *x = left;
280
0
  *y = top;
281
0
  *w = (INT32)wl;
282
0
  *h = (INT32)hl;
283
0
  return rc;
284
0
}
285
286
/**
287
 * Convert a region to a rectangle.
288
 * @param rgn source region
289
 * @param rect destination rectangle
290
 */
291
292
BOOL gdi_RgnToRect(const GDI_RGN* rgn, GDI_RECT* rect)
293
0
{
294
0
  INT64 r = 0;
295
0
  INT64 b = 0;
296
0
  BOOL rc = TRUE;
297
0
  r = rgn->x + rgn->w - 1ll;
298
0
  b = rgn->y + rgn->h - 1ll;
299
300
0
  if ((r < INT32_MIN) || (r > INT32_MAX) || (b < INT32_MIN) || (b > INT32_MAX))
301
0
  {
302
0
    char buffer[256];
303
0
    WLog_ERR(TAG, "Can not create region %s", gdi_regn_str(buffer, sizeof(buffer), rgn));
304
0
    r = rgn->x;
305
0
    b = rgn->y;
306
0
    rc = FALSE;
307
0
  }
308
0
  rect->left = rgn->x;
309
0
  rect->top = rgn->y;
310
0
  rect->right = (INT32)r;
311
0
  rect->bottom = (INT32)b;
312
313
0
  return rc;
314
0
}
315
316
/**
317
 * Convert region coordinates to a rectangle.
318
 * @param x x1
319
 * @param y y1
320
 * @param w width
321
 * @param h height
322
 * @param rect destination rectangle
323
 */
324
325
BOOL gdi_CRgnToRect(INT64 x, INT64 y, INT32 w, INT32 h, GDI_RECT* rect)
326
0
{
327
0
  BOOL invalid = FALSE;
328
0
  const INT64 r = x + w - 1;
329
0
  const INT64 b = y + h - 1;
330
0
  WINPR_ASSERT(x <= INT32_MAX);
331
0
  WINPR_ASSERT(y <= INT32_MAX);
332
0
  WINPR_ASSERT(r <= INT32_MAX);
333
0
  WINPR_ASSERT(b <= INT32_MAX);
334
0
  rect->left = (x > 0) ? (INT32)x : 0;
335
0
  rect->top = (y > 0) ? (INT32)y : 0;
336
0
  rect->right = rect->left;
337
0
  rect->bottom = rect->top;
338
339
0
  if ((w <= 0) || (h <= 0))
340
0
    invalid = TRUE;
341
342
0
  if (r > 0)
343
0
    rect->right = (INT32)r;
344
0
  else
345
0
    invalid = TRUE;
346
347
0
  if (b > 0)
348
0
    rect->bottom = (INT32)b;
349
0
  else
350
0
    invalid = TRUE;
351
352
0
  if (invalid)
353
0
  {
354
0
    WLog_DBG(TAG, "Invisible rectangle %" PRId64 "x%" PRId64 "-%" PRId64 "x%" PRId64, x, y, r,
355
0
             b);
356
0
    return FALSE;
357
0
  }
358
359
0
  return TRUE;
360
0
}
361
362
/**
363
 * Convert a region to rectangular coordinates.
364
 * @param rgn source region
365
 * @param left x1
366
 * @param top y1
367
 * @param right x2
368
 * @param bottom y2
369
 */
370
371
BOOL gdi_RgnToCRect(const GDI_RGN* rgn, INT32* left, INT32* top, INT32* right, INT32* bottom)
372
0
{
373
0
  BOOL rc = TRUE;
374
375
0
  WINPR_ASSERT(rgn);
376
0
  if ((rgn->w < 0) || (rgn->h < 0))
377
0
  {
378
0
    char buffer[256];
379
0
    WLog_ERR(TAG, "Can not create region %s", gdi_regn_str(buffer, sizeof(buffer), rgn));
380
0
    rc = FALSE;
381
0
  }
382
383
0
  *left = rgn->x;
384
0
  *top = rgn->y;
385
0
  *right = rgn->x + rgn->w - 1;
386
0
  *bottom = rgn->y + rgn->h - 1;
387
388
0
  return rc;
389
0
}
390
391
/**
392
 * Convert region coordinates to rectangular coordinates.
393
 * @param x x1
394
 * @param y y1
395
 * @param w width
396
 * @param h height
397
 * @param left x1
398
 * @param top y1
399
 * @param right x2
400
 * @param bottom y2
401
 */
402
403
BOOL gdi_CRgnToCRect(INT32 x, INT32 y, INT32 w, INT32 h, INT32* left, INT32* top, INT32* right,
404
                     INT32* bottom)
405
0
{
406
0
  BOOL rc = TRUE;
407
0
  *left = x;
408
0
  *top = y;
409
0
  *right = 0;
410
411
0
  if (w > 0)
412
0
    *right = x + w - 1;
413
0
  else
414
0
  {
415
0
    WLog_ERR(TAG, "Invalid width");
416
0
    rc = FALSE;
417
0
  }
418
419
0
  *bottom = 0;
420
421
0
  if (h > 0)
422
0
    *bottom = y + h - 1;
423
0
  else
424
0
  {
425
0
    WLog_ERR(TAG, "Invalid height");
426
0
    rc = FALSE;
427
0
  }
428
429
0
  return rc;
430
0
}
431
432
/**
433
 * Check if copying would involve overlapping regions
434
 * @param x x1
435
 * @param y y1
436
 * @param width width
437
 * @param height height
438
 * @param srcx source x1
439
 * @param srcy source y1
440
 * @return nonzero if there is an overlap, 0 otherwise
441
 */
442
443
INLINE BOOL gdi_CopyOverlap(INT32 x, INT32 y, INT32 width, INT32 height, INT32 srcx, INT32 srcy)
444
0
{
445
0
  GDI_RECT dst;
446
0
  GDI_RECT src;
447
0
  gdi_CRgnToRect(x, y, width, height, &dst);
448
0
  gdi_CRgnToRect(srcx, srcy, width, height, &src);
449
450
0
  if (dst.right < src.left)
451
0
    return FALSE;
452
0
  if (dst.left > src.right)
453
0
    return FALSE;
454
0
  if (dst.bottom < src.top)
455
0
    return FALSE;
456
0
  if (dst.top > src.bottom)
457
0
    return FALSE;
458
0
  return TRUE;
459
0
}
460
461
/**
462
 * Set the coordinates of a given rectangle.
463
 * msdn{dd145085}
464
 *
465
 * @param rc rectangle
466
 * @param xLeft x1
467
 * @param yTop y1
468
 * @param xRight x2
469
 * @param yBottom y2
470
 *
471
 * @return nonzero if successful, 0 otherwise
472
 */
473
474
INLINE BOOL gdi_SetRect(GDI_RECT* rc, INT32 xLeft, INT32 yTop, INT32 xRight, INT32 yBottom)
475
0
{
476
0
  if (!rc)
477
0
    return FALSE;
478
0
  if (xLeft > xRight)
479
0
    return FALSE;
480
0
  if (yTop > yBottom)
481
0
    return FALSE;
482
483
0
  rc->left = xLeft;
484
0
  rc->top = yTop;
485
0
  rc->right = xRight;
486
0
  rc->bottom = yBottom;
487
0
  return TRUE;
488
0
}
489
490
/**
491
 * Set the coordinates of a given region.
492
 * @param hRgn region
493
 * @param nXLeft x1
494
 * @param nYLeft y1
495
 * @param nWidth width
496
 * @param nHeight height
497
 * @return nonzero if successful, 0 otherwise
498
 */
499
500
INLINE BOOL gdi_SetRgn(GDI_RGN* hRgn, INT32 nXLeft, INT32 nYLeft, INT32 nWidth, INT32 nHeight)
501
0
{
502
0
  if (!hRgn)
503
0
    return FALSE;
504
505
0
  if ((nWidth < 0) || (nHeight < 0))
506
0
    return FALSE;
507
508
0
  hRgn->x = nXLeft;
509
0
  hRgn->y = nYLeft;
510
0
  hRgn->w = nWidth;
511
0
  hRgn->h = nHeight;
512
0
  hRgn->null = FALSE;
513
0
  return TRUE;
514
0
}
515
516
/**
517
 * Convert rectangular coordinates to a region
518
 * @param hRgn destination region
519
 * @param nLeftRect x1
520
 * @param nTopRect y1
521
 * @param nRightRect x2
522
 * @param nBottomRect y2
523
 * @return nonzero if successful, 0 otherwise
524
 */
525
526
INLINE BOOL gdi_SetRectRgn(GDI_RGN* hRgn, INT32 nLeftRect, INT32 nTopRect, INT32 nRightRect,
527
                           INT32 nBottomRect)
528
0
{
529
0
  if (!gdi_CRectToRgn(nLeftRect, nTopRect, nRightRect, nBottomRect, hRgn))
530
0
    return FALSE;
531
0
  hRgn->null = FALSE;
532
0
  return TRUE;
533
0
}
534
535
/**
536
 * @brief Compare two regions for equality.
537
 * msdn{dd162700}
538
 *
539
 * @param hSrcRgn1 first region
540
 * @param hSrcRgn2 second region
541
 * @return nonzero if both regions are equal, 0 otherwise
542
 */
543
544
INLINE BOOL gdi_EqualRgn(const GDI_RGN* hSrcRgn1, const GDI_RGN* hSrcRgn2)
545
0
{
546
0
  WINPR_ASSERT(hSrcRgn1);
547
0
  WINPR_ASSERT(hSrcRgn2);
548
0
  if ((hSrcRgn1->x == hSrcRgn2->x) && (hSrcRgn1->y == hSrcRgn2->y) &&
549
0
      (hSrcRgn1->w == hSrcRgn2->w) && (hSrcRgn1->h == hSrcRgn2->h))
550
0
  {
551
0
    return TRUE;
552
0
  }
553
554
0
  return FALSE;
555
0
}
556
557
/**
558
 * @brief Copy coordinates from a rectangle to another rectangle
559
 * msdn{dd183481}
560
 *
561
 * @param dst destination rectangle
562
 * @param src source rectangle
563
 * @return nonzero if successful, 0 otherwise
564
 */
565
566
INLINE BOOL gdi_CopyRect(GDI_RECT* dst, const GDI_RECT* src)
567
0
{
568
0
  if (!dst || !src)
569
0
    return FALSE;
570
571
0
  dst->left = src->left;
572
0
  dst->top = src->top;
573
0
  dst->right = src->right;
574
0
  dst->bottom = src->bottom;
575
0
  return TRUE;
576
0
}
577
578
/**
579
 * Check if a point is inside a rectangle.
580
 * msdn{dd162882}
581
 * @param rc rectangle
582
 * @param x point x position
583
 * @param y point y position
584
 * @return nonzero if the point is inside, 0 otherwise
585
 */
586
587
INLINE BOOL gdi_PtInRect(const GDI_RECT* rc, INT32 x, INT32 y)
588
0
{
589
  /*
590
   * points on the left and top sides are considered in,
591
   * while points on the right and bottom sides are considered out
592
   */
593
0
  if ((x >= rc->left) && (x <= rc->right))
594
0
  {
595
0
    if ((y >= rc->top) && (y <= rc->bottom))
596
0
    {
597
0
      return TRUE;
598
0
    }
599
0
  }
600
601
0
  return FALSE;
602
0
}
603
604
/**
605
 * Invalidate a given region, such that it is redrawn on the next region update.
606
 * msdn{dd145003}
607
 * @param hdc device context
608
 * @param x x1
609
 * @param y y1
610
 * @param w width
611
 * @param h height
612
 * @return nonzero on success, 0 otherwise
613
 */
614
615
INLINE BOOL gdi_InvalidateRegion(HGDI_DC hdc, INT32 x, INT32 y, INT32 w, INT32 h)
616
0
{
617
0
  GDI_RECT inv;
618
0
  GDI_RECT rgn;
619
0
  GDI_RGN* invalid = NULL;
620
0
  GDI_RGN* cinvalid = NULL;
621
622
0
  if (!hdc->hwnd)
623
0
    return TRUE;
624
625
0
  if (!hdc->hwnd->invalid)
626
0
    return TRUE;
627
628
0
  if (w == 0 || h == 0)
629
0
    return TRUE;
630
631
0
  cinvalid = hdc->hwnd->cinvalid;
632
633
0
  if ((hdc->hwnd->ninvalid + 1) > (INT64)hdc->hwnd->count)
634
0
  {
635
0
    GDI_RGN* new_rgn = NULL;
636
0
    size_t new_cnt = 2ULL * hdc->hwnd->count;
637
0
    if (new_cnt > UINT32_MAX)
638
0
      return FALSE;
639
640
0
    new_rgn = (GDI_RGN*)realloc(cinvalid, sizeof(GDI_RGN) * new_cnt);
641
642
0
    if (!new_rgn)
643
0
      return FALSE;
644
645
0
    hdc->hwnd->count = (UINT32)new_cnt;
646
0
    cinvalid = new_rgn;
647
0
  }
648
649
0
  gdi_SetRgn(&cinvalid[hdc->hwnd->ninvalid++], x, y, w, h);
650
0
  hdc->hwnd->cinvalid = cinvalid;
651
0
  invalid = hdc->hwnd->invalid;
652
653
0
  if (invalid->null)
654
0
  {
655
0
    invalid->x = x;
656
0
    invalid->y = y;
657
0
    invalid->w = w;
658
0
    invalid->h = h;
659
0
    invalid->null = FALSE;
660
0
    return TRUE;
661
0
  }
662
663
0
  gdi_CRgnToRect(x, y, w, h, &rgn);
664
0
  gdi_RgnToRect(invalid, &inv);
665
666
0
  if (rgn.left < inv.left)
667
0
    inv.left = rgn.left;
668
669
0
  if (rgn.top < inv.top)
670
0
    inv.top = rgn.top;
671
672
0
  if (rgn.right > inv.right)
673
0
    inv.right = rgn.right;
674
675
0
  if (rgn.bottom > inv.bottom)
676
0
    inv.bottom = rgn.bottom;
677
678
0
  gdi_RectToRgn(&inv, invalid);
679
0
  return TRUE;
680
0
}