FreeRDP/libfreerdp/gdi/region.c
2024-08-29 10:47:20 +02:00

671 lines
14 KiB
C

/**
* 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;
(void)_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;
(void)_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)
{
HGDI_RGN new_rgn = NULL;
size_t new_cnt = 2ULL * hdc->hwnd->count;
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;
}