1b3f3a0408
expose functions for overlapping and non overlapping images to use directly
1442 lines
49 KiB
C
1442 lines
49 KiB
C
/**
|
|
* FreeRDP: A Remote Desktop Protocol Implementation
|
|
* GDI Library
|
|
*
|
|
* Copyright 2010-2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
|
* Copyright 2016 Armin Novak <armin.novak@thincast.com>
|
|
* Copyright 2016 Thincast Technologies GmbH
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include <freerdp/config.h>
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <winpr/crt.h>
|
|
#include <winpr/assert.h>
|
|
|
|
#include <freerdp/api.h>
|
|
#include <freerdp/log.h>
|
|
#include <freerdp/freerdp.h>
|
|
|
|
#include <freerdp/gdi/gdi.h>
|
|
#include <freerdp/gdi/dc.h>
|
|
#include <freerdp/gdi/pen.h>
|
|
#include <freerdp/gdi/shape.h>
|
|
#include <freerdp/gdi/region.h>
|
|
#include <freerdp/gdi/bitmap.h>
|
|
|
|
#include "drawing.h"
|
|
#include "clipping.h"
|
|
#include "brush.h"
|
|
#include "line.h"
|
|
#include "gdi.h"
|
|
#include "../core/graphics.h"
|
|
#include "../core/update.h"
|
|
#include "../cache/cache.h"
|
|
|
|
#define TAG FREERDP_TAG("gdi")
|
|
|
|
/* Ternary Raster Operation Table */
|
|
typedef struct
|
|
{
|
|
DWORD code;
|
|
const char* name;
|
|
} rop_table_entry;
|
|
|
|
static const rop_table_entry rop3_code_table[] = { { GDI_BLACKNESS, "0" },
|
|
{ GDI_DPSoon, "DPSoon" },
|
|
{ GDI_DPSona, "DPSona" },
|
|
{ GDI_PSon, "PSon" },
|
|
{ GDI_SDPona, "SDPona" },
|
|
{ GDI_DPon, "DPon" },
|
|
{ GDI_PDSxnon, "PDSxnon" },
|
|
{ GDI_PDSaon, "PDSaon" },
|
|
{ GDI_SDPnaa, "SDPnaa" },
|
|
{ GDI_PDSxon, "PDSxon" },
|
|
{ GDI_DPna, "DPna" },
|
|
{ GDI_PSDnaon, "PSDnaon" },
|
|
{ GDI_SPna, "SPna" },
|
|
{ GDI_PDSnaon, "PDSnaon" },
|
|
{ GDI_PDSonon, "PDSonon" },
|
|
{ GDI_Pn, "Pn" },
|
|
{ GDI_PDSona, "PDSona" },
|
|
{ GDI_NOTSRCERASE, "DSon" },
|
|
{ GDI_SDPxnon, "SDPxnon" },
|
|
{ GDI_SDPaon, "SDPaon" },
|
|
{ GDI_DPSxnon, "DPSxnon" },
|
|
{ GDI_DPSaon, "DPSaon" },
|
|
{ GDI_PSDPSanaxx, "PSDPSanaxx" },
|
|
{ GDI_SSPxDSxaxn, "SSPxDSxaxn" },
|
|
{ GDI_SPxPDxa, "SPxPDxa" },
|
|
{ GDI_SDPSanaxn, "SDPSanaxn" },
|
|
{ GDI_PDSPaox, "PDSPaox" },
|
|
{ GDI_SDPSxaxn, "SDPSxaxn" },
|
|
{ GDI_PSDPaox, "PSDPaox" },
|
|
{ GDI_DSPDxaxn, "DSPDxaxn" },
|
|
{ GDI_PDSox, "PDSox" },
|
|
{ GDI_PDSoan, "PDSoan" },
|
|
{ GDI_DPSnaa, "DPSnaa" },
|
|
{ GDI_SDPxon, "SDPxon" },
|
|
{ GDI_DSna, "DSna" },
|
|
{ GDI_SPDnaon, "SPDnaon" },
|
|
{ GDI_SPxDSxa, "SPxDSxa" },
|
|
{ GDI_PDSPanaxn, "PDSPanaxn" },
|
|
{ GDI_SDPSaox, "SDPSaox" },
|
|
{ GDI_SDPSxnox, "SDPSxnox" },
|
|
{ GDI_DPSxa, "DPSxa" },
|
|
{ GDI_PSDPSaoxxn, "PSDPSaoxxn" },
|
|
{ GDI_DPSana, "DPSana" },
|
|
{ GDI_SSPxPDxaxn, "SSPxPDxaxn" },
|
|
{ GDI_SPDSoax, "SPDSoax" },
|
|
{ GDI_PSDnox, "PSDnox" },
|
|
{ GDI_PSDPxox, "PSDPxox" },
|
|
{ GDI_PSDnoan, "PSDnoan" },
|
|
{ GDI_PSna, "PSna" },
|
|
{ GDI_SDPnaon, "SDPnaon" },
|
|
{ GDI_SDPSoox, "SDPSoox" },
|
|
{ GDI_NOTSRCCOPY, "Sn" },
|
|
{ GDI_SPDSaox, "SPDSaox" },
|
|
{ GDI_SPDSxnox, "SPDSxnox" },
|
|
{ GDI_SDPox, "SDPox" },
|
|
{ GDI_SDPoan, "SDPoan" },
|
|
{ GDI_PSDPoax, "PSDPoax" },
|
|
{ GDI_SPDnox, "SPDnox" },
|
|
{ GDI_SPDSxox, "SPDSxox" },
|
|
{ GDI_SPDnoan, "SPDnoan" },
|
|
{ GDI_PSx, "PSx" },
|
|
{ GDI_SPDSonox, "SPDSonox" },
|
|
{ GDI_SPDSnaox, "SPDSnaox" },
|
|
{ GDI_PSan, "PSan" },
|
|
{ GDI_PSDnaa, "PSDnaa" },
|
|
{ GDI_DPSxon, "DPSxon" },
|
|
{ GDI_SDxPDxa, "SDxPDxa" },
|
|
{ GDI_SPDSanaxn, "SPDSanaxn" },
|
|
{ GDI_SRCERASE, "SDna" },
|
|
{ GDI_DPSnaon, "DPSnaon" },
|
|
{ GDI_DSPDaox, "DSPDaox" },
|
|
{ GDI_PSDPxaxn, "PSDPxaxn" },
|
|
{ GDI_SDPxa, "SDPxa" },
|
|
{ GDI_PDSPDaoxxn, "PDSPDaoxxn" },
|
|
{ GDI_DPSDoax, "DPSDoax" },
|
|
{ GDI_PDSnox, "PDSnox" },
|
|
{ GDI_SDPana, "SDPana" },
|
|
{ GDI_SSPxDSxoxn, "SSPxDSxoxn" },
|
|
{ GDI_PDSPxox, "PDSPxox" },
|
|
{ GDI_PDSnoan, "PDSnoan" },
|
|
{ GDI_PDna, "PDna" },
|
|
{ GDI_DSPnaon, "DSPnaon" },
|
|
{ GDI_DPSDaox, "DPSDaox" },
|
|
{ GDI_SPDSxaxn, "SPDSxaxn" },
|
|
{ GDI_DPSonon, "DPSonon" },
|
|
{ GDI_DSTINVERT, "Dn" },
|
|
{ GDI_DPSox, "DPSox" },
|
|
{ GDI_DPSoan, "DPSoan" },
|
|
{ GDI_PDSPoax, "PDSPoax" },
|
|
{ GDI_DPSnox, "DPSnox" },
|
|
{ GDI_PATINVERT, "DPx" },
|
|
{ GDI_DPSDonox, "DPSDonox" },
|
|
{ GDI_DPSDxox, "DPSDxox" },
|
|
{ GDI_DPSnoan, "DPSnoan" },
|
|
{ GDI_DPSDnaox, "DPSDnaox" },
|
|
{ GDI_DPan, "DPan" },
|
|
{ GDI_PDSxa, "PDSxa" },
|
|
{ GDI_DSPDSaoxxn, "DSPDSaoxxn" },
|
|
{ GDI_DSPDoax, "DSPDoax" },
|
|
{ GDI_SDPnox, "SDPnox" },
|
|
{ GDI_SDPSoax, "SDPSoax" },
|
|
{ GDI_DSPnox, "DSPnox" },
|
|
{ GDI_SRCINVERT, "DSx" },
|
|
{ GDI_SDPSonox, "SDPSonox" },
|
|
{ GDI_DSPDSonoxxn, "DSPDSonoxxn" },
|
|
{ GDI_PDSxxn, "PDSxxn" },
|
|
{ GDI_DPSax, "DPSax" },
|
|
{ GDI_PSDPSoaxxn, "PSDPSoaxxn" },
|
|
{ GDI_SDPax, "SDPax" },
|
|
{ GDI_PDSPDoaxxn, "PDSPDoaxxn" },
|
|
{ GDI_SDPSnoax, "SDPSnoax" },
|
|
{ GDI_PDSxnan, "PDSxnan" },
|
|
{ GDI_PDSana, "PDSana" },
|
|
{ GDI_SSDxPDxaxn, "SSDxPDxaxn" },
|
|
{ GDI_SDPSxox, "SDPSxox" },
|
|
{ GDI_SDPnoan, "SDPnoan" },
|
|
{ GDI_DSPDxox, "DSPDxox" },
|
|
{ GDI_DSPnoan, "DSPnoan" },
|
|
{ GDI_SDPSnaox, "SDPSnaox" },
|
|
{ GDI_DSan, "DSan" },
|
|
{ GDI_PDSax, "PDSax" },
|
|
{ GDI_DSPDSoaxxn, "DSPDSoaxxn" },
|
|
{ GDI_DPSDnoax, "DPSDnoax" },
|
|
{ GDI_SDPxnan, "SDPxnan" },
|
|
{ GDI_SPDSnoax, "SPDSnoax" },
|
|
{ GDI_DPSxnan, "DPSxnan" },
|
|
{ GDI_SPxDSxo, "SPxDSxo" },
|
|
{ GDI_DPSaan, "DPSaan" },
|
|
{ GDI_DPSaa, "DPSaa" },
|
|
{ GDI_SPxDSxon, "SPxDSxon" },
|
|
{ GDI_DPSxna, "DPSxna" },
|
|
{ GDI_SPDSnoaxn, "SPDSnoaxn" },
|
|
{ GDI_SDPxna, "SDPxna" },
|
|
{ GDI_PDSPnoaxn, "PDSPnoaxn" },
|
|
{ GDI_DSPDSoaxx, "DSPDSoaxx" },
|
|
{ GDI_PDSaxn, "PDSaxn" },
|
|
{ GDI_SRCAND, "DSa" },
|
|
{ GDI_SDPSnaoxn, "SDPSnaoxn" },
|
|
{ GDI_DSPnoa, "DSPnoa" },
|
|
{ GDI_DSPDxoxn, "DSPDxoxn" },
|
|
{ GDI_SDPnoa, "SDPnoa" },
|
|
{ GDI_SDPSxoxn, "SDPSxoxn" },
|
|
{ GDI_SSDxPDxax, "SSDxPDxax" },
|
|
{ GDI_PDSanan, "PDSanan" },
|
|
{ GDI_PDSxna, "PDSxna" },
|
|
{ GDI_SDPSnoaxn, "SDPSnoaxn" },
|
|
{ GDI_DPSDPoaxx, "DPSDPoaxx" },
|
|
{ GDI_SPDaxn, "SPDaxn" },
|
|
{ GDI_PSDPSoaxx, "PSDPSoaxx" },
|
|
{ GDI_DPSaxn, "DPSaxn" },
|
|
{ GDI_DPSxx, "DPSxx" },
|
|
{ GDI_PSDPSonoxx, "PSDPSonoxx" },
|
|
{ GDI_SDPSonoxn, "SDPSonoxn" },
|
|
{ GDI_DSxn, "DSxn" },
|
|
{ GDI_DPSnax, "DPSnax" },
|
|
{ GDI_SDPSoaxn, "SDPSoaxn" },
|
|
{ GDI_SPDnax, "SPDnax" },
|
|
{ GDI_DSPDoaxn, "DSPDoaxn" },
|
|
{ GDI_DSPDSaoxx, "DSPDSaoxx" },
|
|
{ GDI_PDSxan, "PDSxan" },
|
|
{ GDI_DPa, "DPa" },
|
|
{ GDI_PDSPnaoxn, "PDSPnaoxn" },
|
|
{ GDI_DPSnoa, "DPSnoa" },
|
|
{ GDI_DPSDxoxn, "DPSDxoxn" },
|
|
{ GDI_PDSPonoxn, "PDSPonoxn" },
|
|
{ GDI_PDxn, "PDxn" },
|
|
{ GDI_DSPnax, "DSPnax" },
|
|
{ GDI_PDSPoaxn, "PDSPoaxn" },
|
|
{ GDI_DPSoa, "DPSoa" },
|
|
{ GDI_DPSoxn, "DPSoxn" },
|
|
{ GDI_DSTCOPY, "D" },
|
|
{ GDI_DPSono, "DPSono" },
|
|
{ GDI_SPDSxax, "SPDSxax" },
|
|
{ GDI_DPSDaoxn, "DPSDaoxn" },
|
|
{ GDI_DSPnao, "DSPnao" },
|
|
{ GDI_DPno, "DPno" },
|
|
{ GDI_PDSnoa, "PDSnoa" },
|
|
{ GDI_PDSPxoxn, "PDSPxoxn" },
|
|
{ GDI_SSPxDSxox, "SSPxDSxox" },
|
|
{ GDI_SDPanan, "SDPanan" },
|
|
{ GDI_PSDnax, "PSDnax" },
|
|
{ GDI_DPSDoaxn, "DPSDoaxn" },
|
|
{ GDI_DPSDPaoxx, "DPSDPaoxx" },
|
|
{ GDI_SDPxan, "SDPxan" },
|
|
{ GDI_PSDPxax, "PSDPxax" },
|
|
{ GDI_DSPDaoxn, "DSPDaoxn" },
|
|
{ GDI_DPSnao, "DPSnao" },
|
|
{ GDI_MERGEPAINT, "DSno" },
|
|
{ GDI_SPDSanax, "SPDSanax" },
|
|
{ GDI_SDxPDxan, "SDxPDxan" },
|
|
{ GDI_DPSxo, "DPSxo" },
|
|
{ GDI_DPSano, "DPSano" },
|
|
{ GDI_MERGECOPY, "PSa" },
|
|
{ GDI_SPDSnaoxn, "SPDSnaoxn" },
|
|
{ GDI_SPDSonoxn, "SPDSonoxn" },
|
|
{ GDI_PSxn, "PSxn" },
|
|
{ GDI_SPDnoa, "SPDnoa" },
|
|
{ GDI_SPDSxoxn, "SPDSxoxn" },
|
|
{ GDI_SDPnax, "SDPnax" },
|
|
{ GDI_PSDPoaxn, "PSDPoaxn" },
|
|
{ GDI_SDPoa, "SDPoa" },
|
|
{ GDI_SPDoxn, "SPDoxn" },
|
|
{ GDI_DPSDxax, "DPSDxax" },
|
|
{ GDI_SPDSaoxn, "SPDSaoxn" },
|
|
{ GDI_SRCCOPY, "S" },
|
|
{ GDI_SDPono, "SDPono" },
|
|
{ GDI_SDPnao, "SDPnao" },
|
|
{ GDI_SPno, "SPno" },
|
|
{ GDI_PSDnoa, "PSDnoa" },
|
|
{ GDI_PSDPxoxn, "PSDPxoxn" },
|
|
{ GDI_PDSnax, "PDSnax" },
|
|
{ GDI_SPDSoaxn, "SPDSoaxn" },
|
|
{ GDI_SSPxPDxax, "SSPxPDxax" },
|
|
{ GDI_DPSanan, "DPSanan" },
|
|
{ GDI_PSDPSaoxx, "PSDPSaoxx" },
|
|
{ GDI_DPSxan, "DPSxan" },
|
|
{ GDI_PDSPxax, "PDSPxax" },
|
|
{ GDI_SDPSaoxn, "SDPSaoxn" },
|
|
{ GDI_DPSDanax, "DPSDanax" },
|
|
{ GDI_SPxDSxan, "SPxDSxan" },
|
|
{ GDI_SPDnao, "SPDnao" },
|
|
{ GDI_SDno, "SDno" },
|
|
{ GDI_SDPxo, "SDPxo" },
|
|
{ GDI_SDPano, "SDPano" },
|
|
{ GDI_PDSoa, "PDSoa" },
|
|
{ GDI_PDSoxn, "PDSoxn" },
|
|
{ GDI_DSPDxax, "DSPDxax" },
|
|
{ GDI_PSDPaoxn, "PSDPaoxn" },
|
|
{ GDI_SDPSxax, "SDPSxax" },
|
|
{ GDI_PDSPaoxn, "PDSPaoxn" },
|
|
{ GDI_SDPSanax, "SDPSanax" },
|
|
{ GDI_SPxPDxan, "SPxPDxan" },
|
|
{ GDI_SSPxDSxax, "SSPxDSxax" },
|
|
{ GDI_DSPDSanaxxn, "DSPDSanaxxn" },
|
|
{ GDI_DPSao, "DPSao" },
|
|
{ GDI_DPSxno, "DPSxno" },
|
|
{ GDI_SDPao, "SDPao" },
|
|
{ GDI_SDPxno, "SDPxno" },
|
|
{ GDI_SRCPAINT, "DSo" },
|
|
{ GDI_SDPnoo, "SDPnoo" },
|
|
{ GDI_PATCOPY, "P" },
|
|
{ GDI_PDSono, "PDSono" },
|
|
{ GDI_PDSnao, "PDSnao" },
|
|
{ GDI_PSno, "PSno" },
|
|
{ GDI_PSDnao, "PSDnao" },
|
|
{ GDI_PDno, "PDno" },
|
|
{ GDI_PDSxo, "PDSxo" },
|
|
{ GDI_PDSano, "PDSano" },
|
|
{ GDI_PDSao, "PDSao" },
|
|
{ GDI_PDSxno, "PDSxno" },
|
|
{ GDI_DPo, "DPo" },
|
|
{ GDI_PATPAINT, "DPSnoo" },
|
|
{ GDI_PSo, "PSo" },
|
|
{ GDI_PSDnoo, "PSDnoo" },
|
|
{ GDI_DPSoo, "DPSoo" },
|
|
{ GDI_WHITENESS, "1" } };
|
|
|
|
/* Hatch Patterns as monochrome data */
|
|
static const BYTE GDI_BS_HATCHED_PATTERNS[] = {
|
|
0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, /* HS_HORIZONTAL */
|
|
0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, /* HS_VERTICAL */
|
|
0xFE, 0xFD, 0xFB, 0xF7, 0xEF, 0xDF, 0xBF, 0x7F, /* HS_FDIAGONAL */
|
|
0x7F, 0xBF, 0xDF, 0xEF, 0xF7, 0xFB, 0xFD, 0xFE, /* HS_BDIAGONAL */
|
|
0xF7, 0xF7, 0xF7, 0x00, 0xF7, 0xF7, 0xF7, 0xF7, /* HS_CROSS */
|
|
0x7E, 0xBD, 0xDB, 0xE7, 0xE7, 0xDB, 0xBD, 0x7E /* HS_DIACROSS */
|
|
};
|
|
|
|
BOOL gdi_decode_color(rdpGdi* gdi, const UINT32 srcColor, UINT32* color, UINT32* format)
|
|
{
|
|
UINT32 SrcFormat = 0;
|
|
|
|
if (!gdi || !color || !gdi->context || !gdi->context->settings)
|
|
return FALSE;
|
|
|
|
const UINT32 ColorDepth =
|
|
freerdp_settings_get_uint32(gdi->context->settings, FreeRDP_ColorDepth);
|
|
|
|
switch (ColorDepth)
|
|
{
|
|
case 32:
|
|
case 24:
|
|
SrcFormat = PIXEL_FORMAT_BGR24;
|
|
break;
|
|
|
|
case 16:
|
|
SrcFormat = PIXEL_FORMAT_RGB16;
|
|
break;
|
|
|
|
case 15:
|
|
SrcFormat = PIXEL_FORMAT_RGB15;
|
|
break;
|
|
|
|
case 8:
|
|
SrcFormat = PIXEL_FORMAT_RGB8;
|
|
break;
|
|
|
|
default:
|
|
return FALSE;
|
|
}
|
|
|
|
if (format)
|
|
*format = gdi->dstFormat;
|
|
|
|
*color = FreeRDPConvertColor(srcColor, SrcFormat, gdi->dstFormat, &gdi->palette);
|
|
return TRUE;
|
|
}
|
|
|
|
/* GDI Helper Functions */
|
|
DWORD gdi_rop3_code(BYTE code)
|
|
{
|
|
return rop3_code_table[code].code;
|
|
}
|
|
|
|
const char* gdi_rop3_code_string(BYTE code)
|
|
{
|
|
return rop3_code_table[code].name;
|
|
}
|
|
|
|
const char* gdi_rop3_string(DWORD rop)
|
|
{
|
|
const size_t count = sizeof(rop3_code_table) / sizeof(rop3_code_table[0]);
|
|
|
|
for (size_t x = 0; x < count; x++)
|
|
{
|
|
if (rop3_code_table[x].code == rop)
|
|
return rop3_code_table[x].name;
|
|
}
|
|
|
|
return "UNKNOWN";
|
|
}
|
|
|
|
UINT32 gdi_get_pixel_format(UINT32 bitsPerPixel)
|
|
{
|
|
UINT32 format = 0;
|
|
|
|
switch (bitsPerPixel)
|
|
{
|
|
case 32:
|
|
format = PIXEL_FORMAT_BGRA32;
|
|
break;
|
|
|
|
case 24:
|
|
format = PIXEL_FORMAT_BGR24;
|
|
break;
|
|
|
|
case 16:
|
|
format = PIXEL_FORMAT_RGB16;
|
|
break;
|
|
|
|
case 15:
|
|
format = PIXEL_FORMAT_RGB15;
|
|
break;
|
|
|
|
case 8:
|
|
format = PIXEL_FORMAT_RGB8;
|
|
break;
|
|
|
|
default:
|
|
WLog_ERR(TAG, "Unsupported color depth %" PRIu32, bitsPerPixel);
|
|
format = 0;
|
|
break;
|
|
}
|
|
|
|
return format;
|
|
}
|
|
|
|
gdiBitmap* gdi_bitmap_new_ex(rdpGdi* gdi, int width, int height, int bpp, BYTE* data)
|
|
{
|
|
gdiBitmap* bitmap = NULL;
|
|
bitmap = (gdiBitmap*)calloc(1, sizeof(gdiBitmap));
|
|
|
|
if (!bitmap)
|
|
goto fail_bitmap;
|
|
|
|
if (!(bitmap->hdc = gdi_CreateCompatibleDC(gdi->hdc)))
|
|
goto fail_hdc;
|
|
|
|
WLog_Print(gdi->log, WLOG_DEBUG, "gdi_bitmap_new: width:%d height:%d bpp:%d", width, height,
|
|
bpp);
|
|
|
|
if (!data)
|
|
bitmap->bitmap = gdi_CreateCompatibleBitmap(gdi->hdc, width, height);
|
|
else
|
|
bitmap->bitmap = gdi_create_bitmap(gdi, width, height, bpp, data);
|
|
|
|
if (!bitmap->bitmap)
|
|
goto fail_bitmap_bitmap;
|
|
|
|
gdi_SelectObject(bitmap->hdc, (HGDIOBJECT)bitmap->bitmap);
|
|
bitmap->org_bitmap = NULL;
|
|
return bitmap;
|
|
fail_bitmap_bitmap:
|
|
gdi_DeleteDC(bitmap->hdc);
|
|
fail_hdc:
|
|
free(bitmap);
|
|
fail_bitmap:
|
|
return NULL;
|
|
}
|
|
|
|
void gdi_bitmap_free_ex(gdiBitmap* bitmap)
|
|
{
|
|
if (bitmap)
|
|
{
|
|
gdi_SelectObject(bitmap->hdc, (HGDIOBJECT)bitmap->org_bitmap);
|
|
gdi_DeleteObject((HGDIOBJECT)bitmap->bitmap);
|
|
gdi_DeleteDC(bitmap->hdc);
|
|
free(bitmap);
|
|
}
|
|
}
|
|
|
|
BOOL gdi_bitmap_update(rdpContext* context, const BITMAP_UPDATE* bitmapUpdate)
|
|
{
|
|
if (!context || !bitmapUpdate || !context->gdi || !context->codecs)
|
|
{
|
|
WLog_ERR(TAG,
|
|
"Invalid arguments: context=%p, bitmapUpdate=%p, context->gdi=%p, "
|
|
"context->codecs=%p",
|
|
context, bitmapUpdate, context ? context->gdi : NULL,
|
|
context ? context->codecs : NULL);
|
|
return FALSE;
|
|
}
|
|
|
|
for (UINT32 index = 0; index < bitmapUpdate->number; index++)
|
|
{
|
|
const BITMAP_DATA* bitmap = &(bitmapUpdate->rectangles[index]);
|
|
rdpBitmap* bmp = Bitmap_Alloc(context);
|
|
|
|
if (!bmp)
|
|
{
|
|
WLog_ERR(TAG, "Bitmap_Alloc failed");
|
|
return FALSE;
|
|
}
|
|
|
|
Bitmap_SetDimensions(bmp, bitmap->width, bitmap->height);
|
|
Bitmap_SetRectangle(bmp, bitmap->destLeft, bitmap->destTop, bitmap->destRight,
|
|
bitmap->destBottom);
|
|
|
|
if (!bmp->Decompress(context, bmp, bitmap->bitmapDataStream, bitmap->width, bitmap->height,
|
|
bitmap->bitsPerPixel, bitmap->bitmapLength, bitmap->compressed,
|
|
RDP_CODEC_ID_NONE))
|
|
{
|
|
WLog_ERR(TAG, "bmp->Decompress failed");
|
|
Bitmap_Free(context, bmp);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!bmp->New(context, bmp))
|
|
{
|
|
WLog_ERR(TAG, "bmp->New failed");
|
|
Bitmap_Free(context, bmp);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!bmp->Paint(context, bmp))
|
|
{
|
|
WLog_ERR(TAG, "bmp->Paint failed");
|
|
Bitmap_Free(context, bmp);
|
|
return FALSE;
|
|
}
|
|
|
|
Bitmap_Free(context, bmp);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static BOOL gdi_palette_update(rdpContext* context, const PALETTE_UPDATE* palette)
|
|
{
|
|
rdpGdi* gdi = NULL;
|
|
|
|
if (!context || !palette)
|
|
return FALSE;
|
|
|
|
gdi = context->gdi;
|
|
gdi->palette.format = gdi->dstFormat;
|
|
|
|
for (UINT32 index = 0; index < palette->number; index++)
|
|
{
|
|
const PALETTE_ENTRY* pe = &(palette->entries[index]);
|
|
gdi->palette.palette[index] =
|
|
FreeRDPGetColor(gdi->dstFormat, pe->red, pe->green, pe->blue, 0xFF);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static BOOL gdi_set_bounds(rdpContext* context, const rdpBounds* bounds)
|
|
{
|
|
rdpGdi* gdi = NULL;
|
|
|
|
if (!context)
|
|
return FALSE;
|
|
|
|
gdi = context->gdi;
|
|
|
|
if (bounds)
|
|
{
|
|
gdi_SetClipRgn(gdi->drawing->hdc, bounds->left, bounds->top,
|
|
bounds->right - bounds->left + 1, bounds->bottom - bounds->top + 1);
|
|
}
|
|
else
|
|
gdi_SetNullClipRgn(gdi->drawing->hdc);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static BOOL gdi_dstblt(rdpContext* context, const DSTBLT_ORDER* dstblt)
|
|
{
|
|
rdpGdi* gdi = NULL;
|
|
|
|
if (!context || !dstblt)
|
|
return FALSE;
|
|
|
|
gdi = context->gdi;
|
|
return gdi_BitBlt(gdi->drawing->hdc, dstblt->nLeftRect, dstblt->nTopRect, dstblt->nWidth,
|
|
dstblt->nHeight, NULL, 0, 0, gdi_rop3_code(dstblt->bRop), &gdi->palette);
|
|
}
|
|
|
|
static BOOL gdi_patblt(rdpContext* context, PATBLT_ORDER* patblt)
|
|
{
|
|
const rdpBrush* brush = &patblt->brush;
|
|
UINT32 foreColor = 0;
|
|
UINT32 backColor = 0;
|
|
UINT32 originalColor = 0;
|
|
HGDI_BRUSH originalBrush = NULL;
|
|
HGDI_BRUSH hbrush = NULL;
|
|
rdpGdi* gdi = context->gdi;
|
|
BOOL ret = FALSE;
|
|
const DWORD rop = gdi_rop3_code(patblt->bRop);
|
|
INT32 nXSrc = 0;
|
|
INT32 nYSrc = 0;
|
|
BYTE data[8 * 8 * 4];
|
|
HGDI_BITMAP hBmp = NULL;
|
|
|
|
if (!gdi_decode_color(gdi, patblt->foreColor, &foreColor, NULL))
|
|
return FALSE;
|
|
|
|
if (!gdi_decode_color(gdi, patblt->backColor, &backColor, NULL))
|
|
return FALSE;
|
|
|
|
originalColor = gdi_SetTextColor(gdi->drawing->hdc, foreColor);
|
|
originalBrush = gdi->drawing->hdc->brush;
|
|
|
|
switch (brush->style)
|
|
{
|
|
case GDI_BS_SOLID:
|
|
hbrush = gdi_CreateSolidBrush(foreColor);
|
|
break;
|
|
|
|
case GDI_BS_HATCHED:
|
|
{
|
|
const BYTE* hatched = NULL;
|
|
hatched = GDI_BS_HATCHED_PATTERNS + (8 * brush->hatch);
|
|
|
|
if (!freerdp_image_copy_from_monochrome(data, gdi->drawing->hdc->format, 0, 0, 0, 8, 8,
|
|
hatched, backColor, foreColor, &gdi->palette))
|
|
goto out_error;
|
|
|
|
hBmp = gdi_CreateBitmapEx(8, 8, gdi->drawing->hdc->format, 0, data, NULL);
|
|
|
|
if (!hBmp)
|
|
goto out_error;
|
|
|
|
hbrush = gdi_CreateHatchBrush(hBmp);
|
|
}
|
|
break;
|
|
|
|
case GDI_BS_PATTERN:
|
|
{
|
|
UINT32 brushFormat = 0;
|
|
|
|
if (brush->bpp > 1)
|
|
{
|
|
UINT32 bpp = brush->bpp;
|
|
|
|
if ((bpp == 16) &&
|
|
(freerdp_settings_get_uint32(context->settings, FreeRDP_ColorDepth) == 15))
|
|
bpp = 15;
|
|
|
|
brushFormat = gdi_get_pixel_format(bpp);
|
|
|
|
if (!freerdp_image_copy_no_overlap(data, gdi->drawing->hdc->format, 0, 0, 0, 8, 8,
|
|
brush->data, brushFormat, 0, 0, 0, &gdi->palette,
|
|
FREERDP_FLIP_NONE))
|
|
goto out_error;
|
|
}
|
|
else
|
|
{
|
|
if (!freerdp_image_copy_from_monochrome(data, gdi->drawing->hdc->format, 0, 0, 0, 8,
|
|
8, brush->data, backColor, foreColor,
|
|
&gdi->palette))
|
|
goto out_error;
|
|
}
|
|
|
|
hBmp = gdi_CreateBitmapEx(8, 8, gdi->drawing->hdc->format, 0, data, NULL);
|
|
|
|
if (!hBmp)
|
|
goto out_error;
|
|
|
|
hbrush = gdi_CreatePatternBrush(hBmp);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
WLog_ERR(TAG, "unimplemented brush style:%" PRIu32 "", brush->style);
|
|
break;
|
|
}
|
|
|
|
if (hbrush)
|
|
{
|
|
hbrush->nXOrg = brush->x;
|
|
hbrush->nYOrg = brush->y;
|
|
gdi->drawing->hdc->brush = hbrush;
|
|
ret = gdi_BitBlt(gdi->drawing->hdc, patblt->nLeftRect, patblt->nTopRect, patblt->nWidth,
|
|
patblt->nHeight, gdi->primary->hdc, nXSrc, nYSrc, rop, &gdi->palette);
|
|
}
|
|
|
|
out_error:
|
|
gdi_DeleteObject((HGDIOBJECT)hBmp);
|
|
gdi_DeleteObject((HGDIOBJECT)hbrush);
|
|
gdi->drawing->hdc->brush = originalBrush;
|
|
gdi_SetTextColor(gdi->drawing->hdc, originalColor);
|
|
return ret;
|
|
}
|
|
|
|
static BOOL gdi_scrblt(rdpContext* context, const SCRBLT_ORDER* scrblt)
|
|
{
|
|
rdpGdi* gdi = NULL;
|
|
|
|
if (!context || !context->gdi)
|
|
return FALSE;
|
|
|
|
gdi = context->gdi;
|
|
return gdi_BitBlt(gdi->drawing->hdc, scrblt->nLeftRect, scrblt->nTopRect, scrblt->nWidth,
|
|
scrblt->nHeight, gdi->primary->hdc, scrblt->nXSrc, scrblt->nYSrc,
|
|
gdi_rop3_code(scrblt->bRop), &gdi->palette);
|
|
}
|
|
|
|
static BOOL gdi_opaque_rect(rdpContext* context, const OPAQUE_RECT_ORDER* opaque_rect)
|
|
{
|
|
GDI_RECT rect;
|
|
HGDI_BRUSH hBrush = NULL;
|
|
UINT32 brush_color = 0;
|
|
rdpGdi* gdi = context->gdi;
|
|
BOOL ret = 0;
|
|
INT32 x = opaque_rect->nLeftRect;
|
|
INT32 y = opaque_rect->nTopRect;
|
|
INT32 w = opaque_rect->nWidth;
|
|
INT32 h = opaque_rect->nHeight;
|
|
gdi_ClipCoords(gdi->drawing->hdc, &x, &y, &w, &h, NULL, NULL);
|
|
gdi_CRgnToRect(x, y, w, h, &rect);
|
|
|
|
if (!gdi_decode_color(gdi, opaque_rect->color, &brush_color, NULL))
|
|
return FALSE;
|
|
|
|
if (!(hBrush = gdi_CreateSolidBrush(brush_color)))
|
|
return FALSE;
|
|
|
|
ret = gdi_FillRect(gdi->drawing->hdc, &rect, hBrush);
|
|
gdi_DeleteObject((HGDIOBJECT)hBrush);
|
|
return ret;
|
|
}
|
|
|
|
static BOOL gdi_multi_opaque_rect(rdpContext* context,
|
|
const MULTI_OPAQUE_RECT_ORDER* multi_opaque_rect)
|
|
{
|
|
GDI_RECT rect;
|
|
HGDI_BRUSH hBrush = NULL;
|
|
UINT32 brush_color = 0;
|
|
rdpGdi* gdi = context->gdi;
|
|
BOOL ret = TRUE;
|
|
|
|
if (!gdi_decode_color(gdi, multi_opaque_rect->color, &brush_color, NULL))
|
|
return FALSE;
|
|
|
|
hBrush = gdi_CreateSolidBrush(brush_color);
|
|
|
|
if (!hBrush)
|
|
return FALSE;
|
|
|
|
for (UINT32 i = 0; i < multi_opaque_rect->numRectangles; i++)
|
|
{
|
|
const DELTA_RECT* rectangle = &multi_opaque_rect->rectangles[i];
|
|
INT32 x = rectangle->left;
|
|
INT32 y = rectangle->top;
|
|
INT32 w = rectangle->width;
|
|
INT32 h = rectangle->height;
|
|
gdi_ClipCoords(gdi->drawing->hdc, &x, &y, &w, &h, NULL, NULL);
|
|
gdi_CRgnToRect(x, y, w, h, &rect);
|
|
ret = gdi_FillRect(gdi->drawing->hdc, &rect, hBrush);
|
|
|
|
if (!ret)
|
|
break;
|
|
}
|
|
|
|
gdi_DeleteObject((HGDIOBJECT)hBrush);
|
|
return ret;
|
|
}
|
|
|
|
static BOOL gdi_line_to(rdpContext* context, const LINE_TO_ORDER* lineTo)
|
|
{
|
|
UINT32 color = 0;
|
|
HGDI_PEN hPen = NULL;
|
|
rdpGdi* gdi = context->gdi;
|
|
INT32 xStart = lineTo->nXStart;
|
|
INT32 yStart = lineTo->nYStart;
|
|
INT32 xEnd = lineTo->nXEnd;
|
|
INT32 yEnd = lineTo->nYEnd;
|
|
INT32 w = 0;
|
|
INT32 h = 0;
|
|
gdi_ClipCoords(gdi->drawing->hdc, &xStart, &yStart, &w, &h, NULL, NULL);
|
|
gdi_ClipCoords(gdi->drawing->hdc, &xEnd, &yEnd, &w, &h, NULL, NULL);
|
|
|
|
if (!gdi_decode_color(gdi, lineTo->penColor, &color, NULL))
|
|
return FALSE;
|
|
|
|
if (!(hPen = gdi_CreatePen(lineTo->penStyle, lineTo->penWidth, color, gdi->drawing->hdc->format,
|
|
&gdi->palette)))
|
|
return FALSE;
|
|
|
|
gdi_SelectObject(gdi->drawing->hdc, (HGDIOBJECT)hPen);
|
|
gdi_SetROP2(gdi->drawing->hdc, lineTo->bRop2);
|
|
gdi_MoveToEx(gdi->drawing->hdc, lineTo->nXStart, lineTo->nYStart, NULL);
|
|
gdi_LineTo(gdi->drawing->hdc, lineTo->nXEnd, lineTo->nYEnd);
|
|
gdi_DeleteObject((HGDIOBJECT)hPen);
|
|
return TRUE;
|
|
}
|
|
|
|
static BOOL gdi_polyline(rdpContext* context, const POLYLINE_ORDER* polyline)
|
|
{
|
|
INT32 x = 0;
|
|
INT32 y = 0;
|
|
UINT32 color = 0;
|
|
HGDI_PEN hPen = NULL;
|
|
DELTA_POINT* points = NULL;
|
|
rdpGdi* gdi = context->gdi;
|
|
INT32 w = 0;
|
|
INT32 h = 0;
|
|
|
|
if (!gdi_decode_color(gdi, polyline->penColor, &color, NULL))
|
|
return FALSE;
|
|
|
|
if (!(hPen = gdi_CreatePen(GDI_PS_SOLID, 1, color, gdi->drawing->hdc->format, &gdi->palette)))
|
|
return FALSE;
|
|
|
|
gdi_SelectObject(gdi->drawing->hdc, (HGDIOBJECT)hPen);
|
|
gdi_SetROP2(gdi->drawing->hdc, polyline->bRop2);
|
|
x = polyline->xStart;
|
|
y = polyline->yStart;
|
|
gdi_ClipCoords(gdi->drawing->hdc, &x, &y, &w, &h, NULL, NULL);
|
|
gdi_MoveToEx(gdi->drawing->hdc, x, y, NULL);
|
|
points = polyline->points;
|
|
|
|
for (UINT32 i = 0; i < polyline->numDeltaEntries; i++)
|
|
{
|
|
x += points[i].x;
|
|
y += points[i].y;
|
|
gdi_ClipCoords(gdi->drawing->hdc, &x, &y, &w, &h, NULL, NULL);
|
|
gdi_LineTo(gdi->drawing->hdc, x, y);
|
|
gdi_MoveToEx(gdi->drawing->hdc, x, y, NULL);
|
|
}
|
|
|
|
gdi_DeleteObject((HGDIOBJECT)hPen);
|
|
return TRUE;
|
|
}
|
|
|
|
static BOOL gdi_memblt(rdpContext* context, MEMBLT_ORDER* memblt)
|
|
{
|
|
gdiBitmap* bitmap = NULL;
|
|
rdpGdi* gdi = NULL;
|
|
|
|
if (!context || !memblt || !context->gdi || !memblt->bitmap)
|
|
return FALSE;
|
|
|
|
bitmap = (gdiBitmap*)memblt->bitmap;
|
|
gdi = context->gdi;
|
|
return gdi_BitBlt(gdi->drawing->hdc, memblt->nLeftRect, memblt->nTopRect, memblt->nWidth,
|
|
memblt->nHeight, bitmap->hdc, memblt->nXSrc, memblt->nYSrc,
|
|
gdi_rop3_code(memblt->bRop), &gdi->palette);
|
|
}
|
|
|
|
static BOOL gdi_mem3blt(rdpContext* context, MEM3BLT_ORDER* mem3blt)
|
|
{
|
|
HGDI_BRUSH originalBrush = NULL;
|
|
rdpGdi* gdi = context->gdi;
|
|
BOOL ret = TRUE;
|
|
const rdpBrush* brush = &mem3blt->brush;
|
|
gdiBitmap* bitmap = (gdiBitmap*)mem3blt->bitmap;
|
|
UINT32 foreColor = 0;
|
|
UINT32 backColor = 0;
|
|
UINT32 originalColor = 0;
|
|
|
|
if (!gdi_decode_color(gdi, mem3blt->foreColor, &foreColor, NULL))
|
|
return FALSE;
|
|
|
|
if (!gdi_decode_color(gdi, mem3blt->backColor, &backColor, NULL))
|
|
return FALSE;
|
|
|
|
originalColor = gdi_SetTextColor(gdi->drawing->hdc, foreColor);
|
|
|
|
switch (brush->style)
|
|
{
|
|
case GDI_BS_SOLID:
|
|
originalBrush = gdi->drawing->hdc->brush;
|
|
gdi->drawing->hdc->brush = gdi_CreateSolidBrush(foreColor);
|
|
|
|
if (!gdi->drawing->hdc->brush)
|
|
{
|
|
ret = FALSE;
|
|
goto out_fail;
|
|
}
|
|
|
|
ret = gdi_BitBlt(gdi->drawing->hdc, mem3blt->nLeftRect, mem3blt->nTopRect,
|
|
mem3blt->nWidth, mem3blt->nHeight, bitmap->hdc, mem3blt->nXSrc,
|
|
mem3blt->nYSrc, gdi_rop3_code(mem3blt->bRop), &gdi->palette);
|
|
gdi_DeleteObject((HGDIOBJECT)gdi->drawing->hdc->brush);
|
|
gdi->drawing->hdc->brush = originalBrush;
|
|
break;
|
|
|
|
case GDI_BS_PATTERN:
|
|
{
|
|
HGDI_BITMAP hBmp = NULL;
|
|
UINT32 brushFormat = 0;
|
|
BYTE* data = (BYTE*)winpr_aligned_malloc(
|
|
8 * 8 * FreeRDPGetBytesPerPixel(gdi->drawing->hdc->format), 16);
|
|
|
|
if (!data)
|
|
{
|
|
ret = FALSE;
|
|
goto out_fail;
|
|
}
|
|
|
|
if (brush->bpp > 1)
|
|
{
|
|
UINT32 bpp = brush->bpp;
|
|
|
|
const UINT32 ColorDepth =
|
|
freerdp_settings_get_uint32(gdi->context->settings, FreeRDP_ColorDepth);
|
|
if ((bpp == 16) && (ColorDepth == 15))
|
|
bpp = 15;
|
|
|
|
brushFormat = gdi_get_pixel_format(bpp);
|
|
|
|
if (!freerdp_image_copy_no_overlap(data, gdi->drawing->hdc->format, 0, 0, 0, 8, 8,
|
|
brush->data, brushFormat, 0, 0, 0, &gdi->palette,
|
|
FREERDP_FLIP_NONE))
|
|
{
|
|
ret = FALSE;
|
|
winpr_aligned_free(data);
|
|
goto out_fail;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!freerdp_image_copy_from_monochrome(data, gdi->drawing->hdc->format, 0, 0, 0, 8,
|
|
8, brush->data, backColor, foreColor,
|
|
&gdi->palette))
|
|
{
|
|
ret = FALSE;
|
|
winpr_aligned_free(data);
|
|
goto out_fail;
|
|
}
|
|
}
|
|
|
|
hBmp = gdi_CreateBitmap(8, 8, gdi->drawing->hdc->format, data);
|
|
|
|
if (!hBmp)
|
|
{
|
|
ret = FALSE;
|
|
winpr_aligned_free(data);
|
|
goto out_fail;
|
|
}
|
|
|
|
originalBrush = gdi->drawing->hdc->brush;
|
|
gdi->drawing->hdc->brush = gdi_CreatePatternBrush(hBmp);
|
|
|
|
if (!gdi->drawing->hdc->brush)
|
|
{
|
|
gdi_DeleteObject((HGDIOBJECT)hBmp);
|
|
goto out_fail;
|
|
}
|
|
|
|
gdi->drawing->hdc->brush->nXOrg = brush->x;
|
|
gdi->drawing->hdc->brush->nYOrg = brush->y;
|
|
ret = gdi_BitBlt(gdi->drawing->hdc, mem3blt->nLeftRect, mem3blt->nTopRect,
|
|
mem3blt->nWidth, mem3blt->nHeight, bitmap->hdc, mem3blt->nXSrc,
|
|
mem3blt->nYSrc, gdi_rop3_code(mem3blt->bRop), &gdi->palette);
|
|
gdi_DeleteObject((HGDIOBJECT)gdi->drawing->hdc->brush);
|
|
gdi_DeleteObject((HGDIOBJECT)hBmp);
|
|
gdi->drawing->hdc->brush = originalBrush;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
WLog_ERR(TAG, "Mem3Blt unimplemented brush style:%" PRIu32 "", brush->style);
|
|
break;
|
|
}
|
|
|
|
out_fail:
|
|
gdi_SetTextColor(gdi->drawing->hdc, originalColor);
|
|
return ret;
|
|
}
|
|
|
|
static BOOL gdi_polygon_sc(rdpContext* context, const POLYGON_SC_ORDER* polygon_sc)
|
|
{
|
|
WLog_WARN(TAG, "not implemented");
|
|
return FALSE;
|
|
}
|
|
|
|
static BOOL gdi_polygon_cb(rdpContext* context, POLYGON_CB_ORDER* polygon_cb)
|
|
{
|
|
WLog_WARN(TAG, "not implemented");
|
|
return FALSE;
|
|
}
|
|
|
|
static BOOL gdi_ellipse_sc(rdpContext* context, const ELLIPSE_SC_ORDER* ellipse_sc)
|
|
{
|
|
WLog_WARN(TAG, "not implemented");
|
|
return FALSE;
|
|
}
|
|
|
|
static BOOL gdi_ellipse_cb(rdpContext* context, const ELLIPSE_CB_ORDER* ellipse_cb)
|
|
{
|
|
WLog_WARN(TAG, "not implemented");
|
|
return FALSE;
|
|
}
|
|
|
|
static BOOL gdi_frame_marker(rdpContext* context, const FRAME_MARKER_ORDER* frameMarker)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
static BOOL gdi_surface_frame_marker(rdpContext* context,
|
|
const SURFACE_FRAME_MARKER* surfaceFrameMarker)
|
|
{
|
|
WLog_Print(context->gdi->log, WLOG_DEBUG, "frameId %" PRIu32 " frameAction %" PRIu32 "",
|
|
surfaceFrameMarker->frameId, surfaceFrameMarker->frameAction);
|
|
|
|
switch (surfaceFrameMarker->frameAction)
|
|
{
|
|
case SURFACECMD_FRAMEACTION_BEGIN:
|
|
break;
|
|
|
|
case SURFACECMD_FRAMEACTION_END:
|
|
if (freerdp_settings_get_uint32(context->settings, FreeRDP_FrameAcknowledge) > 0)
|
|
{
|
|
IFCALL(context->update->SurfaceFrameAcknowledge, context,
|
|
surfaceFrameMarker->frameId);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static BOOL intersect_rect(const rdpGdi* gdi, const SURFACE_BITS_COMMAND* cmd, RECTANGLE_16* prect)
|
|
{
|
|
const UINT32 w = (const UINT32)gdi->width;
|
|
const UINT32 h = (const UINT32)gdi->height;
|
|
|
|
if (cmd->destLeft > w)
|
|
return FALSE;
|
|
if (cmd->destRight > w)
|
|
return FALSE;
|
|
if (cmd->destLeft > cmd->destRight)
|
|
return FALSE;
|
|
if (cmd->destRight > UINT16_MAX)
|
|
return FALSE;
|
|
|
|
if (cmd->destTop > h)
|
|
return FALSE;
|
|
if (cmd->destBottom > h)
|
|
return FALSE;
|
|
if (cmd->destTop > cmd->destBottom)
|
|
return FALSE;
|
|
if (cmd->destBottom > UINT16_MAX)
|
|
return FALSE;
|
|
|
|
prect->left = (const UINT16)cmd->destLeft;
|
|
prect->top = (const UINT16)cmd->destTop;
|
|
prect->right = MIN((UINT16)cmd->destRight, prect->left + cmd->bmp.width);
|
|
prect->bottom = MIN((UINT16)cmd->destBottom, prect->top + cmd->bmp.height);
|
|
return TRUE;
|
|
}
|
|
|
|
static BOOL gdi_surface_bits(rdpContext* context, const SURFACE_BITS_COMMAND* cmd)
|
|
{
|
|
BOOL result = FALSE;
|
|
DWORD format = 0;
|
|
rdpGdi* gdi = NULL;
|
|
size_t size = 0;
|
|
REGION16 region;
|
|
RECTANGLE_16 cmdRect = { 0 };
|
|
UINT32 nbRects = 0;
|
|
const RECTANGLE_16* rects = NULL;
|
|
|
|
if (!context || !cmd)
|
|
return FALSE;
|
|
|
|
gdi = context->gdi;
|
|
WLog_Print(
|
|
gdi->log, WLOG_DEBUG,
|
|
"destLeft %" PRIu32 " destTop %" PRIu32 " destRight %" PRIu32 " destBottom %" PRIu32 " "
|
|
"bpp %" PRIu8 " flags %" PRIx8 " codecID %" PRIu16 " width %" PRIu16 " height %" PRIu16
|
|
" length %" PRIu32 "",
|
|
cmd->destLeft, cmd->destTop, cmd->destRight, cmd->destBottom, cmd->bmp.bpp, cmd->bmp.flags,
|
|
cmd->bmp.codecID, cmd->bmp.width, cmd->bmp.height, cmd->bmp.bitmapDataLength);
|
|
region16_init(®ion);
|
|
|
|
if (!intersect_rect(gdi, cmd, &cmdRect))
|
|
goto out;
|
|
|
|
switch (cmd->bmp.codecID)
|
|
{
|
|
case RDP_CODEC_ID_REMOTEFX:
|
|
case RDP_CODEC_ID_IMAGE_REMOTEFX:
|
|
if (!rfx_process_message(context->codecs->rfx, cmd->bmp.bitmapData,
|
|
cmd->bmp.bitmapDataLength, cmdRect.left, cmdRect.top,
|
|
gdi->primary_buffer, gdi->dstFormat, gdi->stride, gdi->height,
|
|
®ion))
|
|
{
|
|
WLog_ERR(TAG, "Failed to process RemoteFX message");
|
|
goto out;
|
|
}
|
|
|
|
break;
|
|
|
|
case RDP_CODEC_ID_NSCODEC:
|
|
format = gdi->dstFormat;
|
|
|
|
if (!nsc_process_message(
|
|
context->codecs->nsc, cmd->bmp.bpp, cmd->bmp.width, cmd->bmp.height,
|
|
cmd->bmp.bitmapData, cmd->bmp.bitmapDataLength, gdi->primary_buffer, format,
|
|
gdi->stride, cmdRect.left, cmdRect.top, cmdRect.right - cmdRect.left,
|
|
cmdRect.bottom - cmdRect.top, FREERDP_FLIP_VERTICAL))
|
|
{
|
|
WLog_ERR(TAG, "Failed to process NSCodec message");
|
|
goto out;
|
|
}
|
|
|
|
region16_union_rect(®ion, ®ion, &cmdRect);
|
|
break;
|
|
|
|
case RDP_CODEC_ID_NONE:
|
|
format = gdi_get_pixel_format(cmd->bmp.bpp);
|
|
size = 1ull * cmd->bmp.width * cmd->bmp.height * FreeRDPGetBytesPerPixel(format);
|
|
if (size > cmd->bmp.bitmapDataLength)
|
|
{
|
|
WLog_ERR(TAG, "Short nocodec message: got %" PRIu32 " bytes, require %" PRIuz,
|
|
cmd->bmp.bitmapDataLength, size);
|
|
goto out;
|
|
}
|
|
|
|
if (!freerdp_image_copy_no_overlap(
|
|
gdi->primary_buffer, gdi->dstFormat, gdi->stride, cmdRect.left, cmdRect.top,
|
|
cmdRect.right - cmdRect.left, cmdRect.bottom - cmdRect.top, cmd->bmp.bitmapData,
|
|
format, 0, 0, 0, &gdi->palette, FREERDP_FLIP_VERTICAL))
|
|
{
|
|
WLog_ERR(TAG, "Failed to process nocodec message");
|
|
goto out;
|
|
}
|
|
|
|
region16_union_rect(®ion, ®ion, &cmdRect);
|
|
break;
|
|
|
|
default:
|
|
WLog_ERR(TAG, "Unsupported codecID %" PRIu32 "", cmd->bmp.codecID);
|
|
break;
|
|
}
|
|
|
|
if (!(rects = region16_rects(®ion, &nbRects)))
|
|
goto out;
|
|
|
|
for (UINT32 i = 0; i < nbRects; i++)
|
|
{
|
|
UINT32 left = rects[i].left;
|
|
UINT32 top = rects[i].top;
|
|
UINT32 width = rects[i].right - rects[i].left;
|
|
UINT32 height = rects[i].bottom - rects[i].top;
|
|
|
|
if (!gdi_InvalidateRegion(gdi->primary->hdc, left, top, width, height))
|
|
{
|
|
WLog_ERR(TAG, "Failed to update invalid region");
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
result = TRUE;
|
|
out:
|
|
region16_uninit(®ion);
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Register GDI callbacks with libfreerdp-core.
|
|
* @param update current instance
|
|
*/
|
|
|
|
static void gdi_register_update_callbacks(rdpUpdate* update)
|
|
{
|
|
rdpPrimaryUpdate* primary = NULL;
|
|
const rdpSettings* settings = NULL;
|
|
|
|
WINPR_ASSERT(update);
|
|
WINPR_ASSERT(update->context);
|
|
|
|
settings = update->context->settings;
|
|
WINPR_ASSERT(settings);
|
|
|
|
primary = update->primary;
|
|
WINPR_ASSERT(primary);
|
|
|
|
if (freerdp_settings_get_bool(settings, FreeRDP_DeactivateClientDecoding))
|
|
return;
|
|
update->Palette = gdi_palette_update;
|
|
update->SetBounds = gdi_set_bounds;
|
|
primary->DstBlt = gdi_dstblt;
|
|
primary->PatBlt = gdi_patblt;
|
|
primary->ScrBlt = gdi_scrblt;
|
|
primary->OpaqueRect = gdi_opaque_rect;
|
|
primary->DrawNineGrid = NULL;
|
|
primary->MultiDstBlt = NULL;
|
|
primary->MultiPatBlt = NULL;
|
|
primary->MultiScrBlt = NULL;
|
|
primary->MultiOpaqueRect = gdi_multi_opaque_rect;
|
|
primary->MultiDrawNineGrid = NULL;
|
|
primary->LineTo = gdi_line_to;
|
|
primary->Polyline = gdi_polyline;
|
|
primary->MemBlt = gdi_memblt;
|
|
primary->Mem3Blt = gdi_mem3blt;
|
|
primary->SaveBitmap = NULL;
|
|
primary->GlyphIndex = NULL;
|
|
primary->FastIndex = NULL;
|
|
primary->FastGlyph = NULL;
|
|
primary->PolygonSC = gdi_polygon_sc;
|
|
primary->PolygonCB = gdi_polygon_cb;
|
|
primary->EllipseSC = gdi_ellipse_sc;
|
|
primary->EllipseCB = gdi_ellipse_cb;
|
|
update->SurfaceBits = gdi_surface_bits;
|
|
update->SurfaceFrameMarker = gdi_surface_frame_marker;
|
|
update->altsec->FrameMarker = gdi_frame_marker;
|
|
}
|
|
|
|
static BOOL gdi_init_primary(rdpGdi* gdi, UINT32 stride, UINT32 format, BYTE* buffer,
|
|
void (*pfree)(void*), BOOL isLocked)
|
|
{
|
|
WINPR_ASSERT(gdi);
|
|
WINPR_ASSERT(gdi->context);
|
|
WINPR_ASSERT(gdi->context->update);
|
|
if (!isLocked)
|
|
rdp_update_lock(gdi->context->update);
|
|
|
|
gdi->primary = (gdiBitmap*)calloc(1, sizeof(gdiBitmap));
|
|
|
|
if (format > 0)
|
|
gdi->dstFormat = format;
|
|
|
|
if (stride > 0)
|
|
gdi->stride = stride;
|
|
else
|
|
gdi->stride = gdi->width * FreeRDPGetBytesPerPixel(gdi->dstFormat);
|
|
|
|
if (!gdi->primary)
|
|
goto fail_primary;
|
|
|
|
if (!(gdi->primary->hdc = gdi_CreateCompatibleDC(gdi->hdc)))
|
|
goto fail_hdc;
|
|
|
|
if (!buffer)
|
|
{
|
|
gdi->primary->bitmap = gdi_CreateCompatibleBitmap(gdi->hdc, gdi->width, gdi->height);
|
|
}
|
|
else
|
|
{
|
|
gdi->primary->bitmap =
|
|
gdi_CreateBitmapEx(gdi->width, gdi->height, gdi->dstFormat, gdi->stride, buffer, pfree);
|
|
}
|
|
|
|
if (!gdi->primary->bitmap)
|
|
goto fail_bitmap;
|
|
|
|
gdi->stride = gdi->primary->bitmap->scanline;
|
|
gdi_SelectObject(gdi->primary->hdc, (HGDIOBJECT)gdi->primary->bitmap);
|
|
gdi->primary->org_bitmap = NULL;
|
|
gdi->primary_buffer = gdi->primary->bitmap->data;
|
|
|
|
if (!(gdi->primary->hdc->hwnd = (HGDI_WND)calloc(1, sizeof(GDI_WND))))
|
|
goto fail_hwnd;
|
|
|
|
if (!(gdi->primary->hdc->hwnd->invalid = gdi_CreateRectRgn(0, 0, 0, 0)))
|
|
goto fail_hwnd;
|
|
|
|
gdi->primary->hdc->hwnd->invalid->null = TRUE;
|
|
gdi->primary->hdc->hwnd->count = 32;
|
|
|
|
if (!(gdi->primary->hdc->hwnd->cinvalid =
|
|
(HGDI_RGN)calloc(gdi->primary->hdc->hwnd->count, sizeof(GDI_RGN))))
|
|
goto fail_hwnd;
|
|
|
|
gdi->primary->hdc->hwnd->ninvalid = 0;
|
|
|
|
if (!gdi->drawing)
|
|
gdi->drawing = gdi->primary;
|
|
|
|
rdp_update_unlock(gdi->context->update);
|
|
return TRUE;
|
|
fail_hwnd:
|
|
gdi_DeleteObject((HGDIOBJECT)gdi->primary->bitmap);
|
|
fail_bitmap:
|
|
gdi_DeleteDC(gdi->primary->hdc);
|
|
fail_hdc:
|
|
free(gdi->primary);
|
|
gdi->primary = NULL;
|
|
fail_primary:
|
|
rdp_update_unlock(gdi->context->update);
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL gdi_resize(rdpGdi* gdi, UINT32 width, UINT32 height)
|
|
{
|
|
return gdi_resize_ex(gdi, width, height, 0, 0, NULL, NULL);
|
|
}
|
|
|
|
BOOL gdi_resize_ex(rdpGdi* gdi, UINT32 width, UINT32 height, UINT32 stride, UINT32 format,
|
|
BYTE* buffer, void (*pfree)(void*))
|
|
{
|
|
if (!gdi || !gdi->primary)
|
|
return FALSE;
|
|
|
|
if ((width > INT32_MAX) || (height > INT32_MAX))
|
|
return FALSE;
|
|
|
|
if ((gdi->width == (INT32)width) && (gdi->height == (INT32)height) &&
|
|
(!buffer || (gdi->primary_buffer == buffer)))
|
|
return TRUE;
|
|
|
|
WINPR_ASSERT(gdi->context);
|
|
WINPR_ASSERT(gdi->context->update);
|
|
rdp_update_lock(gdi->context->update);
|
|
|
|
if (gdi->drawing == gdi->primary)
|
|
gdi->drawing = NULL;
|
|
|
|
gdi->width = (INT32)width;
|
|
gdi->height = (INT32)height;
|
|
gdi_bitmap_free_ex(gdi->primary);
|
|
gdi->primary = NULL;
|
|
gdi->primary_buffer = NULL;
|
|
return gdi_init_primary(gdi, stride, format, buffer, pfree, TRUE);
|
|
}
|
|
|
|
/**
|
|
* Initialize GDI
|
|
*
|
|
* @param instance A pointer to the instance to use
|
|
* @param format The color format for the local framebuffer
|
|
* @return \b TRUE for success, \b FALSE for failure
|
|
*/
|
|
BOOL gdi_init(freerdp* instance, UINT32 format)
|
|
{
|
|
return gdi_init_ex(instance, format, 0, NULL, winpr_aligned_free);
|
|
}
|
|
|
|
/**
|
|
* Initialize GDI
|
|
*
|
|
* @param instance A pointer to the instance to use
|
|
* @param format The color format for the local framebuffer
|
|
* @param stride The size of a framebuffer line in bytes
|
|
* @param buffer A pointer to a buffer to be used as framebuffer
|
|
* @param pfree A custom function pointer to use to free the framebuffer
|
|
*
|
|
* @return \b TRUE for success, \b FALSE for failure
|
|
*/
|
|
BOOL gdi_init_ex(freerdp* instance, UINT32 format, UINT32 stride, BYTE* buffer,
|
|
void (*pfree)(void*))
|
|
{
|
|
rdpContext* context = NULL;
|
|
UINT32 SrcFormat = 0;
|
|
rdpGdi* gdi = NULL;
|
|
|
|
WINPR_ASSERT(instance);
|
|
|
|
context = instance->context;
|
|
WINPR_ASSERT(context);
|
|
WINPR_ASSERT(context->settings);
|
|
|
|
const UINT32 ColorDepth = freerdp_settings_get_uint32(context->settings, FreeRDP_ColorDepth);
|
|
SrcFormat = gdi_get_pixel_format(ColorDepth);
|
|
gdi = (rdpGdi*)calloc(1, sizeof(rdpGdi));
|
|
|
|
if (!gdi)
|
|
goto fail;
|
|
|
|
context->gdi = gdi;
|
|
gdi->log = WLog_Get(TAG);
|
|
|
|
if (!gdi->log)
|
|
goto fail;
|
|
|
|
gdi->context = context;
|
|
gdi->width = freerdp_settings_get_uint32(context->settings, FreeRDP_DesktopWidth);
|
|
gdi->height = freerdp_settings_get_uint32(context->settings, FreeRDP_DesktopHeight);
|
|
gdi->dstFormat = format;
|
|
/* default internal buffer format */
|
|
WLog_Print(gdi->log, WLOG_INFO, "Local framebuffer format %s",
|
|
FreeRDPGetColorFormatName(gdi->dstFormat));
|
|
WLog_Print(gdi->log, WLOG_INFO, "Remote framebuffer format %s",
|
|
FreeRDPGetColorFormatName(SrcFormat));
|
|
|
|
if (!(gdi->hdc = gdi_GetDC()))
|
|
goto fail;
|
|
|
|
gdi->hdc->format = gdi->dstFormat;
|
|
|
|
if (!gdi_init_primary(gdi, stride, gdi->dstFormat, buffer, pfree, FALSE))
|
|
goto fail;
|
|
|
|
if (!(context->cache = cache_new(context)))
|
|
goto fail;
|
|
|
|
gdi_register_update_callbacks(context->update);
|
|
brush_cache_register_callbacks(context->update);
|
|
glyph_cache_register_callbacks(context->update);
|
|
bitmap_cache_register_callbacks(context->update);
|
|
offscreen_cache_register_callbacks(context->update);
|
|
palette_cache_register_callbacks(context->update);
|
|
|
|
if (!gdi_register_graphics(context->graphics))
|
|
goto fail;
|
|
|
|
return TRUE;
|
|
fail:
|
|
gdi_free(instance);
|
|
WLog_ERR(TAG, "failed to initialize gdi");
|
|
return FALSE;
|
|
}
|
|
|
|
void gdi_free(freerdp* instance)
|
|
{
|
|
rdpGdi* gdi = NULL;
|
|
rdpContext* context = NULL;
|
|
|
|
if (!instance || !instance->context)
|
|
return;
|
|
|
|
gdi = instance->context->gdi;
|
|
|
|
if (gdi)
|
|
{
|
|
gdi_bitmap_free_ex(gdi->primary);
|
|
gdi_DeleteDC(gdi->hdc);
|
|
free(gdi);
|
|
}
|
|
|
|
context = instance->context;
|
|
cache_free(context->cache);
|
|
context->cache = NULL;
|
|
instance->context->gdi = (rdpGdi*)NULL;
|
|
}
|
|
|
|
BOOL gdi_send_suppress_output(rdpGdi* gdi, BOOL suppress)
|
|
{
|
|
RECTANGLE_16 rect;
|
|
rdpSettings* settings = NULL;
|
|
rdpUpdate* update = NULL;
|
|
|
|
if (!gdi || !gdi->context->settings || !gdi->context->update)
|
|
return FALSE;
|
|
|
|
if (gdi->suppressOutput == suppress)
|
|
return TRUE;
|
|
|
|
gdi->suppressOutput = suppress;
|
|
settings = gdi->context->settings;
|
|
update = gdi->context->update;
|
|
rect.left = 0;
|
|
rect.top = 0;
|
|
rect.right = freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth);
|
|
rect.bottom = freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight);
|
|
return update->SuppressOutput(gdi->context, !suppress, &rect);
|
|
}
|