summaryrefslogtreecommitdiffstats
path: root/libfreerdp/gdi
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 01:24:41 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 01:24:41 +0000
commita9bcc81f821d7c66f623779fa5147e728eb3c388 (patch)
tree98676963bcdd537ae5908a067a8eb110b93486a6 /libfreerdp/gdi
parentInitial commit. (diff)
downloadfreerdp3-a9bcc81f821d7c66f623779fa5147e728eb3c388.tar.xz
freerdp3-a9bcc81f821d7c66f623779fa5147e728eb3c388.zip
Adding upstream version 3.3.0+dfsg1.upstream/3.3.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'libfreerdp/gdi')
-rw-r--r--libfreerdp/gdi/CMakeLists.txt35
-rw-r--r--libfreerdp/gdi/bitmap.c660
-rw-r--r--libfreerdp/gdi/brush.c869
-rw-r--r--libfreerdp/gdi/brush.h51
-rw-r--r--libfreerdp/gdi/clipping.c164
-rw-r--r--libfreerdp/gdi/clipping.h44
-rw-r--r--libfreerdp/gdi/dc.c258
-rw-r--r--libfreerdp/gdi/drawing.c152
-rw-r--r--libfreerdp/gdi/drawing.h45
-rw-r--r--libfreerdp/gdi/gdi.c1439
-rw-r--r--libfreerdp/gdi/gdi.h93
-rw-r--r--libfreerdp/gdi/gfx.c1929
-rw-r--r--libfreerdp/gdi/graphics.c463
-rw-r--r--libfreerdp/gdi/graphics.h35
-rw-r--r--libfreerdp/gdi/line.c315
-rw-r--r--libfreerdp/gdi/line.h44
-rw-r--r--libfreerdp/gdi/pen.c64
-rw-r--r--libfreerdp/gdi/region.c671
-rw-r--r--libfreerdp/gdi/shape.c286
-rw-r--r--libfreerdp/gdi/test/CMakeLists.txt40
-rw-r--r--libfreerdp/gdi/test/TestGdiBitBlt.c577
-rw-r--r--libfreerdp/gdi/test/TestGdiClip.c345
-rw-r--r--libfreerdp/gdi/test/TestGdiCreate.c587
-rw-r--r--libfreerdp/gdi/test/TestGdiEllipse.c169
-rw-r--r--libfreerdp/gdi/test/TestGdiLine.c724
-rw-r--r--libfreerdp/gdi/test/TestGdiRect.c168
-rw-r--r--libfreerdp/gdi/test/TestGdiRegion.c256
-rw-r--r--libfreerdp/gdi/test/TestGdiRop3.c208
-rw-r--r--libfreerdp/gdi/test/helpers.c137
-rw-r--r--libfreerdp/gdi/test/helpers.h37
-rw-r--r--libfreerdp/gdi/video.c212
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(&region);
+
+ 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,
+ &region))
+ {
+ 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(&region, &region, &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(&region, &region, &cmdRect);
+ break;
+
+ default:
+ WLog_ERR(TAG, "Unsupported codecID %" PRIu32 "", cmd->bmp.codecID);
+ break;
+ }
+
+ if (!(rects = region16_rects(&region, &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(&region);
+ 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);
+}