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