diff options
Diffstat (limited to 'libfreerdp/gdi')
31 files changed, 11077 insertions, 0 deletions
diff --git a/libfreerdp/gdi/CMakeLists.txt b/libfreerdp/gdi/CMakeLists.txt new file mode 100644 index 0000000..97e1d27 --- /dev/null +++ b/libfreerdp/gdi/CMakeLists.txt @@ -0,0 +1,35 @@ +# FreeRDP: A Remote Desktop Protocol Implementation +# libfreerdp-gdi cmake build script +# +# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com> +# +# 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. + +set(MODULE_NAME "freerdp-gdi") +set(MODULE_PREFIX "FREERDP_GDI") + +include_directories(${CMAKE_CURRENT_SOURCE_DIR}) + +file(GLOB ${MODULE_PREFIX}_SRCS + LIST_DIRECTORIES false + RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} + CONFIGURE_DEPENDS + "*.[ch]" +) + +freerdp_module_add(${${MODULE_PREFIX}_SRCS}) + +if(BUILD_TESTING) + add_subdirectory(test) +endif() + diff --git a/libfreerdp/gdi/bitmap.c b/libfreerdp/gdi/bitmap.c new file mode 100644 index 0000000..915eb40 --- /dev/null +++ b/libfreerdp/gdi/bitmap.c @@ -0,0 +1,660 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * GDI Bitmap 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/codec/color.h> + +#include <freerdp/gdi/region.h> +#include <freerdp/gdi/bitmap.h> +#include <freerdp/log.h> +#include <freerdp/gdi/shape.h> + +#include "brush.h" +#include "clipping.h" +#include "../gdi/gdi.h" + +#define TAG FREERDP_TAG("gdi.bitmap") + +/** + * Get pixel at the given coordinates. msdn{dd144909} + * @param hdc device context + * @param nXPos pixel x position + * @param nYPos pixel y position + * @return pixel color + */ + +UINT32 gdi_GetPixel(HGDI_DC hdc, UINT32 nXPos, UINT32 nYPos) +{ + HGDI_BITMAP hBmp = (HGDI_BITMAP)hdc->selectedObject; + BYTE* data = + &(hBmp->data[(nYPos * hBmp->scanline) + nXPos * FreeRDPGetBytesPerPixel(hBmp->format)]); + return FreeRDPReadColor(data, hBmp->format); +} + +BYTE* gdi_GetPointer(HGDI_BITMAP hBmp, UINT32 X, UINT32 Y) +{ + UINT32 bpp = FreeRDPGetBytesPerPixel(hBmp->format); + return &hBmp->data[(Y * hBmp->width * bpp) + X * bpp]; +} + +/** + * Set pixel at the given coordinates. msdn{dd145078} + * + * @param hBmp device context + * @param X pixel x position + * @param Y pixel y position + * @param crColor new pixel color + * @return the color written + */ + +static INLINE UINT32 gdi_SetPixelBmp(HGDI_BITMAP hBmp, UINT32 X, UINT32 Y, UINT32 crColor) +{ + BYTE* p = &hBmp->data[(Y * hBmp->scanline) + X * FreeRDPGetBytesPerPixel(hBmp->format)]; + FreeRDPWriteColor(p, hBmp->format, crColor); + return crColor; +} + +UINT32 gdi_SetPixel(HGDI_DC hdc, UINT32 X, UINT32 Y, UINT32 crColor) +{ + HGDI_BITMAP hBmp = (HGDI_BITMAP)hdc->selectedObject; + return gdi_SetPixelBmp(hBmp, X, Y, crColor); +} + +/** + * Create a new bitmap with the given width, height, color format and pixel buffer. msdn{dd183485} + * + * @param nWidth width + * @param nHeight height + * @param format the color format used + * @param data pixel buffer + * @return new bitmap + */ + +HGDI_BITMAP gdi_CreateBitmap(UINT32 nWidth, UINT32 nHeight, UINT32 format, BYTE* data) +{ + return gdi_CreateBitmapEx(nWidth, nHeight, format, 0, data, winpr_aligned_free); +} + +/** + * Create a new bitmap with the given width, height, color format and pixel buffer. msdn{dd183485} + * + * @param nWidth width + * @param nHeight height + * @param format the color format used + * @param data pixel buffer + * @param fkt_free The function used for deallocation of the buffer, NULL for none. + * @return new bitmap + */ + +HGDI_BITMAP gdi_CreateBitmapEx(UINT32 nWidth, UINT32 nHeight, UINT32 format, UINT32 stride, + BYTE* data, void (*fkt_free)(void*)) +{ + HGDI_BITMAP hBitmap = (HGDI_BITMAP)calloc(1, sizeof(GDI_BITMAP)); + + if (!hBitmap) + return NULL; + + hBitmap->objectType = GDIOBJECT_BITMAP; + hBitmap->format = format; + + if (stride > 0) + hBitmap->scanline = stride; + else + hBitmap->scanline = nWidth * FreeRDPGetBytesPerPixel(hBitmap->format); + + hBitmap->width = nWidth; + hBitmap->height = nHeight; + hBitmap->data = data; + hBitmap->free = fkt_free; + return hBitmap; +} + +/** + * Create a new bitmap of the given width and height compatible with the current device context. + * msdn{dd183488} + * + * @param hdc device context + * @param nWidth width + * @param nHeight height + * + * @return new bitmap + */ + +HGDI_BITMAP gdi_CreateCompatibleBitmap(HGDI_DC hdc, UINT32 nWidth, UINT32 nHeight) +{ + HGDI_BITMAP hBitmap = (HGDI_BITMAP)calloc(1, sizeof(GDI_BITMAP)); + + if (!hBitmap) + return NULL; + + hBitmap->objectType = GDIOBJECT_BITMAP; + hBitmap->format = hdc->format; + hBitmap->width = nWidth; + hBitmap->height = nHeight; + hBitmap->data = winpr_aligned_malloc( + 1ull * nWidth * nHeight * FreeRDPGetBytesPerPixel(hBitmap->format), 16); + hBitmap->free = winpr_aligned_free; + + if (!hBitmap->data) + { + free(hBitmap); + return NULL; + } + + hBitmap->scanline = nWidth * FreeRDPGetBytesPerPixel(hBitmap->format); + return hBitmap; +} + +static BOOL op_not(UINT32* stack, UINT32* stackp) +{ + if (!stack || !stackp) + return FALSE; + + if (*stackp < 1) + return FALSE; + + stack[(*stackp) - 1] = ~stack[(*stackp) - 1]; + return TRUE; +} + +static BOOL op_and(UINT32* stack, UINT32* stackp) +{ + if (!stack || !stackp) + return FALSE; + + if (*stackp < 2) + return FALSE; + + (*stackp)--; + stack[(*stackp) - 1] &= stack[(*stackp)]; + return TRUE; +} + +static BOOL op_or(UINT32* stack, UINT32* stackp) +{ + if (!stack || !stackp) + return FALSE; + + if (*stackp < 2) + return FALSE; + + (*stackp)--; + stack[(*stackp) - 1] |= stack[(*stackp)]; + return TRUE; +} + +static BOOL op_xor(UINT32* stack, UINT32* stackp) +{ + if (!stack || !stackp) + return FALSE; + + if (*stackp < 2) + return FALSE; + + (*stackp)--; + stack[(*stackp) - 1] ^= stack[(*stackp)]; + return TRUE; +} + +static UINT32 process_rop(UINT32 src, UINT32 dst, UINT32 pat, const char* rop, UINT32 format) +{ + UINT32 stack[10] = { 0 }; + UINT32 stackp = 0; + + while (*rop != '\0') + { + char op = *rop++; + + switch (op) + { + case '0': + stack[stackp++] = FreeRDPGetColor(format, 0, 0, 0, 0xFF); + break; + + case '1': + stack[stackp++] = FreeRDPGetColor(format, 0xFF, 0xFF, 0xFF, 0xFF); + break; + + case 'D': + stack[stackp++] = dst; + break; + + case 'S': + stack[stackp++] = src; + break; + + case 'P': + stack[stackp++] = pat; + break; + + case 'x': + op_xor(stack, &stackp); + break; + + case 'a': + op_and(stack, &stackp); + break; + + case 'o': + op_or(stack, &stackp); + break; + + case 'n': + op_not(stack, &stackp); + break; + + default: + break; + } + } + + return stack[0]; +} + +static INLINE BOOL BitBlt_write(HGDI_DC hdcDest, HGDI_DC hdcSrc, INT32 nXDest, INT32 nYDest, + INT32 nXSrc, INT32 nYSrc, INT32 x, INT32 y, BOOL useSrc, + BOOL usePat, UINT32 style, const char* rop, + const gdiPalette* palette) +{ + UINT32 dstColor = 0; + UINT32 colorA = 0; + UINT32 colorB = 0; + UINT32 colorC = 0; + const INT32 dstX = nXDest + x; + const INT32 dstY = nYDest + y; + BYTE* dstp = gdi_get_bitmap_pointer(hdcDest, dstX, dstY); + + if (!dstp) + { + WLog_ERR(TAG, "dstp=%p", (const void*)dstp); + return FALSE; + } + + colorA = FreeRDPReadColor(dstp, hdcDest->format); + + if (useSrc) + { + const BYTE* srcp = gdi_get_bitmap_pointer(hdcSrc, nXSrc + x, nYSrc + y); + + if (!srcp) + { + WLog_ERR(TAG, "srcp=%p", (const void*)srcp); + return FALSE; + } + + colorC = FreeRDPReadColor(srcp, hdcSrc->format); + colorC = FreeRDPConvertColor(colorC, hdcSrc->format, hdcDest->format, palette); + } + + if (usePat) + { + switch (style) + { + case GDI_BS_SOLID: + colorB = hdcDest->brush->color; + break; + + case GDI_BS_HATCHED: + case GDI_BS_PATTERN: + { + const BYTE* patp = gdi_get_brush_pointer(hdcDest, nXDest + x, nYDest + y); + + if (!patp) + { + WLog_ERR(TAG, "patp=%p", (const void*)patp); + return FALSE; + } + + colorB = FreeRDPReadColor(patp, hdcDest->format); + } + break; + + default: + break; + } + } + + dstColor = process_rop(colorC, colorA, colorB, rop, hdcDest->format); + return FreeRDPWriteColor(dstp, hdcDest->format, dstColor); +} + +static BOOL adjust_src_coordinates(HGDI_DC hdcSrc, INT32 nWidth, INT32 nHeight, INT32* px, + INT32* py) +{ + HGDI_BITMAP hSrcBmp = NULL; + INT32 nXSrc = 0; + INT32 nYSrc = 0; + + if (!hdcSrc || (nWidth < 0) || (nHeight < 0) || !px || !py) + return FALSE; + + hSrcBmp = (HGDI_BITMAP)hdcSrc->selectedObject; + nXSrc = *px; + nYSrc = *py; + + if (!hSrcBmp) + return FALSE; + + if (nYSrc < 0) + { + nYSrc = 0; + nHeight = nHeight + nYSrc; + } + + if ((nXSrc) < 0) + { + nXSrc = 0; + nWidth = nWidth + nXSrc; + } + + if (hSrcBmp->width < (nXSrc + nWidth)) + nXSrc = hSrcBmp->width - nWidth; + + if (hSrcBmp->height < (nYSrc + nHeight)) + nYSrc = hSrcBmp->height - nHeight; + + if ((nXSrc < 0) || (nYSrc < 0)) + return FALSE; + + *px = nXSrc; + *py = nYSrc; + return TRUE; +} + +static BOOL adjust_src_dst_coordinates(HGDI_DC hdcDest, INT32* pnXSrc, INT32* pnYSrc, INT32* pnXDst, + INT32* pnYDst, INT32* pnWidth, INT32* pnHeight) +{ + HGDI_BITMAP hDstBmp = NULL; + volatile INT32 diffX = 0; + volatile INT32 diffY = 0; + volatile INT32 nXSrc = 0; + volatile INT32 nYSrc = 0; + volatile INT32 nXDst = 0; + volatile INT32 nYDst = 0; + volatile INT32 nWidth = 0; + volatile INT32 nHeight = 0; + + if (!hdcDest || !pnXSrc || !pnYSrc || !pnXDst || !pnYDst || !pnWidth || !pnHeight) + return FALSE; + + hDstBmp = (HGDI_BITMAP)hdcDest->selectedObject; + nXSrc = *pnXSrc; + nYSrc = *pnYSrc; + nXDst = *pnXDst; + nYDst = *pnYDst; + nWidth = *pnWidth; + nHeight = *pnHeight; + + if (!hDstBmp) + return FALSE; + + if (nXDst < 0) + { + nXSrc -= nXDst; + nWidth += nXDst; + nXDst = 0; + } + + if (nYDst < 0) + { + nYSrc -= nYDst; + nHeight += nYDst; + nYDst = 0; + } + + diffX = hDstBmp->width - nXDst - nWidth; + + if (diffX < 0) + nWidth += diffX; + + diffY = hDstBmp->height - nYDst - nHeight; + + if (diffY < 0) + nHeight += diffY; + + if ((nXDst < 0) || (nYDst < 0) || (nWidth < 0) || (nHeight < 0)) + { + nXDst = 0; + nYDst = 0; + nWidth = 0; + nHeight = 0; + } + + *pnXSrc = nXSrc; + *pnYSrc = nYSrc; + *pnXDst = nXDst; + *pnYDst = nYDst; + *pnWidth = nWidth; + *pnHeight = nHeight; + return TRUE; +} + +static BOOL BitBlt_process(HGDI_DC hdcDest, INT32 nXDest, INT32 nYDest, INT32 nWidth, INT32 nHeight, + HGDI_DC hdcSrc, INT32 nXSrc, INT32 nYSrc, const char* rop, + const gdiPalette* palette) +{ + UINT32 style = 0; + BOOL useSrc = FALSE; + BOOL usePat = FALSE; + const char* iter = rop; + + while (*iter != '\0') + { + switch (*iter++) + { + case 'P': + usePat = TRUE; + break; + + case 'S': + useSrc = TRUE; + break; + + default: + break; + } + } + + if (!hdcDest) + return FALSE; + + if (!adjust_src_dst_coordinates(hdcDest, &nXSrc, &nYSrc, &nXDest, &nYDest, &nWidth, &nHeight)) + return FALSE; + + if (useSrc && !hdcSrc) + return FALSE; + + if (useSrc) + { + if (!adjust_src_coordinates(hdcSrc, nWidth, nHeight, &nXSrc, &nYSrc)) + return FALSE; + } + + if (usePat) + { + style = gdi_GetBrushStyle(hdcDest); + + switch (style) + { + case GDI_BS_SOLID: + case GDI_BS_HATCHED: + case GDI_BS_PATTERN: + break; + + default: + WLog_ERR(TAG, "Invalid brush!!"); + return FALSE; + } + } + + if ((nXDest > nXSrc) && (nYDest > nYSrc)) + { + for (INT32 y = nHeight - 1; y >= 0; y--) + { + for (INT32 x = nWidth - 1; x >= 0; x--) + { + if (!BitBlt_write(hdcDest, hdcSrc, nXDest, nYDest, nXSrc, nYSrc, x, y, useSrc, + usePat, style, rop, palette)) + return FALSE; + } + } + } + else if (nXDest > nXSrc) + { + for (INT32 y = 0; y < nHeight; y++) + { + for (INT32 x = nWidth - 1; x >= 0; x--) + { + if (!BitBlt_write(hdcDest, hdcSrc, nXDest, nYDest, nXSrc, nYSrc, x, y, useSrc, + usePat, style, rop, palette)) + return FALSE; + } + } + } + else if (nYDest > nYSrc) + { + for (INT32 y = nHeight - 1; y >= 0; y--) + { + for (INT32 x = 0; x < nWidth; x++) + { + if (!BitBlt_write(hdcDest, hdcSrc, nXDest, nYDest, nXSrc, nYSrc, x, y, useSrc, + usePat, style, rop, palette)) + return FALSE; + } + } + } + else + { + for (INT32 y = 0; y < nHeight; y++) + { + for (INT32 x = 0; x < nWidth; x++) + { + if (!BitBlt_write(hdcDest, hdcSrc, nXDest, nYDest, nXSrc, nYSrc, x, y, useSrc, + usePat, style, rop, palette)) + return FALSE; + } + } + } + + return TRUE; +} + +/** + * Perform a bit blit operation on the given pixel buffers. + * msdn{dd183370} + * + * @param hdcDest destination device context + * @param nXDest destination x1 + * @param nYDest destination y1 + * @param nWidth width + * @param nHeight height + * @param hdcSrc source device context + * @param nXSrc source x1 + * @param nYSrc source y1 + * @param rop raster operation code + * @return 0 on failure, non-zero otherwise + */ +BOOL gdi_BitBlt(HGDI_DC hdcDest, INT32 nXDest, INT32 nYDest, INT32 nWidth, INT32 nHeight, + HGDI_DC hdcSrc, INT32 nXSrc, INT32 nYSrc, DWORD rop, const gdiPalette* palette) +{ + HGDI_BITMAP hSrcBmp = NULL; + HGDI_BITMAP hDstBmp = NULL; + + if (!hdcDest) + return FALSE; + + if (!gdi_ClipCoords(hdcDest, &nXDest, &nYDest, &nWidth, &nHeight, &nXSrc, &nYSrc)) + return TRUE; + + /* Check which ROP should be performed. + * Some specific ROP are used heavily and are resource intensive, + * add optimized versions for these here. + * + * For all others fall back to the generic implementation. + */ + switch (rop) + { + case GDI_SRCCOPY: + if (!hdcSrc) + return FALSE; + + if (!adjust_src_dst_coordinates(hdcDest, &nXSrc, &nYSrc, &nXDest, &nYDest, &nWidth, + &nHeight)) + return FALSE; + + if (!adjust_src_coordinates(hdcSrc, nWidth, nHeight, &nXSrc, &nYSrc)) + return FALSE; + + hSrcBmp = (HGDI_BITMAP)hdcSrc->selectedObject; + hDstBmp = (HGDI_BITMAP)hdcDest->selectedObject; + + if (!hSrcBmp || !hDstBmp) + return FALSE; + + if (!freerdp_image_copy(hDstBmp->data, hDstBmp->format, hDstBmp->scanline, nXDest, + nYDest, nWidth, nHeight, hSrcBmp->data, hSrcBmp->format, + hSrcBmp->scanline, nXSrc, nYSrc, palette, FREERDP_FLIP_NONE)) + return FALSE; + + break; + + case GDI_DSTCOPY: + hSrcBmp = (HGDI_BITMAP)hdcDest->selectedObject; + hDstBmp = (HGDI_BITMAP)hdcDest->selectedObject; + + if (!adjust_src_dst_coordinates(hdcDest, &nXSrc, &nYSrc, &nXDest, &nYDest, &nWidth, + &nHeight)) + return FALSE; + + if (!adjust_src_coordinates(hdcDest, nWidth, nHeight, &nXSrc, &nYSrc)) + return FALSE; + + if (!hSrcBmp || !hDstBmp) + return FALSE; + + if (!freerdp_image_copy(hDstBmp->data, hDstBmp->format, hDstBmp->scanline, nXDest, + nYDest, nWidth, nHeight, hSrcBmp->data, hSrcBmp->format, + hSrcBmp->scanline, nXSrc, nYSrc, palette, FREERDP_FLIP_NONE)) + return FALSE; + + break; + + default: + if (!BitBlt_process(hdcDest, nXDest, nYDest, nWidth, nHeight, hdcSrc, nXSrc, nYSrc, + gdi_rop_to_string(rop), palette)) + return FALSE; + + break; + } + + if (!gdi_InvalidateRegion(hdcDest, nXDest, nYDest, nWidth, nHeight)) + return FALSE; + + return TRUE; +} diff --git a/libfreerdp/gdi/brush.c b/libfreerdp/gdi/brush.c new file mode 100644 index 0000000..28aa59d --- /dev/null +++ b/libfreerdp/gdi/brush.c @@ -0,0 +1,869 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * GDI Brush 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. + */ + +/* GDI Brush Functions: http://msdn.microsoft.com/en-us/library/dd183395/ */ + +#include <freerdp/config.h> + +#include <stdio.h> +#include <stdlib.h> + +#include <freerdp/freerdp.h> +#include <freerdp/gdi/gdi.h> +#include <freerdp/gdi/region.h> +#include <freerdp/log.h> + +#include "brush.h" +#include "clipping.h" + +#define TAG FREERDP_TAG("gdi.brush") + +const char* gdi_rop_to_string(UINT32 code) +{ + switch (code) + { + case GDI_BLACKNESS: + return "0"; + + case GDI_DPSoon: + return "DPSoon"; + + case GDI_DPSona: + return "DPSona"; + + case GDI_PSon: + return "PSon"; + + case GDI_SDPona: + return "SDPona"; + + case GDI_DPon: + return "DPon"; + + case GDI_PDSxnon: + return "PDSxnon"; + + case GDI_PDSaon: + return "PDSaon"; + + case GDI_SDPnaa: + return "SDPnaa"; + + case GDI_PDSxon: + return "PDSxon"; + + case GDI_DPna: + return "DPna"; + + case GDI_PSDnaon: + return "PSDnaon"; + + case GDI_SPna: + return "SPna"; + + case GDI_PDSnaon: + return "PDSnaon"; + + case GDI_PDSonon: + return "PDSonon"; + + case GDI_Pn: + return "Pn"; + + case GDI_PDSona: + return "PDSona"; + + case GDI_NOTSRCERASE: + return "DSon"; + + case GDI_SDPxnon: + return "SDPxnon"; + + case GDI_SDPaon: + return "SDPaon"; + + case GDI_DPSxnon: + return "DPSxnon"; + + case GDI_DPSaon: + return "DPSaon"; + + case GDI_PSDPSanaxx: + return "PSDPSanaxx"; + + case GDI_SSPxDSxaxn: + return "SSPxDSxaxn"; + + case GDI_SPxPDxa: + return "SPxPDxa"; + + case GDI_SDPSanaxn: + return "SDPSanaxn"; + + case GDI_PDSPaox: + return "PDSPaox"; + + case GDI_SDPSxaxn: + return "SDPSxaxn"; + + case GDI_PSDPaox: + return "PSDPaox"; + + case GDI_DSPDxaxn: + return "DSPDxaxn"; + + case GDI_PDSox: + return "PDSox"; + + case GDI_PDSoan: + return "PDSoan"; + + case GDI_DPSnaa: + return "DPSnaa"; + + case GDI_SDPxon: + return "SDPxon"; + + case GDI_DSna: + return "DSna"; + + case GDI_SPDnaon: + return "SPDnaon"; + + case GDI_SPxDSxa: + return "SPxDSxa"; + + case GDI_PDSPanaxn: + return "PDSPanaxn"; + + case GDI_SDPSaox: + return "SDPSaox"; + + case GDI_SDPSxnox: + return "SDPSxnox"; + + case GDI_DPSxa: + return "DPSxa"; + + case GDI_PSDPSaoxxn: + return "PSDPSaoxxn"; + + case GDI_DPSana: + return "DPSana"; + + case GDI_SSPxPDxaxn: + return "SSPxPDxaxn"; + + case GDI_SPDSoax: + return "SPDSoax"; + + case GDI_PSDnox: + return "PSDnox"; + + case GDI_PSDPxox: + return "PSDPxox"; + + case GDI_PSDnoan: + return "PSDnoan"; + + case GDI_PSna: + return "PSna"; + + case GDI_SDPnaon: + return "SDPnaon"; + + case GDI_SDPSoox: + return "SDPSoox"; + + case GDI_NOTSRCCOPY: + return "Sn"; + + case GDI_SPDSaox: + return "SPDSaox"; + + case GDI_SPDSxnox: + return "SPDSxnox"; + + case GDI_SDPox: + return "SDPox"; + + case GDI_SDPoan: + return "SDPoan"; + + case GDI_PSDPoax: + return "PSDPoax"; + + case GDI_SPDnox: + return "SPDnox"; + + case GDI_SPDSxox: + return "SPDSxox"; + + case GDI_SPDnoan: + return "SPDnoan"; + + case GDI_PSx: + return "PSx"; + + case GDI_SPDSonox: + return "SPDSonox"; + + case GDI_SPDSnaox: + return "SPDSnaox"; + + case GDI_PSan: + return "PSan"; + + case GDI_PSDnaa: + return "PSDnaa"; + + case GDI_DPSxon: + return "DPSxon"; + + case GDI_SDxPDxa: + return "SDxPDxa"; + + case GDI_SPDSanaxn: + return "SPDSanaxn"; + + case GDI_SRCERASE: + return "SDna"; + + case GDI_DPSnaon: + return "DPSnaon"; + + case GDI_DSPDaox: + return "DSPDaox"; + + case GDI_PSDPxaxn: + return "PSDPxaxn"; + + case GDI_SDPxa: + return "SDPxa"; + + case GDI_PDSPDaoxxn: + return "PDSPDaoxxn"; + + case GDI_DPSDoax: + return "DPSDoax"; + + case GDI_PDSnox: + return "PDSnox"; + + case GDI_SDPana: + return "SDPana"; + + case GDI_SSPxDSxoxn: + return "SSPxDSxoxn"; + + case GDI_PDSPxox: + return "PDSPxox"; + + case GDI_PDSnoan: + return "PDSnoan"; + + case GDI_PDna: + return "PDna"; + + case GDI_DSPnaon: + return "DSPnaon"; + + case GDI_DPSDaox: + return "DPSDaox"; + + case GDI_SPDSxaxn: + return "SPDSxaxn"; + + case GDI_DPSonon: + return "DPSonon"; + + case GDI_DSTINVERT: + return "Dn"; + + case GDI_DPSox: + return "DPSox"; + + case GDI_DPSoan: + return "DPSoan"; + + case GDI_PDSPoax: + return "PDSPoax"; + + case GDI_DPSnox: + return "DPSnox"; + + case GDI_PATINVERT: + return "DPx"; + + case GDI_DPSDonox: + return "DPSDonox"; + + case GDI_DPSDxox: + return "DPSDxox"; + + case GDI_DPSnoan: + return "DPSnoan"; + + case GDI_DPSDnaox: + return "DPSDnaox"; + + case GDI_DPan: + return "DPan"; + + case GDI_PDSxa: + return "PDSxa"; + + case GDI_DSPDSaoxxn: + return "DSPDSaoxxn"; + + case GDI_DSPDoax: + return "DSPDoax"; + + case GDI_SDPnox: + return "SDPnox"; + + case GDI_SDPSoax: + return "SDPSoax"; + + case GDI_DSPnox: + return "DSPnox"; + + case GDI_SRCINVERT: + return "DSx"; + + case GDI_SDPSonox: + return "SDPSonox"; + + case GDI_DSPDSonoxxn: + return "DSPDSonoxxn"; + + case GDI_PDSxxn: + return "PDSxxn"; + + case GDI_DPSax: + return "DPSax"; + + case GDI_PSDPSoaxxn: + return "PSDPSoaxxn"; + + case GDI_SDPax: + return "SDPax"; + + case GDI_PDSPDoaxxn: + return "PDSPDoaxxn"; + + case GDI_SDPSnoax: + return "SDPSnoax"; + + case GDI_PDSxnan: + return "PDSxnan"; + + case GDI_PDSana: + return "PDSana"; + + case GDI_SSDxPDxaxn: + return "SSDxPDxaxn"; + + case GDI_SDPSxox: + return "SDPSxox"; + + case GDI_SDPnoan: + return "SDPnoan"; + + case GDI_DSPDxox: + return "DSPDxox"; + + case GDI_DSPnoan: + return "DSPnoan"; + + case GDI_SDPSnaox: + return "SDPSnaox"; + + case GDI_DSan: + return "DSan"; + + case GDI_PDSax: + return "PDSax"; + + case GDI_DSPDSoaxxn: + return "DSPDSoaxxn"; + + case GDI_DPSDnoax: + return "DPSDnoax"; + + case GDI_SDPxnan: + return "SDPxnan"; + + case GDI_SPDSnoax: + return "SPDSnoax"; + + case GDI_DPSxnan: + return "DPSxnan"; + + case GDI_SPxDSxo: + return "SPxDSxo"; + + case GDI_DPSaan: + return "DPSaan"; + + case GDI_DPSaa: + return "DPSaa"; + + case GDI_SPxDSxon: + return "SPxDSxon"; + + case GDI_DPSxna: + return "DPSxna"; + + case GDI_SPDSnoaxn: + return "SPDSnoaxn"; + + case GDI_SDPxna: + return "SDPxna"; + + case GDI_PDSPnoaxn: + return "PDSPnoaxn"; + + case GDI_DSPDSoaxx: + return "DSPDSoaxx"; + + case GDI_PDSaxn: + return "PDSaxn"; + + case GDI_SRCAND: + return "DSa"; + + case GDI_SDPSnaoxn: + return "SDPSnaoxn"; + + case GDI_DSPnoa: + return "DSPnoa"; + + case GDI_DSPDxoxn: + return "DSPDxoxn"; + + case GDI_SDPnoa: + return "SDPnoa"; + + case GDI_SDPSxoxn: + return "SDPSxoxn"; + + case GDI_SSDxPDxax: + return "SSDxPDxax"; + + case GDI_PDSanan: + return "PDSanan"; + + case GDI_PDSxna: + return "PDSxna"; + + case GDI_SDPSnoaxn: + return "SDPSnoaxn"; + + case GDI_DPSDPoaxx: + return "DPSDPoaxx"; + + case GDI_SPDaxn: + return "SPDaxn"; + + case GDI_PSDPSoaxx: + return "PSDPSoaxx"; + + case GDI_DPSaxn: + return "DPSaxn"; + + case GDI_DPSxx: + return "DPSxx"; + + case GDI_PSDPSonoxx: + return "PSDPSonoxx"; + + case GDI_SDPSonoxn: + return "SDPSonoxn"; + + case GDI_DSxn: + return "DSxn"; + + case GDI_DPSnax: + return "DPSnax"; + + case GDI_SDPSoaxn: + return "SDPSoaxn"; + + case GDI_SPDnax: + return "SPDnax"; + + case GDI_DSPDoaxn: + return "DSPDoaxn"; + + case GDI_DSPDSaoxx: + return "DSPDSaoxx"; + + case GDI_PDSxan: + return "PDSxan"; + + case GDI_DPa: + return "DPa"; + + case GDI_PDSPnaoxn: + return "PDSPnaoxn"; + + case GDI_DPSnoa: + return "DPSnoa"; + + case GDI_DPSDxoxn: + return "DPSDxoxn"; + + case GDI_PDSPonoxn: + return "PDSPonoxn"; + + case GDI_PDxn: + return "PDxn"; + + case GDI_DSPnax: + return "DSPnax"; + + case GDI_PDSPoaxn: + return "PDSPoaxn"; + + case GDI_DPSoa: + return "DPSoa"; + + case GDI_DPSoxn: + return "DPSoxn"; + + case GDI_DSTCOPY: + return "D"; + + case GDI_DPSono: + return "DPSono"; + + case GDI_SPDSxax: + return "SPDSxax"; + + case GDI_DPSDaoxn: + return "DPSDaoxn"; + + case GDI_DSPnao: + return "DSPnao"; + + case GDI_DPno: + return "DPno"; + + case GDI_PDSnoa: + return "PDSnoa"; + + case GDI_PDSPxoxn: + return "PDSPxoxn"; + + case GDI_SSPxDSxox: + return "SSPxDSxox"; + + case GDI_SDPanan: + return "SDPanan"; + + case GDI_PSDnax: + return "PSDnax"; + + case GDI_DPSDoaxn: + return "DPSDoaxn"; + + case GDI_DPSDPaoxx: + return "DPSDPaoxx"; + + case GDI_SDPxan: + return "SDPxan"; + + case GDI_PSDPxax: + return "PSDPxax"; + + case GDI_DSPDaoxn: + return "DSPDaoxn"; + + case GDI_DPSnao: + return "DPSnao"; + + case GDI_MERGEPAINT: + return "DSno"; + + case GDI_SPDSanax: + return "SPDSanax"; + + case GDI_SDxPDxan: + return "SDxPDxan"; + + case GDI_DPSxo: + return "DPSxo"; + + case GDI_DPSano: + return "DPSano"; + + case GDI_MERGECOPY: + return "PSa"; + + case GDI_SPDSnaoxn: + return "SPDSnaoxn"; + + case GDI_SPDSonoxn: + return "SPDSonoxn"; + + case GDI_PSxn: + return "PSxn"; + + case GDI_SPDnoa: + return "SPDnoa"; + + case GDI_SPDSxoxn: + return "SPDSxoxn"; + + case GDI_SDPnax: + return "SDPnax"; + + case GDI_PSDPoaxn: + return "PSDPoaxn"; + + case GDI_SDPoa: + return "SDPoa"; + + case GDI_SPDoxn: + return "SPDoxn"; + + case GDI_DPSDxax: + return "DPSDxax"; + + case GDI_SPDSaoxn: + return "SPDSaoxn"; + + case GDI_SRCCOPY: + return "S"; + + case GDI_SDPono: + return "SDPono"; + + case GDI_SDPnao: + return "SDPnao"; + + case GDI_SPno: + return "SPno"; + + case GDI_PSDnoa: + return "PSDnoa"; + + case GDI_PSDPxoxn: + return "PSDPxoxn"; + + case GDI_PDSnax: + return "PDSnax"; + + case GDI_SPDSoaxn: + return "SPDSoaxn"; + + case GDI_SSPxPDxax: + return "SSPxPDxax"; + + case GDI_DPSanan: + return "DPSanan"; + + case GDI_PSDPSaoxx: + return "PSDPSaoxx"; + + case GDI_DPSxan: + return "DPSxan"; + + case GDI_PDSPxax: + return "PDSPxax"; + + case GDI_SDPSaoxn: + return "SDPSaoxn"; + + case GDI_DPSDanax: + return "DPSDanax"; + + case GDI_SPxDSxan: + return "SPxDSxan"; + + case GDI_SPDnao: + return "SPDnao"; + + case GDI_SDno: + return "SDno"; + + case GDI_SDPxo: + return "SDPxo"; + + case GDI_SDPano: + return "SDPano"; + + case GDI_PDSoa: + return "PDSoa"; + + case GDI_PDSoxn: + return "PDSoxn"; + + case GDI_DSPDxax: + return "DSPDxax"; + + case GDI_PSDPaoxn: + return "PSDPaoxn"; + + case GDI_SDPSxax: + return "SDPSxax"; + + case GDI_PDSPaoxn: + return "PDSPaoxn"; + + case GDI_SDPSanax: + return "SDPSanax"; + + case GDI_SPxPDxan: + return "SPxPDxan"; + + case GDI_SSPxDSxax: + return "SSPxDSxax"; + + case GDI_DSPDSanaxxn: + return "DSPDSanaxxn"; + + case GDI_DPSao: + return "DPSao"; + + case GDI_DPSxno: + return "DPSxno"; + + case GDI_SDPao: + return "SDPao"; + + case GDI_SDPxno: + return "SDPxno"; + + case GDI_SRCPAINT: + return "DSo"; + + case GDI_SDPnoo: + return "SDPnoo"; + + case GDI_PATCOPY: + return "P"; + + case GDI_PDSono: + return "PDSono"; + + case GDI_PDSnao: + return "PDSnao"; + + case GDI_PSno: + return "PSno"; + + case GDI_PSDnao: + return "PSDnao"; + + case GDI_PDno: + return "PDno"; + + case GDI_PDSxo: + return "PDSxo"; + + case GDI_PDSano: + return "PDSano"; + + case GDI_PDSao: + return "PDSao"; + + case GDI_PDSxno: + return "PDSxno"; + + case GDI_DPo: + return "DPo"; + + case GDI_PATPAINT: + return "DPSnoo"; + + case GDI_PSo: + return "PSo"; + + case GDI_PSDnoo: + return "PSDnoo"; + + case GDI_DPSoo: + return "DPSoo"; + + case GDI_WHITENESS: + return "1"; + + case GDI_GLYPH_ORDER: + return "SPaDSnao"; + + default: + return ""; + } +} + +/** + * @brief Create a new solid brush. + * msdn{dd183518} + * + * @param crColor brush color + * @return new brush + */ +HGDI_BRUSH gdi_CreateSolidBrush(UINT32 crColor) +{ + HGDI_BRUSH hBrush = (HGDI_BRUSH)calloc(1, sizeof(GDI_BRUSH)); + + if (!hBrush) + return NULL; + + hBrush->objectType = GDIOBJECT_BRUSH; + hBrush->style = GDI_BS_SOLID; + hBrush->color = crColor; + return hBrush; +} +/** + * @brief Create a new pattern brush. + * msdn{dd183508} + * + * @param hbmp pattern bitmap + * @return new brush + */ +HGDI_BRUSH gdi_CreatePatternBrush(HGDI_BITMAP hbmp) +{ + HGDI_BRUSH hBrush = (HGDI_BRUSH)calloc(1, sizeof(GDI_BRUSH)); + + if (!hBrush) + return NULL; + + hBrush->objectType = GDIOBJECT_BRUSH; + hBrush->style = GDI_BS_PATTERN; + hBrush->pattern = hbmp; + return hBrush; +} + +HGDI_BRUSH gdi_CreateHatchBrush(HGDI_BITMAP hbmp) +{ + HGDI_BRUSH hBrush = (HGDI_BRUSH)calloc(1, sizeof(GDI_BRUSH)); + + if (!hBrush) + return NULL; + + hBrush->objectType = GDIOBJECT_BRUSH; + hBrush->style = GDI_BS_HATCHED; + hBrush->pattern = hbmp; + return hBrush; +} diff --git a/libfreerdp/gdi/brush.h b/libfreerdp/gdi/brush.h new file mode 100644 index 0000000..b7e86c2 --- /dev/null +++ b/libfreerdp/gdi/brush.h @@ -0,0 +1,51 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * GDI Brush 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. + */ + +#ifndef FREERDP_LIB_GDI_BRUSH_H +#define FREERDP_LIB_GDI_BRUSH_H + +#include <freerdp/api.h> +#include <freerdp/gdi/gdi.h> + +#ifdef __cplusplus +extern "C" +{ +#endif + + FREERDP_LOCAL const char* gdi_rop_to_string(UINT32 code); + + FREERDP_LOCAL HGDI_BRUSH gdi_CreateSolidBrush(UINT32 crColor); + FREERDP_LOCAL HGDI_BRUSH gdi_CreatePatternBrush(HGDI_BITMAP hbmp); + FREERDP_LOCAL HGDI_BRUSH gdi_CreateHatchBrush(HGDI_BITMAP hbmp); + + static INLINE UINT32 gdi_GetBrushStyle(HGDI_DC hdc) + { + if (!hdc || !hdc->brush) + return GDI_BS_NULL; + + return hdc->brush->style; + } + +#ifdef __cplusplus +} +#endif + +#endif /* FREERDP_LIB_GDI_BRUSH_H */ diff --git a/libfreerdp/gdi/clipping.c b/libfreerdp/gdi/clipping.c new file mode 100644 index 0000000..45774b6 --- /dev/null +++ b/libfreerdp/gdi/clipping.c @@ -0,0 +1,164 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * GDI Clipping 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/freerdp.h> +#include <freerdp/gdi/gdi.h> + +#include <freerdp/gdi/region.h> + +#include "clipping.h" + +BOOL gdi_SetClipRgn(HGDI_DC hdc, INT32 nXLeft, INT32 nYLeft, INT32 nWidth, INT32 nHeight) +{ + return gdi_SetRgn(hdc->clip, nXLeft, nYLeft, nWidth, nHeight); +} + +/** + * Get the current clipping region. + * msdn{dd144866} + * + * @param hdc device context + * @return clipping region + */ + +HGDI_RGN gdi_GetClipRgn(HGDI_DC hdc) +{ + return hdc->clip; +} + +/** + * Set the current clipping region to null. + * @param hdc device context + * @return nonzero on success, 0 otherwise + */ + +BOOL gdi_SetNullClipRgn(HGDI_DC hdc) +{ + gdi_SetClipRgn(hdc, 0, 0, 0, 0); + hdc->clip->null = TRUE; + return TRUE; +} + +/** + * Clip coordinates according to clipping region + * @param hdc device context + * @param x x1 + * @param y y1 + * @param w width + * @param h height + * @param srcx source x1 + * @param srcy source y1 + * @return nonzero if there is something to draw, 0 otherwise + */ + +BOOL gdi_ClipCoords(HGDI_DC hdc, INT32* x, INT32* y, INT32* w, INT32* h, INT32* srcx, INT32* srcy) +{ + GDI_RECT bmp; + GDI_RECT clip; + GDI_RECT coords; + HGDI_BITMAP hBmp = NULL; + int dx = 0; + int dy = 0; + BOOL draw = TRUE; + + if (hdc == NULL) + return FALSE; + + hBmp = (HGDI_BITMAP)hdc->selectedObject; + + if (hBmp != NULL) + { + if (hdc->clip->null) + { + gdi_CRgnToRect(0, 0, hBmp->width, hBmp->height, &clip); + } + else + { + gdi_RgnToRect(hdc->clip, &clip); + gdi_CRgnToRect(0, 0, hBmp->width, hBmp->height, &bmp); + + if (clip.left < bmp.left) + clip.left = bmp.left; + + if (clip.right > bmp.right) + clip.right = bmp.right; + + if (clip.top < bmp.top) + clip.top = bmp.top; + + if (clip.bottom > bmp.bottom) + clip.bottom = bmp.bottom; + } + } + else + { + gdi_RgnToRect(hdc->clip, &clip); + } + + gdi_CRgnToRect(*x, *y, *w, *h, &coords); + + if (coords.right >= clip.left && coords.left <= clip.right && coords.bottom >= clip.top && + coords.top <= clip.bottom) + { + /* coordinates overlap with clipping region */ + if (coords.left < clip.left) + { + dx = (clip.left - coords.left); + coords.left = clip.left; + } + + if (coords.right > clip.right) + coords.right = clip.right; + + if (coords.top < clip.top) + { + dy = (clip.top - coords.top); + coords.top = clip.top; + } + + if (coords.bottom > clip.bottom) + coords.bottom = clip.bottom; + } + else + { + /* coordinates do not overlap with clipping region */ + coords.left = 0; + coords.right = 0; + coords.top = 0; + coords.bottom = 0; + draw = FALSE; + } + + if (srcx != NULL) + *srcx += dx; + + if (srcy != NULL) + *srcy += dy; + + gdi_RectToCRgn(&coords, x, y, w, h); + return draw; +} diff --git a/libfreerdp/gdi/clipping.h b/libfreerdp/gdi/clipping.h new file mode 100644 index 0000000..d4546fb --- /dev/null +++ b/libfreerdp/gdi/clipping.h @@ -0,0 +1,44 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * GDI Clipping 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. + */ + +#ifndef FREERDP_LIB_GDI_CLIPPING_H +#define FREERDP_LIB_GDI_CLIPPING_H + +#include <freerdp/api.h> +#include <freerdp/gdi/gdi.h> + +#ifdef __cplusplus +extern "C" +{ +#endif + + FREERDP_LOCAL BOOL gdi_SetClipRgn(HGDI_DC hdc, INT32 nXLeft, INT32 nYLeft, INT32 nWidth, + INT32 nHeight); + FREERDP_LOCAL HGDI_RGN gdi_GetClipRgn(HGDI_DC hdc); + FREERDP_LOCAL BOOL gdi_SetNullClipRgn(HGDI_DC hdc); + FREERDP_LOCAL BOOL gdi_ClipCoords(HGDI_DC hdc, INT32* x, INT32* y, INT32* w, INT32* h, + INT32* srcx, INT32* srcy); + +#ifdef __cplusplus +} +#endif + +#endif /* FREERDP_LIB_GDI_CLIPPING_H */ diff --git a/libfreerdp/gdi/dc.c b/libfreerdp/gdi/dc.c new file mode 100644 index 0000000..94562b1 --- /dev/null +++ b/libfreerdp/gdi/dc.c @@ -0,0 +1,258 @@ +/* + * FreeRDP: A Remote Desktop Protocol Implementation + * GDI Device Context 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. + */ + +/* Device Context Functions: http://msdn.microsoft.com/en-us/library/dd183554 */ + +#include <freerdp/config.h> + +#include <stdio.h> +#include <stdlib.h> + +#include <freerdp/freerdp.h> +#include <freerdp/gdi/gdi.h> + +#include <freerdp/gdi/region.h> + +#include <freerdp/gdi/dc.h> + +/** + * @brief Get the current device context (a new one is created each time). + * msdn{dd144871} + * + * @return current device context + */ + +HGDI_DC gdi_GetDC(void) +{ + HGDI_DC hDC = (HGDI_DC)calloc(1, sizeof(GDI_DC)); + + if (!hDC) + return NULL; + + hDC->format = PIXEL_FORMAT_XRGB32; + hDC->drawMode = GDI_R2_BLACK; + hDC->clip = gdi_CreateRectRgn(0, 0, 0, 0); + + if (!hDC->clip) + { + free(hDC); + return NULL; + } + + hDC->clip->null = TRUE; + hDC->hwnd = NULL; + return hDC; +} + +/** + * @brief Create a device context. + * msdn{dd144871} + * + * @return new device context + */ + +HGDI_DC gdi_CreateDC(UINT32 format) +{ + HGDI_DC hDC = NULL; + + if (!(hDC = (HGDI_DC)calloc(1, sizeof(GDI_DC)))) + return NULL; + + hDC->drawMode = GDI_R2_BLACK; + + if (!(hDC->clip = gdi_CreateRectRgn(0, 0, 0, 0))) + goto fail; + + hDC->clip->null = TRUE; + hDC->hwnd = NULL; + hDC->format = format; + + if (!(hDC->hwnd = (HGDI_WND)calloc(1, sizeof(GDI_WND)))) + goto fail; + + if (!(hDC->hwnd->invalid = gdi_CreateRectRgn(0, 0, 0, 0))) + goto fail; + + hDC->hwnd->invalid->null = TRUE; + hDC->hwnd->count = 32; + + if (!(hDC->hwnd->cinvalid = (HGDI_RGN)calloc(hDC->hwnd->count, sizeof(GDI_RGN)))) + goto fail; + + hDC->hwnd->ninvalid = 0; + return hDC; +fail: + gdi_DeleteDC(hDC); + return NULL; +} + +/** + * @brief Create a new device context compatible with the given device context. + * msdn{dd183489} + * @param hdc device context + * @return new compatible device context + */ + +HGDI_DC gdi_CreateCompatibleDC(HGDI_DC hdc) +{ + HGDI_DC hDC = (HGDI_DC)calloc(1, sizeof(GDI_DC)); + + if (!hDC) + return NULL; + + if (!(hDC->clip = gdi_CreateRectRgn(0, 0, 0, 0))) + { + free(hDC); + return NULL; + } + + hDC->clip->null = TRUE; + hDC->format = hdc->format; + hDC->drawMode = hdc->drawMode; + hDC->hwnd = NULL; + return hDC; +} + +/** + * @brief Select a GDI object in the current device context. + * msdn{dd162957} + * + * @param hdc device context + * @param hgdiobject new selected GDI object + * @return previous selected GDI object + */ + +HGDIOBJECT gdi_SelectObject(HGDI_DC hdc, HGDIOBJECT hgdiobject) +{ + HGDIOBJECT previousSelectedObject = hdc->selectedObject; + + if (hgdiobject == NULL) + return NULL; + + if (hgdiobject->objectType == GDIOBJECT_BITMAP) + { + hdc->selectedObject = hgdiobject; + } + else if (hgdiobject->objectType == GDIOBJECT_PEN) + { + previousSelectedObject = (HGDIOBJECT)hdc->pen; + hdc->pen = (HGDI_PEN)hgdiobject; + } + else if (hgdiobject->objectType == GDIOBJECT_BRUSH) + { + previousSelectedObject = (HGDIOBJECT)hdc->brush; + hdc->brush = (HGDI_BRUSH)hgdiobject; + } + else if (hgdiobject->objectType == GDIOBJECT_REGION) + { + hdc->selectedObject = hgdiobject; + previousSelectedObject = (HGDIOBJECT)COMPLEXREGION; + } + else if (hgdiobject->objectType == GDIOBJECT_RECT) + { + hdc->selectedObject = hgdiobject; + previousSelectedObject = (HGDIOBJECT)SIMPLEREGION; + } + else + { + /* Unknown GDI Object Type */ + return NULL; + } + + return previousSelectedObject; +} + +/** + * @brief Delete a GDI object. + * msdn{dd183539} + * @param hgdiobject GDI object + * @return nonzero if successful, 0 otherwise + */ + +BOOL gdi_DeleteObject(HGDIOBJECT hgdiobject) +{ + if (!hgdiobject) + return FALSE; + + if (hgdiobject->objectType == GDIOBJECT_BITMAP) + { + HGDI_BITMAP hBitmap = (HGDI_BITMAP)hgdiobject; + + if (hBitmap->data && hBitmap->free) + { + hBitmap->free(hBitmap->data); + hBitmap->data = NULL; + } + + free(hBitmap); + } + else if (hgdiobject->objectType == GDIOBJECT_PEN) + { + HGDI_PEN hPen = (HGDI_PEN)hgdiobject; + free(hPen); + } + else if (hgdiobject->objectType == GDIOBJECT_BRUSH) + { + HGDI_BRUSH hBrush = (HGDI_BRUSH)hgdiobject; + free(hBrush); + } + else if (hgdiobject->objectType == GDIOBJECT_REGION) + { + free(hgdiobject); + } + else if (hgdiobject->objectType == GDIOBJECT_RECT) + { + free(hgdiobject); + } + else + { + /* Unknown GDI Object Type */ + free(hgdiobject); + return FALSE; + } + + return TRUE; +} + +/** + * @brief Delete device context. + * msdn{dd183533} + * @param hdc device context + * @return nonzero if successful, 0 otherwise + */ + +BOOL gdi_DeleteDC(HGDI_DC hdc) +{ + if (hdc) + { + if (hdc->hwnd) + { + free(hdc->hwnd->cinvalid); + free(hdc->hwnd->invalid); + free(hdc->hwnd); + } + + free(hdc->clip); + free(hdc); + } + + return TRUE; +} diff --git a/libfreerdp/gdi/drawing.c b/libfreerdp/gdi/drawing.c new file mode 100644 index 0000000..d4e241c --- /dev/null +++ b/libfreerdp/gdi/drawing.c @@ -0,0 +1,152 @@ +/* + * FreeRDP: A Remote Desktop Protocol Implementation + * GDI Drawing 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. + */ + +/* GDI Drawing Functions: http://msdn.microsoft.com/en-us/library/dd162760/ */ + +#include <freerdp/config.h> + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include <freerdp/freerdp.h> +#include <freerdp/gdi/gdi.h> + +#include <freerdp/gdi/dc.h> +#include "drawing.h" + +/** + * @brief Set current foreground draw mode. + * msdn{dd144922} + * + * @param hdc device context + * + * @return draw mode + */ + +INT32 gdi_GetROP2(HGDI_DC hdc) +{ + return hdc->drawMode; +} + +/** + * @brief Set current foreground draw mode. + * msdn{dd145088} + * + * @param hdc device context + * @param fnDrawMode draw mode + * + * @return previous draw mode + */ + +INT32 gdi_SetROP2(HGDI_DC hdc, INT32 fnDrawMode) +{ + INT32 prevDrawMode = hdc->drawMode; + + if (fnDrawMode > 0 && fnDrawMode <= 16) + hdc->drawMode = fnDrawMode; + + return prevDrawMode; +} + +/** + * @brief Get the current background color. + * msdn{dd144852} + * + * @param hdc device context + * + * @return background color + */ + +UINT32 gdi_GetBkColor(HGDI_DC hdc) +{ + return hdc->bkColor; +} + +/** + * @brief Set the current background color. + * msdn{dd162964} + * + * @param hdc device color + * @param crColor new background color + * + * @return previous background color + */ + +UINT32 gdi_SetBkColor(HGDI_DC hdc, UINT32 crColor) +{ + UINT32 previousBkColor = hdc->bkColor; + hdc->bkColor = crColor; + return previousBkColor; +} + +/** + * @brief Get the current background mode.\n + * msdn{dd144853} + * + * @param hdc device context + * + * @return background mode + */ + +UINT32 gdi_GetBkMode(HGDI_DC hdc) +{ + return hdc->bkMode; +} + +/** + * @brief Set the current background mode.\n + * msdn{dd162965} + * + * @param hdc device context + * @param iBkMode background mode + * + * @return previous background mode on success, 0 on failure + */ + +INT32 gdi_SetBkMode(HGDI_DC hdc, INT32 iBkMode) +{ + if (iBkMode == GDI_OPAQUE || iBkMode == GDI_TRANSPARENT) + { + INT32 previousBkMode = hdc->bkMode; + hdc->bkMode = iBkMode; + return previousBkMode; + } + + return TRUE; +} + +/** + * @brief Set the current text color.\n + * msdn{dd145093} + * + * @param hdc device context + * @param crColor new text color + * + * @return previous text color + */ + +UINT32 gdi_SetTextColor(HGDI_DC hdc, UINT32 crColor) +{ + UINT32 previousTextColor = hdc->textColor; + hdc->textColor = crColor; + return previousTextColor; +} diff --git a/libfreerdp/gdi/drawing.h b/libfreerdp/gdi/drawing.h new file mode 100644 index 0000000..96bdf65 --- /dev/null +++ b/libfreerdp/gdi/drawing.h @@ -0,0 +1,45 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * GDI Drawing 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. + */ + +#ifndef FREERDP_LIB_GDI_DRAWING_H +#define FREERDP_LIB_GDI_DRAWING_H + +#include <freerdp/api.h> +#include <freerdp/gdi/gdi.h> + +#ifdef __cplusplus +extern "C" +{ +#endif + + FREERDP_LOCAL INT32 gdi_GetROP2(HGDI_DC hdc); + FREERDP_LOCAL INT32 gdi_SetROP2(HGDI_DC hdc, INT32 fnDrawMode); + FREERDP_LOCAL UINT32 gdi_GetBkColor(HGDI_DC hdc); + FREERDP_LOCAL UINT32 gdi_SetBkColor(HGDI_DC hdc, UINT32 crColor); + FREERDP_LOCAL UINT32 gdi_GetBkMode(HGDI_DC hdc); + FREERDP_LOCAL INT32 gdi_SetBkMode(HGDI_DC hdc, INT32 iBkMode); + FREERDP_LOCAL UINT32 gdi_SetTextColor(HGDI_DC hdc, UINT32 crColor); + +#ifdef __cplusplus +} +#endif + +#endif /* FREERDP_LIB_GDI_DRAWING_H */ diff --git a/libfreerdp/gdi/gdi.c b/libfreerdp/gdi/gdi.c new file mode 100644 index 0000000..832ae24 --- /dev/null +++ b/libfreerdp/gdi/gdi.c @@ -0,0 +1,1439 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * GDI Library + * + * 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 <stdlib.h> + +#include <winpr/crt.h> +#include <winpr/assert.h> + +#include <freerdp/api.h> +#include <freerdp/log.h> +#include <freerdp/freerdp.h> + +#include <freerdp/gdi/gdi.h> +#include <freerdp/gdi/dc.h> +#include <freerdp/gdi/pen.h> +#include <freerdp/gdi/shape.h> +#include <freerdp/gdi/region.h> +#include <freerdp/gdi/bitmap.h> + +#include "drawing.h" +#include "clipping.h" +#include "brush.h" +#include "line.h" +#include "gdi.h" +#include "../core/graphics.h" +#include "../core/update.h" +#include "../cache/cache.h" + +#define TAG FREERDP_TAG("gdi") + +/* Ternary Raster Operation Table */ +typedef struct +{ + DWORD code; + const char* name; +} rop_table_entry; + +static const rop_table_entry rop3_code_table[] = { { GDI_BLACKNESS, "0" }, + { GDI_DPSoon, "DPSoon" }, + { GDI_DPSona, "DPSona" }, + { GDI_PSon, "PSon" }, + { GDI_SDPona, "SDPona" }, + { GDI_DPon, "DPon" }, + { GDI_PDSxnon, "PDSxnon" }, + { GDI_PDSaon, "PDSaon" }, + { GDI_SDPnaa, "SDPnaa" }, + { GDI_PDSxon, "PDSxon" }, + { GDI_DPna, "DPna" }, + { GDI_PSDnaon, "PSDnaon" }, + { GDI_SPna, "SPna" }, + { GDI_PDSnaon, "PDSnaon" }, + { GDI_PDSonon, "PDSonon" }, + { GDI_Pn, "Pn" }, + { GDI_PDSona, "PDSona" }, + { GDI_NOTSRCERASE, "DSon" }, + { GDI_SDPxnon, "SDPxnon" }, + { GDI_SDPaon, "SDPaon" }, + { GDI_DPSxnon, "DPSxnon" }, + { GDI_DPSaon, "DPSaon" }, + { GDI_PSDPSanaxx, "PSDPSanaxx" }, + { GDI_SSPxDSxaxn, "SSPxDSxaxn" }, + { GDI_SPxPDxa, "SPxPDxa" }, + { GDI_SDPSanaxn, "SDPSanaxn" }, + { GDI_PDSPaox, "PDSPaox" }, + { GDI_SDPSxaxn, "SDPSxaxn" }, + { GDI_PSDPaox, "PSDPaox" }, + { GDI_DSPDxaxn, "DSPDxaxn" }, + { GDI_PDSox, "PDSox" }, + { GDI_PDSoan, "PDSoan" }, + { GDI_DPSnaa, "DPSnaa" }, + { GDI_SDPxon, "SDPxon" }, + { GDI_DSna, "DSna" }, + { GDI_SPDnaon, "SPDnaon" }, + { GDI_SPxDSxa, "SPxDSxa" }, + { GDI_PDSPanaxn, "PDSPanaxn" }, + { GDI_SDPSaox, "SDPSaox" }, + { GDI_SDPSxnox, "SDPSxnox" }, + { GDI_DPSxa, "DPSxa" }, + { GDI_PSDPSaoxxn, "PSDPSaoxxn" }, + { GDI_DPSana, "DPSana" }, + { GDI_SSPxPDxaxn, "SSPxPDxaxn" }, + { GDI_SPDSoax, "SPDSoax" }, + { GDI_PSDnox, "PSDnox" }, + { GDI_PSDPxox, "PSDPxox" }, + { GDI_PSDnoan, "PSDnoan" }, + { GDI_PSna, "PSna" }, + { GDI_SDPnaon, "SDPnaon" }, + { GDI_SDPSoox, "SDPSoox" }, + { GDI_NOTSRCCOPY, "Sn" }, + { GDI_SPDSaox, "SPDSaox" }, + { GDI_SPDSxnox, "SPDSxnox" }, + { GDI_SDPox, "SDPox" }, + { GDI_SDPoan, "SDPoan" }, + { GDI_PSDPoax, "PSDPoax" }, + { GDI_SPDnox, "SPDnox" }, + { GDI_SPDSxox, "SPDSxox" }, + { GDI_SPDnoan, "SPDnoan" }, + { GDI_PSx, "PSx" }, + { GDI_SPDSonox, "SPDSonox" }, + { GDI_SPDSnaox, "SPDSnaox" }, + { GDI_PSan, "PSan" }, + { GDI_PSDnaa, "PSDnaa" }, + { GDI_DPSxon, "DPSxon" }, + { GDI_SDxPDxa, "SDxPDxa" }, + { GDI_SPDSanaxn, "SPDSanaxn" }, + { GDI_SRCERASE, "SDna" }, + { GDI_DPSnaon, "DPSnaon" }, + { GDI_DSPDaox, "DSPDaox" }, + { GDI_PSDPxaxn, "PSDPxaxn" }, + { GDI_SDPxa, "SDPxa" }, + { GDI_PDSPDaoxxn, "PDSPDaoxxn" }, + { GDI_DPSDoax, "DPSDoax" }, + { GDI_PDSnox, "PDSnox" }, + { GDI_SDPana, "SDPana" }, + { GDI_SSPxDSxoxn, "SSPxDSxoxn" }, + { GDI_PDSPxox, "PDSPxox" }, + { GDI_PDSnoan, "PDSnoan" }, + { GDI_PDna, "PDna" }, + { GDI_DSPnaon, "DSPnaon" }, + { GDI_DPSDaox, "DPSDaox" }, + { GDI_SPDSxaxn, "SPDSxaxn" }, + { GDI_DPSonon, "DPSonon" }, + { GDI_DSTINVERT, "Dn" }, + { GDI_DPSox, "DPSox" }, + { GDI_DPSoan, "DPSoan" }, + { GDI_PDSPoax, "PDSPoax" }, + { GDI_DPSnox, "DPSnox" }, + { GDI_PATINVERT, "DPx" }, + { GDI_DPSDonox, "DPSDonox" }, + { GDI_DPSDxox, "DPSDxox" }, + { GDI_DPSnoan, "DPSnoan" }, + { GDI_DPSDnaox, "DPSDnaox" }, + { GDI_DPan, "DPan" }, + { GDI_PDSxa, "PDSxa" }, + { GDI_DSPDSaoxxn, "DSPDSaoxxn" }, + { GDI_DSPDoax, "DSPDoax" }, + { GDI_SDPnox, "SDPnox" }, + { GDI_SDPSoax, "SDPSoax" }, + { GDI_DSPnox, "DSPnox" }, + { GDI_SRCINVERT, "DSx" }, + { GDI_SDPSonox, "SDPSonox" }, + { GDI_DSPDSonoxxn, "DSPDSonoxxn" }, + { GDI_PDSxxn, "PDSxxn" }, + { GDI_DPSax, "DPSax" }, + { GDI_PSDPSoaxxn, "PSDPSoaxxn" }, + { GDI_SDPax, "SDPax" }, + { GDI_PDSPDoaxxn, "PDSPDoaxxn" }, + { GDI_SDPSnoax, "SDPSnoax" }, + { GDI_PDSxnan, "PDSxnan" }, + { GDI_PDSana, "PDSana" }, + { GDI_SSDxPDxaxn, "SSDxPDxaxn" }, + { GDI_SDPSxox, "SDPSxox" }, + { GDI_SDPnoan, "SDPnoan" }, + { GDI_DSPDxox, "DSPDxox" }, + { GDI_DSPnoan, "DSPnoan" }, + { GDI_SDPSnaox, "SDPSnaox" }, + { GDI_DSan, "DSan" }, + { GDI_PDSax, "PDSax" }, + { GDI_DSPDSoaxxn, "DSPDSoaxxn" }, + { GDI_DPSDnoax, "DPSDnoax" }, + { GDI_SDPxnan, "SDPxnan" }, + { GDI_SPDSnoax, "SPDSnoax" }, + { GDI_DPSxnan, "DPSxnan" }, + { GDI_SPxDSxo, "SPxDSxo" }, + { GDI_DPSaan, "DPSaan" }, + { GDI_DPSaa, "DPSaa" }, + { GDI_SPxDSxon, "SPxDSxon" }, + { GDI_DPSxna, "DPSxna" }, + { GDI_SPDSnoaxn, "SPDSnoaxn" }, + { GDI_SDPxna, "SDPxna" }, + { GDI_PDSPnoaxn, "PDSPnoaxn" }, + { GDI_DSPDSoaxx, "DSPDSoaxx" }, + { GDI_PDSaxn, "PDSaxn" }, + { GDI_SRCAND, "DSa" }, + { GDI_SDPSnaoxn, "SDPSnaoxn" }, + { GDI_DSPnoa, "DSPnoa" }, + { GDI_DSPDxoxn, "DSPDxoxn" }, + { GDI_SDPnoa, "SDPnoa" }, + { GDI_SDPSxoxn, "SDPSxoxn" }, + { GDI_SSDxPDxax, "SSDxPDxax" }, + { GDI_PDSanan, "PDSanan" }, + { GDI_PDSxna, "PDSxna" }, + { GDI_SDPSnoaxn, "SDPSnoaxn" }, + { GDI_DPSDPoaxx, "DPSDPoaxx" }, + { GDI_SPDaxn, "SPDaxn" }, + { GDI_PSDPSoaxx, "PSDPSoaxx" }, + { GDI_DPSaxn, "DPSaxn" }, + { GDI_DPSxx, "DPSxx" }, + { GDI_PSDPSonoxx, "PSDPSonoxx" }, + { GDI_SDPSonoxn, "SDPSonoxn" }, + { GDI_DSxn, "DSxn" }, + { GDI_DPSnax, "DPSnax" }, + { GDI_SDPSoaxn, "SDPSoaxn" }, + { GDI_SPDnax, "SPDnax" }, + { GDI_DSPDoaxn, "DSPDoaxn" }, + { GDI_DSPDSaoxx, "DSPDSaoxx" }, + { GDI_PDSxan, "PDSxan" }, + { GDI_DPa, "DPa" }, + { GDI_PDSPnaoxn, "PDSPnaoxn" }, + { GDI_DPSnoa, "DPSnoa" }, + { GDI_DPSDxoxn, "DPSDxoxn" }, + { GDI_PDSPonoxn, "PDSPonoxn" }, + { GDI_PDxn, "PDxn" }, + { GDI_DSPnax, "DSPnax" }, + { GDI_PDSPoaxn, "PDSPoaxn" }, + { GDI_DPSoa, "DPSoa" }, + { GDI_DPSoxn, "DPSoxn" }, + { GDI_DSTCOPY, "D" }, + { GDI_DPSono, "DPSono" }, + { GDI_SPDSxax, "SPDSxax" }, + { GDI_DPSDaoxn, "DPSDaoxn" }, + { GDI_DSPnao, "DSPnao" }, + { GDI_DPno, "DPno" }, + { GDI_PDSnoa, "PDSnoa" }, + { GDI_PDSPxoxn, "PDSPxoxn" }, + { GDI_SSPxDSxox, "SSPxDSxox" }, + { GDI_SDPanan, "SDPanan" }, + { GDI_PSDnax, "PSDnax" }, + { GDI_DPSDoaxn, "DPSDoaxn" }, + { GDI_DPSDPaoxx, "DPSDPaoxx" }, + { GDI_SDPxan, "SDPxan" }, + { GDI_PSDPxax, "PSDPxax" }, + { GDI_DSPDaoxn, "DSPDaoxn" }, + { GDI_DPSnao, "DPSnao" }, + { GDI_MERGEPAINT, "DSno" }, + { GDI_SPDSanax, "SPDSanax" }, + { GDI_SDxPDxan, "SDxPDxan" }, + { GDI_DPSxo, "DPSxo" }, + { GDI_DPSano, "DPSano" }, + { GDI_MERGECOPY, "PSa" }, + { GDI_SPDSnaoxn, "SPDSnaoxn" }, + { GDI_SPDSonoxn, "SPDSonoxn" }, + { GDI_PSxn, "PSxn" }, + { GDI_SPDnoa, "SPDnoa" }, + { GDI_SPDSxoxn, "SPDSxoxn" }, + { GDI_SDPnax, "SDPnax" }, + { GDI_PSDPoaxn, "PSDPoaxn" }, + { GDI_SDPoa, "SDPoa" }, + { GDI_SPDoxn, "SPDoxn" }, + { GDI_DPSDxax, "DPSDxax" }, + { GDI_SPDSaoxn, "SPDSaoxn" }, + { GDI_SRCCOPY, "S" }, + { GDI_SDPono, "SDPono" }, + { GDI_SDPnao, "SDPnao" }, + { GDI_SPno, "SPno" }, + { GDI_PSDnoa, "PSDnoa" }, + { GDI_PSDPxoxn, "PSDPxoxn" }, + { GDI_PDSnax, "PDSnax" }, + { GDI_SPDSoaxn, "SPDSoaxn" }, + { GDI_SSPxPDxax, "SSPxPDxax" }, + { GDI_DPSanan, "DPSanan" }, + { GDI_PSDPSaoxx, "PSDPSaoxx" }, + { GDI_DPSxan, "DPSxan" }, + { GDI_PDSPxax, "PDSPxax" }, + { GDI_SDPSaoxn, "SDPSaoxn" }, + { GDI_DPSDanax, "DPSDanax" }, + { GDI_SPxDSxan, "SPxDSxan" }, + { GDI_SPDnao, "SPDnao" }, + { GDI_SDno, "SDno" }, + { GDI_SDPxo, "SDPxo" }, + { GDI_SDPano, "SDPano" }, + { GDI_PDSoa, "PDSoa" }, + { GDI_PDSoxn, "PDSoxn" }, + { GDI_DSPDxax, "DSPDxax" }, + { GDI_PSDPaoxn, "PSDPaoxn" }, + { GDI_SDPSxax, "SDPSxax" }, + { GDI_PDSPaoxn, "PDSPaoxn" }, + { GDI_SDPSanax, "SDPSanax" }, + { GDI_SPxPDxan, "SPxPDxan" }, + { GDI_SSPxDSxax, "SSPxDSxax" }, + { GDI_DSPDSanaxxn, "DSPDSanaxxn" }, + { GDI_DPSao, "DPSao" }, + { GDI_DPSxno, "DPSxno" }, + { GDI_SDPao, "SDPao" }, + { GDI_SDPxno, "SDPxno" }, + { GDI_SRCPAINT, "DSo" }, + { GDI_SDPnoo, "SDPnoo" }, + { GDI_PATCOPY, "P" }, + { GDI_PDSono, "PDSono" }, + { GDI_PDSnao, "PDSnao" }, + { GDI_PSno, "PSno" }, + { GDI_PSDnao, "PSDnao" }, + { GDI_PDno, "PDno" }, + { GDI_PDSxo, "PDSxo" }, + { GDI_PDSano, "PDSano" }, + { GDI_PDSao, "PDSao" }, + { GDI_PDSxno, "PDSxno" }, + { GDI_DPo, "DPo" }, + { GDI_PATPAINT, "DPSnoo" }, + { GDI_PSo, "PSo" }, + { GDI_PSDnoo, "PSDnoo" }, + { GDI_DPSoo, "DPSoo" }, + { GDI_WHITENESS, "1" } }; + +/* Hatch Patterns as monochrome data */ +static const BYTE GDI_BS_HATCHED_PATTERNS[] = { + 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, /* HS_HORIZONTAL */ + 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, /* HS_VERTICAL */ + 0xFE, 0xFD, 0xFB, 0xF7, 0xEF, 0xDF, 0xBF, 0x7F, /* HS_FDIAGONAL */ + 0x7F, 0xBF, 0xDF, 0xEF, 0xF7, 0xFB, 0xFD, 0xFE, /* HS_BDIAGONAL */ + 0xF7, 0xF7, 0xF7, 0x00, 0xF7, 0xF7, 0xF7, 0xF7, /* HS_CROSS */ + 0x7E, 0xBD, 0xDB, 0xE7, 0xE7, 0xDB, 0xBD, 0x7E /* HS_DIACROSS */ +}; + +BOOL gdi_decode_color(rdpGdi* gdi, const UINT32 srcColor, UINT32* color, UINT32* format) +{ + UINT32 SrcFormat = 0; + + if (!gdi || !color || !gdi->context || !gdi->context->settings) + return FALSE; + + const UINT32 ColorDepth = + freerdp_settings_get_uint32(gdi->context->settings, FreeRDP_ColorDepth); + + switch (ColorDepth) + { + case 32: + case 24: + SrcFormat = PIXEL_FORMAT_BGR24; + break; + + case 16: + SrcFormat = PIXEL_FORMAT_RGB16; + break; + + case 15: + SrcFormat = PIXEL_FORMAT_RGB15; + break; + + case 8: + SrcFormat = PIXEL_FORMAT_RGB8; + break; + + default: + return FALSE; + } + + if (format) + *format = gdi->dstFormat; + + *color = FreeRDPConvertColor(srcColor, SrcFormat, gdi->dstFormat, &gdi->palette); + return TRUE; +} + +/* GDI Helper Functions */ +DWORD gdi_rop3_code(BYTE code) +{ + return rop3_code_table[code].code; +} + +const char* gdi_rop3_code_string(BYTE code) +{ + return rop3_code_table[code].name; +} + +const char* gdi_rop3_string(DWORD rop) +{ + const size_t count = sizeof(rop3_code_table) / sizeof(rop3_code_table[0]); + + for (size_t x = 0; x < count; x++) + { + if (rop3_code_table[x].code == rop) + return rop3_code_table[x].name; + } + + return "UNKNOWN"; +} + +UINT32 gdi_get_pixel_format(UINT32 bitsPerPixel) +{ + UINT32 format = 0; + + switch (bitsPerPixel) + { + case 32: + format = PIXEL_FORMAT_BGRA32; + break; + + case 24: + format = PIXEL_FORMAT_BGR24; + break; + + case 16: + format = PIXEL_FORMAT_RGB16; + break; + + case 15: + format = PIXEL_FORMAT_RGB15; + break; + + case 8: + format = PIXEL_FORMAT_RGB8; + break; + + default: + WLog_ERR(TAG, "Unsupported color depth %" PRIu32, bitsPerPixel); + format = 0; + break; + } + + return format; +} + +gdiBitmap* gdi_bitmap_new_ex(rdpGdi* gdi, int width, int height, int bpp, BYTE* data) +{ + gdiBitmap* bitmap = NULL; + bitmap = (gdiBitmap*)calloc(1, sizeof(gdiBitmap)); + + if (!bitmap) + goto fail_bitmap; + + if (!(bitmap->hdc = gdi_CreateCompatibleDC(gdi->hdc))) + goto fail_hdc; + + WLog_Print(gdi->log, WLOG_DEBUG, "gdi_bitmap_new: width:%d height:%d bpp:%d", width, height, + bpp); + + if (!data) + bitmap->bitmap = gdi_CreateCompatibleBitmap(gdi->hdc, width, height); + else + bitmap->bitmap = gdi_create_bitmap(gdi, width, height, bpp, data); + + if (!bitmap->bitmap) + goto fail_bitmap_bitmap; + + gdi_SelectObject(bitmap->hdc, (HGDIOBJECT)bitmap->bitmap); + bitmap->org_bitmap = NULL; + return bitmap; +fail_bitmap_bitmap: + gdi_DeleteDC(bitmap->hdc); +fail_hdc: + free(bitmap); +fail_bitmap: + return NULL; +} + +void gdi_bitmap_free_ex(gdiBitmap* bitmap) +{ + if (bitmap) + { + gdi_SelectObject(bitmap->hdc, (HGDIOBJECT)bitmap->org_bitmap); + gdi_DeleteObject((HGDIOBJECT)bitmap->bitmap); + gdi_DeleteDC(bitmap->hdc); + free(bitmap); + } +} + +BOOL gdi_bitmap_update(rdpContext* context, const BITMAP_UPDATE* bitmapUpdate) +{ + if (!context || !bitmapUpdate || !context->gdi || !context->codecs) + { + WLog_ERR(TAG, + "Invalid arguments: context=%p, bitmapUpdate=%p, context->gdi=%p, " + "context->codecs=%p", + context, bitmapUpdate, context ? context->gdi : NULL, + context ? context->codecs : NULL); + return FALSE; + } + + for (UINT32 index = 0; index < bitmapUpdate->number; index++) + { + const BITMAP_DATA* bitmap = &(bitmapUpdate->rectangles[index]); + rdpBitmap* bmp = Bitmap_Alloc(context); + + if (!bmp) + { + WLog_ERR(TAG, "Bitmap_Alloc failed"); + return FALSE; + } + + Bitmap_SetDimensions(bmp, bitmap->width, bitmap->height); + Bitmap_SetRectangle(bmp, bitmap->destLeft, bitmap->destTop, bitmap->destRight, + bitmap->destBottom); + + if (!bmp->Decompress(context, bmp, bitmap->bitmapDataStream, bitmap->width, bitmap->height, + bitmap->bitsPerPixel, bitmap->bitmapLength, bitmap->compressed, + RDP_CODEC_ID_NONE)) + { + WLog_ERR(TAG, "bmp->Decompress failed"); + Bitmap_Free(context, bmp); + return FALSE; + } + + if (!bmp->New(context, bmp)) + { + WLog_ERR(TAG, "bmp->New failed"); + Bitmap_Free(context, bmp); + return FALSE; + } + + if (!bmp->Paint(context, bmp)) + { + WLog_ERR(TAG, "bmp->Paint failed"); + Bitmap_Free(context, bmp); + return FALSE; + } + + Bitmap_Free(context, bmp); + } + + return TRUE; +} + +static BOOL gdi_palette_update(rdpContext* context, const PALETTE_UPDATE* palette) +{ + rdpGdi* gdi = NULL; + + if (!context || !palette) + return FALSE; + + gdi = context->gdi; + gdi->palette.format = gdi->dstFormat; + + for (UINT32 index = 0; index < palette->number; index++) + { + const PALETTE_ENTRY* pe = &(palette->entries[index]); + gdi->palette.palette[index] = + FreeRDPGetColor(gdi->dstFormat, pe->red, pe->green, pe->blue, 0xFF); + } + + return TRUE; +} + +static BOOL gdi_set_bounds(rdpContext* context, const rdpBounds* bounds) +{ + rdpGdi* gdi = NULL; + + if (!context) + return FALSE; + + gdi = context->gdi; + + if (bounds) + { + gdi_SetClipRgn(gdi->drawing->hdc, bounds->left, bounds->top, + bounds->right - bounds->left + 1, bounds->bottom - bounds->top + 1); + } + else + gdi_SetNullClipRgn(gdi->drawing->hdc); + + return TRUE; +} + +static BOOL gdi_dstblt(rdpContext* context, const DSTBLT_ORDER* dstblt) +{ + rdpGdi* gdi = NULL; + + if (!context || !dstblt) + return FALSE; + + gdi = context->gdi; + return gdi_BitBlt(gdi->drawing->hdc, dstblt->nLeftRect, dstblt->nTopRect, dstblt->nWidth, + dstblt->nHeight, NULL, 0, 0, gdi_rop3_code(dstblt->bRop), &gdi->palette); +} + +static BOOL gdi_patblt(rdpContext* context, PATBLT_ORDER* patblt) +{ + const rdpBrush* brush = &patblt->brush; + UINT32 foreColor = 0; + UINT32 backColor = 0; + UINT32 originalColor = 0; + HGDI_BRUSH originalBrush = NULL; + HGDI_BRUSH hbrush = NULL; + rdpGdi* gdi = context->gdi; + BOOL ret = FALSE; + const DWORD rop = gdi_rop3_code(patblt->bRop); + INT32 nXSrc = 0; + INT32 nYSrc = 0; + BYTE data[8 * 8 * 4]; + HGDI_BITMAP hBmp = NULL; + + if (!gdi_decode_color(gdi, patblt->foreColor, &foreColor, NULL)) + return FALSE; + + if (!gdi_decode_color(gdi, patblt->backColor, &backColor, NULL)) + return FALSE; + + originalColor = gdi_SetTextColor(gdi->drawing->hdc, foreColor); + originalBrush = gdi->drawing->hdc->brush; + + switch (brush->style) + { + case GDI_BS_SOLID: + hbrush = gdi_CreateSolidBrush(foreColor); + break; + + case GDI_BS_HATCHED: + { + const BYTE* hatched = NULL; + hatched = GDI_BS_HATCHED_PATTERNS + (8 * brush->hatch); + + if (!freerdp_image_copy_from_monochrome(data, gdi->drawing->hdc->format, 0, 0, 0, 8, 8, + hatched, backColor, foreColor, &gdi->palette)) + goto out_error; + + hBmp = gdi_CreateBitmapEx(8, 8, gdi->drawing->hdc->format, 0, data, NULL); + + if (!hBmp) + goto out_error; + + hbrush = gdi_CreateHatchBrush(hBmp); + } + break; + + case GDI_BS_PATTERN: + { + UINT32 brushFormat = 0; + + if (brush->bpp > 1) + { + UINT32 bpp = brush->bpp; + + if ((bpp == 16) && + (freerdp_settings_get_uint32(context->settings, FreeRDP_ColorDepth) == 15)) + bpp = 15; + + brushFormat = gdi_get_pixel_format(bpp); + + if (!freerdp_image_copy(data, gdi->drawing->hdc->format, 0, 0, 0, 8, 8, brush->data, + brushFormat, 0, 0, 0, &gdi->palette, FREERDP_FLIP_NONE)) + goto out_error; + } + else + { + if (!freerdp_image_copy_from_monochrome(data, gdi->drawing->hdc->format, 0, 0, 0, 8, + 8, brush->data, backColor, foreColor, + &gdi->palette)) + goto out_error; + } + + hBmp = gdi_CreateBitmapEx(8, 8, gdi->drawing->hdc->format, 0, data, NULL); + + if (!hBmp) + goto out_error; + + hbrush = gdi_CreatePatternBrush(hBmp); + } + break; + + default: + WLog_ERR(TAG, "unimplemented brush style:%" PRIu32 "", brush->style); + break; + } + + if (hbrush) + { + hbrush->nXOrg = brush->x; + hbrush->nYOrg = brush->y; + gdi->drawing->hdc->brush = hbrush; + ret = gdi_BitBlt(gdi->drawing->hdc, patblt->nLeftRect, patblt->nTopRect, patblt->nWidth, + patblt->nHeight, gdi->primary->hdc, nXSrc, nYSrc, rop, &gdi->palette); + } + +out_error: + gdi_DeleteObject((HGDIOBJECT)hBmp); + gdi_DeleteObject((HGDIOBJECT)hbrush); + gdi->drawing->hdc->brush = originalBrush; + gdi_SetTextColor(gdi->drawing->hdc, originalColor); + return ret; +} + +static BOOL gdi_scrblt(rdpContext* context, const SCRBLT_ORDER* scrblt) +{ + rdpGdi* gdi = NULL; + + if (!context || !context->gdi) + return FALSE; + + gdi = context->gdi; + return gdi_BitBlt(gdi->drawing->hdc, scrblt->nLeftRect, scrblt->nTopRect, scrblt->nWidth, + scrblt->nHeight, gdi->primary->hdc, scrblt->nXSrc, scrblt->nYSrc, + gdi_rop3_code(scrblt->bRop), &gdi->palette); +} + +static BOOL gdi_opaque_rect(rdpContext* context, const OPAQUE_RECT_ORDER* opaque_rect) +{ + GDI_RECT rect; + HGDI_BRUSH hBrush = NULL; + UINT32 brush_color = 0; + rdpGdi* gdi = context->gdi; + BOOL ret = 0; + INT32 x = opaque_rect->nLeftRect; + INT32 y = opaque_rect->nTopRect; + INT32 w = opaque_rect->nWidth; + INT32 h = opaque_rect->nHeight; + gdi_ClipCoords(gdi->drawing->hdc, &x, &y, &w, &h, NULL, NULL); + gdi_CRgnToRect(x, y, w, h, &rect); + + if (!gdi_decode_color(gdi, opaque_rect->color, &brush_color, NULL)) + return FALSE; + + if (!(hBrush = gdi_CreateSolidBrush(brush_color))) + return FALSE; + + ret = gdi_FillRect(gdi->drawing->hdc, &rect, hBrush); + gdi_DeleteObject((HGDIOBJECT)hBrush); + return ret; +} + +static BOOL gdi_multi_opaque_rect(rdpContext* context, + const MULTI_OPAQUE_RECT_ORDER* multi_opaque_rect) +{ + GDI_RECT rect; + HGDI_BRUSH hBrush = NULL; + UINT32 brush_color = 0; + rdpGdi* gdi = context->gdi; + BOOL ret = TRUE; + + if (!gdi_decode_color(gdi, multi_opaque_rect->color, &brush_color, NULL)) + return FALSE; + + hBrush = gdi_CreateSolidBrush(brush_color); + + if (!hBrush) + return FALSE; + + for (UINT32 i = 0; i < multi_opaque_rect->numRectangles; i++) + { + const DELTA_RECT* rectangle = &multi_opaque_rect->rectangles[i]; + INT32 x = rectangle->left; + INT32 y = rectangle->top; + INT32 w = rectangle->width; + INT32 h = rectangle->height; + gdi_ClipCoords(gdi->drawing->hdc, &x, &y, &w, &h, NULL, NULL); + gdi_CRgnToRect(x, y, w, h, &rect); + ret = gdi_FillRect(gdi->drawing->hdc, &rect, hBrush); + + if (!ret) + break; + } + + gdi_DeleteObject((HGDIOBJECT)hBrush); + return ret; +} + +static BOOL gdi_line_to(rdpContext* context, const LINE_TO_ORDER* lineTo) +{ + UINT32 color = 0; + HGDI_PEN hPen = NULL; + rdpGdi* gdi = context->gdi; + INT32 xStart = lineTo->nXStart; + INT32 yStart = lineTo->nYStart; + INT32 xEnd = lineTo->nXEnd; + INT32 yEnd = lineTo->nYEnd; + INT32 w = 0; + INT32 h = 0; + gdi_ClipCoords(gdi->drawing->hdc, &xStart, &yStart, &w, &h, NULL, NULL); + gdi_ClipCoords(gdi->drawing->hdc, &xEnd, &yEnd, &w, &h, NULL, NULL); + + if (!gdi_decode_color(gdi, lineTo->penColor, &color, NULL)) + return FALSE; + + if (!(hPen = gdi_CreatePen(lineTo->penStyle, lineTo->penWidth, color, gdi->drawing->hdc->format, + &gdi->palette))) + return FALSE; + + gdi_SelectObject(gdi->drawing->hdc, (HGDIOBJECT)hPen); + gdi_SetROP2(gdi->drawing->hdc, lineTo->bRop2); + gdi_MoveToEx(gdi->drawing->hdc, lineTo->nXStart, lineTo->nYStart, NULL); + gdi_LineTo(gdi->drawing->hdc, lineTo->nXEnd, lineTo->nYEnd); + gdi_DeleteObject((HGDIOBJECT)hPen); + return TRUE; +} + +static BOOL gdi_polyline(rdpContext* context, const POLYLINE_ORDER* polyline) +{ + INT32 x = 0; + INT32 y = 0; + UINT32 color = 0; + HGDI_PEN hPen = NULL; + DELTA_POINT* points = NULL; + rdpGdi* gdi = context->gdi; + INT32 w = 0; + INT32 h = 0; + + if (!gdi_decode_color(gdi, polyline->penColor, &color, NULL)) + return FALSE; + + if (!(hPen = gdi_CreatePen(GDI_PS_SOLID, 1, color, gdi->drawing->hdc->format, &gdi->palette))) + return FALSE; + + gdi_SelectObject(gdi->drawing->hdc, (HGDIOBJECT)hPen); + gdi_SetROP2(gdi->drawing->hdc, polyline->bRop2); + x = polyline->xStart; + y = polyline->yStart; + gdi_ClipCoords(gdi->drawing->hdc, &x, &y, &w, &h, NULL, NULL); + gdi_MoveToEx(gdi->drawing->hdc, x, y, NULL); + points = polyline->points; + + for (UINT32 i = 0; i < polyline->numDeltaEntries; i++) + { + x += points[i].x; + y += points[i].y; + gdi_ClipCoords(gdi->drawing->hdc, &x, &y, &w, &h, NULL, NULL); + gdi_LineTo(gdi->drawing->hdc, x, y); + gdi_MoveToEx(gdi->drawing->hdc, x, y, NULL); + } + + gdi_DeleteObject((HGDIOBJECT)hPen); + return TRUE; +} + +static BOOL gdi_memblt(rdpContext* context, MEMBLT_ORDER* memblt) +{ + gdiBitmap* bitmap = NULL; + rdpGdi* gdi = NULL; + + if (!context || !memblt || !context->gdi || !memblt->bitmap) + return FALSE; + + bitmap = (gdiBitmap*)memblt->bitmap; + gdi = context->gdi; + return gdi_BitBlt(gdi->drawing->hdc, memblt->nLeftRect, memblt->nTopRect, memblt->nWidth, + memblt->nHeight, bitmap->hdc, memblt->nXSrc, memblt->nYSrc, + gdi_rop3_code(memblt->bRop), &gdi->palette); +} + +static BOOL gdi_mem3blt(rdpContext* context, MEM3BLT_ORDER* mem3blt) +{ + HGDI_BRUSH originalBrush = NULL; + rdpGdi* gdi = context->gdi; + BOOL ret = TRUE; + const rdpBrush* brush = &mem3blt->brush; + gdiBitmap* bitmap = (gdiBitmap*)mem3blt->bitmap; + UINT32 foreColor = 0; + UINT32 backColor = 0; + UINT32 originalColor = 0; + + if (!gdi_decode_color(gdi, mem3blt->foreColor, &foreColor, NULL)) + return FALSE; + + if (!gdi_decode_color(gdi, mem3blt->backColor, &backColor, NULL)) + return FALSE; + + originalColor = gdi_SetTextColor(gdi->drawing->hdc, foreColor); + + switch (brush->style) + { + case GDI_BS_SOLID: + originalBrush = gdi->drawing->hdc->brush; + gdi->drawing->hdc->brush = gdi_CreateSolidBrush(foreColor); + + if (!gdi->drawing->hdc->brush) + { + ret = FALSE; + goto out_fail; + } + + ret = gdi_BitBlt(gdi->drawing->hdc, mem3blt->nLeftRect, mem3blt->nTopRect, + mem3blt->nWidth, mem3blt->nHeight, bitmap->hdc, mem3blt->nXSrc, + mem3blt->nYSrc, gdi_rop3_code(mem3blt->bRop), &gdi->palette); + gdi_DeleteObject((HGDIOBJECT)gdi->drawing->hdc->brush); + gdi->drawing->hdc->brush = originalBrush; + break; + + case GDI_BS_PATTERN: + { + HGDI_BITMAP hBmp = NULL; + UINT32 brushFormat = 0; + BYTE* data = (BYTE*)winpr_aligned_malloc( + 8 * 8 * FreeRDPGetBytesPerPixel(gdi->drawing->hdc->format), 16); + + if (!data) + { + ret = FALSE; + goto out_fail; + } + + if (brush->bpp > 1) + { + UINT32 bpp = brush->bpp; + + const UINT32 ColorDepth = + freerdp_settings_get_uint32(gdi->context->settings, FreeRDP_ColorDepth); + if ((bpp == 16) && (ColorDepth == 15)) + bpp = 15; + + brushFormat = gdi_get_pixel_format(bpp); + + if (!freerdp_image_copy(data, gdi->drawing->hdc->format, 0, 0, 0, 8, 8, brush->data, + brushFormat, 0, 0, 0, &gdi->palette, FREERDP_FLIP_NONE)) + { + ret = FALSE; + winpr_aligned_free(data); + goto out_fail; + } + } + else + { + if (!freerdp_image_copy_from_monochrome(data, gdi->drawing->hdc->format, 0, 0, 0, 8, + 8, brush->data, backColor, foreColor, + &gdi->palette)) + { + ret = FALSE; + winpr_aligned_free(data); + goto out_fail; + } + } + + hBmp = gdi_CreateBitmap(8, 8, gdi->drawing->hdc->format, data); + + if (!hBmp) + { + ret = FALSE; + winpr_aligned_free(data); + goto out_fail; + } + + originalBrush = gdi->drawing->hdc->brush; + gdi->drawing->hdc->brush = gdi_CreatePatternBrush(hBmp); + + if (!gdi->drawing->hdc->brush) + { + gdi_DeleteObject((HGDIOBJECT)hBmp); + goto out_fail; + } + + gdi->drawing->hdc->brush->nXOrg = brush->x; + gdi->drawing->hdc->brush->nYOrg = brush->y; + ret = gdi_BitBlt(gdi->drawing->hdc, mem3blt->nLeftRect, mem3blt->nTopRect, + mem3blt->nWidth, mem3blt->nHeight, bitmap->hdc, mem3blt->nXSrc, + mem3blt->nYSrc, gdi_rop3_code(mem3blt->bRop), &gdi->palette); + gdi_DeleteObject((HGDIOBJECT)gdi->drawing->hdc->brush); + gdi_DeleteObject((HGDIOBJECT)hBmp); + gdi->drawing->hdc->brush = originalBrush; + } + break; + + default: + WLog_ERR(TAG, "Mem3Blt unimplemented brush style:%" PRIu32 "", brush->style); + break; + } + +out_fail: + gdi_SetTextColor(gdi->drawing->hdc, originalColor); + return ret; +} + +static BOOL gdi_polygon_sc(rdpContext* context, const POLYGON_SC_ORDER* polygon_sc) +{ + WLog_WARN(TAG, "not implemented"); + return FALSE; +} + +static BOOL gdi_polygon_cb(rdpContext* context, POLYGON_CB_ORDER* polygon_cb) +{ + WLog_WARN(TAG, "not implemented"); + return FALSE; +} + +static BOOL gdi_ellipse_sc(rdpContext* context, const ELLIPSE_SC_ORDER* ellipse_sc) +{ + WLog_WARN(TAG, "not implemented"); + return FALSE; +} + +static BOOL gdi_ellipse_cb(rdpContext* context, const ELLIPSE_CB_ORDER* ellipse_cb) +{ + WLog_WARN(TAG, "not implemented"); + return FALSE; +} + +static BOOL gdi_frame_marker(rdpContext* context, const FRAME_MARKER_ORDER* frameMarker) +{ + return TRUE; +} + +static BOOL gdi_surface_frame_marker(rdpContext* context, + const SURFACE_FRAME_MARKER* surfaceFrameMarker) +{ + WLog_Print(context->gdi->log, WLOG_DEBUG, "frameId %" PRIu32 " frameAction %" PRIu32 "", + surfaceFrameMarker->frameId, surfaceFrameMarker->frameAction); + + switch (surfaceFrameMarker->frameAction) + { + case SURFACECMD_FRAMEACTION_BEGIN: + break; + + case SURFACECMD_FRAMEACTION_END: + if (freerdp_settings_get_uint32(context->settings, FreeRDP_FrameAcknowledge) > 0) + { + IFCALL(context->update->SurfaceFrameAcknowledge, context, + surfaceFrameMarker->frameId); + } + + break; + } + + return TRUE; +} + +static BOOL intersect_rect(const rdpGdi* gdi, const SURFACE_BITS_COMMAND* cmd, RECTANGLE_16* prect) +{ + const UINT32 w = (const UINT32)gdi->width; + const UINT32 h = (const UINT32)gdi->height; + + if (cmd->destLeft > w) + return FALSE; + if (cmd->destRight > w) + return FALSE; + if (cmd->destLeft > cmd->destRight) + return FALSE; + if (cmd->destRight > UINT16_MAX) + return FALSE; + + if (cmd->destTop > h) + return FALSE; + if (cmd->destBottom > h) + return FALSE; + if (cmd->destTop > cmd->destBottom) + return FALSE; + if (cmd->destBottom > UINT16_MAX) + return FALSE; + + prect->left = (const UINT16)cmd->destLeft; + prect->top = (const UINT16)cmd->destTop; + prect->right = MIN((UINT16)cmd->destRight, prect->left + cmd->bmp.width); + prect->bottom = MIN((UINT16)cmd->destBottom, prect->top + cmd->bmp.height); + return TRUE; +} + +static BOOL gdi_surface_bits(rdpContext* context, const SURFACE_BITS_COMMAND* cmd) +{ + BOOL result = FALSE; + DWORD format = 0; + rdpGdi* gdi = NULL; + size_t size = 0; + REGION16 region; + RECTANGLE_16 cmdRect = { 0 }; + UINT32 nbRects = 0; + const RECTANGLE_16* rects = NULL; + + if (!context || !cmd) + return FALSE; + + gdi = context->gdi; + WLog_Print( + gdi->log, WLOG_DEBUG, + "destLeft %" PRIu32 " destTop %" PRIu32 " destRight %" PRIu32 " destBottom %" PRIu32 " " + "bpp %" PRIu8 " flags %" PRIx8 " codecID %" PRIu16 " width %" PRIu16 " height %" PRIu16 + " length %" PRIu32 "", + cmd->destLeft, cmd->destTop, cmd->destRight, cmd->destBottom, cmd->bmp.bpp, cmd->bmp.flags, + cmd->bmp.codecID, cmd->bmp.width, cmd->bmp.height, cmd->bmp.bitmapDataLength); + region16_init(®ion); + + if (!intersect_rect(gdi, cmd, &cmdRect)) + goto out; + + switch (cmd->bmp.codecID) + { + case RDP_CODEC_ID_REMOTEFX: + case RDP_CODEC_ID_IMAGE_REMOTEFX: + if (!rfx_process_message(context->codecs->rfx, cmd->bmp.bitmapData, + cmd->bmp.bitmapDataLength, cmdRect.left, cmdRect.top, + gdi->primary_buffer, gdi->dstFormat, gdi->stride, gdi->height, + ®ion)) + { + WLog_ERR(TAG, "Failed to process RemoteFX message"); + goto out; + } + + break; + + case RDP_CODEC_ID_NSCODEC: + format = gdi->dstFormat; + + if (!nsc_process_message( + context->codecs->nsc, cmd->bmp.bpp, cmd->bmp.width, cmd->bmp.height, + cmd->bmp.bitmapData, cmd->bmp.bitmapDataLength, gdi->primary_buffer, format, + gdi->stride, cmdRect.left, cmdRect.top, cmdRect.right - cmdRect.left, + cmdRect.bottom - cmdRect.top, FREERDP_FLIP_VERTICAL)) + { + WLog_ERR(TAG, "Failed to process NSCodec message"); + goto out; + } + + region16_union_rect(®ion, ®ion, &cmdRect); + break; + + case RDP_CODEC_ID_NONE: + format = gdi_get_pixel_format(cmd->bmp.bpp); + size = 1ull * cmd->bmp.width * cmd->bmp.height * FreeRDPGetBytesPerPixel(format); + if (size > cmd->bmp.bitmapDataLength) + { + WLog_ERR(TAG, "Short nocodec message: got %" PRIu32 " bytes, require %" PRIuz, + cmd->bmp.bitmapDataLength, size); + goto out; + } + + if (!freerdp_image_copy(gdi->primary_buffer, gdi->dstFormat, gdi->stride, cmdRect.left, + cmdRect.top, cmdRect.right - cmdRect.left, + cmdRect.bottom - cmdRect.top, cmd->bmp.bitmapData, format, 0, 0, + 0, &gdi->palette, FREERDP_FLIP_VERTICAL)) + { + WLog_ERR(TAG, "Failed to process nocodec message"); + goto out; + } + + region16_union_rect(®ion, ®ion, &cmdRect); + break; + + default: + WLog_ERR(TAG, "Unsupported codecID %" PRIu32 "", cmd->bmp.codecID); + break; + } + + if (!(rects = region16_rects(®ion, &nbRects))) + goto out; + + for (UINT32 i = 0; i < nbRects; i++) + { + UINT32 left = rects[i].left; + UINT32 top = rects[i].top; + UINT32 width = rects[i].right - rects[i].left; + UINT32 height = rects[i].bottom - rects[i].top; + + if (!gdi_InvalidateRegion(gdi->primary->hdc, left, top, width, height)) + { + WLog_ERR(TAG, "Failed to update invalid region"); + goto out; + } + } + + result = TRUE; +out: + region16_uninit(®ion); + return result; +} + +/** + * Register GDI callbacks with libfreerdp-core. + * @param update current instance + */ + +static void gdi_register_update_callbacks(rdpUpdate* update) +{ + rdpPrimaryUpdate* primary = NULL; + const rdpSettings* settings = NULL; + + WINPR_ASSERT(update); + WINPR_ASSERT(update->context); + + settings = update->context->settings; + WINPR_ASSERT(settings); + + primary = update->primary; + WINPR_ASSERT(primary); + + if (freerdp_settings_get_bool(settings, FreeRDP_DeactivateClientDecoding)) + return; + update->Palette = gdi_palette_update; + update->SetBounds = gdi_set_bounds; + primary->DstBlt = gdi_dstblt; + primary->PatBlt = gdi_patblt; + primary->ScrBlt = gdi_scrblt; + primary->OpaqueRect = gdi_opaque_rect; + primary->DrawNineGrid = NULL; + primary->MultiDstBlt = NULL; + primary->MultiPatBlt = NULL; + primary->MultiScrBlt = NULL; + primary->MultiOpaqueRect = gdi_multi_opaque_rect; + primary->MultiDrawNineGrid = NULL; + primary->LineTo = gdi_line_to; + primary->Polyline = gdi_polyline; + primary->MemBlt = gdi_memblt; + primary->Mem3Blt = gdi_mem3blt; + primary->SaveBitmap = NULL; + primary->GlyphIndex = NULL; + primary->FastIndex = NULL; + primary->FastGlyph = NULL; + primary->PolygonSC = gdi_polygon_sc; + primary->PolygonCB = gdi_polygon_cb; + primary->EllipseSC = gdi_ellipse_sc; + primary->EllipseCB = gdi_ellipse_cb; + update->SurfaceBits = gdi_surface_bits; + update->SurfaceFrameMarker = gdi_surface_frame_marker; + update->altsec->FrameMarker = gdi_frame_marker; +} + +static BOOL gdi_init_primary(rdpGdi* gdi, UINT32 stride, UINT32 format, BYTE* buffer, + void (*pfree)(void*), BOOL isLocked) +{ + WINPR_ASSERT(gdi); + WINPR_ASSERT(gdi->context); + WINPR_ASSERT(gdi->context->update); + if (!isLocked) + rdp_update_lock(gdi->context->update); + + gdi->primary = (gdiBitmap*)calloc(1, sizeof(gdiBitmap)); + + if (format > 0) + gdi->dstFormat = format; + + if (stride > 0) + gdi->stride = stride; + else + gdi->stride = gdi->width * FreeRDPGetBytesPerPixel(gdi->dstFormat); + + if (!gdi->primary) + goto fail_primary; + + if (!(gdi->primary->hdc = gdi_CreateCompatibleDC(gdi->hdc))) + goto fail_hdc; + + if (!buffer) + { + gdi->primary->bitmap = gdi_CreateCompatibleBitmap(gdi->hdc, gdi->width, gdi->height); + } + else + { + gdi->primary->bitmap = + gdi_CreateBitmapEx(gdi->width, gdi->height, gdi->dstFormat, gdi->stride, buffer, pfree); + } + + if (!gdi->primary->bitmap) + goto fail_bitmap; + + gdi->stride = gdi->primary->bitmap->scanline; + gdi_SelectObject(gdi->primary->hdc, (HGDIOBJECT)gdi->primary->bitmap); + gdi->primary->org_bitmap = NULL; + gdi->primary_buffer = gdi->primary->bitmap->data; + + if (!(gdi->primary->hdc->hwnd = (HGDI_WND)calloc(1, sizeof(GDI_WND)))) + goto fail_hwnd; + + if (!(gdi->primary->hdc->hwnd->invalid = gdi_CreateRectRgn(0, 0, 0, 0))) + goto fail_hwnd; + + gdi->primary->hdc->hwnd->invalid->null = TRUE; + gdi->primary->hdc->hwnd->count = 32; + + if (!(gdi->primary->hdc->hwnd->cinvalid = + (HGDI_RGN)calloc(gdi->primary->hdc->hwnd->count, sizeof(GDI_RGN)))) + goto fail_hwnd; + + gdi->primary->hdc->hwnd->ninvalid = 0; + + if (!gdi->drawing) + gdi->drawing = gdi->primary; + + rdp_update_unlock(gdi->context->update); + return TRUE; +fail_hwnd: + gdi_DeleteObject((HGDIOBJECT)gdi->primary->bitmap); +fail_bitmap: + gdi_DeleteDC(gdi->primary->hdc); +fail_hdc: + free(gdi->primary); + gdi->primary = NULL; +fail_primary: + rdp_update_unlock(gdi->context->update); + return FALSE; +} + +BOOL gdi_resize(rdpGdi* gdi, UINT32 width, UINT32 height) +{ + return gdi_resize_ex(gdi, width, height, 0, 0, NULL, NULL); +} + +BOOL gdi_resize_ex(rdpGdi* gdi, UINT32 width, UINT32 height, UINT32 stride, UINT32 format, + BYTE* buffer, void (*pfree)(void*)) +{ + if (!gdi || !gdi->primary) + return FALSE; + + if ((width > INT32_MAX) || (height > INT32_MAX)) + return FALSE; + + if ((gdi->width == (INT32)width) && (gdi->height == (INT32)height) && + (!buffer || (gdi->primary_buffer == buffer))) + return TRUE; + + WINPR_ASSERT(gdi->context); + WINPR_ASSERT(gdi->context->update); + rdp_update_lock(gdi->context->update); + + if (gdi->drawing == gdi->primary) + gdi->drawing = NULL; + + gdi->width = (INT32)width; + gdi->height = (INT32)height; + gdi_bitmap_free_ex(gdi->primary); + gdi->primary = NULL; + gdi->primary_buffer = NULL; + return gdi_init_primary(gdi, stride, format, buffer, pfree, TRUE); +} + +/** + * Initialize GDI + * + * @param instance A pointer to the instance to use + * @param format The color format for the local framebuffer + * @return \b TRUE for success, \b FALSE for failure + */ +BOOL gdi_init(freerdp* instance, UINT32 format) +{ + return gdi_init_ex(instance, format, 0, NULL, winpr_aligned_free); +} + +/** + * Initialize GDI + * + * @param instance A pointer to the instance to use + * @param format The color format for the local framebuffer + * @param stride The size of a framebuffer line in bytes + * @param buffer A pointer to a buffer to be used as framebuffer + * @param pfree A custom function pointer to use to free the framebuffer + * + * @return \b TRUE for success, \b FALSE for failure + */ +BOOL gdi_init_ex(freerdp* instance, UINT32 format, UINT32 stride, BYTE* buffer, + void (*pfree)(void*)) +{ + rdpContext* context = NULL; + UINT32 SrcFormat = 0; + rdpGdi* gdi = NULL; + + WINPR_ASSERT(instance); + + context = instance->context; + WINPR_ASSERT(context); + WINPR_ASSERT(context->settings); + + const UINT32 ColorDepth = freerdp_settings_get_uint32(context->settings, FreeRDP_ColorDepth); + SrcFormat = gdi_get_pixel_format(ColorDepth); + gdi = (rdpGdi*)calloc(1, sizeof(rdpGdi)); + + if (!gdi) + goto fail; + + context->gdi = gdi; + gdi->log = WLog_Get(TAG); + + if (!gdi->log) + goto fail; + + gdi->context = context; + gdi->width = freerdp_settings_get_uint32(context->settings, FreeRDP_DesktopWidth); + gdi->height = freerdp_settings_get_uint32(context->settings, FreeRDP_DesktopHeight); + gdi->dstFormat = format; + /* default internal buffer format */ + WLog_Print(gdi->log, WLOG_INFO, "Local framebuffer format %s", + FreeRDPGetColorFormatName(gdi->dstFormat)); + WLog_Print(gdi->log, WLOG_INFO, "Remote framebuffer format %s", + FreeRDPGetColorFormatName(SrcFormat)); + + if (!(gdi->hdc = gdi_GetDC())) + goto fail; + + gdi->hdc->format = gdi->dstFormat; + + if (!gdi_init_primary(gdi, stride, gdi->dstFormat, buffer, pfree, FALSE)) + goto fail; + + if (!(context->cache = cache_new(context))) + goto fail; + + gdi_register_update_callbacks(context->update); + brush_cache_register_callbacks(context->update); + glyph_cache_register_callbacks(context->update); + bitmap_cache_register_callbacks(context->update); + offscreen_cache_register_callbacks(context->update); + palette_cache_register_callbacks(context->update); + + if (!gdi_register_graphics(context->graphics)) + goto fail; + + return TRUE; +fail: + gdi_free(instance); + WLog_ERR(TAG, "failed to initialize gdi"); + return FALSE; +} + +void gdi_free(freerdp* instance) +{ + rdpGdi* gdi = NULL; + rdpContext* context = NULL; + + if (!instance || !instance->context) + return; + + gdi = instance->context->gdi; + + if (gdi) + { + gdi_bitmap_free_ex(gdi->primary); + gdi_DeleteDC(gdi->hdc); + free(gdi); + } + + context = instance->context; + cache_free(context->cache); + context->cache = NULL; + instance->context->gdi = (rdpGdi*)NULL; +} + +BOOL gdi_send_suppress_output(rdpGdi* gdi, BOOL suppress) +{ + RECTANGLE_16 rect; + rdpSettings* settings = NULL; + rdpUpdate* update = NULL; + + if (!gdi || !gdi->context->settings || !gdi->context->update) + return FALSE; + + if (gdi->suppressOutput == suppress) + return TRUE; + + gdi->suppressOutput = suppress; + settings = gdi->context->settings; + update = gdi->context->update; + rect.left = 0; + rect.top = 0; + rect.right = freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth); + rect.bottom = freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight); + return update->SuppressOutput(gdi->context, !suppress, &rect); +} diff --git a/libfreerdp/gdi/gdi.h b/libfreerdp/gdi/gdi.h new file mode 100644 index 0000000..fb653e7 --- /dev/null +++ b/libfreerdp/gdi/gdi.h @@ -0,0 +1,93 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * GDI Library + * + * Copyright 2010-2011 Marc-Andre Moreau <marcandre.moreau@gmail.com> + * + * 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. + */ + +#ifndef FREERDP_LIB_GDI_CORE_H +#define FREERDP_LIB_GDI_CORE_H + +#include "graphics.h" +#include "brush.h" + +#include <freerdp/api.h> + +FREERDP_LOCAL BOOL gdi_bitmap_update(rdpContext* context, const BITMAP_UPDATE* bitmapUpdate); + +FREERDP_LOCAL gdiBitmap* gdi_bitmap_new_ex(rdpGdi* gdi, int width, int height, int bpp, BYTE* data); +FREERDP_LOCAL void gdi_bitmap_free_ex(gdiBitmap* gdi_bmp); + +static INLINE BYTE* gdi_get_bitmap_pointer(HGDI_DC hdcBmp, INT32 x, INT32 y) +{ + BYTE* p; + HGDI_BITMAP hBmp = (HGDI_BITMAP)hdcBmp->selectedObject; + + if ((x >= 0) && (y >= 0) && (x < hBmp->width) && (y < hBmp->height)) + { + p = hBmp->data + (y * hBmp->scanline) + (x * FreeRDPGetBytesPerPixel(hdcBmp->format)); + return p; + } + else + { + WLog_ERR(FREERDP_TAG("gdi"), + "gdi_get_bitmap_pointer: requesting invalid pointer: (%" PRIu32 ",%" PRIu32 + ") in %" PRIu32 "x%" PRIu32 "", + x, y, hBmp->width, hBmp->height); + return 0; + } +} + +/** + * Get current color in brush bitmap according to dest coordinates. msdn{dd183396} + * + * @param x dest x-coordinate + * @param y dest y-coordinate + * @return color pointer + */ +static INLINE BYTE* gdi_get_brush_pointer(HGDI_DC hdcBrush, UINT32 x, UINT32 y) +{ + BYTE* p; + UINT32 brushStyle = gdi_GetBrushStyle(hdcBrush); + + switch (brushStyle) + { + case GDI_BS_PATTERN: + case GDI_BS_HATCHED: + { + HGDI_BITMAP hBmpBrush = hdcBrush->brush->pattern; + /* According to msdn{dd183396}, the system always positions a brush bitmap + * at the brush origin and copy across the client area. + * Calculate the offset of the mapped pixel in the brush bitmap according to + * brush origin and dest coordinates */ + x = (x + hBmpBrush->width - (hdcBrush->brush->nXOrg % hBmpBrush->width)) % + hBmpBrush->width; + y = (y + hBmpBrush->height - (hdcBrush->brush->nYOrg % hBmpBrush->height)) % + hBmpBrush->height; + p = hBmpBrush->data + (y * hBmpBrush->scanline) + + (x * FreeRDPGetBytesPerPixel(hBmpBrush->format)); + return p; + } + break; + + default: + break; + } + + p = (BYTE*)&(hdcBrush->textColor); + return p; +} + +#endif /* FREERDP_LIB_GDI_CORE_H */ diff --git a/libfreerdp/gdi/gfx.c b/libfreerdp/gdi/gfx.c new file mode 100644 index 0000000..9d82ea4 --- /dev/null +++ b/libfreerdp/gdi/gfx.c @@ -0,0 +1,1929 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * GDI Graphics Pipeline + * + * Copyright 2014 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 "../core/update.h" + +#include <freerdp/api.h> +#include <freerdp/log.h> +#include <freerdp/gdi/gfx.h> +#include <freerdp/gdi/region.h> +#include <freerdp/utils/gfx.h> +#include <math.h> + +#define TAG FREERDP_TAG("gdi") + +static BOOL is_rect_valid(const RECTANGLE_16* rect, size_t width, size_t height) +{ + if (!rect) + return FALSE; + if ((rect->left > rect->right) || (rect->right > width)) + return FALSE; + if ((rect->top > rect->bottom) || (rect->bottom > height)) + return FALSE; + return TRUE; +} + +static BOOL is_within_surface(const gdiGfxSurface* surface, const RDPGFX_SURFACE_COMMAND* cmd) +{ + RECTANGLE_16 rect; + if (!surface || !cmd) + return FALSE; + rect.left = (UINT16)MIN(UINT16_MAX, cmd->left); + rect.top = (UINT16)MIN(UINT16_MAX, cmd->top); + rect.right = (UINT16)MIN(UINT16_MAX, cmd->right); + rect.bottom = (UINT16)MIN(UINT16_MAX, cmd->bottom); + if (!is_rect_valid(&rect, surface->width, surface->height)) + { + WLog_ERR(TAG, + "Command rect %" PRIu32 "x%" PRIu32 "-%" PRIu32 "x%" PRIu32 + " not within bounds of %" PRIu32 "x%" PRIu32, + rect.left, rect.top, cmd->width, cmd->height, surface->width, surface->height); + return FALSE; + } + + return TRUE; +} + +static DWORD gfx_align_scanline(DWORD widthInBytes, DWORD alignment) +{ + const UINT32 align = alignment; + const UINT32 pad = align - (widthInBytes % alignment); + UINT32 scanline = widthInBytes; + + if (align != pad) + scanline += pad; + + return scanline; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT gdi_ResetGraphics(RdpgfxClientContext* context, + const RDPGFX_RESET_GRAPHICS_PDU* resetGraphics) +{ + UINT rc = ERROR_INTERNAL_ERROR; + UINT16 count = 0; + UINT32 DesktopWidth = 0; + UINT32 DesktopHeight = 0; + UINT16* pSurfaceIds = NULL; + rdpGdi* gdi = NULL; + rdpUpdate* update = NULL; + rdpSettings* settings = NULL; + + WINPR_ASSERT(context); + WINPR_ASSERT(resetGraphics); + + gdi = (rdpGdi*)context->custom; + WINPR_ASSERT(gdi); + + update = gdi->context->update; + WINPR_ASSERT(update); + + settings = gdi->context->settings; + WINPR_ASSERT(settings); + EnterCriticalSection(&context->mux); + DesktopWidth = resetGraphics->width; + DesktopHeight = resetGraphics->height; + + if (!freerdp_settings_set_uint32(settings, FreeRDP_DesktopWidth, DesktopWidth)) + goto fail; + if (!freerdp_settings_set_uint32(settings, FreeRDP_DesktopHeight, DesktopHeight)) + goto fail; + + if (update) + { + WINPR_ASSERT(update->DesktopResize); + update->DesktopResize(gdi->context); + } + + WINPR_ASSERT(context->GetSurfaceIds); + context->GetSurfaceIds(context, &pSurfaceIds, &count); + + for (UINT32 index = 0; index < count; index++) + { + WINPR_ASSERT(context->GetSurfaceData); + gdiGfxSurface* surface = + (gdiGfxSurface*)context->GetSurfaceData(context, pSurfaceIds[index]); + + if (!surface) + continue; + + memset(surface->data, 0xFF, (size_t)surface->scanline * surface->height); + region16_clear(&surface->invalidRegion); + } + + free(pSurfaceIds); + + if (!freerdp_settings_get_bool(gdi->context->settings, FreeRDP_DeactivateClientDecoding)) + { + const UINT32 width = (UINT32)MAX(0, gdi->width); + const UINT32 height = (UINT32)MAX(0, gdi->height); + + if (!freerdp_client_codecs_reset( + context->codecs, freerdp_settings_get_codecs_flags(settings), width, height)) + { + goto fail; + } + if (!freerdp_client_codecs_reset( + gdi->context->codecs, freerdp_settings_get_codecs_flags(settings), width, height)) + { + goto fail; + } + } + + rc = CHANNEL_RC_OK; +fail: + LeaveCriticalSection(&context->mux); + return rc; +} + +static UINT gdi_OutputUpdate(rdpGdi* gdi, gdiGfxSurface* surface) +{ + UINT rc = ERROR_INTERNAL_ERROR; + UINT32 surfaceX = 0; + UINT32 surfaceY = 0; + RECTANGLE_16 surfaceRect; + const RECTANGLE_16* rects = NULL; + UINT32 nbRects = 0; + double sx = NAN; + double sy = NAN; + rdpUpdate* update = NULL; + + WINPR_ASSERT(gdi); + WINPR_ASSERT(gdi->context); + WINPR_ASSERT(surface); + + update = gdi->context->update; + WINPR_ASSERT(update); + + if (gdi->suppressOutput) + return CHANNEL_RC_OK; + + surfaceX = surface->outputOriginX; + surfaceY = surface->outputOriginY; + surfaceRect.left = 0; + surfaceRect.top = 0; + surfaceRect.right = (UINT16)MIN(UINT16_MAX, surface->mappedWidth); + surfaceRect.bottom = (UINT16)MIN(UINT16_MAX, surface->mappedHeight); + region16_intersect_rect(&(surface->invalidRegion), &(surface->invalidRegion), &surfaceRect); + sx = surface->outputTargetWidth / (double)surface->mappedWidth; + sy = surface->outputTargetHeight / (double)surface->mappedHeight; + + if (!(rects = region16_rects(&surface->invalidRegion, &nbRects)) || !nbRects) + return CHANNEL_RC_OK; + + if (!update_begin_paint(update)) + goto fail; + + for (UINT32 i = 0; i < nbRects; i++) + { + const UINT32 nXSrc = rects[i].left; + const UINT32 nYSrc = rects[i].top; + const UINT32 nXDst = (UINT32)MIN(surfaceX + nXSrc * sx, gdi->width - 1); + const UINT32 nYDst = (UINT32)MIN(surfaceY + nYSrc * sy, gdi->height - 1); + const UINT32 swidth = rects[i].right - rects[i].left; + const UINT32 sheight = rects[i].bottom - rects[i].top; + const UINT32 dwidth = MIN((UINT32)(swidth * sx), (UINT32)gdi->width - nXDst); + const UINT32 dheight = MIN((UINT32)(sheight * sy), (UINT32)gdi->height - nYDst); + + if (!freerdp_image_scale(gdi->primary_buffer, gdi->dstFormat, gdi->stride, nXDst, nYDst, + dwidth, dheight, surface->data, surface->format, surface->scanline, + nXSrc, nYSrc, swidth, sheight)) + { + rc = CHANNEL_RC_NULL_DATA; + goto fail; + } + + gdi_InvalidateRegion(gdi->primary->hdc, (INT32)nXDst, (INT32)nYDst, (INT32)dwidth, + (INT32)dheight); + } + + rc = CHANNEL_RC_OK; +fail: + + if (!update_end_paint(update)) + rc = ERROR_INTERNAL_ERROR; + + region16_clear(&(surface->invalidRegion)); + return rc; +} + +static UINT gdi_WindowUpdate(RdpgfxClientContext* context, gdiGfxSurface* surface) +{ + WINPR_ASSERT(context); + WINPR_ASSERT(surface); + return IFCALLRESULT(CHANNEL_RC_OK, context->UpdateWindowFromSurface, context, surface); +} + +static UINT gdi_UpdateSurfaces(RdpgfxClientContext* context) +{ + UINT16 count = 0; + UINT status = ERROR_INTERNAL_ERROR; + UINT16* pSurfaceIds = NULL; + rdpGdi* gdi = NULL; + + WINPR_ASSERT(context); + + gdi = (rdpGdi*)context->custom; + WINPR_ASSERT(gdi); + + EnterCriticalSection(&context->mux); + + WINPR_ASSERT(context->GetSurfaceIds); + context->GetSurfaceIds(context, &pSurfaceIds, &count); + status = CHANNEL_RC_OK; + + for (UINT32 index = 0; index < count; index++) + { + WINPR_ASSERT(context->GetSurfaceData); + gdiGfxSurface* surface = + (gdiGfxSurface*)context->GetSurfaceData(context, pSurfaceIds[index]); + + if (!surface) + continue; + + /* Already handled in UpdateSurfaceArea callbacks */ + if (context->UpdateSurfaceArea) + { + if (surface->handleInUpdateSurfaceArea) + continue; + } + + if (surface->outputMapped) + status = gdi_OutputUpdate(gdi, surface); + else if (surface->windowMapped) + status = gdi_WindowUpdate(context, surface); + + if (status != CHANNEL_RC_OK) + break; + } + + free(pSurfaceIds); + LeaveCriticalSection(&context->mux); + return status; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT gdi_StartFrame(RdpgfxClientContext* context, const RDPGFX_START_FRAME_PDU* startFrame) +{ + rdpGdi* gdi = NULL; + + WINPR_ASSERT(context); + WINPR_ASSERT(startFrame); + + gdi = (rdpGdi*)context->custom; + WINPR_ASSERT(gdi); + gdi->inGfxFrame = TRUE; + gdi->frameId = startFrame->frameId; + return CHANNEL_RC_OK; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT gdi_EndFrame(RdpgfxClientContext* context, const RDPGFX_END_FRAME_PDU* endFrame) +{ + UINT status = CHANNEL_RC_OK; + rdpGdi* gdi = NULL; + + WINPR_ASSERT(context); + WINPR_ASSERT(endFrame); + + gdi = (rdpGdi*)context->custom; + WINPR_ASSERT(gdi); + IFCALLRET(context->UpdateSurfaces, status, context); + gdi->inGfxFrame = FALSE; + return status; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT gdi_SurfaceCommand_Uncompressed(rdpGdi* gdi, RdpgfxClientContext* context, + const RDPGFX_SURFACE_COMMAND* cmd) +{ + UINT status = CHANNEL_RC_OK; + gdiGfxSurface* surface = NULL; + RECTANGLE_16 invalidRect; + DWORD bpp = 0; + size_t size = 0; + WINPR_ASSERT(gdi); + WINPR_ASSERT(context); + WINPR_ASSERT(cmd); + + WINPR_ASSERT(context->GetSurfaceData); + surface = + (gdiGfxSurface*)context->GetSurfaceData(context, (UINT16)MIN(UINT16_MAX, cmd->surfaceId)); + + if (!surface) + { + WLog_ERR(TAG, "unable to retrieve surfaceData for surfaceId=%" PRIu32 "", cmd->surfaceId); + return ERROR_NOT_FOUND; + } + + if (!is_within_surface(surface, cmd)) + return ERROR_INVALID_DATA; + + bpp = FreeRDPGetBytesPerPixel(cmd->format); + size = 1ull * bpp * cmd->width * cmd->height; + if (cmd->length < size) + { + WLog_ERR(TAG, "Not enough data, got %" PRIu32 ", expected %" PRIuz, cmd->length, size); + return ERROR_INVALID_DATA; + } + + if (!freerdp_image_copy(surface->data, surface->format, surface->scanline, cmd->left, cmd->top, + cmd->width, cmd->height, cmd->data, cmd->format, 0, 0, 0, NULL, + FREERDP_FLIP_NONE)) + return ERROR_INTERNAL_ERROR; + + invalidRect.left = (UINT16)MIN(UINT16_MAX, cmd->left); + invalidRect.top = (UINT16)MIN(UINT16_MAX, cmd->top); + invalidRect.right = (UINT16)MIN(UINT16_MAX, cmd->right); + invalidRect.bottom = (UINT16)MIN(UINT16_MAX, cmd->bottom); + region16_union_rect(&(surface->invalidRegion), &(surface->invalidRegion), &invalidRect); + status = IFCALLRESULT(CHANNEL_RC_OK, context->UpdateSurfaceArea, context, surface->surfaceId, 1, + &invalidRect); + + if (status != CHANNEL_RC_OK) + goto fail; + + if (!gdi->inGfxFrame) + { + status = CHANNEL_RC_NOT_INITIALIZED; + IFCALLRET(context->UpdateSurfaces, status, context); + } + +fail: + return status; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT gdi_SurfaceCommand_RemoteFX(rdpGdi* gdi, RdpgfxClientContext* context, + const RDPGFX_SURFACE_COMMAND* cmd) +{ + UINT status = ERROR_INTERNAL_ERROR; + gdiGfxSurface* surface = NULL; + REGION16 invalidRegion; + const RECTANGLE_16* rects = NULL; + UINT32 nrRects = 0; + WINPR_ASSERT(gdi); + WINPR_ASSERT(context); + WINPR_ASSERT(cmd); + + WINPR_ASSERT(context->GetSurfaceData); + surface = + (gdiGfxSurface*)context->GetSurfaceData(context, (UINT16)MIN(UINT16_MAX, cmd->surfaceId)); + + if (!surface) + { + WLog_ERR(TAG, "unable to retrieve surfaceData for surfaceId=%" PRIu32 "", cmd->surfaceId); + return ERROR_NOT_FOUND; + } + + WINPR_ASSERT(surface->codecs); + rfx_context_set_pixel_format(surface->codecs->rfx, cmd->format); + region16_init(&invalidRegion); + + if (!rfx_process_message(surface->codecs->rfx, cmd->data, cmd->length, cmd->left, cmd->top, + surface->data, surface->format, surface->scanline, surface->height, + &invalidRegion)) + { + WLog_ERR(TAG, "Failed to process RemoteFX message"); + goto fail; + } + + rects = region16_rects(&invalidRegion, &nrRects); + status = IFCALLRESULT(CHANNEL_RC_OK, context->UpdateSurfaceArea, context, surface->surfaceId, + nrRects, rects); + + if (status != CHANNEL_RC_OK) + goto fail; + + for (UINT32 x = 0; x < nrRects; x++) + region16_union_rect(&surface->invalidRegion, &surface->invalidRegion, &rects[x]); + + if (!gdi->inGfxFrame) + { + status = CHANNEL_RC_NOT_INITIALIZED; + IFCALLRET(context->UpdateSurfaces, status, context); + } + +fail: + region16_uninit(&invalidRegion); + return status; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT gdi_SurfaceCommand_ClearCodec(rdpGdi* gdi, RdpgfxClientContext* context, + const RDPGFX_SURFACE_COMMAND* cmd) +{ + INT32 rc = 0; + UINT status = CHANNEL_RC_OK; + gdiGfxSurface* surface = NULL; + RECTANGLE_16 invalidRect; + WINPR_ASSERT(gdi); + WINPR_ASSERT(context); + WINPR_ASSERT(cmd); + + WINPR_ASSERT(context->GetSurfaceData); + surface = + (gdiGfxSurface*)context->GetSurfaceData(context, (UINT16)MIN(UINT16_MAX, cmd->surfaceId)); + + if (!surface) + { + WLog_ERR(TAG, "unable to retrieve surfaceData for surfaceId=%" PRIu32 "", cmd->surfaceId); + return ERROR_NOT_FOUND; + } + + WINPR_ASSERT(surface->codecs); + rc = clear_decompress(surface->codecs->clear, cmd->data, cmd->length, cmd->width, cmd->height, + surface->data, surface->format, surface->scanline, cmd->left, cmd->top, + surface->width, surface->height, &gdi->palette); + + if (rc < 0) + { + WLog_ERR(TAG, "clear_decompress failure: %" PRId32 "", rc); + return ERROR_INTERNAL_ERROR; + } + + invalidRect.left = (UINT16)MIN(UINT16_MAX, cmd->left); + invalidRect.top = (UINT16)MIN(UINT16_MAX, cmd->top); + invalidRect.right = (UINT16)MIN(UINT16_MAX, cmd->right); + invalidRect.bottom = (UINT16)MIN(UINT16_MAX, cmd->bottom); + region16_union_rect(&(surface->invalidRegion), &(surface->invalidRegion), &invalidRect); + status = IFCALLRESULT(CHANNEL_RC_OK, context->UpdateSurfaceArea, context, surface->surfaceId, 1, + &invalidRect); + + if (status != CHANNEL_RC_OK) + goto fail; + + if (!gdi->inGfxFrame) + { + status = CHANNEL_RC_NOT_INITIALIZED; + IFCALLRET(context->UpdateSurfaces, status, context); + } + +fail: + return status; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT gdi_SurfaceCommand_Planar(rdpGdi* gdi, RdpgfxClientContext* context, + const RDPGFX_SURFACE_COMMAND* cmd) +{ + UINT status = CHANNEL_RC_OK; + BYTE* DstData = NULL; + gdiGfxSurface* surface = NULL; + RECTANGLE_16 invalidRect; + WINPR_ASSERT(gdi); + WINPR_ASSERT(context); + WINPR_ASSERT(cmd); + + WINPR_ASSERT(context->GetSurfaceData); + surface = + (gdiGfxSurface*)context->GetSurfaceData(context, (UINT16)MIN(UINT16_MAX, cmd->surfaceId)); + + if (!surface) + { + WLog_ERR(TAG, "unable to retrieve surfaceData for surfaceId=%" PRIu32 "", cmd->surfaceId); + return ERROR_NOT_FOUND; + } + + DstData = surface->data; + + if (!is_within_surface(surface, cmd)) + return ERROR_INVALID_DATA; + + if (!planar_decompress(surface->codecs->planar, cmd->data, cmd->length, cmd->width, cmd->height, + DstData, surface->format, surface->scanline, cmd->left, cmd->top, + cmd->width, cmd->height, FALSE)) + return ERROR_INTERNAL_ERROR; + + invalidRect.left = (UINT16)MIN(UINT16_MAX, cmd->left); + invalidRect.top = (UINT16)MIN(UINT16_MAX, cmd->top); + invalidRect.right = (UINT16)MIN(UINT16_MAX, cmd->right); + invalidRect.bottom = (UINT16)MIN(UINT16_MAX, cmd->bottom); + region16_union_rect(&(surface->invalidRegion), &(surface->invalidRegion), &invalidRect); + status = IFCALLRESULT(CHANNEL_RC_OK, context->UpdateSurfaceArea, context, surface->surfaceId, 1, + &invalidRect); + + if (status != CHANNEL_RC_OK) + goto fail; + + if (!gdi->inGfxFrame) + { + status = CHANNEL_RC_NOT_INITIALIZED; + IFCALLRET(context->UpdateSurfaces, status, context); + } + +fail: + return status; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT gdi_SurfaceCommand_AVC420(rdpGdi* gdi, RdpgfxClientContext* context, + const RDPGFX_SURFACE_COMMAND* cmd) +{ +#ifdef WITH_GFX_H264 + INT32 rc = 0; + UINT status = CHANNEL_RC_OK; + gdiGfxSurface* surface = NULL; + RDPGFX_H264_METABLOCK* meta = NULL; + RDPGFX_AVC420_BITMAP_STREAM* bs = NULL; + WINPR_ASSERT(gdi); + WINPR_ASSERT(context); + WINPR_ASSERT(cmd); + + WINPR_ASSERT(context->GetSurfaceData); + surface = + (gdiGfxSurface*)context->GetSurfaceData(context, (UINT16)MIN(UINT16_MAX, cmd->surfaceId)); + + if (!surface) + { + WLog_ERR(TAG, "unable to retrieve surfaceData for surfaceId=%" PRIu32 "", cmd->surfaceId); + return ERROR_NOT_FOUND; + } + + if (!surface->h264) + { + surface->h264 = h264_context_new(FALSE); + + if (!surface->h264) + { + WLog_ERR(TAG, "unable to create h264 context"); + return ERROR_NOT_ENOUGH_MEMORY; + } + + if (!h264_context_reset(surface->h264, surface->width, surface->height)) + return ERROR_INTERNAL_ERROR; + } + + if (!surface->h264) + return ERROR_NOT_SUPPORTED; + + bs = (RDPGFX_AVC420_BITMAP_STREAM*)cmd->extra; + + if (!bs) + return ERROR_INTERNAL_ERROR; + + meta = &(bs->meta); + rc = avc420_decompress(surface->h264, bs->data, bs->length, surface->data, surface->format, + surface->scanline, surface->width, surface->height, meta->regionRects, + meta->numRegionRects); + + if (rc < 0) + { + WLog_WARN(TAG, "avc420_decompress failure: %" PRId32 ", ignoring update.", rc); + return CHANNEL_RC_OK; + } + + for (UINT32 i = 0; i < meta->numRegionRects; i++) + { + region16_union_rect(&(surface->invalidRegion), &(surface->invalidRegion), + (RECTANGLE_16*)&(meta->regionRects[i])); + } + + status = IFCALLRESULT(CHANNEL_RC_OK, context->UpdateSurfaceArea, context, surface->surfaceId, + meta->numRegionRects, meta->regionRects); + + if (status != CHANNEL_RC_OK) + goto fail; + + if (!gdi->inGfxFrame) + { + status = CHANNEL_RC_NOT_INITIALIZED; + IFCALLRET(context->UpdateSurfaces, status, context); + } + +fail: + return status; +#else + return ERROR_NOT_SUPPORTED; +#endif +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT gdi_SurfaceCommand_AVC444(rdpGdi* gdi, RdpgfxClientContext* context, + const RDPGFX_SURFACE_COMMAND* cmd) +{ +#ifdef WITH_GFX_H264 + INT32 rc = 0; + UINT status = CHANNEL_RC_OK; + gdiGfxSurface* surface = NULL; + RDPGFX_AVC444_BITMAP_STREAM* bs = NULL; + RDPGFX_AVC420_BITMAP_STREAM* avc1 = NULL; + RDPGFX_H264_METABLOCK* meta1 = NULL; + RDPGFX_AVC420_BITMAP_STREAM* avc2 = NULL; + RDPGFX_H264_METABLOCK* meta2 = NULL; + WINPR_ASSERT(gdi); + WINPR_ASSERT(context); + WINPR_ASSERT(cmd); + + WINPR_ASSERT(context->GetSurfaceData); + surface = + (gdiGfxSurface*)context->GetSurfaceData(context, (UINT16)MIN(UINT16_MAX, cmd->surfaceId)); + + if (!surface) + { + WLog_ERR(TAG, "unable to retrieve surfaceData for surfaceId=%" PRIu32 "", cmd->surfaceId); + return ERROR_NOT_FOUND; + } + + if (!surface->h264) + { + surface->h264 = h264_context_new(FALSE); + + if (!surface->h264) + { + WLog_ERR(TAG, "unable to create h264 context"); + return ERROR_NOT_ENOUGH_MEMORY; + } + + if (!h264_context_reset(surface->h264, surface->width, surface->height)) + return ERROR_INTERNAL_ERROR; + } + + if (!surface->h264) + return ERROR_NOT_SUPPORTED; + + bs = (RDPGFX_AVC444_BITMAP_STREAM*)cmd->extra; + + if (!bs) + return ERROR_INTERNAL_ERROR; + + avc1 = &bs->bitstream[0]; + avc2 = &bs->bitstream[1]; + meta1 = &avc1->meta; + meta2 = &avc2->meta; + rc = avc444_decompress(surface->h264, bs->LC, meta1->regionRects, meta1->numRegionRects, + avc1->data, avc1->length, meta2->regionRects, meta2->numRegionRects, + avc2->data, avc2->length, surface->data, surface->format, + surface->scanline, surface->width, surface->height, cmd->codecId); + + if (rc < 0) + { + WLog_WARN(TAG, "avc444_decompress failure: %" PRIu32 ", ignoring update.", status); + return CHANNEL_RC_OK; + } + + for (UINT32 i = 0; i < meta1->numRegionRects; i++) + { + region16_union_rect(&(surface->invalidRegion), &(surface->invalidRegion), + &(meta1->regionRects[i])); + } + + status = IFCALLRESULT(CHANNEL_RC_OK, context->UpdateSurfaceArea, context, surface->surfaceId, + meta1->numRegionRects, meta1->regionRects); + + if (status != CHANNEL_RC_OK) + goto fail; + + for (UINT32 i = 0; i < meta2->numRegionRects; i++) + { + region16_union_rect(&(surface->invalidRegion), &(surface->invalidRegion), + &(meta2->regionRects[i])); + } + + status = IFCALLRESULT(CHANNEL_RC_OK, context->UpdateSurfaceArea, context, surface->surfaceId, + meta2->numRegionRects, meta2->regionRects); + + if (status != CHANNEL_RC_OK) + goto fail; + + if (!gdi->inGfxFrame) + { + status = CHANNEL_RC_NOT_INITIALIZED; + IFCALLRET(context->UpdateSurfaces, status, context); + } + +fail: + return status; +#else + return ERROR_NOT_SUPPORTED; +#endif +} + +static BOOL gdi_apply_alpha(BYTE* data, UINT32 format, UINT32 stride, RECTANGLE_16* rect, + UINT32 startOffsetX, UINT32 count, BYTE a) +{ + UINT32 written = 0; + BOOL first = TRUE; + const UINT32 bpp = FreeRDPGetBytesPerPixel(format); + WINPR_ASSERT(rect); + + for (UINT32 y = rect->top; y < rect->bottom; y++) + { + BYTE* line = &data[stride * y]; + + for (UINT32 x = first ? rect->left + startOffsetX : rect->left; x < rect->right; x++) + { + BYTE r = 0; + BYTE g = 0; + BYTE b = 0; + + if (written == count) + return TRUE; + + BYTE* src = &line[x * bpp]; + UINT32 color = FreeRDPReadColor(src, format); + FreeRDPSplitColor(color, format, &r, &g, &b, NULL, NULL); + color = FreeRDPGetColor(format, r, g, b, a); + FreeRDPWriteColor(src, format, color); + written++; + } + + first = FALSE; + } + + return TRUE; +} +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT gdi_SurfaceCommand_Alpha(rdpGdi* gdi, RdpgfxClientContext* context, + const RDPGFX_SURFACE_COMMAND* cmd) +{ + UINT status = CHANNEL_RC_OK; + UINT16 alphaSig = 0; + UINT16 compressed = 0; + gdiGfxSurface* surface = NULL; + RECTANGLE_16 invalidRect; + wStream buffer; + wStream* s = NULL; + WINPR_ASSERT(gdi); + WINPR_ASSERT(context); + WINPR_ASSERT(cmd); + + s = Stream_StaticConstInit(&buffer, cmd->data, cmd->length); + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 4)) + return ERROR_INVALID_DATA; + + WINPR_ASSERT(context->GetSurfaceData); + surface = + (gdiGfxSurface*)context->GetSurfaceData(context, (UINT16)MIN(UINT16_MAX, cmd->surfaceId)); + + if (!surface) + { + WLog_ERR(TAG, "unable to retrieve surfaceData for surfaceId=%" PRIu32 "", cmd->surfaceId); + return ERROR_NOT_FOUND; + } + + if (!is_within_surface(surface, cmd)) + return ERROR_INVALID_DATA; + + Stream_Read_UINT16(s, alphaSig); + Stream_Read_UINT16(s, compressed); + + if (alphaSig != 0x414C) + return ERROR_INVALID_DATA; + + if (compressed == 0) + { + if (!Stream_CheckAndLogRequiredLengthOfSize(TAG, s, cmd->height, cmd->width)) + return ERROR_INVALID_DATA; + + for (UINT32 y = cmd->top; y < cmd->top + cmd->height; y++) + { + BYTE* line = &surface->data[surface->scanline * y]; + + for (UINT32 x = cmd->left; x < cmd->left + cmd->width; x++) + { + UINT32 color = 0; + BYTE r = 0; + BYTE g = 0; + BYTE b = 0; + BYTE a = 0; + BYTE* src = &line[x * FreeRDPGetBytesPerPixel(surface->format)]; + Stream_Read_UINT8(s, a); + color = FreeRDPReadColor(src, surface->format); + FreeRDPSplitColor(color, surface->format, &r, &g, &b, NULL, NULL); + color = FreeRDPGetColor(surface->format, r, g, b, a); + FreeRDPWriteColor(src, surface->format, color); + } + } + } + else + { + UINT32 startOffsetX = 0; + RECTANGLE_16 rect = { 0 }; + rect.left = (UINT16)MIN(UINT16_MAX, cmd->left); + rect.top = (UINT16)MIN(UINT16_MAX, cmd->top); + rect.right = (UINT16)MIN(UINT16_MAX, cmd->left + cmd->width); + rect.bottom = (UINT16)MIN(UINT16_MAX, cmd->top + cmd->height); + + while (rect.top < rect.bottom) + { + UINT32 count = 0; + BYTE a = 0; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 2)) + return ERROR_INVALID_DATA; + + Stream_Read_UINT8(s, a); + Stream_Read_UINT8(s, count); + + if (count >= 0xFF) + { + if (!Stream_CheckAndLogRequiredLength(TAG, s, 2)) + return ERROR_INVALID_DATA; + + Stream_Read_UINT16(s, count); + + if (count >= 0xFFFF) + { + if (!Stream_CheckAndLogRequiredLength(TAG, s, 4)) + return ERROR_INVALID_DATA; + + Stream_Read_UINT32(s, count); + } + } + + if (!gdi_apply_alpha(surface->data, surface->format, surface->scanline, &rect, + startOffsetX, count, a)) + return ERROR_INTERNAL_ERROR; + + startOffsetX += count; + + while (startOffsetX >= cmd->width) + { + startOffsetX -= cmd->width; + rect.top++; + } + } + } + + invalidRect.left = (UINT16)MIN(UINT16_MAX, cmd->left); + invalidRect.top = (UINT16)MIN(UINT16_MAX, cmd->top); + invalidRect.right = (UINT16)MIN(UINT16_MAX, cmd->right); + invalidRect.bottom = (UINT16)MIN(UINT16_MAX, cmd->bottom); + region16_union_rect(&(surface->invalidRegion), &(surface->invalidRegion), &invalidRect); + status = IFCALLRESULT(CHANNEL_RC_OK, context->UpdateSurfaceArea, context, surface->surfaceId, 1, + &invalidRect); + + if (status != CHANNEL_RC_OK) + goto fail; + + if (!gdi->inGfxFrame) + { + status = CHANNEL_RC_NOT_INITIALIZED; + IFCALLRET(context->UpdateSurfaces, status, context); + } + +fail: + return status; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT gdi_SurfaceCommand_Progressive(rdpGdi* gdi, RdpgfxClientContext* context, + const RDPGFX_SURFACE_COMMAND* cmd) +{ + INT32 rc = 0; + UINT status = CHANNEL_RC_OK; + gdiGfxSurface* surface = NULL; + REGION16 invalidRegion; + const RECTANGLE_16* rects = NULL; + UINT32 nrRects = 0; + /** + * Note: Since this comes via a Wire-To-Surface-2 PDU the + * cmd's top/left/right/bottom/width/height members are always zero! + * The update region is determined during decompression. + */ + WINPR_ASSERT(gdi); + WINPR_ASSERT(context); + WINPR_ASSERT(cmd); + const UINT16 surfaceId = (UINT16)MIN(UINT16_MAX, cmd->surfaceId); + + WINPR_ASSERT(context->GetSurfaceData); + surface = (gdiGfxSurface*)context->GetSurfaceData(context, surfaceId); + + if (!surface) + { + WLog_ERR(TAG, "unable to retrieve surfaceData for surfaceId=%" PRIu32 "", cmd->surfaceId); + return ERROR_NOT_FOUND; + } + + if (!is_within_surface(surface, cmd)) + return ERROR_INVALID_DATA; + + WINPR_ASSERT(surface->codecs); + rc = progressive_create_surface_context(surface->codecs->progressive, surfaceId, surface->width, + surface->height); + + if (rc < 0) + { + WLog_ERR(TAG, "progressive_create_surface_context failure: %" PRId32 "", rc); + return ERROR_INTERNAL_ERROR; + } + + region16_init(&invalidRegion); + + rc = progressive_decompress(surface->codecs->progressive, cmd->data, cmd->length, surface->data, + surface->format, surface->scanline, cmd->left, cmd->top, + &invalidRegion, surfaceId, gdi->frameId); + + if (rc < 0) + { + WLog_ERR(TAG, "progressive_decompress failure: %" PRId32 "", rc); + region16_uninit(&invalidRegion); + return ERROR_INTERNAL_ERROR; + } + + rects = region16_rects(&invalidRegion, &nrRects); + status = IFCALLRESULT(CHANNEL_RC_OK, context->UpdateSurfaceArea, context, surface->surfaceId, + nrRects, rects); + + if (status != CHANNEL_RC_OK) + goto fail; + + for (UINT32 x = 0; x < nrRects; x++) + region16_union_rect(&surface->invalidRegion, &surface->invalidRegion, &rects[x]); + + region16_uninit(&invalidRegion); + + if (!gdi->inGfxFrame) + { + status = CHANNEL_RC_NOT_INITIALIZED; + IFCALLRET(context->UpdateSurfaces, status, context); + } + +fail: + return status; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT gdi_SurfaceCommand(RdpgfxClientContext* context, const RDPGFX_SURFACE_COMMAND* cmd) +{ + UINT status = CHANNEL_RC_OK; + rdpGdi* gdi = NULL; + + if (!context || !cmd) + return ERROR_INVALID_PARAMETER; + + gdi = (rdpGdi*)context->custom; + + EnterCriticalSection(&context->mux); + WLog_Print(gdi->log, WLOG_TRACE, + "surfaceId=%" PRIu32 ", codec=%s [%" PRIu32 "], contextId=%" PRIu32 ", format=%s, " + "left=%" PRIu32 ", top=%" PRIu32 ", right=%" PRIu32 ", bottom=%" PRIu32 + ", width=%" PRIu32 ", height=%" PRIu32 " " + "length=%" PRIu32 ", data=%p, extra=%p", + cmd->surfaceId, rdpgfx_get_codec_id_string(cmd->codecId), cmd->codecId, + cmd->contextId, FreeRDPGetColorFormatName(cmd->format), cmd->left, cmd->top, + cmd->right, cmd->bottom, cmd->width, cmd->height, cmd->length, (void*)cmd->data, + (void*)cmd->extra); + + switch (cmd->codecId) + { + case RDPGFX_CODECID_UNCOMPRESSED: + status = gdi_SurfaceCommand_Uncompressed(gdi, context, cmd); + break; + + case RDPGFX_CODECID_CAVIDEO: + status = gdi_SurfaceCommand_RemoteFX(gdi, context, cmd); + break; + + case RDPGFX_CODECID_CLEARCODEC: + status = gdi_SurfaceCommand_ClearCodec(gdi, context, cmd); + break; + + case RDPGFX_CODECID_PLANAR: + status = gdi_SurfaceCommand_Planar(gdi, context, cmd); + break; + + case RDPGFX_CODECID_AVC420: + status = gdi_SurfaceCommand_AVC420(gdi, context, cmd); + break; + + case RDPGFX_CODECID_AVC444v2: + case RDPGFX_CODECID_AVC444: + status = gdi_SurfaceCommand_AVC444(gdi, context, cmd); + break; + + case RDPGFX_CODECID_ALPHA: + status = gdi_SurfaceCommand_Alpha(gdi, context, cmd); + break; + + case RDPGFX_CODECID_CAPROGRESSIVE: + status = gdi_SurfaceCommand_Progressive(gdi, context, cmd); + break; + + case RDPGFX_CODECID_CAPROGRESSIVE_V2: + WLog_WARN(TAG, "SurfaceCommand %s [0x%08" PRIX32 "] not implemented", + rdpgfx_get_codec_id_string(cmd->codecId), cmd->codecId); + break; + + default: + WLog_WARN(TAG, "Invalid SurfaceCommand %s [0x%08" PRIX32 "]", + rdpgfx_get_codec_id_string(cmd->codecId), cmd->codecId); + break; + } + + LeaveCriticalSection(&context->mux); + return status; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT +gdi_DeleteEncodingContext(RdpgfxClientContext* context, + const RDPGFX_DELETE_ENCODING_CONTEXT_PDU* deleteEncodingContext) +{ + WINPR_ASSERT(context); + WINPR_ASSERT(deleteEncodingContext); + WINPR_UNUSED(context); + WINPR_UNUSED(deleteEncodingContext); + return CHANNEL_RC_OK; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT gdi_CreateSurface(RdpgfxClientContext* context, + const RDPGFX_CREATE_SURFACE_PDU* createSurface) +{ + UINT rc = ERROR_INTERNAL_ERROR; + gdiGfxSurface* surface = NULL; + rdpGdi* gdi = NULL; + WINPR_ASSERT(context); + WINPR_ASSERT(createSurface); + gdi = (rdpGdi*)context->custom; + WINPR_ASSERT(gdi); + WINPR_ASSERT(gdi->context); + EnterCriticalSection(&context->mux); + surface = (gdiGfxSurface*)calloc(1, sizeof(gdiGfxSurface)); + + if (!surface) + goto fail; + + if (!freerdp_settings_get_bool(gdi->context->settings, FreeRDP_DeactivateClientDecoding)) + { + WINPR_ASSERT(context->codecs); + surface->codecs = context->codecs; + + if (!surface->codecs) + { + free(surface); + goto fail; + } + } + + surface->surfaceId = createSurface->surfaceId; + surface->width = gfx_align_scanline(createSurface->width, 16); + surface->height = gfx_align_scanline(createSurface->height, 16); + surface->mappedWidth = createSurface->width; + surface->mappedHeight = createSurface->height; + surface->outputTargetWidth = createSurface->width; + surface->outputTargetHeight = createSurface->height; + + switch (createSurface->pixelFormat) + { + case GFX_PIXEL_FORMAT_ARGB_8888: + surface->format = PIXEL_FORMAT_BGRA32; + break; + + case GFX_PIXEL_FORMAT_XRGB_8888: + surface->format = PIXEL_FORMAT_BGRX32; + break; + + default: + free(surface); + goto fail; + } + + surface->scanline = gfx_align_scanline(surface->width * 4UL, 16); + surface->data = (BYTE*)winpr_aligned_malloc(1ull * surface->scanline * surface->height, 16); + + if (!surface->data) + { + free(surface); + goto fail; + } + + memset(surface->data, 0xFF, (size_t)surface->scanline * surface->height); + region16_init(&surface->invalidRegion); + + WINPR_ASSERT(context->SetSurfaceData); + rc = context->SetSurfaceData(context, surface->surfaceId, (void*)surface); +fail: + LeaveCriticalSection(&context->mux); + return rc; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT gdi_DeleteSurface(RdpgfxClientContext* context, + const RDPGFX_DELETE_SURFACE_PDU* deleteSurface) +{ + UINT rc = CHANNEL_RC_OK; + UINT res = ERROR_INTERNAL_ERROR; + rdpCodecs* codecs = NULL; + gdiGfxSurface* surface = NULL; + EnterCriticalSection(&context->mux); + + WINPR_ASSERT(context->GetSurfaceData); + surface = (gdiGfxSurface*)context->GetSurfaceData(context, deleteSurface->surfaceId); + + if (surface) + { + if (surface->windowMapped) + rc = IFCALLRESULT(CHANNEL_RC_OK, context->UnmapWindowForSurface, context, + surface->windowId); + +#ifdef WITH_GFX_H264 + h264_context_free(surface->h264); +#endif + region16_uninit(&surface->invalidRegion); + codecs = surface->codecs; + winpr_aligned_free(surface->data); + free(surface); + } + + WINPR_ASSERT(context->SetSurfaceData); + res = context->SetSurfaceData(context, deleteSurface->surfaceId, NULL); + if (res) + rc = res; + + if (codecs && codecs->progressive) + progressive_delete_surface_context(codecs->progressive, deleteSurface->surfaceId); + + LeaveCriticalSection(&context->mux); + return rc; +} + +static BOOL intersect_rect(const RECTANGLE_16* rect, const gdiGfxSurface* surface, + RECTANGLE_16* prect) +{ + WINPR_ASSERT(rect); + WINPR_ASSERT(surface); + WINPR_ASSERT(prect); + + if (rect->left > rect->right) + return FALSE; + if (rect->left > surface->width) + return FALSE; + if (rect->top > rect->bottom) + return FALSE; + if (rect->top > surface->height) + return FALSE; + prect->left = rect->left; + prect->top = rect->top; + prect->right = MIN(rect->right, surface->width); + prect->bottom = MIN(rect->bottom, surface->height); + return TRUE; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT gdi_SolidFill(RdpgfxClientContext* context, const RDPGFX_SOLID_FILL_PDU* solidFill) +{ + UINT status = ERROR_INTERNAL_ERROR; + BYTE a = 0; + RECTANGLE_16 invalidRect = { 0 }; + rdpGdi* gdi = (rdpGdi*)context->custom; + + EnterCriticalSection(&context->mux); + + WINPR_ASSERT(context->GetSurfaceData); + gdiGfxSurface* surface = (gdiGfxSurface*)context->GetSurfaceData(context, solidFill->surfaceId); + + if (!surface) + goto fail; + + const BYTE b = solidFill->fillPixel.B; + const BYTE g = solidFill->fillPixel.G; + const BYTE r = solidFill->fillPixel.R; + if (FreeRDPColorHasAlpha(surface->format)) + a = solidFill->fillPixel.XA; + else + a = 0xFF; + const UINT32 color = FreeRDPGetColor(surface->format, r, g, b, a); + + for (UINT16 index = 0; index < solidFill->fillRectCount; index++) + { + const RECTANGLE_16* rect = &(solidFill->fillRects[index]); + + if (!intersect_rect(rect, surface, &invalidRect)) + goto fail; + + const UINT32 nWidth = invalidRect.right - invalidRect.left; + const UINT32 nHeight = invalidRect.bottom - invalidRect.top; + + if (!freerdp_image_fill(surface->data, surface->format, surface->scanline, invalidRect.left, + invalidRect.top, nWidth, nHeight, color)) + goto fail; + + region16_union_rect(&(surface->invalidRegion), &(surface->invalidRegion), &invalidRect); + } + + status = IFCALLRESULT(CHANNEL_RC_OK, context->UpdateSurfaceArea, context, surface->surfaceId, + solidFill->fillRectCount, solidFill->fillRects); + + if (status != CHANNEL_RC_OK) + goto fail; + + LeaveCriticalSection(&context->mux); + + if (!gdi->inGfxFrame) + { + status = CHANNEL_RC_NOT_INITIALIZED; + IFCALLRET(context->UpdateSurfaces, status, context); + } + + return status; +fail: + LeaveCriticalSection(&context->mux); + return status; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT gdi_SurfaceToSurface(RdpgfxClientContext* context, + const RDPGFX_SURFACE_TO_SURFACE_PDU* surfaceToSurface) +{ + UINT status = ERROR_INTERNAL_ERROR; + BOOL sameSurface = 0; + UINT32 nWidth = 0; + UINT32 nHeight = 0; + const RECTANGLE_16* rectSrc = NULL; + RECTANGLE_16 invalidRect; + gdiGfxSurface* surfaceSrc = NULL; + gdiGfxSurface* surfaceDst = NULL; + rdpGdi* gdi = (rdpGdi*)context->custom; + EnterCriticalSection(&context->mux); + rectSrc = &(surfaceToSurface->rectSrc); + + WINPR_ASSERT(context->GetSurfaceData); + surfaceSrc = (gdiGfxSurface*)context->GetSurfaceData(context, surfaceToSurface->surfaceIdSrc); + sameSurface = + (surfaceToSurface->surfaceIdSrc == surfaceToSurface->surfaceIdDest) ? TRUE : FALSE; + + if (!sameSurface) + surfaceDst = + (gdiGfxSurface*)context->GetSurfaceData(context, surfaceToSurface->surfaceIdDest); + else + surfaceDst = surfaceSrc; + + if (!surfaceSrc || !surfaceDst) + goto fail; + + if (!is_rect_valid(rectSrc, surfaceSrc->width, surfaceSrc->height)) + goto fail; + + nWidth = rectSrc->right - rectSrc->left; + nHeight = rectSrc->bottom - rectSrc->top; + + for (UINT16 index = 0; index < surfaceToSurface->destPtsCount; index++) + { + const RDPGFX_POINT16* destPt = &surfaceToSurface->destPts[index]; + const RECTANGLE_16 rect = { destPt->x, destPt->y, + (UINT16)MIN(UINT16_MAX, destPt->x + nWidth), + (UINT16)MIN(UINT16_MAX, destPt->y + nHeight) }; + if (!is_rect_valid(&rect, surfaceDst->width, surfaceDst->height)) + goto fail; + + if (!freerdp_image_copy(surfaceDst->data, surfaceDst->format, surfaceDst->scanline, + destPt->x, destPt->y, nWidth, nHeight, surfaceSrc->data, + surfaceSrc->format, surfaceSrc->scanline, rectSrc->left, + rectSrc->top, NULL, FREERDP_FLIP_NONE)) + goto fail; + + invalidRect = rect; + region16_union_rect(&surfaceDst->invalidRegion, &surfaceDst->invalidRegion, &invalidRect); + status = IFCALLRESULT(CHANNEL_RC_OK, context->UpdateSurfaceArea, context, + surfaceDst->surfaceId, 1, &invalidRect); + + if (status != CHANNEL_RC_OK) + goto fail; + } + + LeaveCriticalSection(&context->mux); + + if (!gdi->inGfxFrame) + { + status = CHANNEL_RC_NOT_INITIALIZED; + IFCALLRET(context->UpdateSurfaces, status, context); + } + + return status; +fail: + LeaveCriticalSection(&context->mux); + return status; +} + +static void gdi_GfxCacheEntryFree(gdiGfxCacheEntry* entry) +{ + if (!entry) + return; + free(entry->data); + free(entry); +} + +static gdiGfxCacheEntry* gdi_GfxCacheEntryNew(UINT64 cacheKey, UINT32 width, UINT32 height, + UINT32 format) +{ + gdiGfxCacheEntry* cacheEntry = (gdiGfxCacheEntry*)calloc(1, sizeof(gdiGfxCacheEntry)); + if (!cacheEntry) + goto fail; + + cacheEntry->cacheKey = cacheKey; + cacheEntry->width = width; + cacheEntry->height = height; + cacheEntry->format = format; + cacheEntry->scanline = gfx_align_scanline(cacheEntry->width * 4, 16); + + if ((cacheEntry->width > 0) && (cacheEntry->height > 0)) + { + cacheEntry->data = (BYTE*)calloc(cacheEntry->height, cacheEntry->scanline); + + if (!cacheEntry->data) + goto fail; + } + return cacheEntry; +fail: + gdi_GfxCacheEntryFree(cacheEntry); + return NULL; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT gdi_SurfaceToCache(RdpgfxClientContext* context, + const RDPGFX_SURFACE_TO_CACHE_PDU* surfaceToCache) +{ + const RECTANGLE_16* rect = NULL; + gdiGfxSurface* surface = NULL; + gdiGfxCacheEntry* cacheEntry = NULL; + UINT rc = ERROR_INTERNAL_ERROR; + EnterCriticalSection(&context->mux); + rect = &(surfaceToCache->rectSrc); + + WINPR_ASSERT(context->GetSurfaceData); + surface = (gdiGfxSurface*)context->GetSurfaceData(context, surfaceToCache->surfaceId); + + if (!surface) + goto fail; + + if (!is_rect_valid(rect, surface->width, surface->height)) + goto fail; + + cacheEntry = gdi_GfxCacheEntryNew(surfaceToCache->cacheKey, (UINT32)(rect->right - rect->left), + (UINT32)(rect->bottom - rect->top), surface->format); + + if (!cacheEntry) + goto fail; + + if (!cacheEntry->data) + goto fail; + + if (!freerdp_image_copy(cacheEntry->data, cacheEntry->format, cacheEntry->scanline, 0, 0, + cacheEntry->width, cacheEntry->height, surface->data, surface->format, + surface->scanline, rect->left, rect->top, NULL, FREERDP_FLIP_NONE)) + goto fail; + + RDPGFX_EVICT_CACHE_ENTRY_PDU evict = { surfaceToCache->cacheSlot }; + WINPR_ASSERT(context->EvictCacheEntry); + context->EvictCacheEntry(context, &evict); + + WINPR_ASSERT(context->SetCacheSlotData); + rc = context->SetCacheSlotData(context, surfaceToCache->cacheSlot, (void*)cacheEntry); +fail: + if (rc != CHANNEL_RC_OK) + gdi_GfxCacheEntryFree(cacheEntry); + LeaveCriticalSection(&context->mux); + return rc; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT gdi_CacheToSurface(RdpgfxClientContext* context, + const RDPGFX_CACHE_TO_SURFACE_PDU* cacheToSurface) +{ + UINT status = ERROR_INTERNAL_ERROR; + gdiGfxSurface* surface = NULL; + gdiGfxCacheEntry* cacheEntry = NULL; + RECTANGLE_16 invalidRect; + rdpGdi* gdi = (rdpGdi*)context->custom; + + EnterCriticalSection(&context->mux); + + WINPR_ASSERT(context->GetSurfaceData); + surface = (gdiGfxSurface*)context->GetSurfaceData(context, cacheToSurface->surfaceId); + + WINPR_ASSERT(context->GetCacheSlotData); + cacheEntry = (gdiGfxCacheEntry*)context->GetCacheSlotData(context, cacheToSurface->cacheSlot); + + if (!surface || !cacheEntry) + goto fail; + + for (UINT16 index = 0; index < cacheToSurface->destPtsCount; index++) + { + const RDPGFX_POINT16* destPt = &cacheToSurface->destPts[index]; + const RECTANGLE_16 rect = { destPt->x, destPt->y, + (UINT16)MIN(UINT16_MAX, destPt->x + cacheEntry->width), + (UINT16)MIN(UINT16_MAX, destPt->y + cacheEntry->height) }; + + if (rectangle_is_empty(&rect)) + continue; + + if (!is_rect_valid(&rect, surface->width, surface->height)) + goto fail; + + if (!freerdp_image_copy(surface->data, surface->format, surface->scanline, destPt->x, + destPt->y, cacheEntry->width, cacheEntry->height, cacheEntry->data, + cacheEntry->format, cacheEntry->scanline, 0, 0, NULL, + FREERDP_FLIP_NONE)) + goto fail; + + invalidRect = rect; + region16_union_rect(&surface->invalidRegion, &surface->invalidRegion, &invalidRect); + status = IFCALLRESULT(CHANNEL_RC_OK, context->UpdateSurfaceArea, context, + surface->surfaceId, 1, &invalidRect); + + if (status != CHANNEL_RC_OK) + goto fail; + } + + LeaveCriticalSection(&context->mux); + + if (!gdi->inGfxFrame) + status = IFCALLRESULT(CHANNEL_RC_NOT_INITIALIZED, context->UpdateSurfaces, context); + else + status = CHANNEL_RC_OK; + + return status; + +fail: + LeaveCriticalSection(&context->mux); + return status; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT gdi_CacheImportReply(RdpgfxClientContext* context, + const RDPGFX_CACHE_IMPORT_REPLY_PDU* cacheImportReply) +{ + UINT16 count = 0; + const UINT16* slots = NULL; + UINT error = CHANNEL_RC_OK; + + slots = cacheImportReply->cacheSlots; + count = cacheImportReply->importedEntriesCount; + + for (UINT16 index = 0; index < count; index++) + { + UINT16 cacheSlot = slots[index]; + + if (cacheSlot == 0) + continue; + + WINPR_ASSERT(context->GetCacheSlotData); + gdiGfxCacheEntry* cacheEntry = + (gdiGfxCacheEntry*)context->GetCacheSlotData(context, cacheSlot); + + if (cacheEntry) + continue; + + cacheEntry = gdi_GfxCacheEntryNew(cacheSlot, 0, 0, PIXEL_FORMAT_BGRX32); + + if (!cacheEntry) + return ERROR_INTERNAL_ERROR; + + WINPR_ASSERT(context->SetCacheSlotData); + error = context->SetCacheSlotData(context, cacheSlot, (void*)cacheEntry); + + if (error) + { + WLog_ERR(TAG, "CacheImportReply: SetCacheSlotData failed with error %" PRIu32 "", + error); + gdi_GfxCacheEntryFree(cacheEntry); + break; + } + } + + return error; +} + +static UINT gdi_ImportCacheEntry(RdpgfxClientContext* context, UINT16 cacheSlot, + const PERSISTENT_CACHE_ENTRY* importCacheEntry) +{ + UINT error = ERROR_INTERNAL_ERROR; + gdiGfxCacheEntry* cacheEntry = NULL; + + if (cacheSlot == 0) + return CHANNEL_RC_OK; + + cacheEntry = gdi_GfxCacheEntryNew(importCacheEntry->key64, importCacheEntry->width, + importCacheEntry->height, PIXEL_FORMAT_BGRX32); + + if (!cacheEntry) + goto fail; + + if (!freerdp_image_copy(cacheEntry->data, cacheEntry->format, cacheEntry->scanline, 0, 0, + cacheEntry->width, cacheEntry->height, importCacheEntry->data, + PIXEL_FORMAT_BGRX32, 0, 0, 0, NULL, FREERDP_FLIP_NONE)) + goto fail; + + RDPGFX_EVICT_CACHE_ENTRY_PDU evict = { cacheSlot }; + WINPR_ASSERT(context->EvictCacheEntry); + error = context->EvictCacheEntry(context, &evict); + if (error != CHANNEL_RC_OK) + goto fail; + + WINPR_ASSERT(context->SetCacheSlotData); + error = context->SetCacheSlotData(context, cacheSlot, (void*)cacheEntry); + +fail: + if (error) + { + gdi_GfxCacheEntryFree(cacheEntry); + WLog_ERR(TAG, "ImportCacheEntry: SetCacheSlotData failed with error %" PRIu32 "", error); + } + + return error; +} + +static UINT gdi_ExportCacheEntry(RdpgfxClientContext* context, UINT16 cacheSlot, + PERSISTENT_CACHE_ENTRY* exportCacheEntry) +{ + gdiGfxCacheEntry* cacheEntry = NULL; + + WINPR_ASSERT(context->GetCacheSlotData); + cacheEntry = (gdiGfxCacheEntry*)context->GetCacheSlotData(context, cacheSlot); + + if (cacheEntry) + { + exportCacheEntry->key64 = cacheEntry->cacheKey; + exportCacheEntry->width = (UINT16)MIN(UINT16_MAX, cacheEntry->width); + exportCacheEntry->height = (UINT16)MIN(UINT16_MAX, cacheEntry->height); + exportCacheEntry->size = cacheEntry->width * cacheEntry->height * 4; + exportCacheEntry->flags = 0; + exportCacheEntry->data = cacheEntry->data; + return CHANNEL_RC_OK; + } + + return ERROR_NOT_FOUND; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT gdi_EvictCacheEntry(RdpgfxClientContext* context, + const RDPGFX_EVICT_CACHE_ENTRY_PDU* evictCacheEntry) +{ + gdiGfxCacheEntry* cacheEntry = NULL; + UINT rc = ERROR_NOT_FOUND; + + WINPR_ASSERT(context); + WINPR_ASSERT(evictCacheEntry); + + EnterCriticalSection(&context->mux); + + WINPR_ASSERT(context->GetCacheSlotData); + cacheEntry = (gdiGfxCacheEntry*)context->GetCacheSlotData(context, evictCacheEntry->cacheSlot); + + gdi_GfxCacheEntryFree(cacheEntry); + + WINPR_ASSERT(context->SetCacheSlotData); + rc = context->SetCacheSlotData(context, evictCacheEntry->cacheSlot, NULL); + LeaveCriticalSection(&context->mux); + return rc; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT gdi_MapSurfaceToOutput(RdpgfxClientContext* context, + const RDPGFX_MAP_SURFACE_TO_OUTPUT_PDU* surfaceToOutput) +{ + UINT rc = ERROR_INTERNAL_ERROR; + gdiGfxSurface* surface = NULL; + EnterCriticalSection(&context->mux); + + WINPR_ASSERT(context->GetSurfaceData); + surface = (gdiGfxSurface*)context->GetSurfaceData(context, surfaceToOutput->surfaceId); + + if (!surface) + goto fail; + + if (surface->windowMapped) + { + WLog_WARN(TAG, "sufrace already windowMapped when trying to set outputMapped"); + goto fail; + } + + surface->outputMapped = TRUE; + surface->outputOriginX = surfaceToOutput->outputOriginX; + surface->outputOriginY = surfaceToOutput->outputOriginY; + surface->outputTargetWidth = surface->mappedWidth; + surface->outputTargetHeight = surface->mappedHeight; + region16_clear(&surface->invalidRegion); + rc = CHANNEL_RC_OK; +fail: + LeaveCriticalSection(&context->mux); + return rc; +} + +static UINT +gdi_MapSurfaceToScaledOutput(RdpgfxClientContext* context, + const RDPGFX_MAP_SURFACE_TO_SCALED_OUTPUT_PDU* surfaceToOutput) +{ + UINT rc = ERROR_INTERNAL_ERROR; + gdiGfxSurface* surface = NULL; + EnterCriticalSection(&context->mux); + + WINPR_ASSERT(context->GetSurfaceData); + surface = (gdiGfxSurface*)context->GetSurfaceData(context, surfaceToOutput->surfaceId); + + if (!surface) + goto fail; + + if (surface->windowMapped) + { + WLog_WARN(TAG, "sufrace already windowMapped when trying to set outputMapped"); + goto fail; + } + + surface->outputMapped = TRUE; + surface->outputOriginX = surfaceToOutput->outputOriginX; + surface->outputOriginY = surfaceToOutput->outputOriginY; + surface->outputTargetWidth = surfaceToOutput->targetWidth; + surface->outputTargetHeight = surfaceToOutput->targetHeight; + region16_clear(&surface->invalidRegion); + rc = CHANNEL_RC_OK; +fail: + LeaveCriticalSection(&context->mux); + return rc; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT gdi_MapSurfaceToWindow(RdpgfxClientContext* context, + const RDPGFX_MAP_SURFACE_TO_WINDOW_PDU* surfaceToWindow) +{ + UINT rc = ERROR_INTERNAL_ERROR; + gdiGfxSurface* surface = NULL; + EnterCriticalSection(&context->mux); + + WINPR_ASSERT(context->GetSurfaceData); + surface = (gdiGfxSurface*)context->GetSurfaceData(context, surfaceToWindow->surfaceId); + + if (!surface) + goto fail; + + if (surface->outputMapped) + { + WLog_WARN(TAG, "sufrace already outputMapped when trying to set windowMapped"); + goto fail; + } + + if (surface->windowMapped) + { + if (surface->windowId != surfaceToWindow->windowId) + { + WLog_WARN(TAG, "sufrace windowId mismatch, has %" PRIu64 ", expected %" PRIu64, + surface->windowId, surfaceToWindow->windowId); + goto fail; + } + } + surface->windowMapped = TRUE; + + surface->windowId = surfaceToWindow->windowId; + surface->mappedWidth = surfaceToWindow->mappedWidth; + surface->mappedHeight = surfaceToWindow->mappedHeight; + surface->outputTargetWidth = surfaceToWindow->mappedWidth; + surface->outputTargetHeight = surfaceToWindow->mappedHeight; + rc = IFCALLRESULT(CHANNEL_RC_OK, context->MapWindowForSurface, context, + surfaceToWindow->surfaceId, surfaceToWindow->windowId); +fail: + LeaveCriticalSection(&context->mux); + return rc; +} + +static UINT +gdi_MapSurfaceToScaledWindow(RdpgfxClientContext* context, + const RDPGFX_MAP_SURFACE_TO_SCALED_WINDOW_PDU* surfaceToWindow) +{ + UINT rc = ERROR_INTERNAL_ERROR; + gdiGfxSurface* surface = NULL; + EnterCriticalSection(&context->mux); + + WINPR_ASSERT(context->GetSurfaceData); + surface = (gdiGfxSurface*)context->GetSurfaceData(context, surfaceToWindow->surfaceId); + + if (!surface) + goto fail; + + if (surface->outputMapped) + { + WLog_WARN(TAG, "sufrace already outputMapped when trying to set windowMapped"); + goto fail; + } + + if (surface->windowMapped) + { + if (surface->windowId != surfaceToWindow->windowId) + { + WLog_WARN(TAG, "sufrace windowId mismatch, has %" PRIu64 ", expected %" PRIu64, + surface->windowId, surfaceToWindow->windowId); + goto fail; + } + } + surface->windowMapped = TRUE; + + surface->windowId = surfaceToWindow->windowId; + surface->mappedWidth = surfaceToWindow->mappedWidth; + surface->mappedHeight = surfaceToWindow->mappedHeight; + surface->outputTargetWidth = surfaceToWindow->targetWidth; + surface->outputTargetHeight = surfaceToWindow->targetHeight; + rc = IFCALLRESULT(CHANNEL_RC_OK, context->MapWindowForSurface, context, + surfaceToWindow->surfaceId, surfaceToWindow->windowId); +fail: + LeaveCriticalSection(&context->mux); + return rc; +} + +BOOL gdi_graphics_pipeline_init(rdpGdi* gdi, RdpgfxClientContext* gfx) +{ + return gdi_graphics_pipeline_init_ex(gdi, gfx, NULL, NULL, NULL); +} + +BOOL gdi_graphics_pipeline_init_ex(rdpGdi* gdi, RdpgfxClientContext* gfx, + pcRdpgfxMapWindowForSurface map, + pcRdpgfxUnmapWindowForSurface unmap, + pcRdpgfxUpdateSurfaceArea update) +{ + rdpContext* context = NULL; + const rdpSettings* settings = NULL; + + if (!gdi || !gfx || !gdi->context || !gdi->context->settings) + return FALSE; + + context = gdi->context; + settings = gdi->context->settings; + + gdi->gfx = gfx; + gfx->custom = (void*)gdi; + gfx->ResetGraphics = gdi_ResetGraphics; + gfx->StartFrame = gdi_StartFrame; + gfx->EndFrame = gdi_EndFrame; + gfx->SurfaceCommand = gdi_SurfaceCommand; + gfx->DeleteEncodingContext = gdi_DeleteEncodingContext; + gfx->CreateSurface = gdi_CreateSurface; + gfx->DeleteSurface = gdi_DeleteSurface; + gfx->SolidFill = gdi_SolidFill; + gfx->SurfaceToSurface = gdi_SurfaceToSurface; + gfx->SurfaceToCache = gdi_SurfaceToCache; + gfx->CacheToSurface = gdi_CacheToSurface; + gfx->CacheImportReply = gdi_CacheImportReply; + gfx->ImportCacheEntry = gdi_ImportCacheEntry; + gfx->ExportCacheEntry = gdi_ExportCacheEntry; + gfx->EvictCacheEntry = gdi_EvictCacheEntry; + gfx->MapSurfaceToOutput = gdi_MapSurfaceToOutput; + gfx->MapSurfaceToWindow = gdi_MapSurfaceToWindow; + gfx->MapSurfaceToScaledOutput = gdi_MapSurfaceToScaledOutput; + gfx->MapSurfaceToScaledWindow = gdi_MapSurfaceToScaledWindow; + gfx->UpdateSurfaces = gdi_UpdateSurfaces; + gfx->MapWindowForSurface = map; + gfx->UnmapWindowForSurface = unmap; + gfx->UpdateSurfaceArea = update; + + if (!freerdp_settings_get_bool(settings, FreeRDP_DeactivateClientDecoding)) + { + const UINT32 w = freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth); + const UINT32 h = freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight); + + gfx->codecs = codecs_new(context); + if (!gfx->codecs) + return FALSE; + if (!freerdp_client_codecs_prepare(gfx->codecs, FREERDP_CODEC_ALL, w, h)) + return FALSE; + } + InitializeCriticalSection(&gfx->mux); + PROFILER_CREATE(gfx->SurfaceProfiler, "GFX-PROFILER") + + /** + * gdi->graphicsReset will be removed in FreeRDP v3 from public headers, + * since the EGFX Reset Graphics PDU seems to be optional. + * There are still some clients that expect and check it and therefore + * we simply initialize it with TRUE here for now. + */ + gdi->graphicsReset = TRUE; + if (freerdp_settings_get_bool(settings, FreeRDP_DeactivateClientDecoding)) + { + gfx->UpdateSurfaceArea = NULL; + gfx->UpdateSurfaces = NULL; + gfx->SurfaceCommand = NULL; + } + + return TRUE; +} + +void gdi_graphics_pipeline_uninit(rdpGdi* gdi, RdpgfxClientContext* gfx) +{ + if (gdi) + gdi->gfx = NULL; + + if (!gfx) + return; + + gfx->custom = NULL; + codecs_free(gfx->codecs); + gfx->codecs = NULL; + DeleteCriticalSection(&gfx->mux); + PROFILER_PRINT_HEADER + PROFILER_PRINT(gfx->SurfaceProfiler) + PROFILER_PRINT_FOOTER + PROFILER_FREE(gfx->SurfaceProfiler) +} diff --git a/libfreerdp/gdi/graphics.c b/libfreerdp/gdi/graphics.c new file mode 100644 index 0000000..28860cf --- /dev/null +++ b/libfreerdp/gdi/graphics.c @@ -0,0 +1,463 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Graphical Objects + * + * Copyright 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 <winpr/crt.h> + +#include <freerdp/log.h> +#include <freerdp/freerdp.h> +#include <freerdp/gdi/dc.h> +#include <freerdp/gdi/shape.h> +#include <freerdp/gdi/region.h> +#include <freerdp/gdi/bitmap.h> + +#include "clipping.h" +#include "drawing.h" +#include "brush.h" +#include "graphics.h" + +#define TAG FREERDP_TAG("gdi") +/* Bitmap Class */ + +HGDI_BITMAP gdi_create_bitmap(rdpGdi* gdi, UINT32 nWidth, UINT32 nHeight, UINT32 SrcFormat, + BYTE* data) +{ + UINT32 nSrcStep = 0; + UINT32 nDstStep = 0; + BYTE* pSrcData = NULL; + BYTE* pDstData = NULL; + HGDI_BITMAP bitmap = NULL; + + if (!gdi) + return NULL; + + nDstStep = nWidth * FreeRDPGetBytesPerPixel(gdi->dstFormat); + pDstData = winpr_aligned_malloc(1ull * nHeight * nDstStep, 16); + + if (!pDstData) + return NULL; + + pSrcData = data; + nSrcStep = nWidth * FreeRDPGetBytesPerPixel(SrcFormat); + + if (!freerdp_image_copy(pDstData, gdi->dstFormat, nDstStep, 0, 0, nWidth, nHeight, pSrcData, + SrcFormat, nSrcStep, 0, 0, &gdi->palette, FREERDP_FLIP_NONE)) + { + winpr_aligned_free(pDstData); + return NULL; + } + + bitmap = gdi_CreateBitmap(nWidth, nHeight, gdi->dstFormat, pDstData); + return bitmap; +} + +static BOOL gdi_Bitmap_New(rdpContext* context, rdpBitmap* bitmap) +{ + gdiBitmap* gdi_bitmap = NULL; + rdpGdi* gdi = context->gdi; + gdi_bitmap = (gdiBitmap*)bitmap; + gdi_bitmap->hdc = gdi_CreateCompatibleDC(gdi->hdc); + + if (!gdi_bitmap->hdc) + return FALSE; + + if (!bitmap->data) + gdi_bitmap->bitmap = gdi_CreateCompatibleBitmap(gdi->hdc, bitmap->width, bitmap->height); + else + { + UINT32 format = bitmap->format; + gdi_bitmap->bitmap = + gdi_create_bitmap(gdi, bitmap->width, bitmap->height, format, bitmap->data); + } + + if (!gdi_bitmap->bitmap) + { + gdi_DeleteDC(gdi_bitmap->hdc); + gdi_bitmap->hdc = NULL; + return FALSE; + } + + gdi_bitmap->hdc->format = gdi_bitmap->bitmap->format; + gdi_SelectObject(gdi_bitmap->hdc, (HGDIOBJECT)gdi_bitmap->bitmap); + gdi_bitmap->org_bitmap = NULL; + return TRUE; +} + +static void gdi_Bitmap_Free(rdpContext* context, rdpBitmap* bitmap) +{ + gdiBitmap* gdi_bitmap = (gdiBitmap*)bitmap; + + if (gdi_bitmap) + { + if (gdi_bitmap->hdc) + gdi_SelectObject(gdi_bitmap->hdc, (HGDIOBJECT)gdi_bitmap->org_bitmap); + + gdi_DeleteObject((HGDIOBJECT)gdi_bitmap->bitmap); + gdi_DeleteDC(gdi_bitmap->hdc); + winpr_aligned_free(bitmap->data); + } + + free(bitmap); +} + +static BOOL gdi_Bitmap_Paint(rdpContext* context, rdpBitmap* bitmap) +{ + gdiBitmap* gdi_bitmap = (gdiBitmap*)bitmap; + UINT32 width = bitmap->right - bitmap->left + 1; + UINT32 height = bitmap->bottom - bitmap->top + 1; + return gdi_BitBlt(context->gdi->primary->hdc, bitmap->left, bitmap->top, width, height, + gdi_bitmap->hdc, 0, 0, GDI_SRCCOPY, &context->gdi->palette); +} + +static BOOL gdi_Bitmap_Decompress(rdpContext* context, rdpBitmap* bitmap, const BYTE* pSrcData, + UINT32 DstWidth, UINT32 DstHeight, UINT32 bpp, UINT32 length, + BOOL compressed, UINT32 codecId) +{ + int status = 0; + UINT32 SrcSize = length; + rdpGdi* gdi = context->gdi; + UINT32 size = DstWidth * DstHeight; + bitmap->compressed = FALSE; + bitmap->format = gdi->dstFormat; + + if ((FreeRDPGetBytesPerPixel(bitmap->format) == 0) || (DstWidth == 0) || (DstHeight == 0) || + (DstWidth > UINT32_MAX / DstHeight) || + (size > (UINT32_MAX / FreeRDPGetBytesPerPixel(bitmap->format)))) + { + WLog_ERR(TAG, "invalid input data"); + return FALSE; + } + + size *= FreeRDPGetBytesPerPixel(bitmap->format); + bitmap->length = size; + bitmap->data = (BYTE*)winpr_aligned_malloc(bitmap->length, 16); + + if (!bitmap->data) + return FALSE; + + if (compressed) + { + if ((codecId == RDP_CODEC_ID_REMOTEFX) || (codecId == RDP_CODEC_ID_IMAGE_REMOTEFX)) + { + REGION16 invalidRegion; + region16_init(&invalidRegion); + + if (!rfx_process_message(context->codecs->rfx, pSrcData, SrcSize, bitmap->left, + bitmap->top, bitmap->data, bitmap->format, gdi->stride, + gdi->height, &invalidRegion)) + { + WLog_ERR(TAG, "rfx_process_message failed"); + return FALSE; + } + + status = 1; + } + else if (codecId == RDP_CODEC_ID_NSCODEC) + { + status = nsc_process_message(context->codecs->nsc, 32, DstWidth, DstHeight, pSrcData, + SrcSize, bitmap->data, bitmap->format, 0, 0, 0, DstWidth, + DstHeight, FREERDP_FLIP_VERTICAL); + + if (status < 1) + { + WLog_ERR(TAG, "nsc_process_message failed"); + return FALSE; + } + + return freerdp_image_copy(bitmap->data, bitmap->format, 0, 0, 0, DstWidth, DstHeight, + pSrcData, PIXEL_FORMAT_XRGB32, 0, 0, 0, &gdi->palette, + FREERDP_FLIP_VERTICAL); + } + else if (bpp < 32) + { + if (!interleaved_decompress(context->codecs->interleaved, pSrcData, SrcSize, DstWidth, + DstHeight, bpp, bitmap->data, bitmap->format, 0, 0, 0, + DstWidth, DstHeight, &gdi->palette)) + { + WLog_ERR(TAG, "interleaved_decompress failed"); + return FALSE; + } + } + else + { + const BOOL fidelity = + freerdp_settings_get_bool(context->settings, FreeRDP_DrawAllowDynamicColorFidelity); + freerdp_planar_switch_bgr(context->codecs->planar, fidelity); + if (!planar_decompress(context->codecs->planar, pSrcData, SrcSize, DstWidth, DstHeight, + bitmap->data, bitmap->format, 0, 0, 0, DstWidth, DstHeight, + TRUE)) + { + WLog_ERR(TAG, "planar_decompress failed"); + return FALSE; + } + } + } + else + { + const UINT32 SrcFormat = gdi_get_pixel_format(bpp); + const size_t sbpp = FreeRDPGetBytesPerPixel(SrcFormat); + const size_t dbpp = FreeRDPGetBytesPerPixel(bitmap->format); + + if ((sbpp == 0) || (dbpp == 0)) + return FALSE; + else + { + const size_t dstSize = SrcSize * dbpp / sbpp; + + if (dstSize < bitmap->length) + { + WLog_ERR(TAG, "dstSize %" PRIuz " < bitmap->length %" PRIu32, dstSize, + bitmap->length); + return FALSE; + } + } + + if (!freerdp_image_copy(bitmap->data, bitmap->format, 0, 0, 0, DstWidth, DstHeight, + pSrcData, SrcFormat, 0, 0, 0, &gdi->palette, FREERDP_FLIP_VERTICAL)) + { + WLog_ERR(TAG, "freerdp_image_copy failed"); + return FALSE; + } + } + + return TRUE; +} + +static BOOL gdi_Bitmap_SetSurface(rdpContext* context, rdpBitmap* bitmap, BOOL primary) +{ + rdpGdi* gdi = NULL; + + if (!context) + return FALSE; + + gdi = context->gdi; + + if (!gdi) + return FALSE; + + if (primary) + gdi->drawing = gdi->primary; + else + gdi->drawing = (gdiBitmap*)bitmap; + + return TRUE; +} + +/* Glyph Class */ +static BOOL gdi_Glyph_New(rdpContext* context, rdpGlyph* glyph) +{ + BYTE* data = NULL; + gdiGlyph* gdi_glyph = NULL; + + if (!context || !glyph) + return FALSE; + + gdi_glyph = (gdiGlyph*)glyph; + gdi_glyph->hdc = gdi_GetDC(); + + if (!gdi_glyph->hdc) + return FALSE; + + gdi_glyph->hdc->format = PIXEL_FORMAT_MONO; + data = freerdp_glyph_convert(glyph->cx, glyph->cy, glyph->aj); + + if (!data) + { + gdi_DeleteDC(gdi_glyph->hdc); + return FALSE; + } + + gdi_glyph->bitmap = gdi_CreateBitmap(glyph->cx, glyph->cy, PIXEL_FORMAT_MONO, data); + + if (!gdi_glyph->bitmap) + { + gdi_DeleteDC(gdi_glyph->hdc); + winpr_aligned_free(data); + return FALSE; + } + + gdi_SelectObject(gdi_glyph->hdc, (HGDIOBJECT)gdi_glyph->bitmap); + gdi_glyph->org_bitmap = NULL; + return TRUE; +} + +static void gdi_Glyph_Free(rdpContext* context, rdpGlyph* glyph) +{ + gdiGlyph* gdi_glyph = NULL; + gdi_glyph = (gdiGlyph*)glyph; + + if (gdi_glyph) + { + gdi_SelectObject(gdi_glyph->hdc, (HGDIOBJECT)gdi_glyph->org_bitmap); + gdi_DeleteObject((HGDIOBJECT)gdi_glyph->bitmap); + gdi_DeleteDC(gdi_glyph->hdc); + free(glyph->aj); + free(glyph); + } +} + +static BOOL gdi_Glyph_Draw(rdpContext* context, const rdpGlyph* glyph, INT32 x, INT32 y, INT32 w, + INT32 h, INT32 sx, INT32 sy, BOOL fOpRedundant) +{ + const gdiGlyph* gdi_glyph = NULL; + rdpGdi* gdi = NULL; + HGDI_BRUSH brush = NULL; + BOOL rc = FALSE; + + if (!context || !glyph) + return FALSE; + + gdi = context->gdi; + gdi_glyph = (const gdiGlyph*)glyph; + + if (!fOpRedundant) + { + GDI_RECT rect = { 0 }; + + if (x > 0) + rect.left = x; + + if (y > 0) + rect.top = y; + + if (x + w > 0) + rect.right = x + w - 1; + + if (y + h > 0) + rect.bottom = y + h - 1; + + if ((rect.left < rect.right) && (rect.top < rect.bottom)) + { + brush = gdi_CreateSolidBrush(gdi->drawing->hdc->bkColor); + + if (!brush) + return FALSE; + + gdi_FillRect(gdi->drawing->hdc, &rect, brush); + gdi_DeleteObject((HGDIOBJECT)brush); + } + } + + brush = gdi_CreateSolidBrush(gdi->drawing->hdc->textColor); + + if (!brush) + return FALSE; + + gdi_SelectObject(gdi->drawing->hdc, (HGDIOBJECT)brush); + rc = gdi_BitBlt(gdi->drawing->hdc, x, y, w, h, gdi_glyph->hdc, sx, sy, GDI_GLYPH_ORDER, + &context->gdi->palette); + gdi_DeleteObject((HGDIOBJECT)brush); + return rc; +} + +static BOOL gdi_Glyph_BeginDraw(rdpContext* context, INT32 x, INT32 y, INT32 width, INT32 height, + UINT32 bgcolor, UINT32 fgcolor, BOOL fOpRedundant) +{ + rdpGdi* gdi = NULL; + + if (!context || !context->gdi) + return FALSE; + + gdi = context->gdi; + + if (!gdi->drawing || !gdi->drawing->hdc) + return FALSE; + + if (!fOpRedundant) + { + if (!gdi_decode_color(gdi, bgcolor, &bgcolor, NULL)) + return FALSE; + + if (!gdi_decode_color(gdi, fgcolor, &fgcolor, NULL)) + return FALSE; + + gdi_SetClipRgn(gdi->drawing->hdc, x, y, width, height); + gdi_SetTextColor(gdi->drawing->hdc, bgcolor); + gdi_SetBkColor(gdi->drawing->hdc, fgcolor); + + if (1) + { + GDI_RECT rect = { 0 }; + HGDI_BRUSH brush = gdi_CreateSolidBrush(fgcolor); + + if (!brush) + return FALSE; + + if (x > 0) + rect.left = x; + + if (y > 0) + rect.top = y; + + rect.right = x + width - 1; + rect.bottom = y + height - 1; + + if ((x + width > rect.left) && (y + height > rect.top)) + gdi_FillRect(gdi->drawing->hdc, &rect, brush); + + gdi_DeleteObject((HGDIOBJECT)brush); + } + + return gdi_SetNullClipRgn(gdi->drawing->hdc); + } + + return TRUE; +} + +static BOOL gdi_Glyph_EndDraw(rdpContext* context, INT32 x, INT32 y, INT32 width, INT32 height, + UINT32 bgcolor, UINT32 fgcolor) +{ + rdpGdi* gdi = NULL; + + if (!context || !context->gdi) + return FALSE; + + gdi = context->gdi; + + if (!gdi->drawing || !gdi->drawing->hdc) + return FALSE; + + gdi_SetNullClipRgn(gdi->drawing->hdc); + return TRUE; +} + +/* Graphics Module */ +BOOL gdi_register_graphics(rdpGraphics* graphics) +{ + rdpBitmap bitmap = { 0 }; + rdpGlyph glyph = { 0 }; + bitmap.size = sizeof(gdiBitmap); + bitmap.New = gdi_Bitmap_New; + bitmap.Free = gdi_Bitmap_Free; + bitmap.Paint = gdi_Bitmap_Paint; + bitmap.Decompress = gdi_Bitmap_Decompress; + bitmap.SetSurface = gdi_Bitmap_SetSurface; + graphics_register_bitmap(graphics, &bitmap); + glyph.size = sizeof(gdiGlyph); + glyph.New = gdi_Glyph_New; + glyph.Free = gdi_Glyph_Free; + glyph.Draw = gdi_Glyph_Draw; + glyph.BeginDraw = gdi_Glyph_BeginDraw; + glyph.EndDraw = gdi_Glyph_EndDraw; + graphics_register_glyph(graphics, &glyph); + return TRUE; +} diff --git a/libfreerdp/gdi/graphics.h b/libfreerdp/gdi/graphics.h new file mode 100644 index 0000000..a35732d --- /dev/null +++ b/libfreerdp/gdi/graphics.h @@ -0,0 +1,35 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Graphical Objects + * + * Copyright 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. + */ + +#ifndef FREERDP_LIB_GDI_GRAPHICS_H +#define FREERDP_LIB_GDI_GRAPHICS_H + +#include <freerdp/gdi/gdi.h> +#include <freerdp/graphics.h> +#include <freerdp/api.h> +#include <freerdp/types.h> + +FREERDP_LOCAL HGDI_BITMAP gdi_create_bitmap(rdpGdi* gdi, UINT32 width, UINT32 height, UINT32 format, + BYTE* data); + +FREERDP_LOCAL BOOL gdi_register_graphics(rdpGraphics* graphics); + +#endif /* FREERDP_LIB_GDI_GRAPHICS_H */ diff --git a/libfreerdp/gdi/line.c b/libfreerdp/gdi/line.c new file mode 100644 index 0000000..95c8288 --- /dev/null +++ b/libfreerdp/gdi/line.c @@ -0,0 +1,315 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * GDI Line 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 <winpr/assert.h> + +#include <freerdp/freerdp.h> +#include <freerdp/gdi/gdi.h> +#include <freerdp/gdi/pen.h> +#include <freerdp/gdi/bitmap.h> +#include <freerdp/gdi/region.h> + +#include "drawing.h" +#include "clipping.h" +#include "line.h" + +static BOOL gdi_rop_color(UINT32 rop, BYTE* pixelPtr, UINT32 pen, UINT32 format) +{ + WINPR_ASSERT(pixelPtr); + const UINT32 srcPixel = FreeRDPReadColor(pixelPtr, format); + UINT32 dstPixel = 0; + + switch (rop) + { + case GDI_R2_BLACK: /* LineTo_BLACK */ + dstPixel = FreeRDPGetColor(format, 0, 0, 0, 0xFF); + break; + + case GDI_R2_NOTMERGEPEN: /* LineTo_NOTMERGEPEN */ + dstPixel = ~(srcPixel | pen); + break; + + case GDI_R2_MASKNOTPEN: /* LineTo_MASKNOTPEN */ + dstPixel = srcPixel & ~pen; + break; + + case GDI_R2_NOTCOPYPEN: /* LineTo_NOTCOPYPEN */ + dstPixel = ~pen; + break; + + case GDI_R2_MASKPENNOT: /* LineTo_MASKPENNOT */ + dstPixel = pen & ~srcPixel; + break; + + case GDI_R2_NOT: /* LineTo_NOT */ + dstPixel = ~srcPixel; + break; + + case GDI_R2_XORPEN: /* LineTo_XORPEN */ + dstPixel = srcPixel ^ pen; + break; + + case GDI_R2_NOTMASKPEN: /* LineTo_NOTMASKPEN */ + dstPixel = ~(srcPixel & pen); + break; + + case GDI_R2_MASKPEN: /* LineTo_MASKPEN */ + dstPixel = srcPixel & pen; + break; + + case GDI_R2_NOTXORPEN: /* LineTo_NOTXORPEN */ + dstPixel = ~(srcPixel ^ pen); + break; + + case GDI_R2_NOP: /* LineTo_NOP */ + dstPixel = srcPixel; + break; + + case GDI_R2_MERGENOTPEN: /* LineTo_MERGENOTPEN */ + dstPixel = srcPixel | ~pen; + break; + + case GDI_R2_COPYPEN: /* LineTo_COPYPEN */ + dstPixel = pen; + break; + + case GDI_R2_MERGEPENNOT: /* LineTo_MERGEPENNOT */ + dstPixel = srcPixel | ~pen; + break; + + case GDI_R2_MERGEPEN: /* LineTo_MERGEPEN */ + dstPixel = srcPixel | pen; + break; + + case GDI_R2_WHITE: /* LineTo_WHITE */ + dstPixel = FreeRDPGetColor(format, 0xFF, 0xFF, 0xFF, 0xFF); + break; + + default: + return FALSE; + } + + return FreeRDPWriteColor(pixelPtr, format, dstPixel); +} + +BOOL gdi_LineTo(HGDI_DC hdc, UINT32 nXEnd, UINT32 nYEnd) +{ + INT32 e2 = 0; + UINT32 pen = 0; + + WINPR_ASSERT(hdc); + const INT32 rop2 = gdi_GetROP2(hdc); + + const INT32 x1 = hdc->pen->posX; + const INT32 y1 = hdc->pen->posY; + const INT32 x2 = nXEnd; + const INT32 y2 = nYEnd; + const INT32 dx = (x1 > x2) ? x1 - x2 : x2 - x1; + const INT32 dy = (y1 > y2) ? y1 - y2 : y2 - y1; + const INT32 sx = (x1 < x2) ? 1 : -1; + const INT32 sy = (y1 < y2) ? 1 : -1; + INT32 e = dx - dy; + INT32 x = x1; + INT32 y = y1; + + WINPR_ASSERT(hdc->clip); + INT32 bx1 = 0; + INT32 by1 = 0; + INT32 bx2 = 0; + INT32 by2 = 0; + if (hdc->clip->null) + { + bx1 = (x1 < x2) ? x1 : x2; + by1 = (y1 < y2) ? y1 : y2; + bx2 = (x1 > x2) ? x1 : x2; + by2 = (y1 > y2) ? y1 : y2; + } + else + { + bx1 = hdc->clip->x; + by1 = hdc->clip->y; + bx2 = bx1 + hdc->clip->w - 1; + by2 = by1 + hdc->clip->h - 1; + } + + HGDI_BITMAP bmp = (HGDI_BITMAP)hdc->selectedObject; + WINPR_ASSERT(bmp); + + bx1 = MAX(bx1, 0); + by1 = MAX(by1, 0); + bx2 = MIN(bx2, bmp->width - 1); + by2 = MIN(by2, bmp->height - 1); + + if (!gdi_InvalidateRegion(hdc, bx1, by1, bx2 - bx1 + 1, by2 - by1 + 1)) + return FALSE; + + pen = gdi_GetPenColor(hdc->pen, bmp->format); + + while (1) + { + if (!(x == x2 && y == y2)) + { + if ((x >= bx1 && x <= bx2) && (y >= by1 && y <= by2)) + { + BYTE* pixel = gdi_GetPointer(bmp, x, y); + WINPR_ASSERT(pixel); + gdi_rop_color(rop2, pixel, pen, bmp->format); + } + } + else + { + break; + } + + e2 = 2 * e; + + if (e2 > -dy) + { + e -= dy; + x += sx; + } + + if (e2 < dx) + { + e += dx; + y += sy; + } + } + + return TRUE; +} + +/** + * Draw one or more straight lines + * @param hdc device context + * @param lppt array of points + * @param cCount number of points + * @return nonzero on success, 0 otherwise + */ +BOOL gdi_PolylineTo(HGDI_DC hdc, GDI_POINT* lppt, DWORD cCount) +{ + WINPR_ASSERT(hdc); + WINPR_ASSERT(lppt || (cCount == 0)); + + for (DWORD i = 0; i < cCount; i++) + { + if (!gdi_LineTo(hdc, lppt[i].x, lppt[i].y)) + return FALSE; + + if (!gdi_MoveToEx(hdc, lppt[i].x, lppt[i].y, NULL)) + return FALSE; + } + + return TRUE; +} + +/** + * Draw one or more straight lines + * @param hdc device context + * @param lppt array of points + * @param cPoints number of points + * @return nonzero on success, 0 otherwise + */ +BOOL gdi_Polyline(HGDI_DC hdc, GDI_POINT* lppt, UINT32 cPoints) +{ + WINPR_ASSERT(hdc); + WINPR_ASSERT(lppt || (cPoints == 0)); + + if (cPoints > 0) + { + GDI_POINT pt = { 0 }; + + if (!gdi_MoveToEx(hdc, lppt[0].x, lppt[0].y, &pt)) + return FALSE; + + for (UINT32 i = 0; i < cPoints; i++) + { + if (!gdi_LineTo(hdc, lppt[i].x, lppt[i].y)) + return FALSE; + + if (!gdi_MoveToEx(hdc, lppt[i].x, lppt[i].y, NULL)) + return FALSE; + } + + if (!gdi_MoveToEx(hdc, pt.x, pt.y, NULL)) + return FALSE; + } + + return TRUE; +} + +/** + * Draw multiple series of connected line segments + * @param hdc device context + * @param lppt array of points + * @param lpdwPolyPoints array of numbers of points per series + * @param cCount count of entries in lpdwPolyPoints + * @return nonzero on success, 0 otherwise + */ +BOOL gdi_PolyPolyline(HGDI_DC hdc, GDI_POINT* lppt, UINT32* lpdwPolyPoints, DWORD cCount) +{ + DWORD j = 0; + + WINPR_ASSERT(hdc); + WINPR_ASSERT(lppt || (cCount == 0)); + WINPR_ASSERT(lpdwPolyPoints || (cCount == 0)); + + for (DWORD i = 0; i < cCount; i++) + { + const UINT32 cPoints = lpdwPolyPoints[i]; + + if (!gdi_Polyline(hdc, &lppt[j], cPoints)) + return FALSE; + + j += cPoints; + } + + return TRUE; +} + +/** + * Move pen from the current device context to a new position. + * @param hdc device context + * @param X x position + * @param Y y position + * @return nonzero on success, 0 otherwise + */ + +BOOL gdi_MoveToEx(HGDI_DC hdc, UINT32 X, UINT32 Y, HGDI_POINT lpPoint) +{ + WINPR_ASSERT(hdc); + + if (lpPoint != NULL) + { + lpPoint->x = hdc->pen->posX; + lpPoint->y = hdc->pen->posY; + } + + hdc->pen->posX = X; + hdc->pen->posY = Y; + return TRUE; +} diff --git a/libfreerdp/gdi/line.h b/libfreerdp/gdi/line.h new file mode 100644 index 0000000..d4f8267 --- /dev/null +++ b/libfreerdp/gdi/line.h @@ -0,0 +1,44 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * GDI Line 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. + */ + +#ifndef FREERDP_LIB_GDI_LINE_H +#define FREERDP_LIB_GDI_LINE_H + +#include <freerdp/api.h> +#include <freerdp/gdi/gdi.h> + +#ifdef __cplusplus +extern "C" +{ +#endif + + FREERDP_LOCAL BOOL gdi_LineTo(HGDI_DC hdc, UINT32 nXEnd, UINT32 nYEnd); + FREERDP_LOCAL BOOL gdi_PolylineTo(HGDI_DC hdc, GDI_POINT* lppt, DWORD cCount); + FREERDP_LOCAL BOOL gdi_Polyline(HGDI_DC hdc, GDI_POINT* lppt, UINT32 cPoints); + FREERDP_LOCAL BOOL gdi_PolyPolyline(HGDI_DC hdc, GDI_POINT* lppt, UINT32* lpdwPolyPoints, + DWORD cCount); + FREERDP_LOCAL BOOL gdi_MoveToEx(HGDI_DC hdc, UINT32 X, UINT32 Y, HGDI_POINT lpPoint); + +#ifdef __cplusplus +} +#endif + +#endif /* FREERDP_LIB_GDI_LINE_H */ diff --git a/libfreerdp/gdi/pen.c b/libfreerdp/gdi/pen.c new file mode 100644 index 0000000..4d0a3a3 --- /dev/null +++ b/libfreerdp/gdi/pen.c @@ -0,0 +1,64 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * GDI Pen Functions + * + * Copyright 2010-2011 Marc-Andre Moreau <marcandre.moreau@gmail.com> + * + * 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. + */ + +/* GDI Pen Functions: http://msdn.microsoft.com/en-us/library/dd162790 */ + +#include <freerdp/config.h> + +#include <stdio.h> +#include <stdlib.h> + +#include <freerdp/api.h> +#include <freerdp/freerdp.h> +#include <freerdp/gdi/gdi.h> + +#include <freerdp/gdi/pen.h> + +/** + * @brief Create a new pen. + * msdn{dd183509} + * + * @param fnPenStyle pen style + * @param nWidth pen width + * @param crColor pen color + * @param format the color format + * @param palette A pointer to a color palette + * + * @return new pen + */ + +HGDI_PEN gdi_CreatePen(UINT32 fnPenStyle, UINT32 nWidth, UINT32 crColor, UINT32 format, + const gdiPalette* palette) +{ + HGDI_PEN hPen = (HGDI_PEN)calloc(1, sizeof(GDI_PEN)); + if (!hPen) + return NULL; + hPen->objectType = GDIOBJECT_PEN; + hPen->style = fnPenStyle; + hPen->color = crColor; + hPen->width = nWidth; + hPen->format = format; + hPen->palette = palette; + return hPen; +} + +UINT32 gdi_GetPenColor(HGDI_PEN pen, UINT32 format) +{ + return FreeRDPConvertColor(pen->color, pen->format, format, pen->palette); +} 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; +} diff --git a/libfreerdp/gdi/shape.c b/libfreerdp/gdi/shape.c new file mode 100644 index 0000000..fdc05f1 --- /dev/null +++ b/libfreerdp/gdi/shape.c @@ -0,0 +1,286 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * GDI Shape 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/freerdp.h> +#include <freerdp/gdi/gdi.h> + +#include <freerdp/gdi/bitmap.h> +#include <freerdp/gdi/region.h> +#include <freerdp/gdi/shape.h> + +#include <freerdp/log.h> + +#include "clipping.h" +#include "../gdi/gdi.h" + +#define TAG FREERDP_TAG("gdi.shape") + +static void Ellipse_Bresenham(HGDI_DC hdc, int x1, int y1, int x2, int y2) +{ + INT32 e = 0; + INT32 e2 = 0; + INT32 dx = 0; + INT32 dy = 0; + INT32 a = 0; + INT32 b = 0; + INT32 c = 0; + a = (x1 < x2) ? x2 - x1 : x1 - x2; + b = (y1 < y2) ? y2 - y1 : y1 - y2; + c = b & 1; + dx = 4 * (1 - a) * b * b; + dy = 4 * (c + 1) * a * a; + e = dx + dy + c * a * a; + + if (x1 > x2) + { + x1 = x2; + x2 += a; + } + + if (y1 > y2) + y1 = y2; + + y1 += (b + 1) / 2; + y2 = y1 - c; + a *= 8 * a; + c = 8 * b * b; + + do + { + gdi_SetPixel(hdc, x2, y1, 0); + gdi_SetPixel(hdc, x1, y1, 0); + gdi_SetPixel(hdc, x1, y2, 0); + gdi_SetPixel(hdc, x2, y2, 0); + e2 = 2 * e; + + if (e2 >= dx) + { + x1++; + x2--; + e += dx += c; + } + + if (e2 <= dy) + { + y1++; + y2--; + e += dy += a; + } + } while (x1 <= x2); + + while (y1 - y2 < b) + { + gdi_SetPixel(hdc, x1 - 1, ++y1, 0); + gdi_SetPixel(hdc, x1 - 1, --y2, 0); + } +} + +/** + * Draw an ellipse + * msdn{dd162510} + * + * @param hdc device context + * @param nLeftRect x1 + * @param nTopRect y1 + * @param nRightRect x2 + * @param nBottomRect y2 + * + * @return nonzero if successful, 0 otherwise + */ +BOOL gdi_Ellipse(HGDI_DC hdc, int nLeftRect, int nTopRect, int nRightRect, int nBottomRect) +{ + Ellipse_Bresenham(hdc, nLeftRect, nTopRect, nRightRect, nBottomRect); + return TRUE; +} + +/** + * Fill a rectangle with the given brush. + * msdn{dd162719} + * + * @param hdc device context + * @param rect rectangle + * @param hbr brush + * + * @return nonzero if successful, 0 otherwise + */ + +BOOL gdi_FillRect(HGDI_DC hdc, const HGDI_RECT rect, HGDI_BRUSH hbr) +{ + UINT32 color = 0; + UINT32 dstColor = 0; + BOOL monochrome = FALSE; + INT32 nXDest = 0; + INT32 nYDest = 0; + INT32 nWidth = 0; + INT32 nHeight = 0; + const BYTE* srcp = NULL; + DWORD formatSize = 0; + gdi_RectToCRgn(rect, &nXDest, &nYDest, &nWidth, &nHeight); + + if (!hdc || !hbr) + return FALSE; + + if (!gdi_ClipCoords(hdc, &nXDest, &nYDest, &nWidth, &nHeight, NULL, NULL)) + return TRUE; + + switch (hbr->style) + { + case GDI_BS_SOLID: + color = hbr->color; + + for (INT32 x = 0; x < nWidth; x++) + { + BYTE* dstp = gdi_get_bitmap_pointer(hdc, nXDest + x, nYDest); + + if (dstp) + FreeRDPWriteColor(dstp, hdc->format, color); + } + + srcp = gdi_get_bitmap_pointer(hdc, nXDest, nYDest); + formatSize = FreeRDPGetBytesPerPixel(hdc->format); + + for (INT32 y = 1; y < nHeight; y++) + { + BYTE* dstp = gdi_get_bitmap_pointer(hdc, nXDest, nYDest + y); + memcpy(dstp, srcp, 1ull * nWidth * formatSize); + } + + break; + + case GDI_BS_HATCHED: + case GDI_BS_PATTERN: + monochrome = (hbr->pattern->format == PIXEL_FORMAT_MONO); + formatSize = FreeRDPGetBytesPerPixel(hbr->pattern->format); + + for (INT32 y = 0; y < nHeight; y++) + { + for (INT32 x = 0; x < nWidth; x++) + { + const UINT32 yOffset = + ((nYDest + y) * hbr->pattern->width % hbr->pattern->height) * formatSize; + const UINT32 xOffset = ((nXDest + x) % hbr->pattern->width) * formatSize; + const BYTE* patp = &hbr->pattern->data[yOffset + xOffset]; + BYTE* dstp = gdi_get_bitmap_pointer(hdc, nXDest + x, nYDest + y); + + if (!patp) + return FALSE; + + if (monochrome) + { + if (*patp == 0) + dstColor = hdc->bkColor; + else + dstColor = hdc->textColor; + } + else + { + dstColor = FreeRDPReadColor(patp, hbr->pattern->format); + dstColor = + FreeRDPConvertColor(dstColor, hbr->pattern->format, hdc->format, NULL); + } + + if (dstp) + FreeRDPWriteColor(dstp, hdc->format, dstColor); + } + } + + break; + + default: + break; + } + + if (!gdi_InvalidateRegion(hdc, nXDest, nYDest, nWidth, nHeight)) + return FALSE; + + return TRUE; +} + +/** + * Draw a polygon + * msdn{dd162814} + * @param hdc device context + * @param lpPoints array of points + * @param nCount number of points + * @return nonzero if successful, 0 otherwise + */ +BOOL gdi_Polygon(HGDI_DC hdc, GDI_POINT* lpPoints, int nCount) +{ + WLog_ERR(TAG, "Not implemented!"); + return FALSE; +} + +/** + * Draw a series of closed polygons + * msdn{dd162818} + * @param hdc device context + * @param lpPoints array of series of points + * @param lpPolyCounts array of number of points in each series + * @param nCount count of number of points in lpPolyCounts + * @return nonzero if successful, 0 otherwise + */ +BOOL gdi_PolyPolygon(HGDI_DC hdc, GDI_POINT* lpPoints, int* lpPolyCounts, int nCount) +{ + WLog_ERR(TAG, "Not implemented!"); + return FALSE; +} + +BOOL gdi_Rectangle(HGDI_DC hdc, INT32 nXDst, INT32 nYDst, INT32 nWidth, INT32 nHeight) +{ + UINT32 color = 0; + + if (!gdi_ClipCoords(hdc, &nXDst, &nYDst, &nWidth, &nHeight, NULL, NULL)) + return TRUE; + + color = hdc->textColor; + + for (INT32 y = 0; y < nHeight; y++) + { + BYTE* dstLeft = gdi_get_bitmap_pointer(hdc, nXDst, nYDst + y); + BYTE* dstRight = gdi_get_bitmap_pointer(hdc, nXDst + nWidth - 1, nYDst + y); + + if (dstLeft) + FreeRDPWriteColor(dstLeft, hdc->format, color); + + if (dstRight) + FreeRDPWriteColor(dstRight, hdc->format, color); + } + + for (INT32 x = 0; x < nWidth; x++) + { + BYTE* dstTop = gdi_get_bitmap_pointer(hdc, nXDst + x, nYDst); + BYTE* dstBottom = gdi_get_bitmap_pointer(hdc, nXDst + x, nYDst + nHeight - 1); + + if (dstTop) + FreeRDPWriteColor(dstTop, hdc->format, color); + + if (dstBottom) + FreeRDPWriteColor(dstBottom, hdc->format, color); + } + + return FALSE; +} diff --git a/libfreerdp/gdi/test/CMakeLists.txt b/libfreerdp/gdi/test/CMakeLists.txt new file mode 100644 index 0000000..b859c16 --- /dev/null +++ b/libfreerdp/gdi/test/CMakeLists.txt @@ -0,0 +1,40 @@ + +set(MODULE_NAME "TestGdi") +set(MODULE_PREFIX "TEST_GDI") + +set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c) + +set(${MODULE_PREFIX}_TESTS + TestGdiRop3.c + # TestGdiLine.c # TODO: This test is broken + TestGdiRegion.c + TestGdiRect.c + TestGdiBitBlt.c + TestGdiCreate.c + TestGdiEllipse.c + TestGdiClip.c) + +create_test_sourcelist(${MODULE_PREFIX}_SRCS + ${${MODULE_PREFIX}_DRIVER} + ${${MODULE_PREFIX}_TESTS}) + +include_directories(..) + +add_library(helpers STATIC + helpers.c) +target_link_libraries(helpers freerdp) + + +add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS}) + +target_link_libraries(${MODULE_NAME} winpr freerdp helpers) + +set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}") + +foreach(test ${${MODULE_PREFIX}_TESTS}) + get_filename_component(TestName ${test} NAME_WE) + add_test(${TestName} ${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME} ${TestName}) +endforeach() + +set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "FreeRDP/Test") + diff --git a/libfreerdp/gdi/test/TestGdiBitBlt.c b/libfreerdp/gdi/test/TestGdiBitBlt.c new file mode 100644 index 0000000..ba746d5 --- /dev/null +++ b/libfreerdp/gdi/test/TestGdiBitBlt.c @@ -0,0 +1,577 @@ + +#include <freerdp/gdi/gdi.h> + +#include <freerdp/gdi/dc.h> +#include <freerdp/gdi/pen.h> +#include <freerdp/gdi/region.h> +#include <freerdp/gdi/bitmap.h> + +#include <winpr/crt.h> + +#include "line.h" +#include "brush.h" +#include "helpers.h" + +/* BitBlt() Test Data */ + +/* source bitmap (16x16) */ +static const BYTE bmp_SRC[256] = { + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF" + "\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF" + "\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF" + "\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF" + "\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF" + "\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF" + "\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF" + "\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" +}; + +/* destination bitmap (16x16) */ +static const BYTE bmp_DST[256] = { + "\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00" +}; + +/* SRCCOPY (0x00CC0020) */ +static const BYTE bmp_SRCCOPY[256] = { + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF" + "\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF" + "\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF" + "\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF" + "\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF" + "\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF" + "\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF" + "\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" +}; + +/* BLACKNESS (0x00000042) */ +static const BYTE bmp_BLACKNESS[256] = { + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +}; + +/* WHITENESS (0x00FF0062) */ +static const BYTE bmp_WHITENESS[256] = { + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" +}; + +/* SRCAND (0x008800C6) */ +static const BYTE bmp_SRCAND[256] = { + "\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF" + "\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00" +}; + +/* SRCPAINT (0x00EE0086) */ +static const BYTE bmp_SRCPAINT[256] = { + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" +}; + +/* SRCINVERT (0x00660046) */ +static const BYTE bmp_SRCINVERT[256] = { + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00" + "\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\xFF\xFF\x00\x00\x00\x00\x00\x00" + "\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF\x00\x00\x00\x00" + "\xFF\xFF\xFF\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\x00\x00\x00" + "\xFF\xFF\xFF\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\x00\x00\x00" + "\xFF\xFF\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00" + "\xFF\xFF\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00" + "\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\xFF\xFF" + "\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\xFF\xFF" + "\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\xFF\xFF\xFF" + "\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\xFF\xFF\xFF" + "\x00\x00\x00\x00\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF" + "\x00\x00\x00\x00\x00\x00\xFF\xFF\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF" + "\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" +}; + +/* SRCERASE (0x00440328) */ +static const BYTE bmp_SRCERASE[256] = { + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00" + "\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF" + "\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" +}; + +/* NOTSRCCOPY (0x00330008) */ +static const BYTE bmp_NOTSRCCOPY[256] = { + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00" + "\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00" + "\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00" + "\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00" + "\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00" + "\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00" + "\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00" + "\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00" + "\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00" + "\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +}; + +/* NOTSRCERASE (0x001100A6) */ +static const BYTE bmp_NOTSRCERASE[256] = { + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +}; + +/* DSTINVERT (0x00550009) */ +static const BYTE bmp_DSTINVERT[256] = { + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" +}; + +/* SPna (0x000C0324) */ +static const BYTE bmp_SPna[256] = { + "\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF" + "\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF" + "\xFF\xFF\x00\x00\xFF\xFF\x00\x00\x00\x00\x00\x00\xFF\xFF\x00\x00" + "\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\x00\x00" + "\x00\x00\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF" + "\x00\x00\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF" + "\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF" + "\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\x00\x00" + "\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\x00\x00" + "\x00\x00\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF" + "\x00\x00\xFF\xFF\x00\x00\x00\x00\x00\x00\xFF\xFF\x00\x00\xFF\xFF" + "\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00" + "\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00" +}; + +/* MERGEPAINT (0x00BB0226) */ +static const BYTE bmp_MERGEPAINT[256] = { + "\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00" +}; + +/* MERGECOPY (0x00C000CA) */ +static const BYTE bmp_MERGECOPY[256] = { + "\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00" + "\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00" + "\x00\x00\xFF\xFF\x00\x00\x00\x00\x00\x00\xFF\xFF\x00\x00\xFF\xFF" + "\x00\x00\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF" + "\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\x00\x00" + "\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF" + "\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF" + "\x00\x00\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF" + "\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\x00\x00" + "\xFF\xFF\x00\x00\xFF\xFF\x00\x00\x00\x00\x00\x00\xFF\xFF\x00\x00" + "\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF" + "\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF" +}; + +/* PATPAINT (0x00FB0A09) */ +static const BYTE bmp_PATPAINT[256] = { + "\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\x00\x00\xFF\xFF\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\xFF\xFF\x00\x00" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF" +}; + +/* PATCOPY (0x00F00021) */ +static const BYTE bmp_PATCOPY[256] = { + "\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00" + "\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00" + "\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF" + "\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF" + "\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00" + "\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00" + "\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF" + "\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF" + "\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00" + "\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00" + "\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF" + "\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF" + "\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00" + "\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00" + "\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF" + "\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF\x00\x00\xFF\xFF" +}; + +/* PATINVERT (0x005A0049) */ +static const BYTE bmp_PATINVERT[256] = { + "\xFF\xFF\x00\x00\xFF\xFF\x00\x00\x00\x00\xFF\xFF\x00\x00\xFF\xFF" + "\xFF\xFF\x00\x00\xFF\xFF\x00\x00\x00\x00\xFF\xFF\x00\x00\xFF\xFF" + "\x00\x00\xFF\xFF\x00\x00\xFF\xFF\xFF\xFF\x00\x00\xFF\xFF\x00\x00" + "\x00\x00\xFF\xFF\x00\x00\xFF\xFF\xFF\xFF\x00\x00\xFF\xFF\x00\x00" + "\xFF\xFF\x00\x00\xFF\xFF\x00\x00\x00\x00\xFF\xFF\x00\x00\xFF\xFF" + "\xFF\xFF\x00\x00\xFF\xFF\x00\x00\x00\x00\xFF\xFF\x00\x00\xFF\xFF" + "\x00\x00\xFF\xFF\x00\x00\xFF\xFF\xFF\xFF\x00\x00\xFF\xFF\x00\x00" + "\x00\x00\xFF\xFF\x00\x00\xFF\xFF\xFF\xFF\x00\x00\xFF\xFF\x00\x00" + "\x00\x00\xFF\xFF\x00\x00\xFF\xFF\xFF\xFF\x00\x00\xFF\xFF\x00\x00" + "\x00\x00\xFF\xFF\x00\x00\xFF\xFF\xFF\xFF\x00\x00\xFF\xFF\x00\x00" + "\xFF\xFF\x00\x00\xFF\xFF\x00\x00\x00\x00\xFF\xFF\x00\x00\xFF\xFF" + "\xFF\xFF\x00\x00\xFF\xFF\x00\x00\x00\x00\xFF\xFF\x00\x00\xFF\xFF" + "\x00\x00\xFF\xFF\x00\x00\xFF\xFF\xFF\xFF\x00\x00\xFF\xFF\x00\x00" + "\x00\x00\xFF\xFF\x00\x00\xFF\xFF\xFF\xFF\x00\x00\xFF\xFF\x00\x00" + "\xFF\xFF\x00\x00\xFF\xFF\x00\x00\x00\x00\xFF\xFF\x00\x00\xFF\xFF" + "\xFF\xFF\x00\x00\xFF\xFF\x00\x00\x00\x00\xFF\xFF\x00\x00\xFF\xFF" +}; + +struct test_bitblt +{ + UINT32 rop; + const BYTE* src; + HGDI_BITMAP bmp; +}; + +static BOOL test_rop(HGDI_DC hdcDst, HGDI_DC hdcSrc, HGDI_BITMAP hBmpSrc, HGDI_BITMAP hBmpDst, + HGDI_BITMAP hBmpDstOriginal, UINT32 rop, HGDI_BITMAP expected, + const gdiPalette* hPalette) +{ + BOOL success = FALSE; + + /* restore original destination bitmap */ + gdi_SelectObject(hdcSrc, (HGDIOBJECT)hBmpDstOriginal); + gdi_SelectObject(hdcDst, (HGDIOBJECT)hBmpDst); + + if (!gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, GDI_SRCCOPY, hPalette)) + goto fail; + + if (!test_assert_bitmaps_equal(hBmpDst, hBmpDstOriginal, gdi_rop_to_string(GDI_SRCCOPY), + hPalette)) + goto fail; + + gdi_SelectObject(hdcSrc, (HGDIOBJECT)hBmpSrc); + if (!gdi_BitBlt(hdcDst, 0, 0, 16, 16, hdcSrc, 0, 0, rop, hPalette)) + goto fail; + + if (!test_assert_bitmaps_equal(hBmpDst, expected, gdi_rop_to_string(rop), hPalette)) + goto fail; + + success = TRUE; +fail: + fprintf(stderr, "[%s] ROP=%s returned %d\n", __func__, gdi_rop_to_string(rop), success); + return success; +} + +static BOOL test_gdi_BitBlt(UINT32 SrcFormat, UINT32 DstFormat) +{ + BOOL rc = FALSE; + BOOL failed = FALSE; + HGDI_DC hdcSrc = NULL; + HGDI_DC hdcDst = NULL; + const UINT32 RawFormat = PIXEL_FORMAT_RGB8; + struct test_bitblt tests[] = { { GDI_SRCCOPY, bmp_SRCCOPY, NULL }, + { GDI_SPna, bmp_SPna, NULL }, + { GDI_BLACKNESS, bmp_BLACKNESS, NULL }, + { GDI_WHITENESS, bmp_WHITENESS, NULL }, + { GDI_SRCAND, bmp_SRCAND, NULL }, + { GDI_SRCPAINT, bmp_SRCPAINT, NULL }, + { GDI_SRCINVERT, bmp_SRCINVERT, NULL }, + { GDI_SRCERASE, bmp_SRCERASE, NULL }, + { GDI_NOTSRCCOPY, bmp_NOTSRCCOPY, NULL }, + { GDI_NOTSRCERASE, bmp_NOTSRCERASE, NULL }, + { GDI_DSTINVERT, bmp_DSTINVERT, NULL }, + { GDI_MERGECOPY, bmp_MERGECOPY, NULL }, + { GDI_MERGEPAINT, bmp_MERGEPAINT, NULL }, + { GDI_PATCOPY, bmp_PATCOPY, NULL }, + { GDI_PATPAINT, bmp_PATPAINT, NULL }, + { GDI_PATINVERT, bmp_PATINVERT, NULL }, + { GDI_DSTINVERT, bmp_SRC, NULL }, + { GDI_DSPDxax, bmp_SRC, NULL }, + { GDI_PSDPxax, bmp_SRC, NULL }, + { GDI_DSna, bmp_SRC, NULL }, + { GDI_DPa, bmp_SRC, NULL }, + { GDI_PDxn, bmp_SRC, NULL }, + { GDI_DSxn, bmp_SRC, NULL }, + { GDI_PSDnox, bmp_SRC, NULL }, + { GDI_PDSona, bmp_SRC, NULL }, + { GDI_DSPDxox, bmp_SRC, NULL }, + { GDI_DPSDonox, bmp_SRC, NULL }, + { GDI_SPDSxax, bmp_SRC, NULL }, + { GDI_DPon, bmp_SRC, NULL }, + { GDI_DPna, bmp_SRC, NULL }, + { GDI_Pn, bmp_SRC, NULL }, + { GDI_PDna, bmp_SRC, NULL }, + { GDI_DPan, bmp_SRC, NULL }, + { GDI_DSan, bmp_SRC, NULL }, + { GDI_DSxn, bmp_SRC, NULL }, + { GDI_DPa, bmp_SRC, NULL }, + { GDI_DSTCOPY, bmp_SRC, NULL }, + { GDI_DPno, bmp_SRC, NULL }, + { GDI_SDno, bmp_SRC, NULL }, + { GDI_PDno, bmp_SRC, NULL }, + { GDI_DPo, bmp_SRC, NULL } }; + const UINT32 number_tests = sizeof(tests) / sizeof(tests[0]); + HGDI_BITMAP hBmpSrc = NULL; + HGDI_BITMAP hBmpDst = NULL; + HGDI_BITMAP hBmpDstOriginal = NULL; + HGDI_BRUSH brush = NULL; + gdiPalette g; + gdiPalette* hPalette = &g; + g.format = DstFormat; + + for (UINT32 x = 0; x < 256; x++) + g.palette[x] = FreeRDPGetColor(DstFormat, x, x, x, 0xFF); + + if (!(hdcSrc = gdi_GetDC())) + { + printf("failed to get gdi device context\n"); + goto fail; + } + + hdcSrc->format = SrcFormat; + + if (!(hdcDst = gdi_GetDC())) + { + printf("failed to get gdi device context\n"); + goto fail; + } + + hdcDst->format = DstFormat; + hBmpSrc = + test_convert_to_bitmap(bmp_SRC, RawFormat, 0, 0, 0, SrcFormat, 0, 0, 0, 16, 16, hPalette); + + if (!hBmpSrc) + goto fail; + + hBmpDst = + test_convert_to_bitmap(bmp_DST, RawFormat, 0, 0, 0, DstFormat, 0, 0, 0, 16, 16, hPalette); + + if (!hBmpDst) + goto fail; + + hBmpDstOriginal = + test_convert_to_bitmap(bmp_DST, RawFormat, 0, 0, 0, SrcFormat, 0, 0, 0, 16, 16, hPalette); + + if (!hBmpDstOriginal) + goto fail; + + for (size_t x = 0; x < ARRAYSIZE(tests); x++) + { + struct test_bitblt* test = &tests[x]; + test->bmp = test_convert_to_bitmap(test->src, RawFormat, 0, 0, 0, SrcFormat, 0, 0, 0, 16, + 16, hPalette); + + if (!test->bmp) + goto fail; + } + + brush = gdi_CreateSolidBrush(0x123456); + gdi_SelectObject(hdcDst, (HGDIOBJECT)brush); + + for (size_t x = 0; x < ARRAYSIZE(tests); x++) + { + struct test_bitblt* test = &tests[x]; + + if (!test_rop(hdcDst, hdcSrc, hBmpSrc, hBmpDst, hBmpDstOriginal, test->rop, test->bmp, + hPalette)) + failed = TRUE; + } + + gdi_SelectObject(hdcDst, NULL); + gdi_DeleteObject((HGDIOBJECT)brush); + rc = !failed; +fail: + + for (size_t x = 0; x < ARRAYSIZE(tests); x++) + { + struct test_bitblt* test = &tests[x]; + gdi_DeleteObject((HGDIOBJECT)test->bmp); + } + + gdi_DeleteObject((HGDIOBJECT)hBmpSrc); + gdi_DeleteObject((HGDIOBJECT)hBmpDst); + gdi_DeleteObject((HGDIOBJECT)hBmpDstOriginal); + gdi_DeleteDC(hdcSrc); + gdi_DeleteDC(hdcDst); + + return rc; +} + +int TestGdiBitBlt(int argc, char* argv[]) +{ + int rc = 0; + const UINT32 formatList[] = { PIXEL_FORMAT_RGB8, PIXEL_FORMAT_RGB15, PIXEL_FORMAT_ARGB15, + PIXEL_FORMAT_RGB16, PIXEL_FORMAT_RGB24, PIXEL_FORMAT_RGBA32, + PIXEL_FORMAT_RGBX32, PIXEL_FORMAT_ARGB32, PIXEL_FORMAT_XRGB32, + PIXEL_FORMAT_BGR15, PIXEL_FORMAT_ABGR15, PIXEL_FORMAT_BGR16, + PIXEL_FORMAT_BGR24, PIXEL_FORMAT_BGRA32, PIXEL_FORMAT_BGRX32, + PIXEL_FORMAT_ABGR32, PIXEL_FORMAT_XBGR32 }; + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + for (size_t x = 0; x < ARRAYSIZE(formatList); x++) + { + /* Skip 8bpp, only supported on remote end. */ + for (size_t y = 1; y < ARRAYSIZE(formatList); y++) + { + if (!test_gdi_BitBlt(formatList[x], formatList[y])) + { + fprintf(stderr, "test_gdi_BitBlt(SrcFormat=%s, DstFormat=%s) failed!\n", + FreeRDPGetColorFormatName(formatList[x]), + FreeRDPGetColorFormatName(formatList[y])); + rc = -1; + } + } + } + + return rc; +} diff --git a/libfreerdp/gdi/test/TestGdiClip.c b/libfreerdp/gdi/test/TestGdiClip.c new file mode 100644 index 0000000..bfef3cd --- /dev/null +++ b/libfreerdp/gdi/test/TestGdiClip.c @@ -0,0 +1,345 @@ + +#include <freerdp/gdi/gdi.h> + +#include <freerdp/gdi/dc.h> +#include <freerdp/gdi/pen.h> +#include <freerdp/gdi/region.h> +#include <freerdp/gdi/bitmap.h> + +#include <winpr/crt.h> + +#include "line.h" +#include "brush.h" +#include "clipping.h" + +static int test_gdi_ClipCoords(void) +{ + int rc = -1; + BOOL draw = 0; + HGDI_DC hdc = NULL; + HGDI_RGN rgn1 = NULL; + HGDI_RGN rgn2 = NULL; + HGDI_BITMAP bmp = NULL; + const UINT32 format = PIXEL_FORMAT_ARGB32; + + if (!(hdc = gdi_GetDC())) + { + printf("failed to get gdi device context\n"); + return -1; + } + + hdc->format = format; + bmp = gdi_CreateBitmapEx(1024, 768, PIXEL_FORMAT_XRGB32, 0, NULL, NULL); + gdi_SelectObject(hdc, (HGDIOBJECT)bmp); + gdi_SetNullClipRgn(hdc); + rgn1 = gdi_CreateRectRgn(0, 0, 0, 0); + rgn2 = gdi_CreateRectRgn(0, 0, 0, 0); + rgn1->null = TRUE; + rgn2->null = TRUE; + /* null clipping region */ + gdi_SetNullClipRgn(hdc); + gdi_SetRgn(rgn1, 20, 20, 100, 100); + gdi_SetRgn(rgn2, 20, 20, 100, 100); + gdi_ClipCoords(hdc, &(rgn1->x), &(rgn1->y), &(rgn1->w), &(rgn1->h), NULL, NULL); + + if (!gdi_EqualRgn(rgn1, rgn2)) + goto fail; + + /* region all inside clipping region */ + gdi_SetClipRgn(hdc, 0, 0, 1024, 768); + gdi_SetRgn(rgn1, 20, 20, 100, 100); + gdi_SetRgn(rgn2, 20, 20, 100, 100); + gdi_ClipCoords(hdc, &(rgn1->x), &(rgn1->y), &(rgn1->w), &(rgn1->h), NULL, NULL); + + if (!gdi_EqualRgn(rgn1, rgn2)) + goto fail; + + /* region all outside clipping region, on the left */ + gdi_SetClipRgn(hdc, 300, 300, 100, 100); + gdi_SetRgn(rgn1, 20, 20, 100, 100); + gdi_SetRgn(rgn2, 0, 0, 0, 0); + draw = gdi_ClipCoords(hdc, &(rgn1->x), &(rgn1->y), &(rgn1->w), &(rgn1->h), NULL, NULL); + + if (draw) + goto fail; + + /* region all outside clipping region, on the right */ + gdi_SetClipRgn(hdc, 300, 300, 100, 100); + gdi_SetRgn(rgn1, 420, 420, 100, 100); + gdi_SetRgn(rgn2, 0, 0, 0, 0); + draw = gdi_ClipCoords(hdc, &(rgn1->x), &(rgn1->y), &(rgn1->w), &(rgn1->h), NULL, NULL); + + if (draw) + goto fail; + + /* region all outside clipping region, on top */ + gdi_SetClipRgn(hdc, 300, 300, 100, 100); + gdi_SetRgn(rgn1, 300, 20, 100, 100); + gdi_SetRgn(rgn2, 0, 0, 0, 0); + draw = gdi_ClipCoords(hdc, &(rgn1->x), &(rgn1->y), &(rgn1->w), &(rgn1->h), NULL, NULL); + + if (draw) + goto fail; + + /* region all outside clipping region, at the bottom */ + gdi_SetClipRgn(hdc, 300, 300, 100, 100); + gdi_SetRgn(rgn1, 300, 420, 100, 100); + gdi_SetRgn(rgn2, 0, 0, 0, 0); + draw = gdi_ClipCoords(hdc, &(rgn1->x), &(rgn1->y), &(rgn1->w), &(rgn1->h), NULL, NULL); + + if (draw) + goto fail; + + /* left outside, right = clip, top = clip, bottom = clip */ + gdi_SetClipRgn(hdc, 300, 300, 100, 100); + gdi_SetRgn(rgn1, 100, 300, 300, 100); + gdi_SetRgn(rgn2, 300, 300, 100, 100); + gdi_ClipCoords(hdc, &(rgn1->x), &(rgn1->y), &(rgn1->w), &(rgn1->h), NULL, NULL); + + if (!gdi_EqualRgn(rgn1, rgn2)) + goto fail; + + /* left outside, right inside, top = clip, bottom = clip */ + gdi_SetClipRgn(hdc, 300, 300, 100, 100); + gdi_SetRgn(rgn1, 100, 300, 250, 100); + gdi_SetRgn(rgn2, 300, 300, 50, 100); + gdi_ClipCoords(hdc, &(rgn1->x), &(rgn1->y), &(rgn1->w), &(rgn1->h), NULL, NULL); + + if (!gdi_EqualRgn(rgn1, rgn2)) + goto fail; + + /* left = clip, right outside, top = clip, bottom = clip */ + gdi_SetClipRgn(hdc, 300, 300, 100, 100); + gdi_SetRgn(rgn1, 300, 300, 300, 100); + gdi_SetRgn(rgn2, 300, 300, 100, 100); + gdi_ClipCoords(hdc, &(rgn1->x), &(rgn1->y), &(rgn1->w), &(rgn1->h), NULL, NULL); + + if (!gdi_EqualRgn(rgn1, rgn2)) + goto fail; + + /* left inside, right outside, top = clip, bottom = clip */ + gdi_SetClipRgn(hdc, 300, 300, 100, 100); + gdi_SetRgn(rgn1, 350, 300, 200, 100); + gdi_SetRgn(rgn2, 350, 300, 50, 100); + gdi_ClipCoords(hdc, &(rgn1->x), &(rgn1->y), &(rgn1->w), &(rgn1->h), NULL, NULL); + + if (!gdi_EqualRgn(rgn1, rgn2)) + goto fail; + + /* top outside, bottom = clip, left = clip, right = clip */ + gdi_SetClipRgn(hdc, 300, 300, 100, 100); + gdi_SetRgn(rgn1, 300, 100, 300, 300); + gdi_SetRgn(rgn2, 300, 300, 100, 100); + gdi_ClipCoords(hdc, &(rgn1->x), &(rgn1->y), &(rgn1->w), &(rgn1->h), NULL, NULL); + + if (!gdi_EqualRgn(rgn1, rgn2)) + goto fail; + + /* top = clip, bottom outside, left = clip, right = clip */ + gdi_SetClipRgn(hdc, 300, 300, 100, 100); + gdi_SetRgn(rgn1, 300, 300, 100, 200); + gdi_SetRgn(rgn2, 300, 300, 100, 100); + gdi_ClipCoords(hdc, &(rgn1->x), &(rgn1->y), &(rgn1->w), &(rgn1->h), NULL, NULL); + + if (!gdi_EqualRgn(rgn1, rgn2)) + goto fail; + + /* top = clip, bottom = clip, top = clip, bottom = clip */ + gdi_SetClipRgn(hdc, 300, 300, 100, 100); + gdi_SetRgn(rgn1, 300, 300, 100, 100); + gdi_SetRgn(rgn2, 300, 300, 100, 100); + gdi_ClipCoords(hdc, &(rgn1->x), &(rgn1->y), &(rgn1->w), &(rgn1->h), NULL, NULL); + + if (!gdi_EqualRgn(rgn1, rgn2)) + goto fail; + + rc = 0; +fail: + gdi_DeleteObject((HGDIOBJECT)rgn1); + gdi_DeleteObject((HGDIOBJECT)rgn2); + gdi_DeleteObject((HGDIOBJECT)bmp); + gdi_DeleteDC(hdc); + return rc; +} + +static int test_gdi_InvalidateRegion(void) +{ + int rc = -1; + HGDI_DC hdc = NULL; + HGDI_RGN rgn1 = NULL; + HGDI_RGN rgn2 = NULL; + HGDI_RGN invalid = NULL; + HGDI_BITMAP bmp = NULL; + const UINT32 format = PIXEL_FORMAT_XRGB32; + + if (!(hdc = gdi_GetDC())) + { + printf("failed to get gdi device context\n"); + return -1; + } + + hdc->format = format; + bmp = gdi_CreateBitmapEx(1024, 768, PIXEL_FORMAT_XRGB32, 0, NULL, NULL); + gdi_SelectObject(hdc, (HGDIOBJECT)bmp); + gdi_SetNullClipRgn(hdc); + hdc->hwnd = (HGDI_WND)calloc(1, sizeof(GDI_WND)); + hdc->hwnd->invalid = gdi_CreateRectRgn(0, 0, 0, 0); + hdc->hwnd->invalid->null = TRUE; + invalid = hdc->hwnd->invalid; + hdc->hwnd->count = 16; + hdc->hwnd->cinvalid = (HGDI_RGN)calloc(hdc->hwnd->count, sizeof(GDI_RGN)); + rgn1 = gdi_CreateRectRgn(0, 0, 0, 0); + rgn2 = gdi_CreateRectRgn(0, 0, 0, 0); + rgn1->null = TRUE; + rgn2->null = TRUE; + /* no previous invalid region */ + invalid->null = TRUE; + gdi_SetRgn(rgn1, 300, 300, 100, 100); + gdi_SetRgn(rgn2, 300, 300, 100, 100); + gdi_InvalidateRegion(hdc, rgn1->x, rgn1->y, rgn1->w, rgn1->h); + + if (!gdi_EqualRgn(invalid, rgn2)) + goto fail; + + /* region same as invalid region */ + gdi_SetRgn(invalid, 300, 300, 100, 100); + gdi_SetRgn(rgn1, 300, 300, 100, 100); + gdi_SetRgn(rgn2, 300, 300, 100, 100); + gdi_InvalidateRegion(hdc, rgn1->x, rgn1->y, rgn1->w, rgn1->h); + + if (!gdi_EqualRgn(invalid, rgn2)) + goto fail; + + /* left outside */ + gdi_SetRgn(invalid, 300, 300, 100, 100); + gdi_SetRgn(rgn1, 100, 300, 300, 100); + gdi_SetRgn(rgn2, 100, 300, 300, 100); + gdi_InvalidateRegion(hdc, rgn1->x, rgn1->y, rgn1->w, rgn1->h); + + if (!gdi_EqualRgn(invalid, rgn2)) + goto fail; + + /* right outside */ + gdi_SetRgn(invalid, 300, 300, 100, 100); + gdi_SetRgn(rgn1, 300, 300, 300, 100); + gdi_SetRgn(rgn2, 300, 300, 300, 100); + gdi_InvalidateRegion(hdc, rgn1->x, rgn1->y, rgn1->w, rgn1->h); + + if (!gdi_EqualRgn(invalid, rgn2)) + goto fail; + + /* top outside */ + gdi_SetRgn(invalid, 300, 300, 100, 100); + gdi_SetRgn(rgn1, 300, 100, 100, 300); + gdi_SetRgn(rgn2, 300, 100, 100, 300); + gdi_InvalidateRegion(hdc, rgn1->x, rgn1->y, rgn1->w, rgn1->h); + + if (!gdi_EqualRgn(invalid, rgn2)) + goto fail; + + /* bottom outside */ + gdi_SetRgn(invalid, 300, 300, 100, 100); + gdi_SetRgn(rgn1, 300, 300, 100, 300); + gdi_SetRgn(rgn2, 300, 300, 100, 300); + gdi_InvalidateRegion(hdc, rgn1->x, rgn1->y, rgn1->w, rgn1->h); + + if (!gdi_EqualRgn(invalid, rgn2)) + goto fail; + + /* left outside, right outside */ + gdi_SetRgn(invalid, 300, 300, 100, 100); + gdi_SetRgn(rgn1, 100, 300, 600, 300); + gdi_SetRgn(rgn2, 100, 300, 600, 300); + gdi_InvalidateRegion(hdc, rgn1->x, rgn1->y, rgn1->w, rgn1->h); + + if (!gdi_EqualRgn(invalid, rgn2)) + goto fail; + + /* top outside, bottom outside */ + gdi_SetRgn(invalid, 300, 300, 100, 100); + gdi_SetRgn(rgn1, 300, 100, 100, 500); + gdi_SetRgn(rgn2, 300, 100, 100, 500); + gdi_InvalidateRegion(hdc, rgn1->x, rgn1->y, rgn1->w, rgn1->h); + + if (!gdi_EqualRgn(invalid, rgn2)) + goto fail; + + /* all outside, left */ + gdi_SetRgn(invalid, 300, 300, 100, 100); + gdi_SetRgn(rgn1, 100, 300, 100, 100); + gdi_SetRgn(rgn2, 100, 300, 300, 100); + gdi_InvalidateRegion(hdc, rgn1->x, rgn1->y, rgn1->w, rgn1->h); + + if (!gdi_EqualRgn(invalid, rgn2)) + goto fail; + + /* all outside, right */ + gdi_SetRgn(invalid, 300, 300, 100, 100); + gdi_SetRgn(rgn1, 700, 300, 100, 100); + gdi_SetRgn(rgn2, 300, 300, 500, 100); + gdi_InvalidateRegion(hdc, rgn1->x, rgn1->y, rgn1->w, rgn1->h); + + if (!gdi_EqualRgn(invalid, rgn2)) + goto fail; + + /* all outside, top */ + gdi_SetRgn(invalid, 300, 300, 100, 100); + gdi_SetRgn(rgn1, 300, 100, 100, 100); + gdi_SetRgn(rgn2, 300, 100, 100, 300); + gdi_InvalidateRegion(hdc, rgn1->x, rgn1->y, rgn1->w, rgn1->h); + + if (!gdi_EqualRgn(invalid, rgn2)) + goto fail; + + /* all outside, bottom */ + gdi_SetRgn(invalid, 300, 300, 100, 100); + gdi_SetRgn(rgn1, 300, 500, 100, 100); + gdi_SetRgn(rgn2, 300, 300, 100, 300); + gdi_InvalidateRegion(hdc, rgn1->x, rgn1->y, rgn1->w, rgn1->h); + + if (!gdi_EqualRgn(invalid, rgn2)) + goto fail; + + /* all outside */ + gdi_SetRgn(invalid, 300, 300, 100, 100); + gdi_SetRgn(rgn1, 100, 100, 600, 600); + gdi_SetRgn(rgn2, 100, 100, 600, 600); + gdi_InvalidateRegion(hdc, rgn1->x, rgn1->y, rgn1->w, rgn1->h); + + if (!gdi_EqualRgn(invalid, rgn2)) + goto fail; + + /* everything */ + gdi_SetRgn(invalid, 300, 300, 100, 100); + gdi_SetRgn(rgn1, 0, 0, 1024, 768); + gdi_SetRgn(rgn2, 0, 0, 1024, 768); + gdi_InvalidateRegion(hdc, rgn1->x, rgn1->y, rgn1->w, rgn1->h); + + if (!gdi_EqualRgn(invalid, rgn2)) + goto fail; + + rc = 0; +fail: + gdi_DeleteObject((HGDIOBJECT)rgn1); + gdi_DeleteObject((HGDIOBJECT)rgn2); + gdi_DeleteObject((HGDIOBJECT)bmp); + gdi_DeleteDC(hdc); + return rc; +} + +int TestGdiClip(int argc, char* argv[]) +{ + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + fprintf(stderr, "test_gdi_ClipCoords()\n"); + + if (test_gdi_ClipCoords() < 0) + return -1; + + fprintf(stderr, "test_gdi_InvalidateRegion()\n"); + + if (test_gdi_InvalidateRegion() < 0) + return -1; + + return 0; +} diff --git a/libfreerdp/gdi/test/TestGdiCreate.c b/libfreerdp/gdi/test/TestGdiCreate.c new file mode 100644 index 0000000..c71b396 --- /dev/null +++ b/libfreerdp/gdi/test/TestGdiCreate.c @@ -0,0 +1,587 @@ + +#include <freerdp/gdi/gdi.h> + +#include <freerdp/gdi/dc.h> +#include <freerdp/gdi/pen.h> +#include <freerdp/gdi/region.h> +#include <freerdp/gdi/bitmap.h> + +#include <winpr/crt.h> + +#include "line.h" +#include "brush.h" +#include "drawing.h" + +static const UINT32 colorFormatList[] = { + PIXEL_FORMAT_RGB15, PIXEL_FORMAT_BGR15, PIXEL_FORMAT_RGB16, PIXEL_FORMAT_BGR16, + PIXEL_FORMAT_RGB24, PIXEL_FORMAT_BGR24, PIXEL_FORMAT_ARGB32, PIXEL_FORMAT_ABGR32, + PIXEL_FORMAT_XRGB32, PIXEL_FORMAT_XBGR32, PIXEL_FORMAT_RGBX32, PIXEL_FORMAT_BGRX32 + +}; +static const UINT32 colorFormatCount = sizeof(colorFormatList) / sizeof(colorFormatList[0]); + +static int test_gdi_GetDC(void) +{ + int rc = -1; + HGDI_DC hdc = NULL; + + if (!(hdc = gdi_GetDC())) + { + printf("failed to get gdi device context\n"); + return -1; + } + + if (hdc->format != PIXEL_FORMAT_XRGB32) + goto fail; + + if (hdc->drawMode != GDI_R2_BLACK) + goto fail; + + rc = 0; +fail: + gdi_DeleteDC(hdc); + return rc; +} + +static int test_gdi_CreateCompatibleDC(void) +{ + int rc = -1; + HGDI_DC hdc = NULL; + HGDI_DC chdc = NULL; + + if (!(hdc = gdi_GetDC())) + { + printf("failed to get gdi device context\n"); + return -1; + } + + hdc->format = PIXEL_FORMAT_RGB16; + hdc->drawMode = GDI_R2_XORPEN; + + if (!(chdc = gdi_CreateCompatibleDC(hdc))) + { + printf("gdi_CreateCompatibleDC failed\n"); + goto fail; + } + + if (chdc->format != hdc->format) + goto fail; + + if (chdc->drawMode != hdc->drawMode) + goto fail; + + rc = 0; +fail: + + if (chdc) + gdi_DeleteDC(chdc); + + gdi_DeleteDC(hdc); + return rc; +} + +static int test_gdi_CreateBitmap(void) +{ + int rc = -1; + UINT32 format = PIXEL_FORMAT_ARGB32; + INT32 width = 0; + INT32 height = 0; + BYTE* data = NULL; + HGDI_BITMAP hBitmap = NULL; + width = 32; + height = 16; + + if (!(data = (BYTE*)winpr_aligned_malloc(width * height * 4, 16))) + { + printf("failed to allocate aligned bitmap data memory\n"); + return -1; + } + + if (!(hBitmap = gdi_CreateBitmap(width, height, format, data))) + { + printf("gdi_CreateBitmap failed\n"); + goto fail; + } + + if (hBitmap->objectType != GDIOBJECT_BITMAP) + goto fail; + + if (hBitmap->format != format) + goto fail; + + if (hBitmap->width != width) + goto fail; + + if (hBitmap->height != height) + goto fail; + + if (hBitmap->data != data) + goto fail; + + rc = 0; +fail: + + if (hBitmap) + gdi_DeleteObject((HGDIOBJECT)hBitmap); + else + free(data); + + return rc; +} + +static int test_gdi_CreateCompatibleBitmap(void) +{ + int rc = -1; + HGDI_DC hdc = NULL; + INT32 width = 0; + INT32 height = 0; + HGDI_BITMAP hBitmap = NULL; + + if (!(hdc = gdi_GetDC())) + { + printf("failed to get gdi device context\n"); + return -1; + } + + hdc->format = PIXEL_FORMAT_ARGB32; + width = 32; + height = 16; + hBitmap = gdi_CreateCompatibleBitmap(hdc, width, height); + + if (hBitmap->objectType != GDIOBJECT_BITMAP) + goto fail; + + if (hBitmap->format != hdc->format) + goto fail; + + if (hBitmap->width != width) + goto fail; + + if (hBitmap->height != height) + goto fail; + + if (!hBitmap->data) + goto fail; + + rc = 0; +fail: + + if (hBitmap) + gdi_DeleteObject((HGDIOBJECT)hBitmap); + + gdi_DeleteDC(hdc); + return rc; +} + +static int test_gdi_CreatePen(void) +{ + int rc = -1; + const UINT32 format = PIXEL_FORMAT_RGBA32; + HGDI_PEN hPen = gdi_CreatePen(GDI_PS_SOLID, 8, 0xAABBCCDD, format, NULL); + + if (!hPen) + { + printf("gdi_CreatePen failed\n"); + return -1; + } + + if (hPen->style != GDI_PS_SOLID) + goto fail; + + if (hPen->width != 8) + goto fail; + + if (hPen->color != 0xAABBCCDD) + goto fail; + + rc = 0; +fail: + gdi_DeleteObject((HGDIOBJECT)hPen); + return rc; +} + +static int test_gdi_CreateSolidBrush(void) +{ + int rc = -1; + HGDI_BRUSH hBrush = gdi_CreateSolidBrush(0xAABBCCDD); + + if (hBrush->objectType != GDIOBJECT_BRUSH) + goto fail; + + if (hBrush->style != GDI_BS_SOLID) + goto fail; + + if (hBrush->color != 0xAABBCCDD) + goto fail; + + rc = 0; +fail: + gdi_DeleteObject((HGDIOBJECT)hBrush); + return rc; +} + +static int test_gdi_CreatePatternBrush(void) +{ + int rc = -1; + HGDI_BRUSH hBrush = NULL; + HGDI_BITMAP hBitmap = NULL; + hBitmap = gdi_CreateBitmap(64, 64, 32, NULL); + hBrush = gdi_CreatePatternBrush(hBitmap); + + if (!hBitmap || !hBrush) + goto fail; + + if (hBrush->objectType != GDIOBJECT_BRUSH) + goto fail; + + if (hBrush->style != GDI_BS_PATTERN) + goto fail; + + if (hBrush->pattern != hBitmap) + goto fail; + + rc = 0; +fail: + + if (hBitmap) + gdi_DeleteObject((HGDIOBJECT)hBitmap); + + if (hBrush) + gdi_DeleteObject((HGDIOBJECT)hBrush); + + return rc; +} + +static int test_gdi_CreateRectRgn(void) +{ + int rc = -1; + INT32 x1 = 32; + INT32 y1 = 64; + INT32 x2 = 128; + INT32 y2 = 256; + HGDI_RGN hRegion = gdi_CreateRectRgn(x1, y1, x2, y2); + + if (!hRegion) + return rc; + + if (hRegion->objectType != GDIOBJECT_REGION) + goto fail; + + if (hRegion->x != x1) + goto fail; + + if (hRegion->y != y1) + goto fail; + + if (hRegion->w != x2 - x1 + 1) + goto fail; + + if (hRegion->h != y2 - y1 + 1) + goto fail; + + if (hRegion->null) + goto fail; + + rc = 0; +fail: + gdi_DeleteObject((HGDIOBJECT)hRegion); + return rc; +} + +static int test_gdi_CreateRect(void) +{ + int rc = -1; + HGDI_RECT hRect = NULL; + INT32 x1 = 32; + INT32 y1 = 64; + INT32 x2 = 128; + INT32 y2 = 256; + + if (!(hRect = gdi_CreateRect(x1, y1, x2, y2))) + { + printf("gdi_CreateRect failed\n"); + return -1; + } + + if (hRect->objectType != GDIOBJECT_RECT) + goto fail; + + if (hRect->left != x1) + goto fail; + + if (hRect->top != y1) + goto fail; + + if (hRect->right != x2) + goto fail; + + if (hRect->bottom != y2) + goto fail; + + rc = 0; +fail: + gdi_DeleteObject((HGDIOBJECT)hRect); + return rc; +} + +static BOOL test_gdi_GetPixel(void) +{ + BOOL rc = TRUE; + + for (UINT32 x = 0; x < colorFormatCount; x++) + { + UINT32 bpp = 0; + HGDI_DC hdc = NULL; + UINT32 width = 128; + UINT32 height = 64; + HGDI_BITMAP hBitmap = NULL; + + if (!(hdc = gdi_GetDC())) + { + printf("failed to get gdi device context\n"); + return -1; + } + + hdc->format = colorFormatList[x]; + hBitmap = gdi_CreateCompatibleBitmap(hdc, width, height); + + if (!hBitmap) + { + gdi_DeleteDC(hdc); + return -1; + } + + gdi_SelectObject(hdc, (HGDIOBJECT)hBitmap); + bpp = FreeRDPGetBytesPerPixel(hBitmap->format); + + for (UINT32 i = 0; i < height; i++) + { + for (UINT32 j = 0; j < width; j++) + { + UINT32 pixel = 0; + const UINT32 color = + FreeRDPGetColor(hBitmap->format, rand(), rand(), rand(), rand()); + FreeRDPWriteColor(&hBitmap->data[i * hBitmap->scanline + j * bpp], hBitmap->format, + color); + pixel = gdi_GetPixel(hdc, j, i); + + if (pixel != color) + { + rc = FALSE; + break; + } + } + + if (!rc) + break; + } + + gdi_DeleteObject((HGDIOBJECT)hBitmap); + gdi_DeleteDC(hdc); + } + + return rc; +} + +static BOOL test_gdi_SetPixel(void) +{ + BOOL rc = TRUE; + + for (UINT32 x = 0; x < colorFormatCount; x++) + { + UINT32 bpp = 0; + HGDI_DC hdc = NULL; + UINT32 width = 128; + UINT32 height = 64; + HGDI_BITMAP hBitmap = NULL; + + if (!(hdc = gdi_GetDC())) + { + printf("failed to get gdi device context\n"); + return FALSE; + } + + hdc->format = colorFormatList[x]; + hBitmap = gdi_CreateCompatibleBitmap(hdc, width, height); + gdi_SelectObject(hdc, (HGDIOBJECT)hBitmap); + bpp = FreeRDPGetBytesPerPixel(hBitmap->format); + + for (UINT32 i = 0; i < height; i++) + { + for (UINT32 j = 0; j < width; j++) + { + UINT32 pixel = 0; + const UINT32 color = + FreeRDPGetColor(hBitmap->format, rand(), rand(), rand(), rand()); + gdi_SetPixel(hdc, j, i, color); + pixel = FreeRDPReadColor(&hBitmap->data[i * hBitmap->scanline + j * bpp], + hBitmap->format); + + if (pixel != color) + { + rc = FALSE; + break; + } + } + + if (!rc) + break; + } + + gdi_DeleteObject((HGDIOBJECT)hBitmap); + gdi_DeleteDC(hdc); + } + + return rc; +} + +static int test_gdi_SetROP2(void) +{ + int rc = -1; + HGDI_DC hdc = NULL; + + if (!(hdc = gdi_GetDC())) + { + printf("failed to get gdi device context\n"); + return -1; + } + + gdi_SetROP2(hdc, GDI_R2_BLACK); + + if (hdc->drawMode != GDI_R2_BLACK) + goto fail; + + rc = 0; +fail: + gdi_DeleteDC(hdc); + return rc; +} + +static int test_gdi_MoveToEx(void) +{ + int rc = -1; + HGDI_DC hdc = NULL; + HGDI_PEN hPen = NULL; + HGDI_POINT prevPoint = NULL; + const UINT32 format = PIXEL_FORMAT_RGBA32; + gdiPalette* palette = NULL; + + if (!(hdc = gdi_GetDC())) + { + printf("failed to get gdi device context\n"); + return -1; + } + + if (!(hPen = gdi_CreatePen(GDI_PS_SOLID, 8, 0xAABBCCDD, format, palette))) + { + printf("gdi_CreatePen failed\n"); + goto fail; + } + + gdi_SelectObject(hdc, (HGDIOBJECT)hPen); + gdi_MoveToEx(hdc, 128, 256, NULL); + + if (hdc->pen->posX != 128) + goto fail; + + if (hdc->pen->posY != 256) + goto fail; + + prevPoint = (HGDI_POINT)malloc(sizeof(GDI_POINT)); + ZeroMemory(prevPoint, sizeof(GDI_POINT)); + gdi_MoveToEx(hdc, 64, 128, prevPoint); + + if (prevPoint->x != 128) + goto fail; + + if (prevPoint->y != 256) + goto fail; + + if (hdc->pen->posX != 64) + goto fail; + + if (hdc->pen->posY != 128) + goto fail; + + rc = 0; +fail: + + if (hPen) + gdi_DeleteObject((HGDIOBJECT)hPen); + + free(prevPoint); + gdi_DeleteDC(hdc); + return rc; +} + +int TestGdiCreate(int argc, char* argv[]) +{ + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + fprintf(stderr, "test_gdi_GetDC()\n"); + + if (test_gdi_GetDC() < 0) + return -1; + + fprintf(stderr, "test_gdi_CreateCompatibleDC()\n"); + + if (test_gdi_CreateCompatibleDC() < 0) + return -1; + + fprintf(stderr, "test_gdi_CreateBitmap()\n"); + + if (test_gdi_CreateBitmap() < 0) + return -1; + + fprintf(stderr, "test_gdi_CreateCompatibleBitmap()\n"); + + if (test_gdi_CreateCompatibleBitmap() < 0) + return -1; + + fprintf(stderr, "test_gdi_CreatePen()\n"); + + if (test_gdi_CreatePen() < 0) + return -1; + + fprintf(stderr, "test_gdi_CreateSolidBrush()\n"); + + if (test_gdi_CreateSolidBrush() < 0) + return -1; + + fprintf(stderr, "test_gdi_CreatePatternBrush()\n"); + + if (test_gdi_CreatePatternBrush() < 0) + return -1; + + fprintf(stderr, "test_gdi_CreateRectRgn()\n"); + + if (test_gdi_CreateRectRgn() < 0) + return -1; + + fprintf(stderr, "test_gdi_CreateRect()\n"); + + if (test_gdi_CreateRect() < 0) + return -1; + + fprintf(stderr, "test_gdi_GetPixel()\n"); + + if (!test_gdi_GetPixel()) + return -1; + + fprintf(stderr, "test_gdi_SetPixel()\n"); + + if (!test_gdi_SetPixel()) + return -1; + + fprintf(stderr, "test_gdi_SetROP2()\n"); + + if (test_gdi_SetROP2() < 0) + return -1; + + fprintf(stderr, "test_gdi_MoveToEx()\n"); + + if (test_gdi_MoveToEx() < 0) + return -1; + + return 0; +} diff --git a/libfreerdp/gdi/test/TestGdiEllipse.c b/libfreerdp/gdi/test/TestGdiEllipse.c new file mode 100644 index 0000000..ca9e629 --- /dev/null +++ b/libfreerdp/gdi/test/TestGdiEllipse.c @@ -0,0 +1,169 @@ + +#include <freerdp/gdi/gdi.h> + +#include <freerdp/gdi/dc.h> +#include <freerdp/gdi/pen.h> +#include <freerdp/gdi/shape.h> +#include <freerdp/gdi/region.h> +#include <freerdp/gdi/bitmap.h> + +#include <winpr/crt.h> +#include <winpr/print.h> + +#include "line.h" +#include "brush.h" +#include "clipping.h" +#include "helpers.h" + +/* Ellipse() Test Data */ + +static const BYTE ellipse_case_1[256] = { + "\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF" + "\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF" + "\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF" + "\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF" + "\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF" + "\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF" + "\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF" +}; + +static const BYTE ellipse_case_2[256] = { + "\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF" + "\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF" + "\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF" + "\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF" +}; + +static const BYTE ellipse_case_3[256] = { + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF" + "\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF" + "\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" +}; + +int TestGdiEllipse(int argc, char* argv[]) +{ + int rc = -1; + const UINT32 RawFormat = PIXEL_FORMAT_RGB8; + const UINT32 colorFormats[] = { PIXEL_FORMAT_RGB15, PIXEL_FORMAT_ARGB15, PIXEL_FORMAT_RGB16, + PIXEL_FORMAT_RGB24, PIXEL_FORMAT_ARGB32, PIXEL_FORMAT_XRGB32, + PIXEL_FORMAT_RGBA32, PIXEL_FORMAT_RGBX32, PIXEL_FORMAT_BGR15, + PIXEL_FORMAT_ABGR15, PIXEL_FORMAT_BGR16, PIXEL_FORMAT_BGR24, + PIXEL_FORMAT_ABGR32, PIXEL_FORMAT_XBGR32, PIXEL_FORMAT_BGRA32, + PIXEL_FORMAT_BGRX32 }; + const UINT32 number_formats = sizeof(colorFormats) / sizeof(colorFormats[0]); + gdiPalette g; + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + for (UINT32 i = 0; i < number_formats; i++) + { + HGDI_DC hdc = NULL; + HGDI_PEN pen = NULL; + HGDI_BITMAP hBmp = NULL; + HGDI_BITMAP hBmp_Ellipse_1 = NULL; + HGDI_BITMAP hBmp_Ellipse_2 = NULL; + HGDI_BITMAP hBmp_Ellipse_3 = NULL; + const UINT32 format = colorFormats[i]; + gdiPalette* hPalette = &g; + g.format = format; + + for (UINT32 j = 0; j < 256; j++) + g.palette[i] = FreeRDPGetColor(format, j, j, j, 0xFF); + + rc = -1; + + if (!(hdc = gdi_GetDC())) + { + printf("failed to get gdi device context\n"); + goto fail; + } + + hdc->format = format; + gdi_SetNullClipRgn(hdc); + + if (!(pen = gdi_CreatePen(1, 1, 0, format, hPalette))) + { + printf("gdi_CreatePen failed\n"); + goto fail; + } + + gdi_SelectObject(hdc, (HGDIOBJECT)pen); + hBmp = gdi_CreateCompatibleBitmap(hdc, 16, 16); + gdi_SelectObject(hdc, (HGDIOBJECT)hBmp); + hBmp_Ellipse_1 = test_convert_to_bitmap(ellipse_case_1, RawFormat, 0, 0, 0, format, 0, 0, 0, + 16, 16, hPalette); + + if (!hBmp_Ellipse_1) + goto fail; + + hBmp_Ellipse_2 = test_convert_to_bitmap(ellipse_case_2, RawFormat, 0, 0, 0, format, 0, 0, 0, + 16, 16, hPalette); + + if (!hBmp_Ellipse_2) + goto fail; + + hBmp_Ellipse_3 = test_convert_to_bitmap(ellipse_case_3, RawFormat, 0, 0, 0, format, 0, 0, 0, + 16, 16, hPalette); + + if (!hBmp_Ellipse_3) + goto fail; + + /* Test Case 1: (0,0) -> (16, 16) */ + if (!gdi_BitBlt(hdc, 0, 0, 16, 16, hdc, 0, 0, GDI_WHITENESS, hPalette)) + { + printf("gdi_BitBlt failed (line #%u)\n", __LINE__); + goto fail; + } + + if (!gdi_Ellipse(hdc, 0, 0, 15, 15)) + goto fail; + + rc = 0; + fail: + gdi_DeleteObject((HGDIOBJECT)hBmp_Ellipse_1); + gdi_DeleteObject((HGDIOBJECT)hBmp_Ellipse_2); + gdi_DeleteObject((HGDIOBJECT)hBmp_Ellipse_3); + gdi_DeleteObject((HGDIOBJECT)hBmp); + gdi_DeleteObject((HGDIOBJECT)pen); + gdi_DeleteDC(hdc); + + if (rc != 0) + break; + } + + return rc; +} diff --git a/libfreerdp/gdi/test/TestGdiLine.c b/libfreerdp/gdi/test/TestGdiLine.c new file mode 100644 index 0000000..f014f37 --- /dev/null +++ b/libfreerdp/gdi/test/TestGdiLine.c @@ -0,0 +1,724 @@ +#include <freerdp/gdi/gdi.h> + +#include <freerdp/gdi/dc.h> +#include <freerdp/gdi/pen.h> +#include <freerdp/gdi/region.h> +#include <freerdp/gdi/bitmap.h> + +#include <winpr/crt.h> +#include <winpr/print.h> + +#include "line.h" +#include "brush.h" +#include "clipping.h" +#include "drawing.h" + +#include "helpers.h" + +/* LineTo() Test Data */ +static const BYTE line_to_case_1[256] = { + "\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" +}; + +static const BYTE line_to_case_2[256] = { + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00" +}; + +static const BYTE line_to_case_3[256] = { + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" +}; + +static const BYTE line_to_case_4[256] = { + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" +}; + +static const BYTE line_to_case_5[256] = { + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" +}; + +static const BYTE line_to_case_6[256] = { + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" +}; + +static const BYTE line_to_case_7[256] = { + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" +}; + +static const BYTE line_to_case_8[256] = { + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" +}; + +static const BYTE line_to_case_9[256] = { + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" +}; + +static const BYTE line_to_case_10[256] = { + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" +}; + +static const BYTE line_to_case_11[256] = { + "\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00" +}; + +static const BYTE* line_to_case[] = { line_to_case_1, line_to_case_2, line_to_case_3, + line_to_case_4, line_to_case_5, line_to_case_6, + line_to_case_7, line_to_case_8, line_to_case_9, + line_to_case_10, line_to_case_11 }; + +static const BYTE line_to_R2_BLACK[256] = { + "\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00" +}; + +static const BYTE line_to_R2_NOTMERGEPEN[256] = { + "\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00" +}; + +static const BYTE line_to_R2_MASKNOTPEN[256] = { + "\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00" +}; + +static const BYTE line_to_R2_NOTCOPYPEN[256] = { + "\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00" +}; + +static const BYTE line_to_R2_MASKPENNOT[256] = { + "\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00" +}; + +static const BYTE line_to_R2_NOT[256] = { + "\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00" +}; + +static const BYTE line_to_R2_XORPEN[256] = { + "\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00" +}; + +static const BYTE line_to_R2_NOTMASKPEN[256] = { + "\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00" +}; + +static const BYTE line_to_R2_MASKPEN[256] = { + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" +}; + +static const BYTE line_to_R2_NOTXORPEN[256] = { + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" +}; + +static const BYTE line_to_R2_NOP[256] = { + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" +}; + +static const BYTE line_to_R2_MERGENOTPEN[256] = { + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" +}; + +static const BYTE line_to_R2_COPYPEN[256] = { + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" +}; + +static const BYTE line_to_R2_MERGEPENNOT[256] = { + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" +}; + +static const BYTE line_to_R2_MERGEPEN[256] = { + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" +}; + +static const BYTE line_to_R2_WHITE[256] = { + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" +}; + +#define LINTETO_NUMBER 11 +struct ropMap +{ + UINT32 rop; + HGDI_BITMAP bmp; + const BYTE* src; +}; + +static BOOL test_line(HGDI_DC hdc, const gdiPalette* hPalette, UINT32 mX, UINT32 mY, UINT32 lX, + UINT32 lY, HGDI_BITMAP hBmp, HGDI_BITMAP hOrgBmp, UINT32 cX, UINT32 cY, + UINT32 cW, UINT32 cH) +{ + if (!gdi_BitBlt(hdc, 0, 0, 16, 16, hdc, 0, 0, GDI_WHITENESS, hPalette)) + return FALSE; + + if ((cX > 0) || (cY > 0) || (cW > 0) || (cH > 0)) + gdi_SetClipRgn(hdc, cX, cY, cW, cH); + + gdi_MoveToEx(hdc, mX, mY, NULL); + gdi_LineTo(hdc, lX, lY); + + if (!test_assert_bitmaps_equal(hBmp, hOrgBmp, "Case 10", hPalette)) + return FALSE; + + return TRUE; +} + +int TestGdiLine(int argc, char* argv[]) +{ + int rc = -1; + + const UINT32 RawFormat = PIXEL_FORMAT_RGB8; + const UINT32 colorFormats[] = { PIXEL_FORMAT_RGB15, PIXEL_FORMAT_ARGB15, PIXEL_FORMAT_RGB16, + PIXEL_FORMAT_RGB24, PIXEL_FORMAT_ARGB32, PIXEL_FORMAT_XRGB32, + PIXEL_FORMAT_RGBA32, PIXEL_FORMAT_RGBX32, PIXEL_FORMAT_BGR15, + PIXEL_FORMAT_ABGR15, PIXEL_FORMAT_BGR16, PIXEL_FORMAT_BGR24, + PIXEL_FORMAT_ABGR32, PIXEL_FORMAT_XBGR32, PIXEL_FORMAT_BGRA32, + PIXEL_FORMAT_BGRX32 }; + const size_t number_formats = sizeof(colorFormats) / sizeof(colorFormats[0]); + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + for (size_t ifmt = 0; ifmt < number_formats; ifmt++) + { + gdiPalette g = { 0 }; + HGDI_DC hdc = NULL; + HGDI_PEN pen = NULL; + HGDI_BITMAP hBmp = NULL; + struct ropMap rop_map[] = { { GDI_R2_BLACK, NULL, line_to_R2_BLACK }, + { GDI_R2_NOTMERGEPEN, NULL, line_to_R2_NOTMERGEPEN }, + { GDI_R2_MASKNOTPEN, NULL, line_to_R2_MASKNOTPEN }, + { GDI_R2_NOTCOPYPEN, NULL, line_to_R2_NOTCOPYPEN }, + { GDI_R2_MASKPENNOT, NULL, line_to_R2_MASKPENNOT }, + { GDI_R2_NOT, NULL, line_to_R2_NOT }, + { GDI_R2_XORPEN, NULL, line_to_R2_XORPEN }, + { GDI_R2_NOTMASKPEN, NULL, line_to_R2_NOTMASKPEN }, + { GDI_R2_MASKPEN, NULL, line_to_R2_MASKPEN }, + { GDI_R2_NOTXORPEN, NULL, line_to_R2_NOTXORPEN }, + { GDI_R2_NOP, NULL, line_to_R2_NOP }, + { GDI_R2_MERGENOTPEN, NULL, line_to_R2_MERGENOTPEN }, + { GDI_R2_COPYPEN, NULL, line_to_R2_COPYPEN }, + { GDI_R2_MERGEPENNOT, NULL, line_to_R2_MERGEPENNOT }, + { GDI_R2_MERGEPEN, NULL, line_to_R2_MERGEPEN }, + { GDI_R2_WHITE, NULL, line_to_R2_WHITE } }; + const size_t map_size = sizeof(rop_map) / sizeof(rop_map[0]); + HGDI_BITMAP hBmp_LineTo[LINTETO_NUMBER] = { NULL }; + gdiPalette* hPalette = &g; + UINT32 penColor = 0; + const UINT32 format = colorFormats[ifmt]; + g.format = format; + + for (unsigned x = 0; x < 256; x++) + g.palette[x] = FreeRDPGetColor(format, x, x, x, 0xFF); + + rc = -1; + + if (!(hdc = gdi_GetDC())) + { + printf("failed to get gdi device context\n"); + goto fail; + } + + hdc->format = format; + gdi_SetNullClipRgn(hdc); + penColor = FreeRDPGetColor(format, 0xFF, 0xFF, 0xFF, 0xFF); + + if (!(pen = gdi_CreatePen(1, 1, penColor, format, hPalette))) + { + printf("gdi_CreatePen failed\n"); + goto fail; + } + + gdi_SelectObject(hdc, (HGDIOBJECT)pen); + hBmp = gdi_CreateCompatibleBitmap(hdc, 16, 16); + gdi_SelectObject(hdc, (HGDIOBJECT)hBmp); + + for (UINT32 x = 0; x < LINTETO_NUMBER; x++) + { + hBmp_LineTo[x] = test_convert_to_bitmap(line_to_case[x], RawFormat, 0, 0, 0, format, 0, + 0, 0, 16, 16, hPalette); + + if (!hBmp_LineTo[x]) + goto fail; + } + + for (UINT32 x = 0; x < map_size; x++) + { + rop_map[x].bmp = test_convert_to_bitmap(rop_map[x].src, RawFormat, 0, 0, 0, format, 0, + 0, 0, 16, 16, hPalette); + + if (!rop_map[x].bmp) + goto fail; + } + + if (!test_line(hdc, hPalette, 0, 0, 15, 15, hBmp, hBmp_LineTo[0], 0, 0, 16, 16)) + goto fail; + + if (!test_line(hdc, hPalette, 15, 15, 0, 0, hBmp, hBmp_LineTo[1], 0, 0, 16, 16)) + goto fail; + + if (!test_line(hdc, hPalette, 15, 0, 0, 15, hBmp, hBmp_LineTo[2], 0, 0, 16, 16)) + goto fail; + + if (!test_line(hdc, hPalette, 0, 15, 15, 0, hBmp, hBmp_LineTo[3], 0, 0, 16, 16)) + goto fail; + + if (!test_line(hdc, hPalette, 0, 8, 15, 8, hBmp, hBmp_LineTo[4], 0, 0, 16, 16)) + goto fail; + + if (!test_line(hdc, hPalette, 15, 8, 0, 8, hBmp, hBmp_LineTo[5], 0, 0, 16, 16)) + goto fail; + + if (!test_line(hdc, hPalette, 8, 0, 8, 15, hBmp, hBmp_LineTo[6], 0, 0, 16, 16)) + goto fail; + + if (!test_line(hdc, hPalette, 8, 15, 8, 0, hBmp, hBmp_LineTo[7], 0, 0, 16, 16)) + goto fail; + + if (!test_line(hdc, hPalette, 4, 4, 12, 12, hBmp, hBmp_LineTo[8], 0, 0, 16, 16)) + goto fail; + + if (!test_line(hdc, hPalette, 0, 0, 16, 16, hBmp, hBmp_LineTo[9], 5, 5, 8, 8)) + goto fail; + + if (!test_line(hdc, hPalette, 0, 0, 26, 26, hBmp, hBmp_LineTo[10], 0, 0, 16, 16)) + goto fail; + + for (UINT32 x = 0; x < map_size; x++) + { + char name[1024] = { 0 }; + _snprintf(name, sizeof(name), "%s [%s]", gdi_rop_to_string(rop_map[x].rop), + FreeRDPGetColorFormatName(hdc->format)); + + /* Test Case 13: (0,0) -> (16,16), R2_NOTMERGEPEN */ + if (!gdi_BitBlt(hdc, 0, 0, 16, 16, hdc, 0, 0, GDI_WHITENESS, hPalette)) + { + printf("gdi_BitBlt failed (line #%u)\n", __LINE__); + goto fail; + } + + gdi_SetClipRgn(hdc, 0, 0, 16, 16); + gdi_MoveToEx(hdc, 0, 0, NULL); + gdi_SetROP2(hdc, rop_map[x].rop); + gdi_LineTo(hdc, 16, 16); + + if (!test_assert_bitmaps_equal(hBmp, rop_map[x].bmp, name, hPalette)) + goto fail; + } + + rc = 0; + fail: + + for (UINT32 x = 0; x < LINTETO_NUMBER; x++) + gdi_DeleteObject((HGDIOBJECT)hBmp_LineTo[x]); + + for (UINT32 x = 0; x < map_size; x++) + gdi_DeleteObject((HGDIOBJECT)rop_map[x].bmp); + + gdi_DeleteObject((HGDIOBJECT)hBmp); + gdi_DeleteObject((HGDIOBJECT)pen); + gdi_DeleteDC(hdc); + + if (rc != 0) + break; + } + + return rc; +} diff --git a/libfreerdp/gdi/test/TestGdiRect.c b/libfreerdp/gdi/test/TestGdiRect.c new file mode 100644 index 0000000..87cf577 --- /dev/null +++ b/libfreerdp/gdi/test/TestGdiRect.c @@ -0,0 +1,168 @@ + +#include <freerdp/gdi/gdi.h> + +#include <freerdp/gdi/dc.h> +#include <freerdp/gdi/pen.h> +#include <freerdp/gdi/shape.h> +#include <freerdp/gdi/region.h> +#include <freerdp/gdi/bitmap.h> + +#include <winpr/crt.h> +#include <winpr/print.h> + +#include "line.h" +#include "brush.h" +#include "clipping.h" + +static int test_gdi_PtInRect(void) +{ + int rc = -1; + HGDI_RECT hRect = NULL; + UINT32 left = 20; + UINT32 top = 40; + UINT32 right = 60; + UINT32 bottom = 80; + + if (!(hRect = gdi_CreateRect(left, top, right, bottom))) + { + printf("gdi_CreateRect failed\n"); + return rc; + } + + if (gdi_PtInRect(hRect, 0, 0)) + goto fail; + + if (gdi_PtInRect(hRect, 500, 500)) + goto fail; + + if (gdi_PtInRect(hRect, 40, 100)) + goto fail; + + if (gdi_PtInRect(hRect, 10, 40)) + goto fail; + + if (!gdi_PtInRect(hRect, 30, 50)) + goto fail; + + if (!gdi_PtInRect(hRect, left, top)) + goto fail; + + if (!gdi_PtInRect(hRect, right, bottom)) + goto fail; + + if (!gdi_PtInRect(hRect, right, 60)) + goto fail; + + if (!gdi_PtInRect(hRect, 40, bottom)) + goto fail; + + rc = 0; +fail: + gdi_DeleteObject((HGDIOBJECT)hRect); + return rc; +} + +static int test_gdi_FillRect(void) +{ + int rc = -1; + HGDI_DC hdc = NULL; + HGDI_RECT hRect = NULL; + HGDI_BRUSH hBrush = NULL; + HGDI_BITMAP hBitmap = NULL; + UINT32 color = 0; + UINT32 pixel = 0; + UINT32 rawPixel = 0; + UINT32 badPixels = 0; + UINT32 goodPixels = 0; + UINT32 width = 200; + UINT32 height = 300; + UINT32 left = 20; + UINT32 top = 40; + UINT32 right = 60; + UINT32 bottom = 80; + + if (!(hdc = gdi_GetDC())) + { + printf("failed to get gdi device context\n"); + goto fail; + } + + hdc->format = PIXEL_FORMAT_XRGB32; + + if (!(hRect = gdi_CreateRect(left, top, right, bottom))) + { + printf("gdi_CreateRect failed\n"); + goto fail; + } + + hBitmap = gdi_CreateCompatibleBitmap(hdc, width, height); + ZeroMemory(hBitmap->data, width * height * FreeRDPGetBytesPerPixel(hdc->format)); + gdi_SelectObject(hdc, (HGDIOBJECT)hBitmap); + color = FreeRDPGetColor(PIXEL_FORMAT_ARGB32, 0xAA, 0xBB, 0xCC, 0xFF); + hBrush = gdi_CreateSolidBrush(color); + gdi_FillRect(hdc, hRect, hBrush); + badPixels = 0; + goodPixels = 0; + + for (UINT32 x = 0; x < width; x++) + { + for (UINT32 y = 0; y < height; y++) + { + rawPixel = gdi_GetPixel(hdc, x, y); + pixel = FreeRDPConvertColor(rawPixel, hdc->format, PIXEL_FORMAT_ARGB32, NULL); + + if (gdi_PtInRect(hRect, x, y)) + { + if (pixel == color) + { + goodPixels++; + } + else + { + printf("actual:%08" PRIX32 " expected:%08" PRIX32 "\n", gdi_GetPixel(hdc, x, y), + color); + badPixels++; + } + } + else + { + if (pixel == color) + { + badPixels++; + } + else + { + goodPixels++; + } + } + } + } + + if (goodPixels != width * height) + goto fail; + + if (badPixels != 0) + goto fail; + + rc = 0; +fail: + gdi_DeleteObject((HGDIOBJECT)hBrush); + gdi_DeleteObject((HGDIOBJECT)hBitmap); + gdi_DeleteObject((HGDIOBJECT)hRect); + gdi_DeleteDC(hdc); + return rc; +} + +int TestGdiRect(int argc, char* argv[]) +{ + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + if (test_gdi_PtInRect() < 0) + return -1; + + if (test_gdi_FillRect() < 0) + return -1; + + return 0; +} diff --git a/libfreerdp/gdi/test/TestGdiRegion.c b/libfreerdp/gdi/test/TestGdiRegion.c new file mode 100644 index 0000000..3f7ed93 --- /dev/null +++ b/libfreerdp/gdi/test/TestGdiRegion.c @@ -0,0 +1,256 @@ + +#include <freerdp/gdi/gdi.h> + +#include <freerdp/gdi/dc.h> +#include <freerdp/gdi/pen.h> +#include <freerdp/gdi/region.h> +#include <freerdp/gdi/bitmap.h> + +#include <winpr/crt.h> +#include <winpr/print.h> + +#include "helpers.h" + +int TestGdiRegion(int argc, char* argv[]) +{ + int rc = -1; + INT32 x = 0; + INT32 y = 0; + INT32 w = 0; + INT32 h = 0; + INT32 l = 0; + INT32 r = 0; + INT32 t = 0; + INT32 b = 0; + HGDI_RGN rgn1 = NULL; + HGDI_RGN rgn2 = NULL; + HGDI_RECT rect1 = NULL; + HGDI_RECT rect2 = NULL; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + rgn1 = gdi_CreateRectRgn(111, 2, 65, 77); + rect1 = gdi_CreateRect(2311, 11, 42, 17); + if (rgn1 || rect1) + goto fail; + rgn1 = gdi_CreateRectRgn(1, 2, 65, 77); + rgn2 = gdi_CreateRectRgn(11, 2, 65, 77); + rect1 = gdi_CreateRect(23, 11, 42, 17); + rect2 = gdi_CreateRect(23, 11, 42, 17); + if (!rgn1 || !rgn2 || !rect1 || !rect2) + goto fail; + + if (!gdi_RectToRgn(rect1, rgn1)) + goto fail; + if (rgn1->x != rect1->left) + goto fail; + if (rgn1->y != rect1->top) + goto fail; + if (rgn1->w != (rect1->right - rect1->left + 1)) + goto fail; + if (rgn1->h != (rect1->bottom - rect1->top + 1)) + goto fail; + + if (gdi_CRectToRgn(1123, 111, 333, 444, rgn2)) + goto fail; + if (gdi_CRectToRgn(123, 1111, 333, 444, rgn2)) + goto fail; + if (!gdi_CRectToRgn(123, 111, 333, 444, rgn2)) + goto fail; + if (rgn2->x != 123) + goto fail; + if (rgn2->y != 111) + goto fail; + if (rgn2->w != (333 - 123 + 1)) + goto fail; + if (rgn2->h != (444 - 111 + 1)) + goto fail; + + if (!gdi_RectToCRgn(rect1, &x, &y, &w, &h)) + goto fail; + if (rect1->left != x) + goto fail; + if (rect1->top != y) + goto fail; + if (rect1->right != (x + w - 1)) + goto fail; + if (rect1->bottom != (y + h - 1)) + goto fail; + + w = 23; + h = 42; + if (gdi_CRectToCRgn(1, 2, 0, 4, &x, &y, &w, &h)) + goto fail; + if ((w != 0) || (h != 0)) + goto fail; + w = 23; + h = 42; + if (gdi_CRectToCRgn(1, 2, 3, 1, &x, &y, &w, &h)) + goto fail; + if ((w != 0) || (h != 0)) + goto fail; + w = 23; + h = 42; + if (!gdi_CRectToCRgn(1, 2, 3, 4, &x, &y, &w, &h)) + goto fail; + if (x != 1) + goto fail; + if (y != 2) + goto fail; + if (w != (3 - 1 + 1)) + goto fail; + if (h != (4 - 2 + 1)) + goto fail; + + if (!gdi_RgnToRect(rgn1, rect2)) + goto fail; + + if (rgn1->x != rect2->left) + goto fail; + if (rgn1->y != rect2->top) + goto fail; + if (rgn1->w != (rect2->right - rect2->left + 1)) + goto fail; + if (rgn1->h != (rect2->bottom - rect2->top + 1)) + goto fail; + + if (gdi_CRgnToRect(1, 2, 0, 4, rect2)) + goto fail; + if (gdi_CRgnToRect(1, 2, -1, 4, rect2)) + goto fail; + if (gdi_CRgnToRect(1, 2, 3, 0, rect2)) + goto fail; + if (gdi_CRgnToRect(1, 2, 3, -1, rect2)) + goto fail; + if (!gdi_CRgnToRect(1, 2, 3, 4, rect2)) + goto fail; + if (rect2->left != 1) + goto fail; + if (rect2->right != (1 + 3 - 1)) + goto fail; + if (rect2->top != 2) + goto fail; + if (rect2->bottom != (2 + 4 - 1)) + goto fail; + + if (!gdi_RgnToCRect(rgn1, &l, &t, &r, &b)) + goto fail; + if (rgn1->x != l) + goto fail; + if (rgn1->y != t) + goto fail; + if (rgn1->w != (r - l + 1)) + goto fail; + if (rgn1->h != (b - t + 1)) + goto fail; + + if (gdi_CRgnToCRect(1, 2, -1, 4, &l, &t, &r, &b)) + goto fail; + if (gdi_CRgnToCRect(1, 2, 0, 4, &l, &t, &r, &b)) + goto fail; + if (gdi_CRgnToCRect(1, 2, 3, -1, &l, &t, &r, &b)) + goto fail; + if (gdi_CRgnToCRect(1, 2, 3, -0, &l, &t, &r, &b)) + goto fail; + if (!gdi_CRgnToCRect(1, 2, 3, 4, &l, &t, &r, &b)) + goto fail; + if (l != 1) + goto fail; + if (t != 2) + goto fail; + if (r != (1 + 3 - 1)) + goto fail; + if (b != 2 + 4 - 1) + goto fail; + + if (gdi_CopyOverlap(1, 2, 5, 3, -5, 3)) + goto fail; + if (gdi_CopyOverlap(1, 2, 5, 3, 3, -2)) + goto fail; + if (!gdi_CopyOverlap(1, 2, 5, 3, 2, 3)) + goto fail; + + if (gdi_SetRect(rect2, -4, 500, 66, -5)) + goto fail; + if (gdi_SetRect(rect2, -4, -500, -66, -5)) + goto fail; + if (!gdi_SetRect(rect2, -4, 500, 66, 754)) + goto fail; + + if (gdi_SetRgn(NULL, -23, -42, 33, 99)) + goto fail; + if (gdi_SetRgn(rgn2, -23, -42, -33, 99)) + goto fail; + if (gdi_SetRgn(rgn2, -23, -42, 33, -99)) + goto fail; + if (!gdi_SetRgn(rgn2, -23, -42, 33, 99)) + goto fail; + if (rgn2->x != -23) + goto fail; + if (rgn2->y != -42) + goto fail; + if (rgn2->w != 33) + goto fail; + if (rgn2->h != 99) + goto fail; + if (rgn2->null) + goto fail; + + if (gdi_SetRectRgn(NULL, 33, 22, 44, 33)) + goto fail; + if (gdi_SetRectRgn(rgn1, 331, 22, 44, 33)) + goto fail; + if (gdi_SetRectRgn(rgn1, 33, 122, 44, 33)) + goto fail; + if (!gdi_SetRectRgn(rgn1, 33, 22, 44, 33)) + goto fail; + if (rgn1->x != 33) + goto fail; + if (rgn1->y != 22) + goto fail; + if (rgn1->w != (44 - 33 + 1)) + goto fail; + if (rgn1->h != (33 - 22 + 1)) + goto fail; + + if (gdi_EqualRgn(rgn1, rgn2)) + goto fail; + if (!gdi_EqualRgn(rgn1, rgn1)) + goto fail; + + if (gdi_CopyRect(rect1, NULL)) + goto fail; + if (gdi_CopyRect(NULL, rect1)) + goto fail; + if (gdi_CopyRect(NULL, NULL)) + goto fail; + if (!gdi_CopyRect(rect1, rect2)) + goto fail; + + if (rect1->left != rect2->left) + goto fail; + if (rect1->top != rect2->top) + goto fail; + if (rect1->right != rect2->right) + goto fail; + if (rect1->bottom != rect2->bottom) + goto fail; + + if (gdi_PtInRect(rect1, -23, 550)) + goto fail; + if (gdi_PtInRect(rect1, 2, 3)) + goto fail; + if (!gdi_PtInRect(rect1, 2, 550)) + goto fail; + + // BOOL gdi_InvalidateRegion(HGDI_DC hdc, INT32 x, INT32 y, INT32 w, INT32 h); + + rc = 0; +fail: + free(rgn1); + free(rgn2); + free(rect1); + free(rect2); + return rc; +} diff --git a/libfreerdp/gdi/test/TestGdiRop3.c b/libfreerdp/gdi/test/TestGdiRop3.c new file mode 100644 index 0000000..2756633 --- /dev/null +++ b/libfreerdp/gdi/test/TestGdiRop3.c @@ -0,0 +1,208 @@ + +#include <winpr/crt.h> +#include <winpr/winpr.h> +#include <winpr/collections.h> + +/** + * Ternary Raster Operations: + * See "Windows Graphics Programming: Win32 GDI and DirectDraw", chapter 11. Advanced Bitmap + * Graphics + * + * Operators: + * + * AND & a + * OR | o + * NOT ~ n + * XOR ^ x + * + * Operands: + * + * Pen/Brush P + * Destination D + * Source S + * + * Example: + * + * Raster operation which returns P if S is 1 or D otherwise: + * (rop_S & rop_P) | (~rop_S & rop_D); -> 0xE2 (0x00E20746) + * + * Postfix notation: DSPDxax + * Infix notation: D^(S&(P^D))), (S&P)|(~S&D) + * + * DSPDxax using D^(S&(P^D)): + * + * mov eax, P // P + * xor eax, D // P^D + * and eax, S // S&(P^D) + * xor eax, D // D^(S&(P^D)) + * mov D, eax // write result + * + * DSPDxax using (S&P)|(~S&D): + * + * mov eax, S // S + * and eax, P // S&P + * mov ebx, S // S + * not ebx // ~S + * and ebx, D // ~D&D + * or eax, ebx // (S&P)|(~S&D) + * mov D, eax // write result + * + * Raster operation lower word encoding: + * + * _______________________________________________________________________________ + * | | | | | | | | | | | | | | | | | + * | Op5 | Op4 | Op3 | Op2 | Op1 | Not| Parse String | Offset | + * |____|____|____|____|____|____|____|____|____|____|____|____|____|____|____|____| + * 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + * + * Operator: + * 0: NOT + * 1: XOR + * 2: OR + * 3: AND + * + * Parse String: + * 0: SPDDDDDD + * 1: SPDSPDSP + * 2: SDPSDPSD + * 3: DDDDDDDD + * 4: DDDDDDDD + * 5: S+SP-DSS + * 6: S+SP-PDS + * 7: S+SD-PDS + * + * The lower word for 0x00E20746 is 0x0746 (00000111 01000110) + * + * 00 Op5 (NOT, n) + * 00 Op4 (NOT, n) + * 01 Op3 (XOR, x) + * 11 Op2 (AND, a) + * 01 Op1 (XOR, x) + * 0 Not (unused) + * 001 String (SPDSPDSP) + * 10 Offset (2) + * + * We shift SPDSPDSP to the left by 2: DSPDSPSP + * + * We have 5 operators: 3 binary operators and the last two are unary operators, + * so only four operands are needed. The parse string is truncated to reflect + * the number of operands we need: DSPD + * + * The operator string (from Op1 to Op5) is xaxnn, which can be simplified to xax + * + * The complete string representing the operation is DSPDxax + * + */ + +static char* gdi_convert_postfix_to_infix(const char* postfix) +{ + size_t length = 0; + BOOL unary = 0; + wStack* stack = NULL; + size_t al = 0; + size_t bl = 0; + size_t cl = 0; + size_t dl = 0; + char* a = NULL; + char* b = NULL; + char* c = NULL; + char* d = NULL; + bl = cl = dl = 0; + stack = Stack_New(FALSE); + length = strlen(postfix); + + for (size_t i = 0; i < length; i++) + { + if ((postfix[i] == 'P') || (postfix[i] == 'D') || (postfix[i] == 'S')) + { + /* token is an operand, push on the stack */ + a = malloc(2); + a[0] = postfix[i]; + a[1] = '\0'; + // printf("Operand: %s\n", a); + Stack_Push(stack, a); + } + else + { + /* token is an operator */ + unary = FALSE; + c = malloc(2); + c[0] = postfix[i]; + c[1] = '\0'; + + if (c[0] == 'a') + { + c[0] = '&'; + } + else if (c[0] == 'o') + { + c[0] = '|'; + } + else if (c[0] == 'n') + { + c[0] = '~'; + unary = TRUE; + } + else if (c[0] == 'x') + { + c[0] = '^'; + } + else + { + printf("invalid operator: %c\n", c[0]); + } + + // printf("Operator: %s\n", c); + a = (char*)Stack_Pop(stack); + + if (unary) + b = NULL; + else + b = (char*)Stack_Pop(stack); + + al = strlen(a); + + if (b) + bl = strlen(b); + + cl = 1; + dl = al + bl + cl + 3; + d = malloc(dl + 1); + sprintf_s(d, dl, "(%s%s%s)", b ? b : "", c, a); + Stack_Push(stack, d); + free(a); + free(b); + free(c); + } + } + + d = (char*)Stack_Pop(stack); + Stack_Free(stack); + return d; +} + +static const char* test_ROP3[] = { "DSPDxax", "PSDPxax", "SPna", "DSna", "DPa", + "PDxn", "DSxn", "PSDnox", "PDSona", "DSPDxox", + "DPSDonox", "SPDSxax", "DPon", "DPna", "Pn", + "PDna", "DPan", "DSan", "DSxn", "DPa", + "D", "DPno", "SDno", "PDno", "DPo" }; + +int TestGdiRop3(int argc, char* argv[]) +{ + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + for (size_t index = 0; index < sizeof(test_ROP3) / sizeof(test_ROP3[0]); index++) + { + const char* postfix = test_ROP3[index]; + char* infix = gdi_convert_postfix_to_infix(postfix); + + if (!infix) + return -1; + + printf("%s\t\t%s\n", postfix, infix); + free(infix); + } + + return 0; +} diff --git a/libfreerdp/gdi/test/helpers.c b/libfreerdp/gdi/test/helpers.c new file mode 100644 index 0000000..b04b476 --- /dev/null +++ b/libfreerdp/gdi/test/helpers.c @@ -0,0 +1,137 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * GDI Library Tests + * + * 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 "helpers.h" + +HGDI_BITMAP test_convert_to_bitmap(const BYTE* src, UINT32 SrcFormat, UINT32 SrcStride, UINT32 xSrc, + UINT32 ySrc, UINT32 DstFormat, UINT32 DstStride, UINT32 xDst, + UINT32 yDst, UINT32 nWidth, UINT32 nHeight, + const gdiPalette* hPalette) +{ + HGDI_BITMAP bmp = NULL; + BYTE* data = NULL; + + if (DstStride == 0) + DstStride = nWidth * FreeRDPGetBytesPerPixel(DstFormat); + + data = winpr_aligned_malloc(DstStride * nHeight, 16); + + if (!data) + return NULL; + + if (!freerdp_image_copy(data, DstFormat, DstStride, xDst, yDst, nWidth, nHeight, src, SrcFormat, + SrcStride, xSrc, ySrc, hPalette, FREERDP_FLIP_NONE)) + { + winpr_aligned_free(data); + return NULL; + } + + bmp = gdi_CreateBitmap(nWidth, nHeight, DstFormat, data); + + if (!bmp) + { + winpr_aligned_free(data); + return NULL; + } + + return bmp; +} + +static void test_dump_data(unsigned char* p, int len, int width, const char* name) +{ + unsigned char* line = p; + int thisline = 0; + int offset = 0; + printf("\n%s[%d][%d]:\n", name, len / width, width); + + while (offset < len) + { + int i = 0; + printf("%04x ", offset); + thisline = len - offset; + + if (thisline > width) + thisline = width; + + for (; i < thisline; i++) + printf("%02x ", line[i]); + + for (; i < width; i++) + printf(" "); + + printf("\n"); + offset += thisline; + line += thisline; + } + + printf("\n"); + fflush(stdout); +} + +void test_dump_bitmap(HGDI_BITMAP hBmp, const char* name) +{ + UINT32 stride = hBmp->width * FreeRDPGetBytesPerPixel(hBmp->format); + test_dump_data(hBmp->data, hBmp->height * stride, stride, name); +} + +static BOOL CompareBitmaps(HGDI_BITMAP hBmp1, HGDI_BITMAP hBmp2, const gdiPalette* palette) +{ + const BYTE* p1 = hBmp1->data; + const BYTE* p2 = hBmp2->data; + const UINT32 minw = (hBmp1->width < hBmp2->width) ? hBmp1->width : hBmp2->width; + const UINT32 minh = (hBmp1->height < hBmp2->height) ? hBmp1->height : hBmp2->height; + + for (UINT32 y = 0; y < minh; y++) + { + for (UINT32 x = 0; x < minw; x++) + { + UINT32 colorA = FreeRDPReadColor(p1, hBmp1->format); + UINT32 colorB = FreeRDPReadColor(p2, hBmp2->format); + p1 += FreeRDPGetBytesPerPixel(hBmp1->format); + p2 += FreeRDPGetBytesPerPixel(hBmp2->format); + + if (hBmp1->format != hBmp2->format) + colorB = FreeRDPConvertColor(colorB, hBmp2->format, hBmp1->format, palette); + + if (colorA != colorB) + return FALSE; + } + } + + return TRUE; +} + +BOOL test_assert_bitmaps_equal(HGDI_BITMAP hBmpActual, HGDI_BITMAP hBmpExpected, const char* name, + const gdiPalette* palette) +{ + BOOL bitmapsEqual = CompareBitmaps(hBmpActual, hBmpExpected, palette); + + if (!bitmapsEqual) + { + printf("Testing ROP %s [%s|%s]\n", name, FreeRDPGetColorFormatName(hBmpActual->format), + FreeRDPGetColorFormatName(hBmpExpected->format)); + test_dump_bitmap(hBmpActual, "Actual"); + test_dump_bitmap(hBmpExpected, "Expected"); + fflush(stdout); + fflush(stderr); + return TRUE; // TODO: Fix test cases + } + + return bitmapsEqual; +} diff --git a/libfreerdp/gdi/test/helpers.h b/libfreerdp/gdi/test/helpers.h new file mode 100644 index 0000000..d8be19c --- /dev/null +++ b/libfreerdp/gdi/test/helpers.h @@ -0,0 +1,37 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * GDI Library Tests + * + * 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. + */ + +#ifndef GDI_TEST_HELPERS_H +#define GDI_TEST_HELPERS_H + +#include <freerdp/codec/color.h> +#include <freerdp/gdi/bitmap.h> + +HGDI_BITMAP test_convert_to_bitmap(const BYTE* src, UINT32 SrcFormat, UINT32 SrcStride, UINT32 xSrc, + UINT32 ySrc, UINT32 DstFormat, UINT32 DstStride, UINT32 xDst, + UINT32 yDst, UINT32 nWidth, UINT32 nHeight, + + const gdiPalette* hPalette); + +void test_dump_bitmap(HGDI_BITMAP hBmp, const char* name); +BOOL test_assert_bitmaps_equal(HGDI_BITMAP hBmpActual, HGDI_BITMAP hBmpExpected, const char* name, + const gdiPalette* palette); + +#endif /* __GDI_CORE_H */ diff --git a/libfreerdp/gdi/video.c b/libfreerdp/gdi/video.c new file mode 100644 index 0000000..b574fb7 --- /dev/null +++ b/libfreerdp/gdi/video.c @@ -0,0 +1,212 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Video Optimized Remoting Virtual Channel Extension for X11 + * + * Copyright 2017 David Fort <contact@hardening-consulting.com> + * + * 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 "../core/update.h" + +#include <winpr/assert.h> + +#include <freerdp/client/geometry.h> +#include <freerdp/client/video.h> +#include <freerdp/gdi/gdi.h> +#include <freerdp/gdi/video.h> +#include <freerdp/gdi/region.h> + +#define TAG FREERDP_TAG("video") + +void gdi_video_geometry_init(rdpGdi* gdi, GeometryClientContext* geom) +{ + WINPR_ASSERT(gdi); + WINPR_ASSERT(geom); + + gdi->geometry = geom; + + if (gdi->video) + { + VideoClientContext* video = gdi->video; + + WINPR_ASSERT(video); + WINPR_ASSERT(video->setGeometry); + video->setGeometry(video, gdi->geometry); + } +} + +void gdi_video_geometry_uninit(rdpGdi* gdi, GeometryClientContext* geom) +{ + WINPR_ASSERT(gdi); + WINPR_ASSERT(geom); + WINPR_UNUSED(gdi); + WINPR_UNUSED(geom); +} + +static VideoSurface* gdiVideoCreateSurface(VideoClientContext* video, UINT32 x, UINT32 y, + UINT32 width, UINT32 height) +{ + return VideoClient_CreateCommonContext(sizeof(VideoSurface), x, y, width, height); +} + +static BOOL gdiVideoShowSurface(VideoClientContext* video, const VideoSurface* surface, + UINT32 destinationWidth, UINT32 destinationHeight) +{ + BOOL rc = FALSE; + rdpGdi* gdi = NULL; + rdpUpdate* update = NULL; + + WINPR_ASSERT(video); + WINPR_ASSERT(surface); + + gdi = (rdpGdi*)video->custom; + WINPR_ASSERT(gdi); + WINPR_ASSERT(gdi->context); + + update = gdi->context->update; + WINPR_ASSERT(update); + + if (!update_begin_paint(update)) + goto fail; + + if ((gdi->width < 0) || (gdi->height < 0)) + goto fail; + else + { + const UINT32 nXSrc = surface->x; + const UINT32 nYSrc = surface->y; + const UINT32 nXDst = nXSrc; + const UINT32 nYDst = nYSrc; + const UINT32 width = (destinationWidth + surface->x < (UINT32)gdi->width) + ? destinationWidth + : (UINT32)gdi->width - surface->x; + const UINT32 height = (destinationHeight + surface->y < (UINT32)gdi->height) + ? destinationHeight + : (UINT32)gdi->height - surface->y; + + WINPR_ASSERT(gdi->primary_buffer); + WINPR_ASSERT(gdi->primary); + WINPR_ASSERT(gdi->primary->hdc); + + if (!freerdp_image_scale(gdi->primary_buffer, gdi->primary->hdc->format, gdi->stride, nXDst, + nYDst, width, height, surface->data, surface->format, + surface->scanline, 0, 0, surface->w, surface->h)) + goto fail; + + if ((nXDst > INT32_MAX) || (nYDst > INT32_MAX) || (width > INT32_MAX) || + (height > INT32_MAX)) + goto fail; + + gdi_InvalidateRegion(gdi->primary->hdc, (INT32)nXDst, (INT32)nYDst, (INT32)width, + (INT32)height); + } + + rc = TRUE; +fail: + + if (!update_end_paint(update)) + return FALSE; + + return rc; +} + +static BOOL gdiVideoDeleteSurface(VideoClientContext* video, VideoSurface* surface) +{ + WINPR_UNUSED(video); + VideoClient_DestroyCommonContext(surface); + return TRUE; +} + +void gdi_video_control_init(rdpGdi* gdi, VideoClientContext* video) +{ + WINPR_ASSERT(gdi); + WINPR_ASSERT(video); + + gdi->video = video; + video->custom = gdi; + video->createSurface = gdiVideoCreateSurface; + video->showSurface = gdiVideoShowSurface; + video->deleteSurface = gdiVideoDeleteSurface; + video->setGeometry(video, gdi->geometry); +} + +void gdi_video_control_uninit(rdpGdi* gdi, VideoClientContext* video) +{ + WINPR_ASSERT(gdi); + gdi->video = NULL; +} + +static void gdi_video_timer(void* context, const TimerEventArgs* timer) +{ + rdpContext* ctx = (rdpContext*)context; + rdpGdi* gdi = NULL; + + WINPR_ASSERT(ctx); + WINPR_ASSERT(timer); + + gdi = ctx->gdi; + + if (gdi && gdi->video) + gdi->video->timer(gdi->video, timer->now); +} + +void gdi_video_data_init(rdpGdi* gdi, VideoClientContext* video) +{ + WINPR_ASSERT(gdi); + WINPR_ASSERT(gdi->context); + PubSub_SubscribeTimer(gdi->context->pubSub, gdi_video_timer); +} + +void gdi_video_data_uninit(rdpGdi* gdi, VideoClientContext* context) +{ + WINPR_ASSERT(gdi); + WINPR_ASSERT(gdi->context); + PubSub_UnsubscribeTimer(gdi->context->pubSub, gdi_video_timer); +} + +VideoSurface* VideoClient_CreateCommonContext(size_t size, UINT32 x, UINT32 y, UINT32 w, UINT32 h) +{ + VideoSurface* ret = NULL; + + WINPR_ASSERT(size >= sizeof(VideoSurface)); + + ret = calloc(1, size); + if (!ret) + return NULL; + + ret->format = PIXEL_FORMAT_BGRX32; + ret->x = x; + ret->y = y; + ret->w = w; + ret->h = h; + ret->alignedWidth = ret->w + 32 - ret->w % 16; + ret->alignedHeight = ret->h + 32 - ret->h % 16; + + ret->scanline = ret->alignedWidth * FreeRDPGetBytesPerPixel(ret->format); + ret->data = winpr_aligned_malloc(1ull * ret->scanline * ret->alignedHeight, 64); + if (!ret->data) + goto fail; + return ret; +fail: + VideoClient_DestroyCommonContext(ret); + return NULL; +} + +void VideoClient_DestroyCommonContext(VideoSurface* surface) +{ + if (!surface) + return; + winpr_aligned_free(surface->data); + free(surface); +} |