/** * FreeRDP: A Remote Desktop Protocol Implementation * * Copyright 2014 Marc-Andre Moreau * * 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 #include #include #include #include "shadow_surface.h" #include "shadow_capture.h" int shadow_capture_align_clip_rect(RECTANGLE_16* rect, const RECTANGLE_16* clip) { int dx = 0; int dy = 0; dx = (rect->left % 16); if (dx != 0) { rect->left -= dx; rect->right += dx; } dx = (rect->right % 16); if (dx != 0) { rect->right += (16 - dx); } dy = (rect->top % 16); if (dy != 0) { rect->top -= dy; rect->bottom += dy; } dy = (rect->bottom % 16); if (dy != 0) { rect->bottom += (16 - dy); } if (rect->left < clip->left) rect->left = clip->left; if (rect->top < clip->top) rect->top = clip->top; if (rect->right > clip->right) rect->right = clip->right; if (rect->bottom > clip->bottom) rect->bottom = clip->bottom; return 1; } #if !defined(WITHOUT_FREERDP_3x_DEPRECATED) int shadow_capture_compare(const BYTE* WINPR_RESTRICT pData1, UINT32 nStep1, UINT32 nWidth, UINT32 nHeight, const BYTE* WINPR_RESTRICT pData2, UINT32 nStep2, RECTANGLE_16* WINPR_RESTRICT rect) { return shadow_capture_compare_with_format(pData1, PIXEL_FORMAT_BGRX32, nStep1, nWidth, nHeight, pData2, PIXEL_FORMAT_BGRX32, nStep2, rect); } #endif WINPR_ATTR_NODISCARD 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, nullptr); FreeRDPSplitColor(colorB, formatB, &br, &bg, &bb, &ba, nullptr); if (ar != br) return FALSE; if (ag != bg) return FALSE; if (ab != bb) return FALSE; if (aa != ba) return FALSE; return TRUE; } WINPR_ATTR_NODISCARD static BOOL pixel_equal(const BYTE* WINPR_RESTRICT a, UINT32 formatA, const BYTE* WINPR_RESTRICT 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++) { 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; } return TRUE; } WINPR_ATTR_NODISCARD static BOOL color_equal_no_alpha(UINT32 colorA, UINT32 formatA, UINT32 colorB, UINT32 formatB) { BYTE ar = 0; BYTE ag = 0; BYTE ab = 0; BYTE br = 0; BYTE bg = 0; BYTE bb = 0; FreeRDPSplitColor(colorA, formatA, &ar, &ag, &ab, nullptr, nullptr); FreeRDPSplitColor(colorB, formatB, &br, &bg, &bb, nullptr, nullptr); if (ar != br) return FALSE; if (ag != bg) return FALSE; if (ab != bb) return FALSE; return TRUE; } WINPR_ATTR_NODISCARD static BOOL pixel_equal_no_alpha(const BYTE* WINPR_RESTRICT a, UINT32 formatA, const BYTE* WINPR_RESTRICT 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++) { const UINT32 colorA = FreeRDPReadColor(&a[bppA * x], formatA); const UINT32 colorB = FreeRDPReadColor(&b[bppB * x], formatB); if (!color_equal_no_alpha(colorA, formatA, colorB, formatB)) return FALSE; } return TRUE; } WINPR_ATTR_NODISCARD static BOOL pixel_equal_same_format(const BYTE* WINPR_RESTRICT a, UINT32 formatA, const BYTE* WINPR_RESTRICT b, UINT32 formatB, size_t count) { if (formatA != formatB) return FALSE; const size_t bppA = FreeRDPGetBytesPerPixel(formatA); return memcmp(a, b, count * bppA) == 0; } typedef BOOL (*pixel_equal_fn_t)(const BYTE* WINPR_RESTRICT a, UINT32 formatA, const BYTE* WINPR_RESTRICT b, UINT32 formatB, size_t count); WINPR_ATTR_NODISCARD static pixel_equal_fn_t get_comparison_fn(DWORD format1, DWORD format2) { if (format1 == format2) return pixel_equal_same_format; const UINT32 bpp1 = FreeRDPGetBitsPerPixel(format1); if (!FreeRDPColorHasAlpha(format1) || !FreeRDPColorHasAlpha(format2)) { /* In case we have RGBA32 and RGBX32 or similar assume the alpha data is equal. * This allows us to use the fast memcmp comparison. */ if ((bpp1 == 32) && FreeRDPAreColorFormatsEqualNoAlpha(format1, format2)) { switch (format1) { case PIXEL_FORMAT_ARGB32: case PIXEL_FORMAT_XRGB32: case PIXEL_FORMAT_ABGR32: case PIXEL_FORMAT_XBGR32: return pixel_equal; case PIXEL_FORMAT_RGBA32: case PIXEL_FORMAT_RGBX32: case PIXEL_FORMAT_BGRA32: case PIXEL_FORMAT_BGRX32: return pixel_equal; default: break; } } return pixel_equal_no_alpha; } else return pixel_equal_no_alpha; } int shadow_capture_compare_with_format(const BYTE* WINPR_RESTRICT pData1, UINT32 format1, UINT32 nStep1, UINT32 nWidth, UINT32 nHeight, const BYTE* WINPR_RESTRICT pData2, UINT32 format2, UINT32 nStep2, RECTANGLE_16* WINPR_RESTRICT rect) { pixel_equal_fn_t pixel_equal_fn = get_comparison_fn(format1, format2); 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 = WINPR_C_ARRAY_INIT; WINPR_ASSERT(rect); *rect = empty; for (size_t ty = 0; ty < nrow; ty++) { BOOL rowEqual = TRUE; size_t th = ((ty + 1) == nrow) ? (nHeight % 16) : 16; 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 * 16ULL * nStep1) + (tx * 16ull * bppA)]; const BYTE* p2 = &pData2[(ty * 16ULL * nStep2) + (tx * 16ull * bppB)]; for (size_t k = 0; k < th; k++) { if (!pixel_equal_fn(p1, format1, p2, format2, tw)) { equal = FALSE; break; } p1 += nStep1; p2 += nStep2; } if (!equal) { rowEqual = FALSE; if (l > tx) l = (UINT32)tx; if (r < tx) r = (UINT32)tx; } } if (!rowEqual) { allEqual = FALSE; if (t > ty) t = (UINT32)ty; if (b < ty) b = (UINT32)ty; } } 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; } rdpShadowCapture* shadow_capture_new(rdpShadowServer* server) { WINPR_ASSERT(server); rdpShadowCapture* capture = (rdpShadowCapture*)calloc(1, sizeof(rdpShadowCapture)); if (!capture) return nullptr; capture->server = server; if (!InitializeCriticalSectionAndSpinCount(&(capture->lock), 4000)) { WINPR_PRAGMA_DIAG_PUSH WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC shadow_capture_free(capture); WINPR_PRAGMA_DIAG_POP return nullptr; } return capture; } void shadow_capture_free(rdpShadowCapture* capture) { if (!capture) return; DeleteCriticalSection(&(capture->lock)); free(capture); }