From dc4bd6609becf5e20a0ef05e26cce41938ac3d27 Mon Sep 17 00:00:00 2001 From: nin Date: Fri, 1 Mar 2024 20:51:08 +0100 Subject: [PATCH] [server,shadow] add image format support for capture when capturing bitmap data take the source image format into account on copy and compare. --- include/freerdp/server/shadow.h | 10 +- server/shadow/X11/x11_shadow.c | 18 +-- server/shadow/X11/x11_shadow.h | 1 + server/shadow/shadow_capture.c | 212 ++++++++++++++++++++++---------- 4 files changed, 165 insertions(+), 76 deletions(-) diff --git a/include/freerdp/server/shadow.h b/include/freerdp/server/shadow.h index 9ddb8ae9f..1b9f19ae5 100644 --- a/include/freerdp/server/shadow.h +++ b/include/freerdp/server/shadow.h @@ -325,10 +325,14 @@ extern "C" WINPR_ATTR_MALLOC(shadow_server_free, 1) FREERDP_API rdpShadowServer* shadow_server_new(void); - FREERDP_API int shadow_capture_align_clip_rect(RECTANGLE_16* rect, RECTANGLE_16* clip); - FREERDP_API int shadow_capture_compare(BYTE* pData1, UINT32 nStep1, UINT32 nWidth, - UINT32 nHeight, BYTE* pData2, UINT32 nStep2, + FREERDP_API int shadow_capture_align_clip_rect(RECTANGLE_16* rect, const RECTANGLE_16* clip); + FREERDP_API int shadow_capture_compare(const BYTE* pData1, UINT32 nStep1, UINT32 nWidth, + UINT32 nHeight, const BYTE* pData2, UINT32 nStep2, RECTANGLE_16* rect); + FREERDP_API int shadow_capture_compare_with_format(const BYTE* pData1, UINT32 format1, + UINT32 nStep1, UINT32 nWidth, UINT32 nHeight, + const BYTE* pData2, UINT32 format2, + UINT32 nStep2, RECTANGLE_16* rect); FREERDP_API void shadow_subsystem_frame_update(rdpShadowSubsystem* subsystem); diff --git a/server/shadow/X11/x11_shadow.c b/server/shadow/X11/x11_shadow.c index 5c1fab18d..7babaaccf 100644 --- a/server/shadow/X11/x11_shadow.c +++ b/server/shadow/X11/x11_shadow.c @@ -796,9 +796,10 @@ static int x11_shadow_screen_grab(x11ShadowSubsystem* subsystem) subsystem->xshm_gc, 0, 0, subsystem->width, subsystem->height, 0, 0); EnterCriticalSection(&surface->lock); - status = shadow_capture_compare(surface->data, surface->scanline, surface->width, - surface->height, (BYTE*)&(image->data[surface->width * 4]), - image->bytes_per_line, &invalidRect); + status = shadow_capture_compare_with_format( + surface->data, surface->format, surface->scanline, surface->width, surface->height, + (BYTE*)&(image->data[surface->width * 4ull]), subsystem->format, image->bytes_per_line, + &invalidRect); LeaveCriticalSection(&surface->lock); } else @@ -810,9 +811,9 @@ static int x11_shadow_screen_grab(x11ShadowSubsystem* subsystem) if (image) { - status = shadow_capture_compare(surface->data, surface->scanline, surface->width, - surface->height, (BYTE*)image->data, - image->bytes_per_line, &invalidRect); + status = shadow_capture_compare_with_format( + surface->data, surface->format, surface->scanline, surface->width, surface->height, + (BYTE*)image->data, subsystem->format, image->bytes_per_line, &invalidRect); } LeaveCriticalSection(&surface->lock); if (!image) @@ -854,7 +855,7 @@ static int x11_shadow_screen_grab(x11ShadowSubsystem* subsystem) WINPR_ASSERT(height >= 0); success = freerdp_image_copy(surface->data, surface->format, surface->scanline, x, y, (UINT32)width, (UINT32)height, (BYTE*)image->data, - PIXEL_FORMAT_BGRX32, (UINT32)image->bytes_per_line, x, y, + subsystem->format, (UINT32)image->bytes_per_line, x, y, NULL, FREERDP_FLIP_NONE); LeaveCriticalSection(&surface->lock); if (!success) @@ -1392,6 +1393,9 @@ static int x11_shadow_subsystem_init(rdpShadowSubsystem* sub) subsystem->use_xfixes, subsystem->use_xinerama, subsystem->use_xdamage, subsystem->use_xshm); } + + subsystem->format = (ImageByteOrder(subsystem->display) == LSBFirst) ? PIXEL_FORMAT_BGRX32 + : PIXEL_FORMAT_XRGB32; return 1; } diff --git a/server/shadow/X11/x11_shadow.h b/server/shadow/X11/x11_shadow.h index aca2d63db..5ab8beb76 100644 --- a/server/shadow/X11/x11_shadow.h +++ b/server/shadow/X11/x11_shadow.h @@ -100,6 +100,7 @@ struct x11_shadow_subsystem #ifdef WITH_XFIXES int xfixes_cursor_notify_event; #endif + UINT32 format; }; #ifdef __cplusplus diff --git a/server/shadow/shadow_capture.c b/server/shadow/shadow_capture.c index 4711dede2..d3d344211 100644 --- a/server/shadow/shadow_capture.c +++ b/server/shadow/shadow_capture.c @@ -29,7 +29,7 @@ #define TAG SERVER_TAG("shadow") -int shadow_capture_align_clip_rect(RECTANGLE_16* rect, RECTANGLE_16* clip) +int shadow_capture_align_clip_rect(RECTANGLE_16* rect, const RECTANGLE_16* clip) { int dx = 0; int dy = 0; @@ -78,55 +78,40 @@ int shadow_capture_align_clip_rect(RECTANGLE_16* rect, RECTANGLE_16* clip) return 1; } -int shadow_capture_compare(BYTE* pData1, UINT32 nStep1, UINT32 nWidth, UINT32 nHeight, BYTE* pData2, - UINT32 nStep2, RECTANGLE_16* rect) +int shadow_capture_compare(const BYTE* pData1, UINT32 nStep1, UINT32 nWidth, UINT32 nHeight, + const BYTE* pData2, UINT32 nStep2, RECTANGLE_16* rect) { - BOOL equal = 0; - BOOL allEqual = 0; + BOOL allEqual = TRUE; UINT32 tw = 0; - UINT32 th = 0; - UINT32 nrow = 0; - UINT32 ncol = 0; - UINT32 l = 0; - UINT32 t = 0; + const UINT32 nrow = (nHeight + 15) / 16; + const UINT32 ncol = (nWidth + 15) / 16; + UINT32 l = ncol + 1; + UINT32 t = nrow + 1; UINT32 r = 0; UINT32 b = 0; - BYTE* p1 = NULL; - BYTE* p2 = NULL; - BOOL rows[1024]; -#ifdef WITH_DEBUG_SHADOW_CAPTURE - BOOL cols[1024] = { FALSE }; -#endif - allEqual = TRUE; - ZeroMemory(rect, sizeof(RECTANGLE_16)); - FillMemory(rows, sizeof(rows), 0xFF); -#ifdef WITH_DEBUG_SHADOW_CAPTURE - FillMemory(cols, sizeof(cols), 0xFF); -#endif - nrow = (nHeight + 15) / 16; - ncol = (nWidth + 15) / 16; - l = ncol + 1; - r = 0; - t = nrow + 1; - b = 0; + const RECTANGLE_16 empty = { 0 }; + WINPR_ASSERT(rect); + + *rect = empty; for (UINT32 ty = 0; ty < nrow; ty++) { - th = ((ty + 1) == nrow) ? (nHeight % 16) : 16; + BOOL rowEqual = TRUE; + UINT32 th = ((ty + 1) == nrow) ? (nHeight % 16) : 16; if (!th) th = 16; for (UINT32 tx = 0; tx < ncol; tx++) { - equal = TRUE; + BOOL equal = TRUE; tw = ((tx + 1) == ncol) ? (nWidth % 16) : 16; if (!tw) tw = 16; - p1 = &pData1[(ty * 16 * nStep1) + (tx * 16 * 4)]; - p2 = &pData2[(ty * 16 * nStep2) + (tx * 16 * 4)]; + const BYTE* p1 = &pData1[(ty * 16 * nStep1) + (tx * 16 * 4)]; + const BYTE* p2 = &pData2[(ty * 16 * nStep2) + (tx * 16 * 4)]; for (UINT32 k = 0; k < th; k++) { @@ -142,11 +127,7 @@ int shadow_capture_compare(BYTE* pData1, UINT32 nStep1, UINT32 nWidth, UINT32 nH if (!equal) { - rows[ty] = FALSE; -#ifdef WITH_DEBUG_SHADOW_CAPTURE - cols[tx] = FALSE; -#endif - + rowEqual = FALSE; if (l > tx) l = tx; @@ -155,7 +136,7 @@ int shadow_capture_compare(BYTE* pData1, UINT32 nStep1, UINT32 nWidth, UINT32 nH } } - if (!rows[ty]) + if (!rowEqual) { allEqual = FALSE; @@ -187,46 +168,145 @@ int shadow_capture_compare(BYTE* pData1, UINT32 nStep1, UINT32 nWidth, UINT32 nH if (rect->bottom > nHeight) rect->bottom = (UINT16)nHeight; -#ifdef WITH_DEBUG_SHADOW_CAPTURE - size_t size = ncol + 1; - char* col_str = calloc(size, sizeof(char)); + return 1; +} - if (!col_str) +static BOOL color_equal(UINT32 colorA, UINT32 formatA, UINT32 colorB, UINT32 formatB) +{ + BYTE ar = 0; + BYTE ag = 0; + BYTE ab = 0; + BYTE aa = 0; + BYTE br = 0; + BYTE bg = 0; + BYTE bb = 0; + BYTE ba = 0; + FreeRDPSplitColor(colorA, formatA, &ar, &ag, &ab, &aa, NULL); + FreeRDPSplitColor(colorB, formatB, &br, &bg, &bb, &ba, NULL); + + if (ar != br) + return FALSE; + if (ag != bg) + return FALSE; + if (ab != bb) + return FALSE; + if (aa != ba) + return FALSE; + return TRUE; +} + +static BOOL pixel_equal(const BYTE* a, UINT32 formatA, const BYTE* b, UINT32 formatB, size_t count) +{ + const size_t bppA = FreeRDPGetBytesPerPixel(formatA); + const size_t bppB = FreeRDPGetBytesPerPixel(formatB); + + for (size_t x = 0; x < count; x++) { - WLog_ERR(TAG, "calloc failed!"); - return 1; + const UINT32 colorA = FreeRDPReadColor(&a[bppA * x], formatA); + const UINT32 colorB = FreeRDPReadColor(&b[bppB * x], formatB); + if (!color_equal(colorA, formatA, colorB, formatB)) + return FALSE; } - for (UINT32 tx = 0; tx < ncol; tx++) - sprintf_s(&col_str[tx], size - tx, "-"); + return TRUE; +} - WLog_INFO(TAG, "%s", col_str); +int shadow_capture_compare_with_format(const BYTE* pData1, UINT32 format1, UINT32 nStep1, + UINT32 nWidth, UINT32 nHeight, const BYTE* pData2, + UINT32 format2, UINT32 nStep2, RECTANGLE_16* rect) +{ + if (format1 == format2) + return shadow_capture_compare(pData1, nStep1, nWidth, nHeight, pData2, nStep2, rect); - for (UINT32 tx = 0; tx < ncol; tx++) - sprintf_s(&col_str[tx], size - tx, "%c", cols[tx] ? 'O' : 'X'); + BOOL allEqual = TRUE; + UINT32 tw = 0; + const UINT32 nrow = (nHeight + 15) / 16; + const UINT32 ncol = (nWidth + 15) / 16; + UINT32 l = ncol + 1; + UINT32 t = nrow + 1; + UINT32 r = 0; + UINT32 b = 0; + const size_t bppA = FreeRDPGetBytesPerPixel(format1); + const size_t bppB = FreeRDPGetBytesPerPixel(format2); + const RECTANGLE_16 empty = { 0 }; + WINPR_ASSERT(rect); - WLog_INFO(TAG, "%s", col_str); + *rect = empty; - for (UINT32 tx = 0; tx < ncol; tx++) - sprintf_s(&col_str[tx], size - tx, "-"); - - WLog_INFO(TAG, "%s", col_str); - - for (UINT32 ty = 0; ty < nrow; ty++) + for (size_t ty = 0; ty < nrow; ty++) { - for (UINT32 tx = 0; tx < ncol; tx++) - sprintf_s(&col_str[tx], size - tx, "%c", cols[tx] ? 'O' : 'X'); + BOOL rowEqual = TRUE; + size_t th = ((ty + 1) == nrow) ? (nHeight % 16) : 16; - WLog_INFO(TAG, "%s", col_str); - WLog_INFO(TAG, "|%s|", rows[ty] ? "O" : "X"); + if (!th) + th = 16; + + for (size_t tx = 0; tx < ncol; tx++) + { + BOOL equal = TRUE; + tw = ((tx + 1) == ncol) ? (nWidth % 16) : 16; + + if (!tw) + tw = 16; + + const BYTE* p1 = &pData1[(ty * 16 * nStep1) + (tx * 16ull * bppA)]; + const BYTE* p2 = &pData2[(ty * 16 * nStep2) + (tx * 16ull * bppB)]; + + for (size_t k = 0; k < th; k++) + { + if (!pixel_equal(p1, format1, p2, format2, tw)) + { + equal = FALSE; + break; + } + + p1 += nStep1; + p2 += nStep2; + } + + if (!equal) + { + rowEqual = FALSE; + if (l > tx) + l = tx; + + if (r < tx) + r = tx; + } + } + + if (!rowEqual) + { + allEqual = FALSE; + + if (t > ty) + t = ty; + + if (b < ty) + b = ty; + } } - WLog_INFO(TAG, - "left: %" PRIu32 " top: %" PRIu32 " right: %" PRIu32 " bottom: %" PRIu32 - " ncol: %" PRIu32 " nrow: %" PRIu32, - l, t, r, b, ncol, nrow); - free(col_str); -#endif + if (allEqual) + return 0; + + WINPR_ASSERT(l * 16 <= UINT16_MAX); + WINPR_ASSERT(t * 16 <= UINT16_MAX); + WINPR_ASSERT((r + 1) * 16 <= UINT16_MAX); + WINPR_ASSERT((b + 1) * 16 <= UINT16_MAX); + rect->left = (UINT16)l * 16; + rect->top = (UINT16)t * 16; + rect->right = (UINT16)(r + 1) * 16; + rect->bottom = (UINT16)(b + 1) * 16; + + WINPR_ASSERT(nWidth <= UINT16_MAX); + if (rect->right > nWidth) + rect->right = (UINT16)nWidth; + + WINPR_ASSERT(nHeight <= UINT16_MAX); + if (rect->bottom > nHeight) + rect->bottom = (UINT16)nHeight; + return 1; }