diff options
Diffstat (limited to '')
-rw-r--r-- | libfreerdp/gdi/region.c | 671 |
1 files changed, 671 insertions, 0 deletions
diff --git a/libfreerdp/gdi/region.c b/libfreerdp/gdi/region.c new file mode 100644 index 0000000..31d638f --- /dev/null +++ b/libfreerdp/gdi/region.c @@ -0,0 +1,671 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * GDI Region Functions + * + * Copyright 2010-2011 Marc-Andre Moreau <marcandre.moreau@gmail.com> + * Copyright 2016 Armin Novak <armin.novak@thincast.com> + * Copyright 2016 Thincast Technologies GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <freerdp/config.h> + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include <freerdp/api.h> +#include <freerdp/freerdp.h> +#include <freerdp/gdi/gdi.h> + +#include <freerdp/gdi/region.h> + +#include <freerdp/log.h> + +#define TAG FREERDP_TAG("gdi.region") + +static char* gdi_rect_str(char* buffer, size_t size, const HGDI_RECT rect) +{ + if (!buffer || (size < 1) || !rect) + return NULL; + + _snprintf(buffer, size - 1, + "[top/left=%" PRId32 "x%" PRId32 "-bottom/right%" PRId32 "x%" PRId32 "]", rect->top, + rect->left, rect->bottom, rect->right); + buffer[size - 1] = '\0'; + + return buffer; +} + +static char* gdi_regn_str(char* buffer, size_t size, const HGDI_RGN rgn) +{ + if (!buffer || (size < 1) || !rgn) + return NULL; + + _snprintf(buffer, size - 1, "[%" PRId32 "x%" PRId32 "-%" PRId32 "x%" PRId32 "]", rgn->x, rgn->y, + rgn->w, rgn->h); + buffer[size - 1] = '\0'; + + return buffer; +} + +/** + * Create a region from rectangular coordinates. + * msdn{dd183514} + * + * @param nLeftRect x1 + * @param nTopRect y1 + * @param nRightRect x2 + * @param nBottomRect y2 + * + * @return new region + */ + +HGDI_RGN gdi_CreateRectRgn(INT32 nLeftRect, INT32 nTopRect, INT32 nRightRect, INT32 nBottomRect) +{ + INT64 w = 0; + INT64 h = 0; + HGDI_RGN hRgn = NULL; + + w = nRightRect - nLeftRect + 1ll; + h = nBottomRect - nTopRect + 1ll; + if ((w < 0) || (h < 0) || (w > INT32_MAX) || (h > INT32_MAX)) + { + WLog_ERR(TAG, + "Can not create region top/left=%" PRId32 "x%" PRId32 "-bottom/right=%" PRId32 + "x%" PRId32, + nTopRect, nLeftRect, nBottomRect, nRightRect); + return NULL; + } + hRgn = (HGDI_RGN)calloc(1, sizeof(GDI_RGN)); + + if (!hRgn) + return NULL; + + hRgn->objectType = GDIOBJECT_REGION; + hRgn->x = nLeftRect; + hRgn->y = nTopRect; + hRgn->w = w; + hRgn->h = h; + hRgn->null = FALSE; + return hRgn; +} + +/** + * Create a new rectangle. + * @param xLeft x1 + * @param yTop y1 + * @param xRight x2 + * @param yBottom y2 + * @return new rectangle + */ + +HGDI_RECT gdi_CreateRect(INT32 xLeft, INT32 yTop, INT32 xRight, INT32 yBottom) +{ + HGDI_RECT hRect = NULL; + + if (xLeft > xRight) + return NULL; + if (yTop > yBottom) + return NULL; + + hRect = (HGDI_RECT)calloc(1, sizeof(GDI_RECT)); + + if (!hRect) + return NULL; + + hRect->objectType = GDIOBJECT_RECT; + hRect->left = xLeft; + hRect->top = yTop; + hRect->right = xRight; + hRect->bottom = yBottom; + return hRect; +} + +/** + * Convert a rectangle to a region. + * @param rect source rectangle + * @param rgn destination region + */ + +BOOL gdi_RectToRgn(const HGDI_RECT rect, HGDI_RGN rgn) +{ + BOOL rc = TRUE; + INT64 w = 0; + INT64 h = 0; + w = rect->right - rect->left + 1ll; + h = rect->bottom - rect->top + 1ll; + + if ((w < 0) || (h < 0) || (w > INT32_MAX) || (h > INT32_MAX)) + { + WLog_ERR(TAG, + "Can not create region top/left=%" PRId32 "x%" PRId32 "-bottom/right=%" PRId32 + "x%" PRId32, + rect->top, rect->left, rect->bottom, rect->right); + w = 0; + h = 0; + rc = FALSE; + } + + rgn->x = rect->left; + rgn->y = rect->top; + rgn->w = w; + rgn->h = h; + + return rc; +} + +/** + * Convert rectangular coordinates to a region. + * @param left x1 + * @param top y1 + * @param right x2 + * @param bottom y2 + * @param rgn destination region + */ + +BOOL gdi_CRectToRgn(INT32 left, INT32 top, INT32 right, INT32 bottom, HGDI_RGN rgn) +{ + BOOL rc = TRUE; + INT64 w = 0; + INT64 h = 0; + w = right - left + 1ll; + h = bottom - top + 1ll; + + if (!rgn) + return FALSE; + + if ((w < 0) || (h < 0) || (w > INT32_MAX) || (h > INT32_MAX)) + { + WLog_ERR(TAG, + "Can not create region top/left=%" PRId32 "x%" PRId32 "-bottom/right=%" PRId32 + "x%" PRId32, + top, left, bottom, right); + w = 0; + h = 0; + rc = FALSE; + } + + rgn->x = left; + rgn->y = top; + rgn->w = w; + rgn->h = h; + return rc; +} + +/** + * Convert a rectangle to region coordinates. + * @param rect source rectangle + * @param x x1 + * @param y y1 + * @param w width + * @param h height + */ + +BOOL gdi_RectToCRgn(const HGDI_RECT rect, INT32* x, INT32* y, INT32* w, INT32* h) +{ + BOOL rc = TRUE; + *x = rect->left; + *y = rect->top; + INT64 tmp = rect->right - rect->left + 1; + if ((tmp < 0) || (tmp > INT32_MAX)) + { + char buffer[256]; + WLog_ERR(TAG, "rectangle invalid %s", gdi_rect_str(buffer, sizeof(buffer), rect)); + *w = 0; + rc = FALSE; + } + else + *w = tmp; + tmp = rect->bottom - rect->top + 1; + if ((tmp < 0) || (tmp > INT32_MAX)) + { + char buffer[256]; + WLog_ERR(TAG, "rectangle invalid %s", gdi_rect_str(buffer, sizeof(buffer), rect)); + *h = 0; + rc = FALSE; + } + else + *h = tmp; + return rc; +} + +/** + * Convert rectangular coordinates to region coordinates. + * @param left x1 + * @param top y1 + * @param right x2 + * @param bottom y2 + * @param x x1 + * @param y y1 + * @param w width + * @param h height + */ + +BOOL gdi_CRectToCRgn(INT32 left, INT32 top, INT32 right, INT32 bottom, INT32* x, INT32* y, INT32* w, + INT32* h) +{ + INT64 wl = 0; + INT64 hl = 0; + BOOL rc = TRUE; + wl = right - left + 1ll; + hl = bottom - top + 1ll; + + if ((left > right) || (top > bottom) || (wl <= 0) || (hl <= 0) || (wl > INT32_MAX) || + (hl > INT32_MAX)) + { + WLog_ERR(TAG, + "Can not create region top/left=%" PRId32 "x%" PRId32 "-bottom/right=%" PRId32 + "x%" PRId32, + top, left, bottom, right); + wl = 0; + hl = 0; + rc = FALSE; + } + + *x = left; + *y = top; + *w = wl; + *h = hl; + return rc; +} + +/** + * Convert a region to a rectangle. + * @param rgn source region + * @param rect destination rectangle + */ + +BOOL gdi_RgnToRect(const HGDI_RGN rgn, HGDI_RECT rect) +{ + INT64 r = 0; + INT64 b = 0; + BOOL rc = TRUE; + r = rgn->x + rgn->w - 1ll; + b = rgn->y + rgn->h - 1ll; + + if ((r < INT32_MIN) || (r > INT32_MAX) || (b < INT32_MIN) || (b > INT32_MAX)) + { + char buffer[256]; + WLog_ERR(TAG, "Can not create region %s", gdi_regn_str(buffer, sizeof(buffer), rgn)); + r = rgn->x; + b = rgn->y; + rc = FALSE; + } + rect->left = rgn->x; + rect->top = rgn->y; + rect->right = r; + rect->bottom = b; + + return rc; +} + +/** + * Convert region coordinates to a rectangle. + * @param x x1 + * @param y y1 + * @param w width + * @param h height + * @param rect destination rectangle + */ + +INLINE BOOL gdi_CRgnToRect(INT64 x, INT64 y, INT32 w, INT32 h, HGDI_RECT rect) +{ + BOOL invalid = FALSE; + const INT64 r = x + w - 1; + const INT64 b = y + h - 1; + rect->left = (x > 0) ? x : 0; + rect->top = (y > 0) ? y : 0; + rect->right = rect->left; + rect->bottom = rect->top; + + if ((w <= 0) || (h <= 0)) + invalid = TRUE; + + if (r > 0) + rect->right = r; + else + invalid = TRUE; + + if (b > 0) + rect->bottom = b; + else + invalid = TRUE; + + if (invalid) + { + WLog_DBG(TAG, "Invisible rectangle %" PRId64 "x%" PRId64 "-%" PRId64 "x%" PRId64, x, y, r, + b); + return FALSE; + } + + return TRUE; +} + +/** + * Convert a region to rectangular coordinates. + * @param rgn source region + * @param left x1 + * @param top y1 + * @param right x2 + * @param bottom y2 + */ + +BOOL gdi_RgnToCRect(const HGDI_RGN rgn, INT32* left, INT32* top, INT32* right, INT32* bottom) +{ + BOOL rc = TRUE; + if ((rgn->w < 0) || (rgn->h < 0)) + { + char buffer[256]; + WLog_ERR(TAG, "Can not create region %s", gdi_regn_str(buffer, sizeof(buffer), rgn)); + rc = FALSE; + } + + *left = rgn->x; + *top = rgn->y; + *right = rgn->x + rgn->w - 1; + *bottom = rgn->y + rgn->h - 1; + + return rc; +} + +/** + * Convert region coordinates to rectangular coordinates. + * @param x x1 + * @param y y1 + * @param w width + * @param h height + * @param left x1 + * @param top y1 + * @param right x2 + * @param bottom y2 + */ + +INLINE BOOL gdi_CRgnToCRect(INT32 x, INT32 y, INT32 w, INT32 h, INT32* left, INT32* top, + INT32* right, INT32* bottom) +{ + BOOL rc = TRUE; + *left = x; + *top = y; + *right = 0; + + if (w > 0) + *right = x + w - 1; + else + { + WLog_ERR(TAG, "Invalid width"); + rc = FALSE; + } + + *bottom = 0; + + if (h > 0) + *bottom = y + h - 1; + else + { + WLog_ERR(TAG, "Invalid height"); + rc = FALSE; + } + + return rc; +} + +/** + * Check if copying would involve overlapping regions + * @param x x1 + * @param y y1 + * @param width width + * @param height height + * @param srcx source x1 + * @param srcy source y1 + * @return nonzero if there is an overlap, 0 otherwise + */ + +INLINE BOOL gdi_CopyOverlap(INT32 x, INT32 y, INT32 width, INT32 height, INT32 srcx, INT32 srcy) +{ + GDI_RECT dst; + GDI_RECT src; + gdi_CRgnToRect(x, y, width, height, &dst); + gdi_CRgnToRect(srcx, srcy, width, height, &src); + + if (dst.right < src.left) + return FALSE; + if (dst.left > src.right) + return FALSE; + if (dst.bottom < src.top) + return FALSE; + if (dst.top > src.bottom) + return FALSE; + return TRUE; +} + +/** + * Set the coordinates of a given rectangle. + * msdn{dd145085} + * + * @param rc rectangle + * @param xLeft x1 + * @param yTop y1 + * @param xRight x2 + * @param yBottom y2 + * + * @return nonzero if successful, 0 otherwise + */ + +INLINE BOOL gdi_SetRect(HGDI_RECT rc, INT32 xLeft, INT32 yTop, INT32 xRight, INT32 yBottom) +{ + if (!rc) + return FALSE; + if (xLeft > xRight) + return FALSE; + if (yTop > yBottom) + return FALSE; + + rc->left = xLeft; + rc->top = yTop; + rc->right = xRight; + rc->bottom = yBottom; + return TRUE; +} + +/** + * Set the coordinates of a given region. + * @param hRgn region + * @param nXLeft x1 + * @param nYLeft y1 + * @param nWidth width + * @param nHeight height + * @return nonzero if successful, 0 otherwise + */ + +INLINE BOOL gdi_SetRgn(HGDI_RGN hRgn, INT32 nXLeft, INT32 nYLeft, INT32 nWidth, INT32 nHeight) +{ + if (!hRgn) + return FALSE; + + if ((nWidth < 0) || (nHeight < 0)) + return FALSE; + + hRgn->x = nXLeft; + hRgn->y = nYLeft; + hRgn->w = nWidth; + hRgn->h = nHeight; + hRgn->null = FALSE; + return TRUE; +} + +/** + * Convert rectangular coordinates to a region + * @param hRgn destination region + * @param nLeftRect x1 + * @param nTopRect y1 + * @param nRightRect x2 + * @param nBottomRect y2 + * @return nonzero if successful, 0 otherwise + */ + +INLINE BOOL gdi_SetRectRgn(HGDI_RGN hRgn, INT32 nLeftRect, INT32 nTopRect, INT32 nRightRect, + INT32 nBottomRect) +{ + if (!gdi_CRectToRgn(nLeftRect, nTopRect, nRightRect, nBottomRect, hRgn)) + return FALSE; + hRgn->null = FALSE; + return TRUE; +} + +/** + * @brief Compare two regions for equality. + * msdn{dd162700} + * + * @param hSrcRgn1 first region + * @param hSrcRgn2 second region + * @return nonzero if both regions are equal, 0 otherwise + */ + +INLINE BOOL gdi_EqualRgn(const HGDI_RGN hSrcRgn1, const HGDI_RGN hSrcRgn2) +{ + if ((hSrcRgn1->x == hSrcRgn2->x) && (hSrcRgn1->y == hSrcRgn2->y) && + (hSrcRgn1->w == hSrcRgn2->w) && (hSrcRgn1->h == hSrcRgn2->h)) + { + return TRUE; + } + + return FALSE; +} + +/** + * @brief Copy coordinates from a rectangle to another rectangle + * msdn{dd183481} + * + * @param dst destination rectangle + * @param src source rectangle + * @return nonzero if successful, 0 otherwise + */ + +INLINE BOOL gdi_CopyRect(HGDI_RECT dst, const HGDI_RECT src) +{ + if (!dst || !src) + return FALSE; + + dst->left = src->left; + dst->top = src->top; + dst->right = src->right; + dst->bottom = src->bottom; + return TRUE; +} + +/** + * Check if a point is inside a rectangle. + * msdn{dd162882} + * @param rc rectangle + * @param x point x position + * @param y point y position + * @return nonzero if the point is inside, 0 otherwise + */ + +INLINE BOOL gdi_PtInRect(const HGDI_RECT rc, INT32 x, INT32 y) +{ + /* + * points on the left and top sides are considered in, + * while points on the right and bottom sides are considered out + */ + if ((x >= rc->left) && (x <= rc->right)) + { + if ((y >= rc->top) && (y <= rc->bottom)) + { + return TRUE; + } + } + + return FALSE; +} + +/** + * Invalidate a given region, such that it is redrawn on the next region update. + * msdn{dd145003} + * @param hdc device context + * @param x x1 + * @param y y1 + * @param w width + * @param h height + * @return nonzero on success, 0 otherwise + */ + +INLINE BOOL gdi_InvalidateRegion(HGDI_DC hdc, INT32 x, INT32 y, INT32 w, INT32 h) +{ + GDI_RECT inv; + GDI_RECT rgn; + HGDI_RGN invalid = NULL; + HGDI_RGN cinvalid = NULL; + + if (!hdc->hwnd) + return TRUE; + + if (!hdc->hwnd->invalid) + return TRUE; + + if (w == 0 || h == 0) + return TRUE; + + cinvalid = hdc->hwnd->cinvalid; + + if ((hdc->hwnd->ninvalid + 1) > (INT64)hdc->hwnd->count) + { + size_t new_cnt = 0; + HGDI_RGN new_rgn = NULL; + new_cnt = hdc->hwnd->count * 2; + if (new_cnt > UINT32_MAX) + return FALSE; + + new_rgn = (HGDI_RGN)realloc(cinvalid, sizeof(GDI_RGN) * new_cnt); + + if (!new_rgn) + return FALSE; + + hdc->hwnd->count = new_cnt; + cinvalid = new_rgn; + } + + gdi_SetRgn(&cinvalid[hdc->hwnd->ninvalid++], x, y, w, h); + hdc->hwnd->cinvalid = cinvalid; + invalid = hdc->hwnd->invalid; + + if (invalid->null) + { + invalid->x = x; + invalid->y = y; + invalid->w = w; + invalid->h = h; + invalid->null = FALSE; + return TRUE; + } + + gdi_CRgnToRect(x, y, w, h, &rgn); + gdi_RgnToRect(invalid, &inv); + + if (rgn.left < inv.left) + inv.left = rgn.left; + + if (rgn.top < inv.top) + inv.top = rgn.top; + + if (rgn.right > inv.right) + inv.right = rgn.right; + + if (rgn.bottom > inv.bottom) + inv.bottom = rgn.bottom; + + gdi_RectToRgn(&inv, invalid); + return TRUE; +} |