Coverage Report

Created: 2025-11-24 06:38

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/FreeRDP/libfreerdp/gdi/region.c
Line
Count
Source
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
  WINPR_ASSERT(rect);
147
0
  WINPR_ASSERT(rgn);
148
149
0
  BOOL rc = TRUE;
150
0
  INT64 w = rect->right - rect->left + 1ll;
151
0
  INT64 h = rect->bottom - rect->top + 1ll;
152
153
0
  if ((w < 0) || (h < 0) || (w > INT32_MAX) || (h > INT32_MAX))
154
0
  {
155
0
    WLog_ERR(TAG,
156
0
             "Can not create region top/left=%" PRId32 "x%" PRId32 "-bottom/right=%" PRId32
157
0
             "x%" PRId32,
158
0
             rect->top, rect->left, rect->bottom, rect->right);
159
0
    w = 0;
160
0
    h = 0;
161
0
    rc = FALSE;
162
0
  }
163
164
0
  rgn->x = rect->left;
165
0
  rgn->y = rect->top;
166
0
  rgn->w = (INT32)w;
167
0
  rgn->h = (INT32)h;
168
169
0
  return rc;
170
0
}
171
172
/**
173
 * Convert rectangular coordinates to a region.
174
 * @param left x1
175
 * @param top y1
176
 * @param right x2
177
 * @param bottom y2
178
 * @param rgn destination region
179
 */
180
181
BOOL gdi_CRectToRgn(INT32 left, INT32 top, INT32 right, INT32 bottom, GDI_RGN* rgn)
182
0
{
183
0
  BOOL rc = TRUE;
184
0
  INT64 w = 0;
185
0
  INT64 h = 0;
186
0
  w = right - left + 1ll;
187
0
  h = bottom - top + 1ll;
188
189
0
  if (!rgn)
190
0
    return FALSE;
191
192
0
  if ((w < 0) || (h < 0) || (w > INT32_MAX) || (h > INT32_MAX))
193
0
  {
194
0
    WLog_ERR(TAG,
195
0
             "Can not create region top/left=%" PRId32 "x%" PRId32 "-bottom/right=%" PRId32
196
0
             "x%" PRId32,
197
0
             top, left, bottom, right);
198
0
    w = 0;
199
0
    h = 0;
200
0
    rc = FALSE;
201
0
  }
202
203
0
  rgn->x = left;
204
0
  rgn->y = top;
205
0
  rgn->w = (INT32)w;
206
0
  rgn->h = (INT32)h;
207
0
  return rc;
208
0
}
209
210
/**
211
 * Convert a rectangle to region coordinates.
212
 * @param rect source rectangle
213
 * @param x x1
214
 * @param y y1
215
 * @param w width
216
 * @param h height
217
 */
218
219
BOOL gdi_RectToCRgn(const GDI_RECT* rect, INT32* x, INT32* y, INT32* w, INT32* h)
220
0
{
221
0
  BOOL rc = TRUE;
222
0
  *x = rect->left;
223
0
  *y = rect->top;
224
0
  INT64 tmp = rect->right - rect->left + 1;
225
0
  if ((tmp < 0) || (tmp > INT32_MAX))
226
0
  {
227
0
    char buffer[256];
228
0
    WLog_ERR(TAG, "rectangle invalid %s", gdi_rect_str(buffer, sizeof(buffer), rect));
229
0
    *w = 0;
230
0
    rc = FALSE;
231
0
  }
232
0
  else
233
0
    *w = (INT32)tmp;
234
0
  tmp = rect->bottom - rect->top + 1;
235
0
  if ((tmp < 0) || (tmp > INT32_MAX))
236
0
  {
237
0
    char buffer[256];
238
0
    WLog_ERR(TAG, "rectangle invalid %s", gdi_rect_str(buffer, sizeof(buffer), rect));
239
0
    *h = 0;
240
0
    rc = FALSE;
241
0
  }
242
0
  else
243
0
    *h = (INT32)tmp;
244
0
  return rc;
245
0
}
246
247
/**
248
 * Convert rectangular coordinates to region coordinates.
249
 * @param left x1
250
 * @param top y1
251
 * @param right x2
252
 * @param bottom y2
253
 * @param x x1
254
 * @param y y1
255
 * @param w width
256
 * @param h height
257
 */
258
259
BOOL gdi_CRectToCRgn(INT32 left, INT32 top, INT32 right, INT32 bottom, INT32* x, INT32* y, INT32* w,
260
                     INT32* h)
261
0
{
262
0
  INT64 wl = 0;
263
0
  INT64 hl = 0;
264
0
  BOOL rc = TRUE;
265
0
  wl = right - left + 1ll;
266
0
  hl = bottom - top + 1ll;
267
268
0
  if ((left > right) || (top > bottom) || (wl <= 0) || (hl <= 0) || (wl > INT32_MAX) ||
269
0
      (hl > INT32_MAX))
270
0
  {
271
0
    WLog_ERR(TAG,
272
0
             "Can not create region top/left=%" PRId32 "x%" PRId32 "-bottom/right=%" PRId32
273
0
             "x%" PRId32,
274
0
             top, left, bottom, right);
275
0
    wl = 0;
276
0
    hl = 0;
277
0
    rc = FALSE;
278
0
  }
279
280
0
  *x = left;
281
0
  *y = top;
282
0
  *w = (INT32)wl;
283
0
  *h = (INT32)hl;
284
0
  return rc;
285
0
}
286
287
/**
288
 * Convert a region to a rectangle.
289
 * @param rgn source region
290
 * @param rect destination rectangle
291
 */
292
293
BOOL gdi_RgnToRect(const GDI_RGN* rgn, GDI_RECT* rect)
294
0
{
295
0
  INT64 r = 0;
296
0
  INT64 b = 0;
297
0
  BOOL rc = TRUE;
298
0
  r = rgn->x + rgn->w - 1ll;
299
0
  b = rgn->y + rgn->h - 1ll;
300
301
0
  if ((r < INT32_MIN) || (r > INT32_MAX) || (b < INT32_MIN) || (b > INT32_MAX))
302
0
  {
303
0
    char buffer[256];
304
0
    WLog_ERR(TAG, "Can not create region %s", gdi_regn_str(buffer, sizeof(buffer), rgn));
305
0
    r = rgn->x;
306
0
    b = rgn->y;
307
0
    rc = FALSE;
308
0
  }
309
0
  rect->left = rgn->x;
310
0
  rect->top = rgn->y;
311
0
  rect->right = (INT32)r;
312
0
  rect->bottom = (INT32)b;
313
314
0
  return rc;
315
0
}
316
317
/**
318
 * Convert region coordinates to a rectangle.
319
 * @param x x1
320
 * @param y y1
321
 * @param w width
322
 * @param h height
323
 * @param rect destination rectangle
324
 */
325
326
BOOL gdi_CRgnToRect(INT64 x, INT64 y, INT32 w, INT32 h, GDI_RECT* rect)
327
0
{
328
0
  BOOL invalid = FALSE;
329
0
  const INT64 r = x + w - 1;
330
0
  const INT64 b = y + h - 1;
331
0
  WINPR_ASSERT(x <= INT32_MAX);
332
0
  WINPR_ASSERT(y <= INT32_MAX);
333
0
  WINPR_ASSERT(r <= INT32_MAX);
334
0
  WINPR_ASSERT(b <= INT32_MAX);
335
0
  rect->left = (x > 0) ? (INT32)x : 0;
336
0
  rect->top = (y > 0) ? (INT32)y : 0;
337
0
  rect->right = rect->left;
338
0
  rect->bottom = rect->top;
339
340
0
  if ((w <= 0) || (h <= 0))
341
0
    invalid = TRUE;
342
343
0
  if (r > 0)
344
0
    rect->right = (INT32)r;
345
0
  else
346
0
    invalid = TRUE;
347
348
0
  if (b > 0)
349
0
    rect->bottom = (INT32)b;
350
0
  else
351
0
    invalid = TRUE;
352
353
0
  if (invalid)
354
0
  {
355
0
    WLog_DBG(TAG, "Invisible rectangle %" PRId64 "x%" PRId64 "-%" PRId64 "x%" PRId64, x, y, r,
356
0
             b);
357
0
    return FALSE;
358
0
  }
359
360
0
  return TRUE;
361
0
}
362
363
/**
364
 * Convert a region to rectangular coordinates.
365
 * @param rgn source region
366
 * @param left x1
367
 * @param top y1
368
 * @param right x2
369
 * @param bottom y2
370
 */
371
372
BOOL gdi_RgnToCRect(const GDI_RGN* rgn, INT32* left, INT32* top, INT32* right, INT32* bottom)
373
0
{
374
0
  BOOL rc = TRUE;
375
376
0
  WINPR_ASSERT(rgn);
377
0
  if ((rgn->w < 0) || (rgn->h < 0))
378
0
  {
379
0
    char buffer[256];
380
0
    WLog_ERR(TAG, "Can not create region %s", gdi_regn_str(buffer, sizeof(buffer), rgn));
381
0
    rc = FALSE;
382
0
  }
383
384
0
  *left = rgn->x;
385
0
  *top = rgn->y;
386
0
  *right = rgn->x + rgn->w - 1;
387
0
  *bottom = rgn->y + rgn->h - 1;
388
389
0
  return rc;
390
0
}
391
392
/**
393
 * Convert region coordinates to rectangular coordinates.
394
 * @param x x1
395
 * @param y y1
396
 * @param w width
397
 * @param h height
398
 * @param left x1
399
 * @param top y1
400
 * @param right x2
401
 * @param bottom y2
402
 */
403
404
BOOL gdi_CRgnToCRect(INT32 x, INT32 y, INT32 w, INT32 h, INT32* left, INT32* top, INT32* right,
405
                     INT32* bottom)
406
0
{
407
0
  BOOL rc = TRUE;
408
0
  *left = x;
409
0
  *top = y;
410
0
  *right = 0;
411
412
0
  if (w > 0)
413
0
    *right = x + w - 1;
414
0
  else
415
0
  {
416
0
    WLog_ERR(TAG, "Invalid width");
417
0
    rc = FALSE;
418
0
  }
419
420
0
  *bottom = 0;
421
422
0
  if (h > 0)
423
0
    *bottom = y + h - 1;
424
0
  else
425
0
  {
426
0
    WLog_ERR(TAG, "Invalid height");
427
0
    rc = FALSE;
428
0
  }
429
430
0
  return rc;
431
0
}
432
433
/**
434
 * Check if copying would involve overlapping regions
435
 * @param x x1
436
 * @param y y1
437
 * @param width width
438
 * @param height height
439
 * @param srcx source x1
440
 * @param srcy source y1
441
 * @return nonzero if there is an overlap, 0 otherwise
442
 */
443
444
inline BOOL gdi_CopyOverlap(INT32 x, INT32 y, INT32 width, INT32 height, INT32 srcx, INT32 srcy)
445
0
{
446
0
  GDI_RECT dst;
447
0
  GDI_RECT src;
448
0
  gdi_CRgnToRect(x, y, width, height, &dst);
449
0
  gdi_CRgnToRect(srcx, srcy, width, height, &src);
450
451
0
  if (dst.right < src.left)
452
0
    return FALSE;
453
0
  if (dst.left > src.right)
454
0
    return FALSE;
455
0
  if (dst.bottom < src.top)
456
0
    return FALSE;
457
0
  if (dst.top > src.bottom)
458
0
    return FALSE;
459
0
  return TRUE;
460
0
}
461
462
/**
463
 * Set the coordinates of a given rectangle.
464
 * msdn{dd145085}
465
 *
466
 * @param rc rectangle
467
 * @param xLeft x1
468
 * @param yTop y1
469
 * @param xRight x2
470
 * @param yBottom y2
471
 *
472
 * @return nonzero if successful, 0 otherwise
473
 */
474
475
inline BOOL gdi_SetRect(GDI_RECT* rc, INT32 xLeft, INT32 yTop, INT32 xRight, INT32 yBottom)
476
0
{
477
0
  if (!rc)
478
0
    return FALSE;
479
0
  if (xLeft > xRight)
480
0
    return FALSE;
481
0
  if (yTop > yBottom)
482
0
    return FALSE;
483
484
0
  rc->left = xLeft;
485
0
  rc->top = yTop;
486
0
  rc->right = xRight;
487
0
  rc->bottom = yBottom;
488
0
  return TRUE;
489
0
}
490
491
/**
492
 * Set the coordinates of a given region.
493
 * @param hRgn region
494
 * @param nXLeft x1
495
 * @param nYLeft y1
496
 * @param nWidth width
497
 * @param nHeight height
498
 * @return nonzero if successful, 0 otherwise
499
 */
500
501
inline BOOL gdi_SetRgn(GDI_RGN* hRgn, INT32 nXLeft, INT32 nYLeft, INT32 nWidth, INT32 nHeight)
502
0
{
503
0
  if (!hRgn)
504
0
    return FALSE;
505
506
0
  if ((nWidth < 0) || (nHeight < 0))
507
0
    return FALSE;
508
509
0
  hRgn->x = nXLeft;
510
0
  hRgn->y = nYLeft;
511
0
  hRgn->w = nWidth;
512
0
  hRgn->h = nHeight;
513
0
  hRgn->null = FALSE;
514
0
  return TRUE;
515
0
}
516
517
/**
518
 * Convert rectangular coordinates to a region
519
 * @param hRgn destination region
520
 * @param nLeftRect x1
521
 * @param nTopRect y1
522
 * @param nRightRect x2
523
 * @param nBottomRect y2
524
 * @return nonzero if successful, 0 otherwise
525
 */
526
527
inline BOOL gdi_SetRectRgn(GDI_RGN* hRgn, INT32 nLeftRect, INT32 nTopRect, INT32 nRightRect,
528
                           INT32 nBottomRect)
529
0
{
530
0
  if (!gdi_CRectToRgn(nLeftRect, nTopRect, nRightRect, nBottomRect, hRgn))
531
0
    return FALSE;
532
0
  hRgn->null = FALSE;
533
0
  return TRUE;
534
0
}
535
536
/**
537
 * @brief Compare two regions for equality.
538
 * msdn{dd162700}
539
 *
540
 * @param hSrcRgn1 first region
541
 * @param hSrcRgn2 second region
542
 * @return nonzero if both regions are equal, 0 otherwise
543
 */
544
545
inline BOOL gdi_EqualRgn(const GDI_RGN* hSrcRgn1, const GDI_RGN* hSrcRgn2)
546
0
{
547
0
  WINPR_ASSERT(hSrcRgn1);
548
0
  WINPR_ASSERT(hSrcRgn2);
549
0
  if ((hSrcRgn1->x == hSrcRgn2->x) && (hSrcRgn1->y == hSrcRgn2->y) &&
550
0
      (hSrcRgn1->w == hSrcRgn2->w) && (hSrcRgn1->h == hSrcRgn2->h))
551
0
  {
552
0
    return TRUE;
553
0
  }
554
555
0
  return FALSE;
556
0
}
557
558
/**
559
 * @brief Copy coordinates from a rectangle to another rectangle
560
 * msdn{dd183481}
561
 *
562
 * @param dst destination rectangle
563
 * @param src source rectangle
564
 * @return nonzero if successful, 0 otherwise
565
 */
566
567
inline BOOL gdi_CopyRect(GDI_RECT* dst, const GDI_RECT* src)
568
0
{
569
0
  if (!dst || !src)
570
0
    return FALSE;
571
572
0
  dst->left = src->left;
573
0
  dst->top = src->top;
574
0
  dst->right = src->right;
575
0
  dst->bottom = src->bottom;
576
0
  return TRUE;
577
0
}
578
579
/**
580
 * Check if a point is inside a rectangle.
581
 * msdn{dd162882}
582
 * @param rc rectangle
583
 * @param x point x position
584
 * @param y point y position
585
 * @return nonzero if the point is inside, 0 otherwise
586
 */
587
588
inline BOOL gdi_PtInRect(const GDI_RECT* rc, INT32 x, INT32 y)
589
0
{
590
  /*
591
   * points on the left and top sides are considered in,
592
   * while points on the right and bottom sides are considered out
593
   */
594
0
  if ((x >= rc->left) && (x <= rc->right))
595
0
  {
596
0
    if ((y >= rc->top) && (y <= rc->bottom))
597
0
    {
598
0
      return TRUE;
599
0
    }
600
0
  }
601
602
0
  return FALSE;
603
0
}
604
605
/**
606
 * Invalidate a given region, such that it is redrawn on the next region update.
607
 * msdn{dd145003}
608
 * @param hdc device context
609
 * @param x x1
610
 * @param y y1
611
 * @param w width
612
 * @param h height
613
 * @return nonzero on success, 0 otherwise
614
 */
615
616
inline BOOL gdi_InvalidateRegion(HGDI_DC hdc, INT32 x, INT32 y, INT32 w, INT32 h)
617
0
{
618
0
  GDI_RECT inv;
619
0
  GDI_RECT rgn;
620
0
  GDI_RGN* invalid = NULL;
621
0
  GDI_RGN* cinvalid = NULL;
622
623
0
  if (!hdc->hwnd)
624
0
    return TRUE;
625
626
0
  if (!hdc->hwnd->invalid)
627
0
    return TRUE;
628
629
0
  if (w == 0 || h == 0)
630
0
    return TRUE;
631
632
0
  cinvalid = hdc->hwnd->cinvalid;
633
634
0
  if ((hdc->hwnd->ninvalid + 1) > (INT64)hdc->hwnd->count)
635
0
  {
636
0
    GDI_RGN* new_rgn = NULL;
637
0
    size_t new_cnt = 2ULL * hdc->hwnd->count;
638
0
    if (new_cnt > UINT32_MAX)
639
0
      return FALSE;
640
641
0
    new_rgn = (GDI_RGN*)realloc(cinvalid, sizeof(GDI_RGN) * new_cnt);
642
643
0
    if (!new_rgn)
644
0
      return FALSE;
645
646
0
    hdc->hwnd->count = (UINT32)new_cnt;
647
0
    cinvalid = new_rgn;
648
0
  }
649
650
0
  gdi_SetRgn(&cinvalid[hdc->hwnd->ninvalid++], x, y, w, h);
651
0
  hdc->hwnd->cinvalid = cinvalid;
652
0
  invalid = hdc->hwnd->invalid;
653
654
0
  if (invalid->null)
655
0
  {
656
0
    invalid->x = x;
657
0
    invalid->y = y;
658
0
    invalid->w = w;
659
0
    invalid->h = h;
660
0
    invalid->null = FALSE;
661
0
    return TRUE;
662
0
  }
663
664
0
  gdi_CRgnToRect(x, y, w, h, &rgn);
665
0
  gdi_RgnToRect(invalid, &inv);
666
667
0
  if (rgn.left < inv.left)
668
0
    inv.left = rgn.left;
669
670
0
  if (rgn.top < inv.top)
671
0
    inv.top = rgn.top;
672
673
0
  if (rgn.right > inv.right)
674
0
    inv.right = rgn.right;
675
676
0
  if (rgn.bottom > inv.bottom)
677
0
    inv.bottom = rgn.bottom;
678
679
0
  gdi_RectToRgn(&inv, invalid);
680
  return TRUE;
681
0
}