Milestone 5: deliver embedded RDP sessions and lifecycle hardening

This commit is contained in:
Keith Smith
2026-03-03 18:59:26 -07:00
parent 230a401386
commit 36006bd4aa
2941 changed files with 724359 additions and 77 deletions

View File

@@ -0,0 +1,34 @@
# 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})
option(WITH_GFX_FRAME_DUMP "Dump GFX commands to directory" OFF)
if(WITH_GFX_FRAME_DUMP)
freerdp_definition_add(WITH_GFX_FRAME_DUMP)
endif()
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_INTERNAL)
add_subdirectory(test)
endif()

View File

@@ -0,0 +1,675 @@
/**
* 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 * WINPR_ASSERTING_INT_CAST(uint32_t, 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, nullptr 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 nullptr;
hBitmap->objectType = GDIOBJECT_BITMAP;
hBitmap->format = format;
if (stride > 0)
hBitmap->scanline = stride;
else
hBitmap->scanline = nWidth * FreeRDPGetBytesPerPixel(hBitmap->format);
hBitmap->width = WINPR_ASSERTING_INT_CAST(int, nWidth);
hBitmap->height = WINPR_ASSERTING_INT_CAST(int, 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 nullptr;
hBitmap->objectType = GDIOBJECT_BITMAP;
hBitmap->format = hdc->format;
WINPR_ASSERT(nWidth <= INT32_MAX);
hBitmap->width = (INT32)nWidth;
WINPR_ASSERT(nHeight <= INT32_MAX);
hBitmap->height = (INT32)nHeight;
size_t size = 1ull * nWidth * nHeight * FreeRDPGetBytesPerPixel(hBitmap->format);
hBitmap->data = winpr_aligned_malloc(size, 16);
hBitmap->free = winpr_aligned_free;
if (!hBitmap->data)
{
free(hBitmap);
return nullptr;
}
/* Initialize with 0xff */
memset(hBitmap->data, 0xff, size);
hBitmap->scanline = nWidth * FreeRDPGetBytesPerPixel(hBitmap->format);
return hBitmap;
}
static BOOL op_not(UINT32* stack, const 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] = WINPR_C_ARRAY_INIT;
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, WINPR_ASSERTING_INT_CAST(uint32_t, nXDest + x),
WINPR_ASSERTING_INT_CAST(uint32_t, 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 = nullptr;
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 = nullptr;
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 = nullptr;
HGDI_BITMAP hDstBmp = nullptr;
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,
WINPR_ASSERTING_INT_CAST(UINT32, nXDest),
WINPR_ASSERTING_INT_CAST(UINT32, nYDest),
WINPR_ASSERTING_INT_CAST(UINT32, nWidth),
WINPR_ASSERTING_INT_CAST(UINT32, nHeight), hSrcBmp->data, hSrcBmp->format,
hSrcBmp->scanline, WINPR_ASSERTING_INT_CAST(UINT32, nXSrc),
WINPR_ASSERTING_INT_CAST(UINT32, 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,
WINPR_ASSERTING_INT_CAST(UINT32, nXDest),
WINPR_ASSERTING_INT_CAST(UINT32, nYDest),
WINPR_ASSERTING_INT_CAST(UINT32, nWidth),
WINPR_ASSERTING_INT_CAST(UINT32, nHeight), hSrcBmp->data, hSrcBmp->format,
hSrcBmp->scanline, WINPR_ASSERTING_INT_CAST(UINT32, nXSrc),
WINPR_ASSERTING_INT_CAST(UINT32, 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;
}
return gdi_InvalidateRegion(hdcDest, nXDest, nYDest, nWidth, nHeight);
}

View File

@@ -0,0 +1,867 @@
/**
* 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"
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 nullptr;
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 nullptr;
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 nullptr;
hBrush->objectType = GDIOBJECT_BRUSH;
hBrush->style = GDI_BS_HATCHED;
hBrush->pattern = hbmp;
return hBrush;
}

View File

@@ -0,0 +1,60 @@
/**
* 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 <winpr/cast.h>
#include <freerdp/api.h>
#include <freerdp/gdi/gdi.h>
#ifdef __cplusplus
extern "C"
{
#endif
WINPR_ATTR_NODISCARD
FREERDP_LOCAL const char* gdi_rop_to_string(UINT32 code);
WINPR_ATTR_NODISCARD
FREERDP_LOCAL HGDI_BRUSH gdi_CreateSolidBrush(UINT32 crColor);
WINPR_ATTR_NODISCARD
FREERDP_LOCAL HGDI_BRUSH gdi_CreatePatternBrush(HGDI_BITMAP hbmp);
WINPR_ATTR_NODISCARD
FREERDP_LOCAL HGDI_BRUSH gdi_CreateHatchBrush(HGDI_BITMAP hbmp);
WINPR_ATTR_NODISCARD
static inline UINT32 gdi_GetBrushStyle(HGDI_DC hdc)
{
if (!hdc || !hdc->brush)
return GDI_BS_NULL;
return WINPR_ASSERTING_INT_CAST(UINT32, hdc->brush->style);
}
#ifdef __cplusplus
}
#endif
#endif /* FREERDP_LIB_GDI_BRUSH_H */

View File

@@ -0,0 +1,170 @@
/**
* 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
*/
GDI_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)
{
if (!gdi_SetClipRgn(hdc, 0, 0, 0, 0))
return FALSE;
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 = WINPR_C_ARRAY_INIT;
GDI_RECT clip = WINPR_C_ARRAY_INIT;
GDI_RECT coords = WINPR_C_ARRAY_INIT;
int dx = 0;
int dy = 0;
BOOL draw = TRUE;
if (hdc == nullptr)
return FALSE;
HGDI_BITMAP hBmp = (HGDI_BITMAP)hdc->selectedObject;
if (hBmp != nullptr)
{
if (hdc->clip->null)
{
if (!gdi_CRgnToRect(0, 0, hBmp->width, hBmp->height, &clip))
return TRUE;
}
else
{
if (!gdi_RgnToRect(hdc->clip, &clip))
return TRUE;
if (!gdi_CRgnToRect(0, 0, hBmp->width, hBmp->height, &bmp))
return TRUE;
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
{
if (!gdi_RgnToRect(hdc->clip, &clip))
return TRUE;
}
if (!gdi_CRgnToRect(*x, *y, *w, *h, &coords))
return TRUE;
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 != nullptr)
*srcx += dx;
if (srcy != nullptr)
*srcy += dy;
if (!gdi_RectToCRgn(&coords, x, y, w, h))
return FALSE;
return draw;
}

View File

@@ -0,0 +1,51 @@
/**
* 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
WINPR_ATTR_NODISCARD
FREERDP_LOCAL BOOL gdi_SetClipRgn(HGDI_DC hdc, INT32 nXLeft, INT32 nYLeft, INT32 nWidth,
INT32 nHeight);
WINPR_ATTR_NODISCARD
FREERDP_LOCAL GDI_RGN* gdi_GetClipRgn(HGDI_DC hdc);
WINPR_ATTR_NODISCARD
FREERDP_LOCAL BOOL gdi_SetNullClipRgn(HGDI_DC hdc);
WINPR_ATTR_NODISCARD
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 */

258
third_party/FreeRDP/libfreerdp/gdi/dc.c vendored Normal file
View File

@@ -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 nullptr;
hDC->format = PIXEL_FORMAT_XRGB32;
hDC->drawMode = GDI_R2_BLACK;
hDC->clip = gdi_CreateRectRgn(0, 0, 0, 0);
if (!hDC->clip)
{
free(hDC);
return nullptr;
}
hDC->clip->null = TRUE;
hDC->hwnd = nullptr;
return hDC;
}
/**
* @brief Create a device context.
* msdn{dd144871}
*
* @return new device context
*/
HGDI_DC gdi_CreateDC(UINT32 format)
{
HGDI_DC hDC = nullptr;
if (!(hDC = (HGDI_DC)calloc(1, sizeof(GDI_DC))))
return nullptr;
hDC->drawMode = GDI_R2_BLACK;
if (!(hDC->clip = gdi_CreateRectRgn(0, 0, 0, 0)))
goto fail;
hDC->clip->null = TRUE;
hDC->hwnd = nullptr;
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 = (GDI_RGN*)calloc(hDC->hwnd->count, sizeof(GDI_RGN))))
goto fail;
hDC->hwnd->ninvalid = 0;
return hDC;
fail:
gdi_DeleteDC(hDC);
return nullptr;
}
/**
* @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 nullptr;
if (!(hDC->clip = gdi_CreateRectRgn(0, 0, 0, 0)))
{
free(hDC);
return nullptr;
}
hDC->clip->null = TRUE;
hDC->format = hdc->format;
hDC->drawMode = hdc->drawMode;
hDC->hwnd = nullptr;
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 == nullptr)
return nullptr;
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 nullptr;
}
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 = nullptr;
}
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;
}

View File

@@ -0,0 +1,151 @@
/*
* 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.
* msdn{dd144853}
*
* @param hdc device context
*
* @return background mode
*/
INT32 gdi_GetBkMode(HGDI_DC hdc)
{
return hdc->bkMode;
}
/**
* @brief Set the current background mode.
* 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.
* https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-settextcolor
*
* @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;
}

View File

@@ -0,0 +1,54 @@
/**
* 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
WINPR_ATTR_NODISCARD
FREERDP_LOCAL INT32 gdi_GetROP2(HGDI_DC hdc);
FREERDP_LOCAL INT32 gdi_SetROP2(HGDI_DC hdc, INT32 fnDrawMode);
WINPR_ATTR_NODISCARD
FREERDP_LOCAL UINT32 gdi_GetBkColor(HGDI_DC hdc);
FREERDP_LOCAL UINT32 gdi_SetBkColor(HGDI_DC hdc, UINT32 crColor);
WINPR_ATTR_NODISCARD
FREERDP_LOCAL INT32 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 */

1538
third_party/FreeRDP/libfreerdp/gdi/gdi.c vendored Normal file

File diff suppressed because it is too large Load Diff

103
third_party/FreeRDP/libfreerdp/gdi/gdi.h vendored Normal file
View File

@@ -0,0 +1,103 @@
/**
* 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 <winpr/cast.h>
#include "graphics.h"
#include "brush.h"
#include <freerdp/api.h>
WINPR_ATTR_NODISCARD
FREERDP_LOCAL BOOL gdi_bitmap_update(rdpContext* context, const BITMAP_UPDATE* bitmapUpdate);
FREERDP_LOCAL void gdi_bitmap_free_ex(gdiBitmap* bitmap);
WINPR_ATTR_MALLOC(gdi_bitmap_free_ex, 1)
WINPR_ATTR_NODISCARD
FREERDP_LOCAL gdiBitmap* gdi_bitmap_new_ex(rdpGdi* gdi, int width, int height, int bpp, BYTE* data);
WINPR_ATTR_NODISCARD
static inline BYTE* gdi_get_bitmap_pointer(HGDI_DC hdcBmp, INT32 x, INT32 y)
{
HGDI_BITMAP hBmp = (HGDI_BITMAP)hdcBmp->selectedObject;
if ((x >= 0) && (y >= 0) && (x < hBmp->width) && (y < hBmp->height))
{
BYTE* p = hBmp->data + (WINPR_ASSERTING_INT_CAST(size_t, y) * hBmp->scanline) +
(WINPR_ASSERTING_INT_CAST(size_t, x) * FreeRDPGetBytesPerPixel(hdcBmp->format));
return p;
}
else
{
WLog_ERR(FREERDP_TAG("gdi"),
"gdi_get_bitmap_pointer: requesting invalid pointer: (%" PRId32 ",%" PRId32
") in %" PRId32 "x%" PRId32 "",
x, y, hBmp->width, hBmp->height);
return nullptr;
}
}
/**
* 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
*/
WINPR_ATTR_NODISCARD
static inline BYTE* gdi_get_brush_pointer(HGDI_DC hdcBrush, UINT32 x, UINT32 y)
{
BYTE* p = nullptr;
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 */
const UINT32 w = WINPR_ASSERTING_INT_CAST(UINT32, hBmpBrush->width);
const UINT32 h = WINPR_ASSERTING_INT_CAST(UINT32, hBmpBrush->height);
WINPR_ASSERT(w > 0);
WINPR_ASSERT(h > 0);
x = (x + w - (WINPR_ASSERTING_INT_CAST(UINT32, hdcBrush->brush->nXOrg) % w)) % w;
y = (y + h - (WINPR_ASSERTING_INT_CAST(UINT32, hdcBrush->brush->nYOrg) % h)) % h;
p = hBmpBrush->data + (1ULL * y * hBmpBrush->scanline) +
(1ULL * x * FreeRDPGetBytesPerPixel(hBmpBrush->format));
return p;
}
default:
break;
}
p = (BYTE*)&(hdcBrush->textColor);
return p;
}
#endif /* FREERDP_LIB_GDI_CORE_H */

2016
third_party/FreeRDP/libfreerdp/gdi/gfx.c vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,470 @@
/**
* 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 = nullptr;
BYTE* pDstData = nullptr;
HGDI_BITMAP bitmap = nullptr;
if (!gdi)
return nullptr;
nDstStep = nWidth * FreeRDPGetBytesPerPixel(gdi->dstFormat);
pDstData = winpr_aligned_malloc(1ull * nHeight * nDstStep, 16);
if (!pDstData)
return nullptr;
pSrcData = data;
nSrcStep = nWidth * FreeRDPGetBytesPerPixel(SrcFormat);
if (!freerdp_image_copy_no_overlap(pDstData, gdi->dstFormat, nDstStep, 0, 0, nWidth, nHeight,
pSrcData, SrcFormat, nSrcStep, 0, 0, &gdi->palette,
FREERDP_FLIP_NONE))
{
winpr_aligned_free(pDstData);
return nullptr;
}
bitmap = gdi_CreateBitmap(nWidth, nHeight, gdi->dstFormat, pDstData);
return bitmap;
}
static BOOL gdi_Bitmap_New(rdpContext* context, rdpBitmap* bitmap)
{
gdiBitmap* gdi_bitmap = nullptr;
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 = nullptr;
return FALSE;
}
gdi_bitmap->hdc->format = gdi_bitmap->bitmap->format;
gdi_SelectObject(gdi_bitmap->hdc, (HGDIOBJECT)gdi_bitmap->bitmap);
gdi_bitmap->org_bitmap = nullptr;
return TRUE;
}
static void gdi_Bitmap_Free(WINPR_ATTR_UNUSED 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, WINPR_ASSERTING_INT_CAST(int, bitmap->left),
WINPR_ASSERTING_INT_CAST(int, bitmap->top),
WINPR_ASSERTING_INT_CAST(int, width), WINPR_ASSERTING_INT_CAST(int, 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)
{
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 = WINPR_C_ARRAY_INIT;
region16_init(&invalidRegion);
const BOOL rc =
rfx_process_message(context->codecs->rfx, pSrcData, SrcSize, bitmap->left,
bitmap->top, bitmap->data, bitmap->format, gdi->stride,
WINPR_ASSERTING_INT_CAST(UINT32, gdi->height), &invalidRegion);
region16_uninit(&invalidRegion);
if (!rc)
{
WLog_ERR(TAG, "rfx_process_message failed");
return FALSE;
}
}
else if (codecId == RDP_CODEC_ID_NSCODEC)
{
const int 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_no_overlap(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 (!freerdp_bitmap_decompress_planar(context->codecs->planar, pSrcData, SrcSize,
DstWidth, DstHeight, bitmap->data, bitmap->format,
0, 0, 0, DstWidth, DstHeight, TRUE))
{
WLog_ERR(TAG, "freerdp_bitmap_decompress_planar 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_no_overlap(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 = nullptr;
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)
{
if (!context || !glyph)
return FALSE;
gdiGlyph* gdi_glyph = (gdiGlyph*)glyph;
gdi_glyph->hdc = gdi_GetDC();
if (!gdi_glyph->hdc)
return FALSE;
gdi_glyph->hdc->format = PIXEL_FORMAT_MONO;
BYTE* data = freerdp_glyph_convert_ex(glyph->cx, glyph->cy, glyph->aj, glyph->cb);
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 = nullptr;
return TRUE;
}
static void gdi_Glyph_Free(WINPR_ATTR_UNUSED rdpContext* context, rdpGlyph* glyph)
{
gdiGlyph* gdi_glyph = nullptr;
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 = nullptr;
rdpGdi* gdi = nullptr;
HGDI_BRUSH brush = nullptr;
BOOL rc = FALSE;
if (!context || !glyph)
return FALSE;
gdi = context->gdi;
gdi_glyph = (const gdiGlyph*)glyph;
if (!fOpRedundant)
{
GDI_RECT rect = WINPR_C_ARRAY_INIT;
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;
const BOOL res = gdi_FillRect(gdi->drawing->hdc, &rect, brush);
gdi_DeleteObject((HGDIOBJECT)brush);
if (!res)
return res;
}
}
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)
{
if (!context || !context->gdi)
return FALSE;
rdpGdi* gdi = context->gdi;
if (!gdi->drawing || !gdi->drawing->hdc)
return FALSE;
if (!fOpRedundant)
{
if (!gdi_decode_color(gdi, bgcolor, &bgcolor, nullptr))
return FALSE;
if (!gdi_decode_color(gdi, fgcolor, &fgcolor, nullptr))
return FALSE;
if (!gdi_SetClipRgn(gdi->drawing->hdc, x, y, width, height))
return FALSE;
gdi_SetTextColor(gdi->drawing->hdc, bgcolor);
gdi_SetBkColor(gdi->drawing->hdc, fgcolor);
{
GDI_RECT rect = WINPR_C_ARRAY_INIT;
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;
BOOL res = TRUE;
if ((x + width > rect.left) && (y + height > rect.top))
res = gdi_FillRect(gdi->drawing->hdc, &rect, brush);
gdi_DeleteObject((HGDIOBJECT)brush);
if (!res)
return FALSE;
}
return gdi_SetNullClipRgn(gdi->drawing->hdc);
}
return TRUE;
}
static BOOL gdi_Glyph_EndDraw(rdpContext* context, WINPR_ATTR_UNUSED INT32 x,
WINPR_ATTR_UNUSED INT32 y, WINPR_ATTR_UNUSED INT32 width,
WINPR_ATTR_UNUSED INT32 height, WINPR_ATTR_UNUSED UINT32 bgcolor,
WINPR_ATTR_UNUSED UINT32 fgcolor)
{
rdpGdi* gdi = nullptr;
if (!context || !context->gdi)
return FALSE;
gdi = context->gdi;
if (!gdi->drawing || !gdi->drawing->hdc)
return FALSE;
return gdi_SetNullClipRgn(gdi->drawing->hdc);
}
/* Graphics Module */
BOOL gdi_register_graphics(rdpGraphics* graphics)
{
rdpBitmap bitmap = WINPR_C_ARRAY_INIT;
rdpGlyph glyph = WINPR_C_ARRAY_INIT;
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;
}

View File

@@ -0,0 +1,37 @@
/**
* 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>
WINPR_ATTR_NODISCARD
FREERDP_LOCAL HGDI_BITMAP gdi_create_bitmap(rdpGdi* gdi, UINT32 width, UINT32 height, UINT32 format,
BYTE* data);
WINPR_ATTR_NODISCARD
FREERDP_LOCAL BOOL gdi_register_graphics(rdpGraphics* graphics);
#endif /* FREERDP_LIB_GDI_GRAPHICS_H */

View File

@@ -0,0 +1,316 @@
/**
* 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(INT32 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, INT32 nXEnd, INT32 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 = WINPR_ASSERTING_INT_CAST(int32_t, nXEnd);
const INT32 y2 = WINPR_ASSERTING_INT_CAST(int32_t, 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, WINPR_ASSERTING_INT_CAST(uint32_t, x),
WINPR_ASSERTING_INT_CAST(uint32_t, 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, nullptr))
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 = WINPR_C_ARRAY_INIT;
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, nullptr))
return FALSE;
}
if (!gdi_MoveToEx(hdc, pt.x, pt.y, nullptr))
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, const 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, INT32 X, INT32 Y, HGDI_POINT lpPoint)
{
WINPR_ASSERT(hdc);
if (lpPoint != nullptr)
{
lpPoint->x = hdc->pen->posX;
lpPoint->y = hdc->pen->posY;
}
hdc->pen->posX = X;
hdc->pen->posY = Y;
return TRUE;
}

View File

@@ -0,0 +1,53 @@
/**
* 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
WINPR_ATTR_NODISCARD
FREERDP_LOCAL BOOL gdi_LineTo(HGDI_DC hdc, INT32 nXEnd, INT32 nYEnd);
WINPR_ATTR_NODISCARD
FREERDP_LOCAL BOOL gdi_PolylineTo(HGDI_DC hdc, GDI_POINT* lppt, DWORD cCount);
WINPR_ATTR_NODISCARD
FREERDP_LOCAL BOOL gdi_Polyline(HGDI_DC hdc, GDI_POINT* lppt, UINT32 cPoints);
WINPR_ATTR_NODISCARD
FREERDP_LOCAL BOOL gdi_PolyPolyline(HGDI_DC hdc, GDI_POINT* lppt, const UINT32* lpdwPolyPoints,
DWORD cCount);
WINPR_ATTR_NODISCARD
FREERDP_LOCAL BOOL gdi_MoveToEx(HGDI_DC hdc, INT32 X, INT32 Y, HGDI_POINT lpPoint);
#ifdef __cplusplus
}
#endif
#endif /* FREERDP_LIB_GDI_LINE_H */

View File

@@ -0,0 +1,65 @@
/**
* 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 nullptr;
hPen->objectType = GDIOBJECT_PEN;
hPen->style = fnPenStyle;
hPen->color = crColor;
WINPR_ASSERT(nWidth <= INT32_MAX);
hPen->width = (int)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);
}

View File

@@ -0,0 +1,689 @@
/**
* 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 <winpr/wtypes.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 GDI_RECT* rect)
{
if (!buffer || (size < 1) || !rect)
return nullptr;
(void)_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 GDI_RGN* rgn)
{
if (!buffer || (size < 1) || !rgn)
return nullptr;
(void)_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
*/
GDI_RGN* gdi_CreateRectRgn(INT32 nLeftRect, INT32 nTopRect, INT32 nRightRect, INT32 nBottomRect)
{
INT64 w = 0;
INT64 h = 0;
GDI_RGN* hRgn = nullptr;
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 nullptr;
}
hRgn = (GDI_RGN*)calloc(1, sizeof(GDI_RGN));
if (!hRgn)
return nullptr;
hRgn->objectType = GDIOBJECT_REGION;
hRgn->x = nLeftRect;
hRgn->y = nTopRect;
hRgn->w = (INT32)w;
hRgn->h = (INT32)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
*/
GDI_RECT* gdi_CreateRect(INT32 xLeft, INT32 yTop, INT32 xRight, INT32 yBottom)
{
GDI_RECT* hRect = nullptr;
if (xLeft > xRight)
return nullptr;
if (yTop > yBottom)
return nullptr;
hRect = (GDI_RECT*)calloc(1, sizeof(GDI_RECT));
if (!hRect)
return nullptr;
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 GDI_RECT* rect, GDI_RGN* rgn)
{
WINPR_ASSERT(rect);
WINPR_ASSERT(rgn);
BOOL rc = TRUE;
INT64 w = rect->right - rect->left + 1ll;
INT64 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 = (INT32)w;
rgn->h = (INT32)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, GDI_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 = (INT32)w;
rgn->h = (INT32)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 GDI_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 = (INT32)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 = (INT32)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 = (INT32)wl;
*h = (INT32)hl;
return rc;
}
/**
* Convert a region to a rectangle.
* @param rgn source region
* @param rect destination rectangle
*/
BOOL gdi_RgnToRect(const GDI_RGN* rgn, GDI_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 = (INT32)r;
rect->bottom = (INT32)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
*/
BOOL gdi_CRgnToRect(INT64 x, INT64 y, INT32 w, INT32 h, GDI_RECT* rect)
{
BOOL invalid = FALSE;
const INT64 r = x + w - 1;
const INT64 b = y + h - 1;
WINPR_ASSERT(x <= INT32_MAX);
WINPR_ASSERT(y <= INT32_MAX);
WINPR_ASSERT(r <= INT32_MAX);
WINPR_ASSERT(b <= INT32_MAX);
rect->left = (x > 0) ? (INT32)x : 0;
rect->top = (y > 0) ? (INT32)y : 0;
rect->right = rect->left;
rect->bottom = rect->top;
if ((w <= 0) || (h <= 0))
invalid = TRUE;
if (r > 0)
rect->right = (INT32)r;
else
invalid = TRUE;
if (b > 0)
rect->bottom = (INT32)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 GDI_RGN* rgn, INT32* left, INT32* top, INT32* right, INT32* bottom)
{
BOOL rc = TRUE;
WINPR_ASSERT(rgn);
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
*/
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;
if (!gdi_CRgnToRect(x, y, width, height, &dst))
return FALSE;
if (!gdi_CRgnToRect(srcx, srcy, width, height, &src))
return FALSE;
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(GDI_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(GDI_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(GDI_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 GDI_RGN* hSrcRgn1, const GDI_RGN* hSrcRgn2)
{
WINPR_ASSERT(hSrcRgn1);
WINPR_ASSERT(hSrcRgn2);
return ((hSrcRgn1->x == hSrcRgn2->x) && (hSrcRgn1->y == hSrcRgn2->y) &&
(hSrcRgn1->w == hSrcRgn2->w) && (hSrcRgn1->h == hSrcRgn2->h));
}
/**
* @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(GDI_RECT* dst, const GDI_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 GDI_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;
GDI_RGN* invalid = nullptr;
GDI_RGN* cinvalid = nullptr;
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)
{
GDI_RGN* new_rgn = nullptr;
size_t new_cnt = 2ULL * hdc->hwnd->count;
if (new_cnt > UINT32_MAX)
return FALSE;
new_rgn = (GDI_RGN*)realloc(cinvalid, sizeof(GDI_RGN) * new_cnt);
if (!new_rgn)
return FALSE;
hdc->hwnd->count = (UINT32)new_cnt;
cinvalid = new_rgn;
}
hdc->hwnd->cinvalid = cinvalid;
invalid = hdc->hwnd->invalid;
if (!gdi_SetRgn(&cinvalid[hdc->hwnd->ninvalid++], x, y, w, h))
return FALSE;
if (!gdi_CRgnToRect(x, y, w, h, &rgn))
{
invalid->x = 0;
invalid->y = 0;
invalid->w = 0;
invalid->h = 0;
invalid->null = TRUE;
return TRUE;
}
if (invalid->null)
{
invalid->x = x;
invalid->y = y;
invalid->w = w;
invalid->h = h;
invalid->null = FALSE;
return TRUE;
}
if (!gdi_RgnToRect(invalid, &inv))
return FALSE;
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;
return gdi_RectToRgn(&inv, invalid);
}

View File

@@ -0,0 +1,299 @@
/**
* 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")
WINPR_ATTR_NODISCARD
static BOOL Ellipse_Bresenham(HGDI_DC hdc, int x1, int y1, int x2, int y2)
{
INT32 a = (x1 < x2) ? x2 - x1 : x1 - x2;
const INT32 b = (y1 < y2) ? y2 - y1 : y1 - y2;
INT32 c = b & 1;
INT32 dx = 4 * (1 - a) * b * b;
INT32 dy = 4 * (c + 1) * a * a;
INT32 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, WINPR_ASSERTING_INT_CAST(UINT32, x2),
WINPR_ASSERTING_INT_CAST(UINT32, y1), 0);
gdi_SetPixel(hdc, WINPR_ASSERTING_INT_CAST(UINT32, x1),
WINPR_ASSERTING_INT_CAST(UINT32, y1), 0);
gdi_SetPixel(hdc, WINPR_ASSERTING_INT_CAST(UINT32, x1),
WINPR_ASSERTING_INT_CAST(UINT32, y2), 0);
gdi_SetPixel(hdc, WINPR_ASSERTING_INT_CAST(UINT32, x2),
WINPR_ASSERTING_INT_CAST(UINT32, y2), 0);
const INT32 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)
{
y1++;
y2--;
gdi_SetPixel(hdc, WINPR_ASSERTING_INT_CAST(uint32_t, x1 - 1),
WINPR_ASSERTING_INT_CAST(uint32_t, y1), 0);
gdi_SetPixel(hdc, WINPR_ASSERTING_INT_CAST(uint32_t, x1 - 1),
WINPR_ASSERTING_INT_CAST(uint32_t, y2), 0);
}
return TRUE;
}
/**
* 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)
{
return Ellipse_Bresenham(hdc, nLeftRect, nTopRect, nRightRect, nBottomRect);
}
/**
* 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 GDI_RECT* rect, HGDI_BRUSH hbr)
{
INT32 nXDest = 0;
INT32 nYDest = 0;
INT32 nWidth = 0;
INT32 nHeight = 0;
if (!gdi_RectToCRgn(rect, &nXDest, &nYDest, &nWidth, &nHeight))
return FALSE;
if (!hdc || !hbr)
return FALSE;
if (!gdi_ClipCoords(hdc, &nXDest, &nYDest, &nWidth, &nHeight, nullptr, nullptr))
return TRUE;
switch (hbr->style)
{
case GDI_BS_SOLID:
{
const UINT32 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);
}
const BYTE* srcp = gdi_get_bitmap_pointer(hdc, nXDest, nYDest);
const UINT32 formatSize = FreeRDPGetBytesPerPixel(hdc->format);
if (formatSize == 0)
return FALSE;
for (INT32 y = 1; y < nHeight; y++)
{
BYTE* dstp = gdi_get_bitmap_pointer(hdc, nXDest, nYDest + y);
if (!dstp)
return FALSE;
memcpy(dstp, srcp, 1ull * WINPR_ASSERTING_INT_CAST(size_t, nWidth) * formatSize);
}
}
break;
case GDI_BS_HATCHED:
case GDI_BS_PATTERN:
{
const BOOL monochrome = (hbr->pattern->format == PIXEL_FORMAT_MONO);
const UINT32 formatSize = FreeRDPGetBytesPerPixel(hbr->pattern->format);
if (formatSize == 0)
return FALSE;
for (INT32 y = 0; y < nHeight; y++)
{
for (INT32 x = 0; x < nWidth; x++)
{
const size_t yOffset =
((1ULL * WINPR_ASSERTING_INT_CAST(size_t, nYDest) +
WINPR_ASSERTING_INT_CAST(size_t, y)) *
WINPR_ASSERTING_INT_CAST(size_t, hbr->pattern->width) %
WINPR_ASSERTING_INT_CAST(size_t, hbr->pattern->height)) *
formatSize;
const size_t xOffset = ((1ULL * WINPR_ASSERTING_INT_CAST(size_t, nXDest) +
WINPR_ASSERTING_INT_CAST(size_t, x)) %
WINPR_ASSERTING_INT_CAST(size_t, hbr->pattern->width)) *
formatSize;
const BYTE* patp = &hbr->pattern->data[yOffset + xOffset];
UINT32 dstColor = 0;
if (monochrome)
{
if (*patp == 0)
dstColor = hdc->bkColor;
else
dstColor = hdc->textColor;
}
else
{
const UINT32 tmp = FreeRDPReadColor(patp, hbr->pattern->format);
dstColor =
FreeRDPConvertColor(tmp, hbr->pattern->format, hdc->format, nullptr);
}
BYTE* dstp = gdi_get_bitmap_pointer(hdc, nXDest + x, nYDest + y);
if (dstp)
FreeRDPWriteColor(dstp, hdc->format, dstColor);
}
}
}
break;
default:
break;
}
return gdi_InvalidateRegion(hdc, nXDest, nYDest, nWidth, nHeight);
}
/**
* 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(WINPR_ATTR_UNUSED HGDI_DC hdc, WINPR_ATTR_UNUSED GDI_POINT* lpPoints,
WINPR_ATTR_UNUSED 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(WINPR_ATTR_UNUSED HGDI_DC hdc, WINPR_ATTR_UNUSED GDI_POINT* lpPoints,
WINPR_ATTR_UNUSED int* lpPolyCounts, WINPR_ATTR_UNUSED 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, nullptr, nullptr))
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;
}

View File

@@ -0,0 +1,37 @@
set(MODULE_NAME "TestGdi")
set(MODULE_PREFIX "TEST_GDI")
disable_warnings_for_directory(${CMAKE_CURRENT_BINARY_DIR})
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")

View File

@@ -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:
(void)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 = nullptr;
HGDI_DC hdcDst = nullptr;
const UINT32 RawFormat = PIXEL_FORMAT_RGB8;
struct test_bitblt tests[] = { { GDI_SRCCOPY, bmp_SRCCOPY, nullptr },
{ GDI_SPna, bmp_SPna, nullptr },
{ GDI_BLACKNESS, bmp_BLACKNESS, nullptr },
{ GDI_WHITENESS, bmp_WHITENESS, nullptr },
{ GDI_SRCAND, bmp_SRCAND, nullptr },
{ GDI_SRCPAINT, bmp_SRCPAINT, nullptr },
{ GDI_SRCINVERT, bmp_SRCINVERT, nullptr },
{ GDI_SRCERASE, bmp_SRCERASE, nullptr },
{ GDI_NOTSRCCOPY, bmp_NOTSRCCOPY, nullptr },
{ GDI_NOTSRCERASE, bmp_NOTSRCERASE, nullptr },
{ GDI_DSTINVERT, bmp_DSTINVERT, nullptr },
{ GDI_MERGECOPY, bmp_MERGECOPY, nullptr },
{ GDI_MERGEPAINT, bmp_MERGEPAINT, nullptr },
{ GDI_PATCOPY, bmp_PATCOPY, nullptr },
{ GDI_PATPAINT, bmp_PATPAINT, nullptr },
{ GDI_PATINVERT, bmp_PATINVERT, nullptr },
{ GDI_DSTINVERT, bmp_SRC, nullptr },
{ GDI_DSPDxax, bmp_SRC, nullptr },
{ GDI_PSDPxax, bmp_SRC, nullptr },
{ GDI_DSna, bmp_SRC, nullptr },
{ GDI_DPa, bmp_SRC, nullptr },
{ GDI_PDxn, bmp_SRC, nullptr },
{ GDI_DSxn, bmp_SRC, nullptr },
{ GDI_PSDnox, bmp_SRC, nullptr },
{ GDI_PDSona, bmp_SRC, nullptr },
{ GDI_DSPDxox, bmp_SRC, nullptr },
{ GDI_DPSDonox, bmp_SRC, nullptr },
{ GDI_SPDSxax, bmp_SRC, nullptr },
{ GDI_DPon, bmp_SRC, nullptr },
{ GDI_DPna, bmp_SRC, nullptr },
{ GDI_Pn, bmp_SRC, nullptr },
{ GDI_PDna, bmp_SRC, nullptr },
{ GDI_DPan, bmp_SRC, nullptr },
{ GDI_DSan, bmp_SRC, nullptr },
{ GDI_DSxn, bmp_SRC, nullptr },
{ GDI_DPa, bmp_SRC, nullptr },
{ GDI_DSTCOPY, bmp_SRC, nullptr },
{ GDI_DPno, bmp_SRC, nullptr },
{ GDI_SDno, bmp_SRC, nullptr },
{ GDI_PDno, bmp_SRC, nullptr },
{ GDI_DPo, bmp_SRC, nullptr } };
const UINT32 number_tests = sizeof(tests) / sizeof(tests[0]);
HGDI_BITMAP hBmpSrc = nullptr;
HGDI_BITMAP hBmpDst = nullptr;
HGDI_BITMAP hBmpDstOriginal = nullptr;
HGDI_BRUSH brush = nullptr;
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, nullptr);
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]))
{
(void)fprintf(stderr, "test_gdi_BitBlt(SrcFormat=%s, DstFormat=%s) failed!\n",
FreeRDPGetColorFormatName(formatList[x]),
FreeRDPGetColorFormatName(formatList[y]));
rc = -1;
}
}
}
return rc;
}

View File

@@ -0,0 +1,364 @@
#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 bool test_gdi_coords(size_t testNr, HGDI_DC hdc, const GDI_RGN* clip, const GDI_RGN* input,
const GDI_RGN* expect)
{
bool rc = false;
WINPR_ASSERT(hdc);
WINPR_ASSERT(input);
HGDI_RGN rgn1 = gdi_CreateRectRgn(0, 0, 0, 0);
HGDI_RGN rgn2 = gdi_CreateRectRgn(0, 0, 0, 0);
if (!rgn1 || !rgn2)
{
(void)fprintf(stderr, "[%s:%" PRIuz "] gdi_CreateRectRgn failed: rgn1=%p, rgn2=%p\n",
__func__, testNr, rgn1, rgn2);
goto fail;
}
if (!clip)
{
if (!gdi_SetNullClipRgn(hdc))
{
(void)fprintf(stderr, "[%s:%" PRIuz "] gdi_SetNullClipRgn failed\n", __func__, testNr);
goto fail;
}
}
else if (!gdi_SetClipRgn(hdc, clip->x, clip->y, clip->w, clip->h))
{
(void)fprintf(stderr, "[%s:%" PRIuz "] gdi_SetClipRgn failed\n", __func__, testNr);
goto fail;
}
if (!gdi_SetRgn(rgn1, input->x, input->y, input->w, input->h))
{
(void)fprintf(stderr, "[%s:%" PRIuz "] gdi_SetRgn (rgn1) failed\n", __func__, testNr);
goto fail;
}
if (expect)
{
if (!gdi_SetRgn(rgn2, expect->x, expect->y, expect->w, expect->h))
{
(void)fprintf(stderr, "[%s:%" PRIuz "] gdi_SetRgn (rgn2) failed\n", __func__, testNr);
goto fail;
}
}
const BOOL draw =
gdi_ClipCoords(hdc, &(rgn1->x), &(rgn1->y), &(rgn1->w), &(rgn1->h), nullptr, nullptr);
if (expect)
{
if (!draw)
{
(void)fprintf(stderr,
"[%s:%" PRIuz "] gdi_ClipCoords failed: expected TRUE, got FALSE\n",
__func__, testNr);
goto fail;
}
if (!gdi_EqualRgn(rgn1, rgn2))
{
char buffer1[64] = WINPR_C_ARRAY_INIT;
char buffer2[64] = WINPR_C_ARRAY_INIT;
(void)fprintf(stderr, "[%s:%" PRIuz "] gdi_EqualRgn failed: expected %s, got %s\n",
__func__, testNr, buffer1, buffer2);
goto fail;
}
}
else
{
if (draw)
{
(void)fprintf(stderr,
"[%s:%" PRIuz "] gdi_ClipCoords failed: expected FALSE, got TRUE\n",
__func__, testNr);
goto fail;
}
}
rc = true;
fail:
gdi_DeleteObject((HGDIOBJECT)rgn1);
gdi_DeleteObject((HGDIOBJECT)rgn2);
return rc;
}
static int test_gdi_ClipCoords(void)
{
int rc = -1;
HGDI_DC hdc = nullptr;
HGDI_BITMAP bmp = nullptr;
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, nullptr, nullptr);
gdi_SelectObject(hdc, (HGDIOBJECT)bmp);
gdi_SetNullClipRgn(hdc);
struct testcase_t
{
const GDI_RGN* clip;
const GDI_RGN* in;
const GDI_RGN* expect;
};
const GDI_RGN rgn0x0_1024x768 = { 0, 0, 0, 1024, 768, FALSE };
const GDI_RGN rgn20x20_100x100 = { 0, 20, 20, 100, 100, FALSE };
const GDI_RGN rgn100x300_250x100 = { 0, 100, 300, 250, 100, FALSE };
const GDI_RGN rgn100x300_300x100 = { 0, 100, 300, 300, 100, FALSE };
const GDI_RGN rgn300x20_100x100 = { 0, 300, 20, 100, 100, FALSE };
const GDI_RGN rgn300x100_300x300 = { 0, 300, 100, 300, 300, FALSE };
const GDI_RGN rgn300x300_50x100 = { 0, 300, 300, 50, 100, FALSE };
const GDI_RGN rgn300x300_100x100 = { 0, 300, 300, 100, 100, FALSE };
const GDI_RGN rgn300x300_300x100 = { 0, 300, 300, 300, 100, FALSE };
const GDI_RGN rgn300x300_300x200 = { 0, 300, 300, 100, 200, FALSE };
const GDI_RGN rgn300x420_100x100 = { 0, 300, 420, 100, 100, FALSE };
const GDI_RGN rgn350x300_50x100 = { 0, 350, 300, 50, 100, FALSE };
const GDI_RGN rgn350x300_200x100 = { 0, 350, 300, 200, 100, FALSE };
const GDI_RGN rgn420x420_100x100 = { 0, 420, 420, 100, 100, FALSE };
const struct testcase_t testcases[] = {
/* null clipping region */
{ nullptr, &rgn20x20_100x100, &rgn20x20_100x100 },
/* region all inside clipping region */
{ &rgn0x0_1024x768, &rgn20x20_100x100, &rgn20x20_100x100 },
/* region all outside clipping region, on the left */
{ &rgn300x300_100x100, &rgn20x20_100x100, nullptr },
/* region all outside clipping region, on the right */
{ &rgn300x300_100x100, &rgn420x420_100x100, nullptr },
/* region all outside clipping region, on top */
{ &rgn300x300_100x100, &rgn300x20_100x100, nullptr },
/* region all outside clipping region, at the bottom */
{ &rgn300x300_100x100, &rgn300x420_100x100, nullptr },
/* left outside, right = clip, top = clip, bottom = clip */
{ &rgn300x300_100x100, &rgn100x300_300x100, &rgn300x300_100x100 },
/* left outside, right inside, top = clip, bottom = clip */
{ &rgn300x300_100x100, &rgn100x300_250x100, &rgn300x300_50x100 },
/* left = clip, right outside, top = clip, bottom = clip */
{ &rgn300x300_100x100, &rgn300x300_300x100, &rgn300x300_100x100 },
/* left inside, right outside, top = clip, bottom = clip */
{ &rgn300x300_100x100, &rgn350x300_200x100, &rgn350x300_50x100 },
/* top outside, bottom = clip, left = clip, right = clip */
{ &rgn300x300_100x100, &rgn300x100_300x300, &rgn300x300_100x100 },
/* top = clip, bottom outside, left = clip, right = clip */
{ &rgn300x300_100x100, &rgn300x300_300x200, &rgn300x300_100x100 },
/* top = clip, bottom = clip, top = clip, bottom = clip */
{ &rgn300x300_100x100, &rgn300x300_100x100, &rgn300x300_100x100 }
};
for (size_t x = 0; x < ARRAYSIZE(testcases); x++)
{
const struct testcase_t* cur = &testcases[x];
if (!test_gdi_coords(x, hdc, cur->clip, cur->in, cur->expect))
goto fail;
}
rc = 0;
fail:
gdi_DeleteObject((HGDIOBJECT)bmp);
gdi_DeleteDC(hdc);
return rc;
}
static int test_gdi_InvalidateRegion(void)
{
int rc = -1;
HGDI_DC hdc = nullptr;
HGDI_RGN rgn1 = nullptr;
HGDI_RGN rgn2 = nullptr;
HGDI_RGN invalid = nullptr;
HGDI_BITMAP bmp = nullptr;
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, nullptr, nullptr);
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);
(void)fprintf(stderr, "test_gdi_ClipCoords()\n");
if (test_gdi_ClipCoords() < 0)
return -1;
(void)fprintf(stderr, "test_gdi_InvalidateRegion()\n");
if (test_gdi_InvalidateRegion() < 0)
return -1;
return 0;
}

View File

@@ -0,0 +1,600 @@
#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/crypto.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 = nullptr;
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 = nullptr;
HGDI_DC chdc = nullptr;
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 = nullptr;
HGDI_BITMAP hBitmap = nullptr;
width = 32;
height = 16;
if (!(data = (BYTE*)winpr_aligned_malloc(4ULL * width * height, 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
winpr_aligned_free(data);
return rc;
}
static int test_gdi_CreateCompatibleBitmap(void)
{
int rc = -1;
HGDI_DC hdc = nullptr;
INT32 width = 0;
INT32 height = 0;
HGDI_BITMAP hBitmap = nullptr;
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, nullptr);
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 = nullptr;
HGDI_BITMAP hBitmap = nullptr;
hBitmap = gdi_CreateBitmap(64, 64, 32, nullptr);
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;
GDI_RECT* hRect = nullptr;
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 BYTE prand(void)
{
BYTE tmp = 0;
if (winpr_RAND(&tmp, sizeof(tmp)) < 0)
{
(void)fprintf(stderr, "winpr_RAND faild,retry...\n");
// NOLINTNEXTLINE(concurrency-mt-unsafe)
exit(-1);
}
return tmp;
}
static BOOL test_gdi_GetPixel(void)
{
BOOL rc = TRUE;
for (UINT32 x = 0; x < colorFormatCount; x++)
{
UINT32 bpp = 0;
HGDI_DC hdc = nullptr;
UINT32 width = 128;
UINT32 height = 64;
HGDI_BITMAP hBitmap = nullptr;
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, prand(), prand(), prand(), prand());
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 = nullptr;
UINT32 width = 128;
UINT32 height = 64;
HGDI_BITMAP hBitmap = nullptr;
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, prand(), prand(), prand(), prand());
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 = nullptr;
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 = nullptr;
HGDI_PEN hPen = nullptr;
HGDI_POINT prevPoint = nullptr;
const UINT32 format = PIXEL_FORMAT_RGBA32;
gdiPalette* palette = nullptr;
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, nullptr);
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);
(void)fprintf(stderr, "test_gdi_GetDC()\n");
if (test_gdi_GetDC() < 0)
return -1;
(void)fprintf(stderr, "test_gdi_CreateCompatibleDC()\n");
if (test_gdi_CreateCompatibleDC() < 0)
return -1;
(void)fprintf(stderr, "test_gdi_CreateBitmap()\n");
if (test_gdi_CreateBitmap() < 0)
return -1;
(void)fprintf(stderr, "test_gdi_CreateCompatibleBitmap()\n");
if (test_gdi_CreateCompatibleBitmap() < 0)
return -1;
(void)fprintf(stderr, "test_gdi_CreatePen()\n");
if (test_gdi_CreatePen() < 0)
return -1;
(void)fprintf(stderr, "test_gdi_CreateSolidBrush()\n");
if (test_gdi_CreateSolidBrush() < 0)
return -1;
(void)fprintf(stderr, "test_gdi_CreatePatternBrush()\n");
if (test_gdi_CreatePatternBrush() < 0)
return -1;
(void)fprintf(stderr, "test_gdi_CreateRectRgn()\n");
if (test_gdi_CreateRectRgn() < 0)
return -1;
(void)fprintf(stderr, "test_gdi_CreateRect()\n");
if (test_gdi_CreateRect() < 0)
return -1;
(void)fprintf(stderr, "test_gdi_GetPixel()\n");
if (!test_gdi_GetPixel())
return -1;
(void)fprintf(stderr, "test_gdi_SetPixel()\n");
if (!test_gdi_SetPixel())
return -1;
(void)fprintf(stderr, "test_gdi_SetROP2()\n");
if (test_gdi_SetROP2() < 0)
return -1;
(void)fprintf(stderr, "test_gdi_MoveToEx()\n");
if (test_gdi_MoveToEx() < 0)
return -1;
return 0;
}

View File

@@ -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 = nullptr;
HGDI_PEN pen = nullptr;
HGDI_BITMAP hBmp = nullptr;
HGDI_BITMAP hBmp_Ellipse_1 = nullptr;
HGDI_BITMAP hBmp_Ellipse_2 = nullptr;
HGDI_BITMAP hBmp_Ellipse_3 = nullptr;
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;
}

View File

@@ -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, nullptr);
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 = WINPR_C_ARRAY_INIT;
HGDI_DC hdc = nullptr;
HGDI_PEN pen = nullptr;
HGDI_BITMAP hBmp = nullptr;
struct ropMap rop_map[] = { { GDI_R2_BLACK, nullptr, line_to_R2_BLACK },
{ GDI_R2_NOTMERGEPEN, nullptr, line_to_R2_NOTMERGEPEN },
{ GDI_R2_MASKNOTPEN, nullptr, line_to_R2_MASKNOTPEN },
{ GDI_R2_NOTCOPYPEN, nullptr, line_to_R2_NOTCOPYPEN },
{ GDI_R2_MASKPENNOT, nullptr, line_to_R2_MASKPENNOT },
{ GDI_R2_NOT, nullptr, line_to_R2_NOT },
{ GDI_R2_XORPEN, nullptr, line_to_R2_XORPEN },
{ GDI_R2_NOTMASKPEN, nullptr, line_to_R2_NOTMASKPEN },
{ GDI_R2_MASKPEN, nullptr, line_to_R2_MASKPEN },
{ GDI_R2_NOTXORPEN, nullptr, line_to_R2_NOTXORPEN },
{ GDI_R2_NOP, nullptr, line_to_R2_NOP },
{ GDI_R2_MERGENOTPEN, nullptr, line_to_R2_MERGENOTPEN },
{ GDI_R2_COPYPEN, nullptr, line_to_R2_COPYPEN },
{ GDI_R2_MERGEPENNOT, nullptr, line_to_R2_MERGEPENNOT },
{ GDI_R2_MERGEPEN, nullptr, line_to_R2_MERGEPEN },
{ GDI_R2_WHITE, nullptr, line_to_R2_WHITE } };
const size_t map_size = sizeof(rop_map) / sizeof(rop_map[0]);
HGDI_BITMAP hBmp_LineTo[LINTETO_NUMBER] = { nullptr };
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] = WINPR_C_ARRAY_INIT;
_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, nullptr);
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;
}

View File

@@ -0,0 +1,175 @@
#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;
GDI_RECT* hRect = nullptr;
UINT32 left = 20;
UINT32 top = 40;
UINT32 right = 60;
UINT32 bottom = 80;
if (!(hRect = gdi_CreateRect(
WINPR_ASSERTING_INT_CAST(int, left), WINPR_ASSERTING_INT_CAST(int, top),
WINPR_ASSERTING_INT_CAST(int, right), WINPR_ASSERTING_INT_CAST(int, 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, WINPR_ASSERTING_INT_CAST(int, left),
WINPR_ASSERTING_INT_CAST(int, top)))
goto fail;
if (!gdi_PtInRect(hRect, WINPR_ASSERTING_INT_CAST(int, right),
WINPR_ASSERTING_INT_CAST(int, bottom)))
goto fail;
if (!gdi_PtInRect(hRect, WINPR_ASSERTING_INT_CAST(int, right), 60))
goto fail;
if (!gdi_PtInRect(hRect, 40, WINPR_ASSERTING_INT_CAST(int, bottom)))
goto fail;
rc = 0;
fail:
gdi_DeleteObject((HGDIOBJECT)hRect);
return rc;
}
static int test_gdi_FillRect(void)
{
int rc = -1;
HGDI_DC hdc = nullptr;
GDI_RECT* hRect = nullptr;
HGDI_BRUSH hBrush = nullptr;
HGDI_BITMAP hBitmap = nullptr;
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(
WINPR_ASSERTING_INT_CAST(int, left), WINPR_ASSERTING_INT_CAST(int, top),
WINPR_ASSERTING_INT_CAST(int, right), WINPR_ASSERTING_INT_CAST(int, bottom))))
{
printf("gdi_CreateRect failed\n");
goto fail;
}
hBitmap = gdi_CreateCompatibleBitmap(hdc, width, height);
ZeroMemory(hBitmap->data, 1ULL * 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, nullptr);
if (gdi_PtInRect(hRect, WINPR_ASSERTING_INT_CAST(int, x),
WINPR_ASSERTING_INT_CAST(int, 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;
}

View File

@@ -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 = nullptr;
HGDI_RGN rgn2 = nullptr;
GDI_RECT* rect1 = nullptr;
GDI_RECT* rect2 = nullptr;
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(nullptr, -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(nullptr, 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, nullptr))
goto fail;
if (gdi_CopyRect(nullptr, rect1))
goto fail;
if (gdi_CopyRect(nullptr, nullptr))
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;
}

View File

@@ -0,0 +1,219 @@
#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)
{
BOOL unary = 0;
size_t al = 0;
size_t bl = 0;
wStack* stack = Stack_New(FALSE);
size_t length = strlen(postfix);
for (size_t i = 0; i < length; i++)
{
BOOL success = FALSE;
if ((postfix[i] == 'P') || (postfix[i] == 'D') || (postfix[i] == 'S'))
{
/* token is an operand, push on the stack */
char* a = malloc(2);
if (!a)
goto end;
a[0] = postfix[i];
a[1] = '\0';
// printf("Operand: %s\n", a);
Stack_Push(stack, a);
}
else
{
char* a = nullptr;
char* b = nullptr;
/* token is an operator */
unary = FALSE;
char* c = malloc(2);
if (!c)
goto fail;
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 (!a)
goto fail;
if (!unary)
b = (char*)Stack_Pop(stack);
al = strlen(a);
if (b)
bl = strlen(b);
size_t cl = 1;
size_t dl = al + bl + cl + 3;
char* d = malloc(dl + 1);
if (!d)
goto fail;
(void)sprintf_s(d, dl, "(%s%s%s)", b ? b : "", c, a);
Stack_Push(stack, d);
success = TRUE;
fail:
free(a);
free(b);
free(c);
if (!success)
goto end;
}
}
char* d = (char*)Stack_Pop(stack);
Stack_Free(stack);
return d;
end:
Stack_Free(stack);
return nullptr;
}
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;
}

View File

@@ -0,0 +1,141 @@
/**
* 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 = nullptr;
BYTE* data = nullptr;
if (DstStride == 0)
DstStride = nWidth * FreeRDPGetBytesPerPixel(DstFormat);
data = winpr_aligned_malloc(1ULL * DstStride * nHeight, 16);
if (!data)
return nullptr;
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 nullptr;
}
bmp = gdi_CreateBitmap(nWidth, nHeight, DstFormat, data);
if (!bmp)
{
winpr_aligned_free(data);
return nullptr;
}
return bmp;
}
static void test_dump_data(unsigned char* p, size_t len, size_t width, const char* name)
{
unsigned char* line = p;
const size_t stride = (width > 0) ? len / width : 1;
size_t offset = 0;
printf("\n%s[%" PRIuz "][%" PRIuz "]:\n", name, stride, width);
while (offset < len)
{
size_t i = 0;
printf("%04" PRIxz " ", offset);
size_t 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");
(void)fflush(stdout);
}
void test_dump_bitmap(HGDI_BITMAP hBmp, const char* name)
{
const size_t stride =
WINPR_ASSERTING_INT_CAST(size_t, hBmp->width) * FreeRDPGetBytesPerPixel(hBmp->format);
test_dump_data(hBmp->data, stride * WINPR_ASSERTING_INT_CAST(uint32_t, hBmp->height), 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 = WINPR_ASSERTING_INT_CAST(
uint32_t, (hBmp1->width < hBmp2->width) ? hBmp1->width : hBmp2->width);
const UINT32 minh = WINPR_ASSERTING_INT_CAST(
uint32_t, (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");
(void)fflush(stdout);
(void)fflush(stderr);
return TRUE; // TODO: Fix test cases
}
return bitmapsEqual;
}

View File

@@ -0,0 +1,43 @@
/**
* 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>
WINPR_ATTR_NODISCARD
FREERDP_LOCAL
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);
FREERDP_LOCAL
void test_dump_bitmap(HGDI_BITMAP hBmp, const char* name);
WINPR_ATTR_NODISCARD
FREERDP_LOCAL
BOOL test_assert_bitmaps_equal(HGDI_BITMAP hBmpActual, HGDI_BITMAP hBmpExpected, const char* name,
const gdiPalette* palette);
#endif /* __GDI_CORE_H */

View File

@@ -0,0 +1,193 @@
/**
* 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>
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(WINPR_ATTR_UNUSED 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 = nullptr;
rdpUpdate* update = nullptr;
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;
{
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;
rc = gdi_InvalidateRegion(gdi->primary->hdc, (INT32)nXDst, (INT32)nYDst, (INT32)width,
(INT32)height);
}
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, WINPR_ATTR_UNUSED VideoClientContext* video)
{
WINPR_ASSERT(gdi);
gdi->video = nullptr;
}
void gdi_video_data_init(WINPR_ATTR_UNUSED rdpGdi* gdi, WINPR_ATTR_UNUSED VideoClientContext* video)
{
WINPR_ASSERT(gdi);
WINPR_ASSERT(gdi->context);
}
void gdi_video_data_uninit(WINPR_ATTR_UNUSED rdpGdi* gdi,
WINPR_ATTR_UNUSED VideoClientContext* context)
{
WINPR_ASSERT(gdi);
WINPR_ASSERT(gdi->context);
}
VideoSurface* VideoClient_CreateCommonContext(size_t size, UINT32 x, UINT32 y, UINT32 w, UINT32 h)
{
VideoSurface* ret = nullptr;
WINPR_ASSERT(size >= sizeof(VideoSurface));
ret = calloc(1, size);
if (!ret)
return nullptr;
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 nullptr;
}
void VideoClient_DestroyCommonContext(VideoSurface* surface)
{
if (!surface)
return;
winpr_aligned_free(surface->data);
free(surface);
}