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