Coverage Report

Created: 2024-05-20 06:11

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