Coverage Report

Created: 2023-09-25 06:56

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