From 6a8755a7637f2f006c2692ac3f5458f6162564a6 Mon Sep 17 00:00:00 2001 From: Armin Novak Date: Mon, 6 May 2019 13:18:31 +0200 Subject: [PATCH] Added image scaling api for software drawing. For future GFX channel functions an image scaling function is required. This moves the implementation from wayland client to core library and adds support for the much faster SWScale library. --- client/Wayland/CMakeLists.txt | 9 --- client/Wayland/wlfreerdp.c | 46 +------------- cmake/ConfigOptions.cmake | 11 ++-- cmake/FindSWScale.cmake | 14 +++++ include/freerdp/codec/color.h | 28 ++++++++- libfreerdp/CMakeLists.txt | 22 +++++++ libfreerdp/codec/color.c | 114 ++++++++++++++++++++++++++++++++++ 7 files changed, 186 insertions(+), 58 deletions(-) create mode 100644 cmake/FindSWScale.cmake diff --git a/client/Wayland/CMakeLists.txt b/client/Wayland/CMakeLists.txt index 2256c397c..a9e19e36e 100644 --- a/client/Wayland/CMakeLists.txt +++ b/client/Wayland/CMakeLists.txt @@ -37,16 +37,7 @@ set(${MODULE_PREFIX}_SRCS wlf_channels.h ) -find_package(Cairo) - list (APPEND ${MODULE_PREFIX}_LIBS freerdp-client freerdp uwac) -if (CAIRO_FOUND) - add_definitions(-DCAIRO_FOUND=1) - include_directories(${CAIRO_INCLUDE_DIR}) - list(APPEND ${MODULE_PREFIX}_LIBS ${CAIRO_LIBRARY}) -else(CAIRO_FOUND) - message(WARNING "libcairo not detected, compiling without wayland smart scaling support!") -endif(CAIRO_FOUND) add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS}) diff --git a/client/Wayland/wlfreerdp.c b/client/Wayland/wlfreerdp.c index 99fa39fa3..09a49c1fb 100644 --- a/client/Wayland/wlfreerdp.c +++ b/client/Wayland/wlfreerdp.c @@ -34,9 +34,6 @@ #include #include -#if defined(CAIRO_FOUND) -#include -#endif #include "wlfreerdp.h" #include "wlf_input.h" @@ -623,44 +620,8 @@ BOOL wlf_copy_image(const void* src, size_t srcStride, size_t srcWidth, size_t s if (scale) { -#if defined(CAIRO_FOUND) - const double sx = (double)dstWidth / (double)srcWidth; - const double sy = (double)dstHeight / (double)srcHeight; - cairo_t* cairo_context; - cairo_surface_t* csrc, *cdst; - - if ((srcWidth > INT_MAX) || (srcHeight > INT_MAX) || (srcStride > INT_MAX)) - return FALSE; - - if ((dstWidth > INT_MAX) || (dstHeight > INT_MAX) || (dstStride > INT_MAX)) - return FALSE; - - csrc = cairo_image_surface_create_for_data((void*)src, CAIRO_FORMAT_ARGB32, (int)srcWidth, - (int)srcHeight, (int)srcStride); - cdst = cairo_image_surface_create_for_data(dst, CAIRO_FORMAT_ARGB32, (int)dstWidth, - (int)dstHeight, (int)dstStride); - - if (!csrc || !cdst) - goto fail; - - cairo_context = cairo_create(cdst); - - if (!cairo_context) - goto fail2; - - cairo_scale(cairo_context, sx, sy); - cairo_set_operator(cairo_context, CAIRO_OPERATOR_SOURCE); - cairo_set_source_surface(cairo_context, csrc, 0, 0); - cairo_paint(cairo_context); - rc = TRUE; - fail2: - cairo_destroy(cairo_context); - fail: - cairo_surface_destroy(csrc); - cairo_surface_destroy(cdst); -#else - WLog_WARN(TAG, "SmartScaling requested but compiled without libcairo support!"); -#endif + return freerdp_image_scale(dst, PIXEL_FORMAT_BGRA32, dstStride, 0, 0, dstWidth, dstHeight, + src, PIXEL_FORMAT_BGRA32, srcStride, 0, 0, srcWidth, srcHeight); } else { @@ -698,8 +659,6 @@ BOOL wlf_scale_coordinates(rdpContext* context, UINT32* px, UINT32* py, BOOL fro if (!context->settings->SmartSizing) return TRUE; - /* If libcairo is not compiled, smart scaling is ignored. */ -#if defined(CAIRO_FOUND) gdi = context->gdi; if (UwacWindowGetDrawingBufferGeometry(wlf->window, &geometry, NULL) != UWAC_SUCCESS) @@ -719,6 +678,5 @@ BOOL wlf_scale_coordinates(rdpContext* context, UINT32* px, UINT32* py, BOOL fro *py /= sy; } -#endif return TRUE; } diff --git a/cmake/ConfigOptions.cmake b/cmake/ConfigOptions.cmake index 3e3589fa0..12a826bae 100644 --- a/cmake/ConfigOptions.cmake +++ b/cmake/ConfigOptions.cmake @@ -145,11 +145,14 @@ endif(WITH_FFMPEG) option(USE_VERSION_FROM_GIT_TAG "Extract FreeRDP version from git tag." OFF) -if(ANDROID) -include(ConfigOptionsAndroid) +option(WITH_CAIRO "Use CAIRO image library for screen resizing" OFF) +option(WITH_SWSCALE "Use SWScale image library for screen resizing" OFF) + +if (ANDROID) + include(ConfigOptionsAndroid) endif(ANDROID) -if(IOS) -include(ConfigOptionsiOS) +if (IOS) + include(ConfigOptionsiOS) endif(IOS) diff --git a/cmake/FindSWScale.cmake b/cmake/FindSWScale.cmake new file mode 100644 index 000000000..aa7747963 --- /dev/null +++ b/cmake/FindSWScale.cmake @@ -0,0 +1,14 @@ + +include(FindPkgConfig) + +if (PKG_CONFIG_FOUND) + pkg_check_modules(SWScale swscale) +endif() + +find_path(SWScale_INCLUDE_DIR libswscale / swscale.h PATHS $ {SWScale_INCLUDE_DIRS}) +find_library(SWScale_LIBRARY swscale PATHS $ {SWScale_LIBRARY_DIRS}) + +FIND_PACKAGE_HANDLE_STANDARD_ARGS(SWScale DEFAULT_MSG SWScale_INCLUDE_DIR SWScale_LIBRARY) + +mark_as_advanced(SWScale_INCLUDE_DIR SWScale_LIBRARY) + diff --git a/include/freerdp/codec/color.h b/include/freerdp/codec/color.h index 7e439522b..d33c65c4f 100644 --- a/include/freerdp/codec/color.h +++ b/include/freerdp/codec/color.h @@ -752,7 +752,7 @@ static INLINE BOOL WriteColor(BYTE* dst, UINT32 format, UINT32 color) * @return The converted pixel color in dstFormat representation */ static INLINE UINT32 FreeRDPConvertColor(UINT32 color, UINT32 srcFormat, - UINT32 dstFormat, const gdiPalette* palette) + UINT32 dstFormat, const gdiPalette* palette) { BYTE r = 0; BYTE g = 0; @@ -862,6 +862,32 @@ FREERDP_API BOOL freerdp_image_copy(BYTE* pDstData, DWORD DstFormat, UINT32 nSrcStep, UINT32 nXSrc, UINT32 nYSrc, const gdiPalette* palette, UINT32 flags); +/*** + * + * @param pDstData destination buffer + * @param DstFormat destination buffer format + * @param nDstStep destination buffer stride (line in bytes) 0 for default + * @param nXDst destination buffer offset x + * @param nYDst destination buffer offset y + * @param nDstWidth width of destination in pixels + * @param nDstHeight height of destination in pixels + * @param pSrcData source buffer + * @param SrcFormat source buffer format + * @param nSrcStep source buffer stride (line in bytes) 0 for default + * @param nXSrc source buffer x offset in pixels + * @param nYSrc source buffer y offset in pixels + * @param nSrcWidth width of source in pixels + * @param nSrcHeight height of source in pixels + * + * @return TRUE if success, FALSE otherwise + */ +FREERDP_API BOOL freerdp_image_scale(BYTE* pDstData, DWORD DstFormat, + UINT32 nDstStep, UINT32 nXDst, UINT32 nYDst, + UINT32 nDstWidth, UINT32 nDstHeight, + const BYTE* pSrcData, DWORD SrcFormat, + UINT32 nSrcStep, UINT32 nXSrc, UINT32 nYSrc, + UINT32 nSrcWidth, UINT32 nSrcHeight); + /*** * * @param pDstData destionation buffer diff --git a/libfreerdp/CMakeLists.txt b/libfreerdp/CMakeLists.txt index f64ce090b..97baf54db 100644 --- a/libfreerdp/CMakeLists.txt +++ b/libfreerdp/CMakeLists.txt @@ -79,6 +79,28 @@ macro (freerdp_definition_add) set (LIBFREERDP_DEFINITIONS ${LIBFREERDP_DEFINITIONS} PARENT_SCOPE) endmacro() +if (WITH_SWSCALE) + find_package(SWScale REQUIRED) +endif(WITH_SWSCALE) +if (WITH_CAIRO) + find_package(Cairo REQUIRED) +endif(WITH_CAIRO) + +if (SWScale_FOUND) + add_definitions(-DSWSCALE_FOUND=1) + include_directories(${SWScale_INCLUDE_DIR}) + freerdp_library_add(${SWScale_LIBRARY}) +else(SWScale_FOUND) + + if (CAIRO_FOUND) + add_definitions(-DCAIRO_FOUND=1) + include_directories(${CAIRO_INCLUDE_DIR}) + freerdp_library_add(${CAIRO_LIBRARY}) + else(CAIRO_FOUND) + message(WARNING "neigter swscale nor libcairo detected, compiling without image scaling support!") + endif(CAIRO_FOUND) +endif(SWScale_FOUND) + set(${MODULE_PREFIX}_SUBMODULES utils common diff --git a/libfreerdp/codec/color.c b/libfreerdp/codec/color.c index 8a5e37197..5a7a626ef 100644 --- a/libfreerdp/codec/color.c +++ b/libfreerdp/codec/color.c @@ -33,6 +33,14 @@ #include #include +#if defined(CAIRO_FOUND) +#include +#endif + +#if defined(SWSCALE_FOUND) +#include +#endif + #define TAG FREERDP_TAG("color") BYTE* freerdp_glyph_convert(UINT32 width, UINT32 height, const BYTE* data) @@ -570,3 +578,109 @@ BOOL freerdp_image_fill(BYTE* pDstData, DWORD DstFormat, return TRUE; } + +#if defined(SWSCALE_FOUND) +static int av_format_for_buffer(UINT32 format) +{ + switch (format) + { + case PIXEL_FORMAT_ARGB32: + return AV_PIX_FMT_BGRA; + + case PIXEL_FORMAT_XRGB32: + return AV_PIX_FMT_BGR0; + + case PIXEL_FORMAT_BGRA32: + return AV_PIX_FMT_RGBA; + + case PIXEL_FORMAT_BGRX32: + return AV_PIX_FMT_RGB0; + + default: + return AV_PIX_FMT_NONE; + } +} +#endif + +BOOL freerdp_image_scale(BYTE* pDstData, DWORD DstFormat, UINT32 nDstStep, + UINT32 nXDst, UINT32 nYDst, UINT32 nDstWidth, UINT32 nDstHeight, + const BYTE* pSrcData, DWORD SrcFormat, UINT32 nSrcStep, + UINT32 nXSrc, UINT32 nYSrc, UINT32 nSrcWidth, UINT32 nSrcHeight) +{ + BOOL rc = FALSE; + const BYTE* src = &pSrcData[nXSrc * 4 + nYSrc * nSrcStep]; + BYTE* dst = &pDstData[nXDst * 4 + nYDst * nDstStep]; +#if defined(SWSCALE_FOUND) + { + int res; + struct SwsContext* resize; + int srcFormat = av_format_for_buffer(SrcFormat); + int dstFormat = av_format_for_buffer(DstFormat); + const int srcStep[1] = { nSrcStep }; + const int dstStep[1] = { nDstStep }; + + if ((srcFormat == AV_PIX_FMT_NONE) || (dstFormat == AV_PIX_FMT_NONE)) + return FALSE; + + resize = sws_getContext(nSrcWidth + nXSrc, nSrcHeight + nYSrc, srcFormat, + nDstWidth + nXDst, nDstHeight + nYDst, dstFormat, + SWS_BICUBIC, NULL, NULL, NULL); + + if (!resize) + goto fail; + + res = sws_scale(resize, &pSrcData, srcStep, 0, nSrcHeight + nYSrc, &pDstData, dstStep); + rc = (res == (nDstHeight + nYDst)); + fail: + sws_freeContext(resize); + } +#elif defined(CAIRO_FOUND) + { + const double sx = (double)nDstWidth / (double)nSrcWidth; + const double sy = (double)nDstHeight / (double)nSrcHeight; + cairo_t* cairo_context; + cairo_surface_t* csrc, *cdst; + + if ((nSrcWidth > INT_MAX) || (nSrcHeight > INT_MAX) || (nSrcStep > INT_MAX)) + return FALSE; + + if ((nDstWidth > INT_MAX) || (nDstHeight > INT_MAX) || (nDstStep > INT_MAX)) + return FALSE; + + csrc = cairo_image_surface_create_for_data((void*)src, + CAIRO_FORMAT_ARGB32, (int)nSrcWidth, (int)nSrcHeight, (int)nSrcStep); + cdst = cairo_image_surface_create_for_data(dst, + CAIRO_FORMAT_ARGB32, (int)nDstWidth, (int)nDstHeight, (int)nDstStep); + + if (!csrc || !cdst) + goto fail; + + cairo_context = cairo_create(cdst); + + if (!cairo_context) + goto fail2; + + cairo_scale(cairo_context, sx, sy); + cairo_set_operator(cairo_context, CAIRO_OPERATOR_SOURCE); + cairo_set_source_surface(cairo_context, csrc, 0, 0); + cairo_paint(cairo_context); + rc = TRUE; + fail2: + cairo_destroy(cairo_context); + fail: + cairo_surface_destroy(csrc); + cairo_surface_destroy(cdst); + } +#else + + if ((nDstWidth == nSrcWidth) && (nDstHeight == nSrcHeight)) + { + return freerdp_image_copy(pDstData, DstFormat, nDstStep, nXDst, nYDst, nDstWidth, nDstHeight, + pSrcData, SrcFormat, nSrcStep, nXSrc, nYSrc, + NULL, FREERDP_FLIP_NONE); + } + + WLog_WARN(TAG, "SmartScaling requested but compiled without libcairo support!"); +#endif + return rc; +}