You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
6788 lines
255 KiB
6788 lines
255 KiB
/****************************************************************************/
|
|
// noeint.c
|
|
//
|
|
// RDP Order Encoder Display Driver internal functions
|
|
//
|
|
// Copyright (C) 1996-2000 Microsoft Corporation
|
|
/****************************************************************************/
|
|
|
|
#include <precmpdd.h>
|
|
#pragma hdrstop
|
|
|
|
#include <limits.h>
|
|
|
|
#define TRC_FILE "noeint"
|
|
#include <adcg.h>
|
|
#include <atrcapi.h>
|
|
|
|
#include <noedisp.h>
|
|
|
|
#include <noadisp.h>
|
|
#include <nsbcdisp.h>
|
|
#include <nschdisp.h>
|
|
#include <nprcount.h>
|
|
#include <oe2.h>
|
|
|
|
#define DC_INCLUDE_DATA
|
|
#include <ndddata.c>
|
|
#include <nsbcddat.c>
|
|
#include <oe2data.c>
|
|
#undef DC_INCLUDE_DATA
|
|
|
|
#include <noedata.c>
|
|
|
|
#include <nchdisp.h>
|
|
#include <nbadisp.h>
|
|
#include <nbainl.h>
|
|
#include <noeinl.h>
|
|
|
|
#include <nsbcdisp.h>
|
|
#include <nsbcinl.h>
|
|
#include <at128.h>
|
|
#include <tsgdiplusenums.h>
|
|
|
|
|
|
#define BAD_FRAG_INDEX 0xffff
|
|
|
|
#ifdef NotUsed
|
|
/****************************************************************************/
|
|
// OEConvertMask
|
|
//
|
|
// Convert a colour mask to bit depth and shift values.
|
|
/****************************************************************************/
|
|
void RDPCALL OEConvertMask(
|
|
ULONG mask,
|
|
PUSHORT pBitDepth,
|
|
PUSHORT pShift)
|
|
{
|
|
UINT16 count;
|
|
|
|
DC_BEGIN_FN("OEConvertMask");
|
|
|
|
/************************************************************************/
|
|
/* A color mask is a bitwise field containing a 1 where the color uses */
|
|
/* the bit entry for the color and a 0 to indicate that it is not used */
|
|
/* for the color index. */
|
|
/* */
|
|
/* The bit sequences for each color must be one set of continuous data, */
|
|
/* so for example 00011100 is valid but 00110100 is not. An example */
|
|
/* bitmask for a 16 bit palette is as follows. */
|
|
/* */
|
|
/* Red ----> 11111000 00000000 - 0xF800 - 5 bits - 11 shift */
|
|
/* Green --> 00000111 11100000 - 0x07E0 - 6 bits - 5 shift */
|
|
/* Blue ---> 00000000 00011111 - 0x001F - 5 bits - 0 shift */
|
|
/* */
|
|
/* This function converts the mask to a bit and shift value. */
|
|
/************************************************************************/
|
|
|
|
// Set up default values.
|
|
*pShift = 0;
|
|
*pBitDepth = 0;
|
|
|
|
// Make sure we have a valid mask.
|
|
if (mask == 0) {
|
|
TRC_NRM((TB, "Ignoring mask"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
// Keep shifting the mask right until we hit a non-zero value in bit 0.
|
|
// Store the resulting count as the color shift.
|
|
count = 0;
|
|
while ((mask & 1) == 0) {
|
|
mask >>= 1;
|
|
count++;
|
|
}
|
|
*pShift = count;
|
|
|
|
// Keep shifting the mask right until we hit a zero value in bit 0.
|
|
// Store the resulting count as the color bit depth.
|
|
count = 0;
|
|
while ((mask & 1) != 0) {
|
|
mask >>= 1;
|
|
count++;
|
|
}
|
|
*pBitDepth = count;
|
|
|
|
TRC_DBG((TB, "Shift %hd bits %hd", *pShift, *pBitDepth));
|
|
|
|
DC_END_FN();
|
|
}
|
|
#endif // NotUsed
|
|
|
|
|
|
/****************************************************************************/
|
|
// OEConvertColor
|
|
//
|
|
// Converts a color from the NT Display Driver to a DCCOLOR.
|
|
/****************************************************************************/
|
|
void RDPCALL OEConvertColor(
|
|
PDD_PDEV ppdev,
|
|
DCCOLOR *pDCColor,
|
|
ULONG osColor,
|
|
XLATEOBJ *pxlo)
|
|
{
|
|
ULONG realIndex;
|
|
|
|
DC_BEGIN_FN("OEConvertColor");
|
|
|
|
// Check if color translation is required.
|
|
if (pxlo == NULL || pxlo->flXlate == XO_TRIVIAL) {
|
|
// Use the OS color without translation.
|
|
realIndex = osColor;
|
|
}
|
|
else {
|
|
// Convert from BMP to device color.
|
|
realIndex = XLATEOBJ_iXlate(pxlo, osColor);
|
|
if (realIndex == -1) {
|
|
TRC_ERR((TB, "Failed to convert color 0x%lx", osColor));
|
|
memset(pDCColor, 0, sizeof(DCCOLOR));
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
|
|
TRC_DBG((TB, "Device color 0x%lX", realIndex));
|
|
|
|
#ifdef DC_HICOLOR
|
|
if (ppdev->cClientBitsPerPel == 24) {
|
|
TRC_DBG((TB, "using real RGB value %06lx", realIndex));
|
|
pDCColor->u.rgb.red = ((RGBQUAD *)&realIndex)->rgbRed;
|
|
pDCColor->u.rgb.green = ((RGBQUAD *)&realIndex)->rgbGreen;
|
|
pDCColor->u.rgb.blue = ((RGBQUAD *)&realIndex)->rgbBlue;
|
|
}
|
|
else if ((ppdev->cClientBitsPerPel == 16) ||
|
|
(ppdev->cClientBitsPerPel == 15)) {
|
|
TRC_DBG((TB, "using 16bpp color %04lx", realIndex));
|
|
((BYTE *)pDCColor)[0] = (BYTE)realIndex;
|
|
((BYTE *)pDCColor)[1] = (BYTE)(realIndex >> 8);
|
|
((BYTE *)pDCColor)[2] = 0;
|
|
}
|
|
else
|
|
#endif
|
|
if (oeColorIndexSupported) {
|
|
TRC_DBG((TB, "using index %d", realIndex));
|
|
pDCColor->u.index = (BYTE)realIndex;
|
|
|
|
// Zero out the rest of the color.
|
|
pDCColor->u.rgb.green = 0;
|
|
pDCColor->u.rgb.blue = 0;
|
|
}
|
|
else {
|
|
pDCColor->u.rgb.red = (BYTE)ppdev->Palette[realIndex].peRed;
|
|
pDCColor->u.rgb.green= (BYTE)ppdev->Palette[realIndex].peGreen;
|
|
pDCColor->u.rgb.blue = (BYTE)ppdev->Palette[realIndex].peBlue;
|
|
TRC_DBG((TB, "Red %x green %x blue %x", pDCColor->u.rgb.red,
|
|
pDCColor->u.rgb.green, pDCColor->u.rgb.blue));
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
DC_END_FN();
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
// OESendBrushOrder
|
|
//
|
|
// Allocates and sends brush orders. Returns FALSE on failure.
|
|
/****************************************************************************/
|
|
BOOL RDPCALL OESendBrushOrder(
|
|
PDD_PDEV ppdev,
|
|
POE_BRUSH_DATA pBrush,
|
|
PBYTE pBits,
|
|
UINT32 oeBrushId)
|
|
{
|
|
PINT_ORDER pOrder;
|
|
PTS_CACHE_BRUSH_ORDER pBrushOrder;
|
|
unsigned cbOrderSize;
|
|
BOOL rc;
|
|
|
|
DC_BEGIN_FN("OESendBrushOrder");
|
|
|
|
// Calculate and allocate brush order buffer.
|
|
cbOrderSize = sizeof(TS_CACHE_BRUSH_ORDER) -
|
|
FIELDSIZE(TS_CACHE_BRUSH_ORDER, brushData) + pBrush->iBytes;
|
|
pOrder = OA_AllocOrderMem(ppdev, cbOrderSize);
|
|
if (pOrder != NULL) {
|
|
// We've successfully allocated the order, so fill in the details.
|
|
pBrushOrder = (PTS_CACHE_BRUSH_ORDER)pOrder->OrderData;
|
|
pBrushOrder->header.extraFlags = 0;
|
|
pBrushOrder->header.orderType = TS_CACHE_BRUSH;
|
|
pBrushOrder->header.orderHdr.controlFlags = TS_STANDARD | TS_SECONDARY;
|
|
pBrushOrder->cacheEntry = (char)pBrush->cacheEntry;
|
|
pBrushOrder->iBitmapFormat = (char)pBrush->iBitmapFormat;
|
|
pBrushOrder->cx = (char)pBrush->sizlBitmap.cx;
|
|
pBrushOrder->cy = (char)pBrush->sizlBitmap.cy;
|
|
pBrushOrder->style = pBrush->style;
|
|
|
|
// Copy over the brush bits.
|
|
pBrushOrder->iBytes = (char)pBrush->iBytes;
|
|
memcpy(pBrushOrder->brushData, pBits, pBrush->iBytes);
|
|
|
|
pBrushOrder->header.orderLength = (UINT16)
|
|
TS_CALCULATE_SECONDARY_ORDER_ORDERLENGTH(cbOrderSize);
|
|
|
|
INC_OUTCOUNTER(OUT_CACHEBRUSH);
|
|
ADD_OUTCOUNTER(OUT_CACHEBRUSH_BYTES, cbOrderSize);
|
|
OA_AppendToOrderList(pOrder);
|
|
|
|
TRC_NRM((TB, "Brush Data PDU (%08lx, %08lx):%02ld entry(%02ld:%02ld), "
|
|
"format:%ld, cx/cy:%02ld/%02ld",
|
|
pBrush->key1, pBrush->key2, pBrushOrder->iBytes, oeBrushId,
|
|
pBrushOrder->cacheEntry, pBrushOrder->iBitmapFormat,
|
|
pBrushOrder->cx, pBrushOrder->cy));
|
|
|
|
rc = TRUE;
|
|
}
|
|
else {
|
|
TRC_ERR((TB, "Failed to allocate brush order"));
|
|
pBrush->style = BS_NULL;
|
|
rc = FALSE;
|
|
}
|
|
|
|
DC_END_FN();
|
|
return rc;
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
/* Function: OEStoreBrush */
|
|
/* */
|
|
/* Description: Store the brush data required for pattern realted orders. */
|
|
/* This function is called by DrvRealiseBrush when it has data */
|
|
/* to be stored about a brush. */
|
|
/* */
|
|
/* Parameters: pbo - BRUSHOBJ of the brush to be stored */
|
|
/* style - Style of the brush (as defined in the DC-Share */
|
|
/* protocol) */
|
|
/* iBitmapFormat - color depth of brush */
|
|
/* sizlBitmap - dimensions of brush */
|
|
/* iBytes - number of brush bytes */
|
|
/* pBits - Pointer to the bits which are used to define */
|
|
/* a BS_PATTERN brush. */
|
|
/* pxlo - XLATEOBJ for the brush. */
|
|
/* hatch - Standard Windows hatch pattern index for a */
|
|
/* BS_HATCHED brush. */
|
|
/* pEncode - brush color encoding table */
|
|
/* pColors - table of unique colors */
|
|
/* numColors - number of unique colors */
|
|
/****************************************************************************/
|
|
BOOL RDPCALL OEStoreBrush(
|
|
PDD_PDEV ppdev,
|
|
BRUSHOBJ *pbo,
|
|
BYTE style,
|
|
ULONG iBitmapFormat,
|
|
SIZEL *sizlBitmap,
|
|
ULONG iBytes,
|
|
PBYTE pBits,
|
|
XLATEOBJ *pxlo,
|
|
BYTE hatch,
|
|
PBYTE pEncode,
|
|
PUINT32 pColors,
|
|
UINT32 numColors)
|
|
{
|
|
BOOL rc = FALSE;
|
|
ULONG i, j;
|
|
PBYTE pData = pBits;
|
|
PULONG pColorTable;
|
|
POE_BRUSH_DATA pBrush;
|
|
BOOL bFoundIt;
|
|
DCCOLOR devColor;
|
|
UINT32 brushSupportLevel, brushSize;
|
|
PVOID pUserDefined = NULL;
|
|
|
|
DC_BEGIN_FN("OEStoreBrush");
|
|
|
|
#ifdef DC_HICOLOR
|
|
// Determine which realized brush size we will need
|
|
if (numColors <= 2) {
|
|
brushSize = 8;
|
|
}
|
|
else if (numColors <= MAX_BRUSH_ENCODE_COLORS) {
|
|
if (ppdev->cClientBitsPerPel == 24)
|
|
brushSize = 28;
|
|
else if (ppdev->cClientBitsPerPel > 8) // 15 and 16 bpp
|
|
brushSize = 24;
|
|
else
|
|
brushSize = 20;
|
|
}
|
|
else {
|
|
brushSize = iBytes;
|
|
}
|
|
#else
|
|
// Determine which realized brush size we will need
|
|
if (numColors <= 2)
|
|
brushSize = 8;
|
|
else if (numColors <= MAX_BRUSH_ENCODE_COLORS)
|
|
brushSize = 20;
|
|
else
|
|
brushSize = 64;
|
|
#endif
|
|
|
|
// Allocate the space for the brush data.
|
|
pBrush = (POE_BRUSH_DATA)BRUSHOBJ_pvAllocRbrush(pbo,
|
|
sizeof(OE_BRUSH_DATA) - FIELDSIZE(OE_BRUSH_DATA, brushData) +
|
|
brushSize);
|
|
if (pBrush != NULL) {
|
|
// Reset the brush definition (init the minimum size).
|
|
memset(pBrush, 0, sizeof(OE_BRUSH_DATA));
|
|
|
|
// Set the new brush data. Brush fore and back colors are set below
|
|
// depending on the brush style.
|
|
pBrush->style = style;
|
|
pBrush->hatch = hatch;
|
|
pBrush->iBitmapFormat = iBitmapFormat;
|
|
pBrush->sizlBitmap = *sizlBitmap;
|
|
pBrush->iBytes = brushSize;
|
|
pBrush->cacheEntry = -1;
|
|
}
|
|
else {
|
|
TRC_ERR((TB, "No memory"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
// For pattern brushes, copy the brush specific data.
|
|
if (style == BS_PATTERN) {
|
|
brushSupportLevel = pddShm->sbc.caps.brushSupportLevel;
|
|
|
|
// Encode all monochrome brushes as 1 bpp - hence the name!
|
|
if (numColors <= 2) {
|
|
switch (iBitmapFormat) {
|
|
// already in the right format
|
|
case BMF_1BPP:
|
|
// Store the foreground and background colours for the brush
|
|
OEConvertColor(ppdev, &pBrush->fore, 0, pxlo);
|
|
OEConvertColor(ppdev, &pBrush->back, 1, pxlo);
|
|
TRC_ASSERT((pxlo != NULL), (TB, "pxlo is NULL"));
|
|
break;
|
|
|
|
// convert to 1bpp
|
|
case BMF_4BPP:
|
|
case BMF_8BPP:
|
|
case BMF_16BPP:
|
|
case BMF_24BPP:
|
|
// Store the foreground and background colours for the brush
|
|
#ifdef DC_HICOLOR
|
|
// The convert color fn takes care of the color depth
|
|
OEConvertColor(ppdev, &pBrush->fore, pColors[0], NULL);
|
|
OEConvertColor(ppdev, &pBrush->back, pColors[1], NULL);
|
|
#else
|
|
pBrush->fore.u.rgb.red = 0;
|
|
pBrush->fore.u.rgb.green = 0;
|
|
pBrush->fore.u.rgb.blue = 0;
|
|
pBrush->back.u.rgb.red = 0;
|
|
pBrush->back.u.rgb.green = 0;
|
|
pBrush->back.u.rgb.blue = 0;
|
|
pBrush->fore.u.index = (BYTE)pColors[0];
|
|
pBrush->back.u.index = (BYTE)pColors[1];
|
|
#endif
|
|
|
|
// Each pixel is represented by 1 bit
|
|
#ifdef DC_HICOLOR
|
|
if (ppdev->cClientBitsPerPel > 8) {
|
|
// Don't use endcoding table for hicolor sessions
|
|
for (i = 0; i < 8; i++) {
|
|
pBits[i] = (BYTE)((pBits[i * 8] << 7) & 0x80);
|
|
for (j = 1; j < 8; j++)
|
|
pBits[i] |= (BYTE)(pBits[i * 8 + j] <<
|
|
(7 - j));
|
|
}
|
|
}
|
|
else {
|
|
#endif
|
|
for (i = 0; i < 8; i++) {
|
|
pBits[i] = (BYTE)
|
|
(((pEncode[pBits[i * 8]]) << 7) & 0x80);
|
|
for (j = 1; j < 8; j++)
|
|
pBits[i] |= (BYTE)
|
|
((pEncode[pBits[i * 8 + j]]) <<
|
|
(7 - j));
|
|
}
|
|
#ifdef DC_HICOLOR
|
|
}
|
|
#endif
|
|
|
|
TRC_DBG((TB, "Encoded Bytes:"));
|
|
TRC_DBG((TB, "%02lx %02lx %02lx %02lx",
|
|
pBits[0], pBits[1], pBits[2], pBits[3]));
|
|
TRC_DBG((TB, "%02lx %02lx %02lx %02lx",
|
|
pBits[4], pBits[5], pBits[6], pBits[7]));
|
|
|
|
iBitmapFormat = pBrush->iBitmapFormat = BMF_1BPP;
|
|
iBytes = pBrush->iBytes = 8;
|
|
break;
|
|
|
|
default:
|
|
TRC_ASSERT((FALSE), (TB, "Unknown brush depth: %ld",
|
|
iBitmapFormat));
|
|
}
|
|
|
|
// if brush caching is enabled, check the cache
|
|
if ((brushSupportLevel > TS_BRUSH_DEFAULT) &&
|
|
(sbcEnabled & SBC_BRUSH_CACHE_ENABLED)) {
|
|
UINT32 key1, key2;
|
|
|
|
key1 = pBits[0] +
|
|
((ULONG) pBits[1] << 8) +
|
|
((ULONG) pBits[2] << 16) +
|
|
((ULONG) pBits[3] << 24);
|
|
|
|
key2 = pBits[4] +
|
|
((ULONG) pBits[5] << 8) +
|
|
((ULONG) pBits[6] << 16) +
|
|
((ULONG) pBits[7] << 24);
|
|
|
|
pBrush->key1 = key1;
|
|
pBrush->key2 = key2;
|
|
bFoundIt = CH_SearchCache(sbcSmallBrushCacheHandle, key1, key2,
|
|
&pUserDefined, &pBrush->cacheEntry);
|
|
|
|
pBrush->style = (BYTE) (TS_CACHED_BRUSH | iBitmapFormat);
|
|
|
|
// this brush was already cached
|
|
if (bFoundIt) {
|
|
pBrush->hatch = (BYTE) pBrush->cacheEntry;
|
|
pBrush->brushId = (INT32) (UINT_PTR) pUserDefined;
|
|
pddCacheStats[BRUSH].CacheHits++;
|
|
}
|
|
|
|
// cache and send the brush
|
|
else {
|
|
pBrush->hatch = pBrush->cacheEntry = (BYTE)CH_CacheKey(
|
|
sbcSmallBrushCacheHandle, key1, key2,
|
|
(PVOID)ULongToPtr(pddShm->shareId));
|
|
|
|
pBrush->brushId = pddShm->shareId;
|
|
|
|
OESendBrushOrder(ppdev, pBrush, pBits, pBrush->brushId);
|
|
TRC_NRM((TB, "Small Brush(%08lx,%08lx):%02ld, "
|
|
"F/S/H(%ld/%d/%d), ID %02ld:%02ld",
|
|
key1, key2, iBytes, iBitmapFormat, style, hatch,
|
|
pBrush->brushId, pBrush->hatch));
|
|
}
|
|
|
|
// Note: this branch intentionally does NOT copy the brush
|
|
// bits into the brush realization, but leaves that data zero.
|
|
// This causes OE2 to think this field never changes. If the
|
|
// brush becomes stale across reconnects, then the cache is
|
|
// restored using key1/key2 since in this case the keys == data
|
|
}
|
|
|
|
else {
|
|
// Copy the brush bits. Since this is an 8x8 mono bitmap, we can
|
|
// copy the first byte of the brush data for each scan line.
|
|
// NOTE however that the brush structures sent over the wire
|
|
// re-use the hatching variable as the first byte of the brush
|
|
// data.
|
|
pData = pBits;
|
|
pBrush->hatch = *pData;
|
|
TRC_DBG((TB, " Hatch: %d", *pData));
|
|
|
|
pData++;
|
|
|
|
for (i = 0; i < 7; i++)
|
|
pBrush->brushData[i] = pData[i];
|
|
}
|
|
}
|
|
|
|
// Else we have to use the large brush cache. Note that DrvRealize
|
|
// will not ask us to cache a brush if the client does not support
|
|
// color brushes.
|
|
else {
|
|
CHDataKeyContext CHContext;
|
|
CH_CreateKeyFromFirstData(&CHContext, pData, iBytes);
|
|
#ifdef DC_HICOLOR
|
|
/****************************************************************/
|
|
/* If we're running in high color mode, the way we encode the */
|
|
/* colors means that brushes with the same pattern but */
|
|
/* different colors will appear the same. E.g. a brush that */
|
|
/* starts lt blue, dk blue, pink will be encoded as 0,1,2 with */
|
|
/* the color table set to */
|
|
/* */
|
|
/* 0 = lt blue */
|
|
/* 1 = dk blue */
|
|
/* 2 = pink */
|
|
/* */
|
|
/* Now consider a brush that goes green, blue, purple. It too */
|
|
/* will be encoded as 0,1,2, with the color table set to */
|
|
/* */
|
|
/* 0 = green */
|
|
/* 1 = blue */
|
|
/* 2 = purple */
|
|
/* */
|
|
/* Thus a check simply on the pel values will find a false */
|
|
/* match with the first brush. To avoid this, we need to build */
|
|
/* the cache key from the pels AND the color table. */
|
|
/****************************************************************/
|
|
if ((ppdev->cClientBitsPerPel > 8) &&
|
|
(numColors <= MAX_BRUSH_ENCODE_COLORS))
|
|
CH_CreateKeyFromNextData(&CHContext, pColors,
|
|
4 * sizeof(UINT32));
|
|
#endif
|
|
pBrush->key1 = CHContext.Key1;
|
|
pBrush->key2 = CHContext.Key2;
|
|
|
|
// see if it is already cached
|
|
bFoundIt = CH_SearchCache(sbcLargeBrushCacheHandle,
|
|
CHContext.Key1, CHContext.Key2,
|
|
&pUserDefined, &pBrush->cacheEntry);
|
|
|
|
#ifdef DC_HICOLOR
|
|
pBrush->iBitmapFormat = iBitmapFormat;
|
|
#else
|
|
// Only send 8 bpp brushes for simplicity
|
|
iBitmapFormat = pBrush->iBitmapFormat = BMF_8BPP;
|
|
#endif
|
|
pBrush->style = (BYTE) (TS_CACHED_BRUSH | iBitmapFormat);
|
|
pBrush->fore.u.rgb.red = 0;
|
|
pBrush->fore.u.rgb.green = 0;
|
|
pBrush->fore.u.rgb.blue = 0;
|
|
pBrush->back.u.rgb.red = 0;
|
|
pBrush->back.u.rgb.green = 0;
|
|
pBrush->back.u.rgb.blue = 0;
|
|
|
|
// this brush was already cached
|
|
if (bFoundIt) {
|
|
pBrush->hatch = (BYTE) pBrush->cacheEntry;
|
|
pBrush->brushId = (INT32) (UINT_PTR) pUserDefined;
|
|
pddCacheStats[BRUSH].CacheHits++;
|
|
}
|
|
|
|
// else cache and send the brush
|
|
else {
|
|
pBrush->hatch = pBrush->cacheEntry = (BYTE)CH_CacheKey(
|
|
sbcLargeBrushCacheHandle, CHContext.Key1,
|
|
CHContext.Key2, (PVOID) ULongToPtr(pddShm->shareId));
|
|
|
|
#ifdef DC_HICOLOR
|
|
// The vast majority of brushes are less than 4 unique colors
|
|
// Note that to have got here, it has however got more than
|
|
// 2 colors or we'd have sent it as mono!
|
|
if (numColors <= MAX_BRUSH_ENCODE_COLORS) {
|
|
UINT32 currIndex;
|
|
|
|
// This code assumse that MAX_BRUSH_ENCODE_COLORS is 4!
|
|
// If not, the sizes will be wrong
|
|
TRC_ASSERT((MAX_BRUSH_ENCODE_COLORS == 4),
|
|
(TB, "Max Brush Encode colors must be 4"));
|
|
|
|
// Encode as 2 bits per pixel. We have to use the
|
|
// pEncode table for lo color; for hi color we don't
|
|
// need it because the pColors array contains the actual
|
|
// colors rather than indices into a color table
|
|
currIndex = 0;
|
|
|
|
if (ppdev->cClientBitsPerPel > 8) {
|
|
for (i = 0; i < (iBytes / 4); i++) {
|
|
pBrush->brushData[i] =
|
|
(((BYTE) pBits[currIndex ]) << 6) |
|
|
(((BYTE) pBits[currIndex + 1]) << 4) |
|
|
(((BYTE) pBits[currIndex + 2]) << 2) |
|
|
(((BYTE) pBits[currIndex + 3]));
|
|
currIndex += 4;
|
|
}
|
|
|
|
// Tag on the encoding table - remembering that the
|
|
// size differs by color depth
|
|
if (ppdev->cClientBitsPerPel == 24) {
|
|
RGBTRIPLE *pIntoData =
|
|
(RGBTRIPLE *)&(pBrush->brushData[16]);
|
|
|
|
TRC_DBG((TB, "Encoding table:"));
|
|
for (i = 0; i < 4; i++) {
|
|
TRC_DBG((TB, "%d %08lx", i,
|
|
(UINT32)pColors[i]));
|
|
pIntoData[i] = *((RGBTRIPLE * )&pColors[i]);
|
|
}
|
|
pBrush->iBytes = iBytes = 28;
|
|
}
|
|
else {
|
|
BYTE *pIntoData =
|
|
(BYTE *)&(pBrush->brushData[16]);
|
|
|
|
TRC_DBG((TB, "Encoding table:"));
|
|
for (i = 0; i < 4; i++) {
|
|
TRC_DBG((TB, "%d %08lx", i,
|
|
(UINT32)pColors[i]));
|
|
pIntoData[i * 2] = (BYTE)pColors[i];
|
|
pIntoData[i * 2 + 1] = (BYTE)(pColors[i] >> 8);
|
|
}
|
|
pBrush->iBytes = iBytes = 24;
|
|
}
|
|
}
|
|
else {
|
|
for (i = 0; i < (iBytes / 4); i++) {
|
|
pBrush->brushData[i] =
|
|
(((BYTE)pEncode[pBits[currIndex ]]) << 6) |
|
|
(((BYTE)pEncode[pBits[currIndex + 1]]) << 4) |
|
|
(((BYTE)pEncode[pBits[currIndex + 2]]) << 2) |
|
|
(((BYTE)pEncode[pBits[currIndex + 3]]));
|
|
currIndex += 4;
|
|
}
|
|
|
|
// Tag on the encoding table
|
|
TRC_DBG((TB, "Encoding table:"));
|
|
for (i = 0; i < 4; i++) {
|
|
TRC_DBG((TB, "%d %08lx", i, (UINT32)pColors[i]));
|
|
pBrush->brushData[i + 16] = (BYTE) pColors[i];
|
|
}
|
|
pBrush->iBytes = iBytes = 20;
|
|
}
|
|
}
|
|
|
|
// Else, leave it as N bytes per pixel
|
|
else {
|
|
memcpy(pBrush->brushData, pBits, iBytes);
|
|
TRC_ALT((TB, "Non-compressed N-bpp brush (colors=%ld):",
|
|
numColors));
|
|
}
|
|
#else
|
|
|
|
// The vast majority of brushes are less than 4 unique colors
|
|
if (numColors <= MAX_BRUSH_ENCODE_COLORS) {
|
|
UINT32 currIndex;
|
|
|
|
// Encode as 2 bits per pixel
|
|
currIndex = 0;
|
|
for (i = 0; i < (iBytes / MAX_BRUSH_ENCODE_COLORS); i++) {
|
|
pBrush->brushData[i] =
|
|
(((BYTE) pEncode[pBits[currIndex]]) << 6) |
|
|
(((BYTE) pEncode[pBits[currIndex + 1]]) << 4) |
|
|
(((BYTE) pEncode[pBits[currIndex + 2]]) << 2) |
|
|
(((BYTE) pEncode[pBits[currIndex + 3]]));
|
|
currIndex += MAX_BRUSH_ENCODE_COLORS;
|
|
|
|
}
|
|
|
|
// Tag on the encoding table
|
|
for (i = 0; i < MAX_BRUSH_ENCODE_COLORS; i++) {
|
|
pBrush->brushData[i + 16] = (BYTE) pColors[i];
|
|
}
|
|
pBrush->iBytes = iBytes = 20;
|
|
}
|
|
|
|
// Else, leave it as 1 byte per pixel
|
|
else {
|
|
for (i = 0; i < iBytes; i++)
|
|
pBrush->brushData[i] = pBits[i];
|
|
|
|
TRC_ALT((TB, "Non-compressed 8-bpp brush (colors=%ld):",
|
|
numColors));
|
|
}
|
|
#endif
|
|
pBrush->brushId = pddShm->shareId;
|
|
OESendBrushOrder(ppdev, pBrush, pBrush->brushData,
|
|
pBrush->brushId);
|
|
TRC_NRM((TB, "Large Brush(%08lx,%08lx):%02ld, "
|
|
"F/S/H(%ld/%d/%d), ID %02ld:%02ld",
|
|
CHContext.Key1, CHContext.Key2, iBytes,
|
|
iBitmapFormat, style, hatch,
|
|
pBrush->brushId, pBrush->hatch));
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if (pColors) {
|
|
// Store the foreground and background colors for the brush
|
|
OEConvertColor(ppdev, &pBrush->fore, pColors[0], pxlo);
|
|
OEConvertColor(ppdev, &pBrush->back, pColors[1], pxlo);
|
|
}
|
|
}
|
|
|
|
rc = TRUE;
|
|
INC_OUTCOUNTER(OUT_BRUSH_STORED);
|
|
|
|
DC_EXIT_POINT:
|
|
DC_END_FN();
|
|
return rc;
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
/* Function: OEReCacheBrush */
|
|
/* */
|
|
/* Description: This routine is called when we discover a GRE cached brush */
|
|
/* which was realized in a previous session. In this case we */
|
|
/* must recache the brush and send it to the client. */
|
|
/****************************************************************************/
|
|
BOOL RDPCALL OEReCacheBrush(
|
|
PDD_PDEV ppdev,
|
|
POE_BRUSH_DATA pBrush)
|
|
{
|
|
BOOL rc = FALSE;
|
|
PVOID pUserDefined = NULL;
|
|
UINT32 key1, key2;
|
|
CHCACHEHANDLE hBrushCache;
|
|
BYTE brushData[8];
|
|
PBYTE pBits;
|
|
|
|
DC_BEGIN_FN("OEReCacheBrush");
|
|
|
|
key1 = pBrush->key1;
|
|
key2 = pBrush->key2;
|
|
|
|
if (pBrush->iBitmapFormat == BMF_1BPP) {
|
|
brushData[0] = key1 & 0x000000FF;
|
|
brushData[1] = (key1 >> 8) & 0x000000FF;
|
|
brushData[2] = (key1 >> 16) & 0x000000FF;
|
|
brushData[3] = (key1 >> 24) & 0x000000FF;
|
|
brushData[4] = key2 & 0x000000FF;
|
|
brushData[5] = (key2 >> 8) & 0x000000FF;
|
|
brushData[6] = (key2 >> 16) & 0x000000FF;
|
|
brushData[7] = (key2 >> 24) & 0x000000FF;
|
|
pBits = brushData;
|
|
|
|
if ((pddShm->sbc.caps.brushSupportLevel > TS_BRUSH_DEFAULT) &&
|
|
(sbcEnabled & SBC_BRUSH_CACHE_ENABLED)) {
|
|
hBrushCache = sbcSmallBrushCacheHandle;
|
|
}
|
|
else {
|
|
int i;
|
|
|
|
// Copy the brush bits. Since this is an 8x8 mono bitmap, we can
|
|
// copy the first byte of the brush data for each scan line.
|
|
// NOTE however that the brush structures sent over the wire
|
|
// re-use the hatching variable as the first byte of the brush
|
|
// data.
|
|
pBrush->style = BS_PATTERN;
|
|
pBrush->brushId = pddShm->shareId;
|
|
pBrush->cacheEntry = -1;
|
|
pBrush->hatch = *pBits++;
|
|
|
|
for (i = 0; i < 7; i++)
|
|
pBrush->brushData[i] = pBits[i];
|
|
|
|
rc = TRUE;
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
else {
|
|
if ((pddShm->sbc.caps.brushSupportLevel > TS_BRUSH_DEFAULT) &&
|
|
(sbcEnabled & SBC_BRUSH_CACHE_ENABLED)) {
|
|
hBrushCache = sbcLargeBrushCacheHandle;
|
|
pBits = pBrush->brushData;
|
|
}
|
|
else {
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
|
|
// cache and send the brush
|
|
pBrush->hatch = pBrush->cacheEntry = (BYTE)CH_CacheKey(
|
|
hBrushCache, key1, key2, (PVOID) ULongToPtr(pddShm->shareId));
|
|
pBrush->brushId = pddShm->shareId;
|
|
|
|
rc = OESendBrushOrder(ppdev, pBrush, pBits, pBrush->brushId);
|
|
|
|
if (rc) {
|
|
TRC_ERR((TB, "Re-cached brush(%08lx,%08lx):%02ld, ID %02ld:%02ld",
|
|
key1, key2, pBrush->iBytes, pBrush->brushId, pBrush->hatch));
|
|
|
|
INC_OUTCOUNTER(OUT_BRUSH_STORED);
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
DC_END_FN();
|
|
return rc;
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
// OECheckBrushIsSimple
|
|
//
|
|
// Check that the brush is a 'simple' object the protocol can send.
|
|
/****************************************************************************/
|
|
BOOL RDPCALL OECheckBrushIsSimple(
|
|
PDD_PDEV ppdev,
|
|
BRUSHOBJ *pbo,
|
|
POE_BRUSH_DATA *ppBrush)
|
|
{
|
|
BOOL rc;
|
|
POE_BRUSH_DATA pBrush = NULL;
|
|
UINT32 style;
|
|
|
|
DC_BEGIN_FN("OECheckBrushIsSimple");
|
|
|
|
// A 'simple' brush satisfies any of the following.
|
|
// 1) It is a solid color.
|
|
// 2) It is a valid brush as stored by DrvRealizeBrush.
|
|
|
|
// Check for a simple solid color.
|
|
if (pbo->iSolidColor != -1) {
|
|
// Use the reserved brush definition to set up the solid colour.
|
|
TRC_DBG((TB, "Simple solid colour %08lx", pbo->iSolidColor));
|
|
pBrush = &oeBrushData;
|
|
|
|
// Set up the specific data for this brush.
|
|
OEConvertColor(ppdev, &pBrush->fore, pbo->iSolidColor, NULL);
|
|
|
|
pBrush->back.u.index = 0;
|
|
pBrush->back.u.rgb.red = 0;
|
|
pBrush->back.u.rgb.green = 0;
|
|
pBrush->back.u.rgb.blue = 0;
|
|
pBrush->style = BS_SOLID;
|
|
pBrush->hatch = 0;
|
|
RtlFillMemory(pBrush->brushData, sizeof(pBrush->brushData), 0);
|
|
|
|
rc = TRUE;
|
|
DC_QUIT;
|
|
}
|
|
|
|
rc = FALSE;
|
|
|
|
// Check brush definition (which was stored when we realized the
|
|
// brush). Here we find out if we've realised (cached) the brush already.
|
|
// This is counted as a automatic cache read. Subsequent routines
|
|
// increment the hit count if the brush was already cached.
|
|
pddCacheStats[BRUSH].CacheReads++;
|
|
|
|
pBrush = (POE_BRUSH_DATA)pbo->pvRbrush;
|
|
if (pBrush == NULL) {
|
|
pBrush = (POE_BRUSH_DATA)BRUSHOBJ_pvGetRbrush(pbo);
|
|
if (pBrush == NULL) {
|
|
// We can get NULL returned from BRUSHOBJ_pvGetRbrush when the
|
|
// brush is NULL or in low-memory situations (when the brush
|
|
// realization may fail).
|
|
TRC_NRM((TB, "NULL returned from BRUSHOBJ_pvGetRbrush"));
|
|
INC_OUTCOUNTER(OUT_CHECKBRUSH_NOREALIZATION);
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
|
|
// If brush caching make sure this brush isn't from a previous session
|
|
else if (pBrush->style & TS_CACHED_BRUSH) {
|
|
if (pBrush->brushId == pddShm->shareId) {
|
|
pddCacheStats[BRUSH].CacheHits++;
|
|
}
|
|
else {
|
|
TRC_ERR((TB, "Stale brush [%ld] detected! (%ld != %ld)",
|
|
pBrush->cacheEntry, pBrush->brushId, pddShm->shareId));
|
|
if (!OEReCacheBrush(ppdev, pBrush)) {
|
|
TRC_NRM((TB, "Unencodable brush, failed to ReCacheBrush"));
|
|
INC_OUTCOUNTER(OUT_CHECKBRUSH_COMPLEX);
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check it is an encodable brush. We cannot encode
|
|
// - BS_NULL
|
|
// - anything other than BS_SOLID or BS_PATTERN if
|
|
// oeSendSolidPatternBrushOnly is TRUE.
|
|
style = pBrush->style;
|
|
if ((style == BS_NULL) ||
|
|
(oeSendSolidPatternBrushOnly &&
|
|
(style != BS_SOLID) &&
|
|
(style != BS_PATTERN) &&
|
|
(!(style & TS_CACHED_BRUSH))))
|
|
{
|
|
TRC_NRM((TB, "Unencodable brush type %d", style));
|
|
INC_OUTCOUNTER(OUT_CHECKBRUSH_COMPLEX);
|
|
DC_QUIT;
|
|
}
|
|
|
|
// Everything passed - let's use this brush.
|
|
rc = TRUE;
|
|
|
|
DC_EXIT_POINT:
|
|
// Return the brush definition
|
|
*ppBrush = pBrush;
|
|
TRC_DBG((TB, "Returning %d - 0x%p", rc, pBrush));
|
|
|
|
DC_END_FN();
|
|
return rc;
|
|
}
|
|
|
|
#ifdef PERF_SPOILING
|
|
/****************************************************************************/
|
|
// OEIsSDAIncluded
|
|
//
|
|
// Returns TRUE if the list of RECTs passed in all lie completely within
|
|
// our current SDA bounds.
|
|
/****************************************************************************/
|
|
BOOL RDPCALL OEIsSDAIncluded(PRECTL prc, UINT count)
|
|
{
|
|
|
|
BOOL rc = FALSE;
|
|
unsigned uCurrentSDARect;
|
|
PRECTL pSDARect;
|
|
UINT i;
|
|
|
|
DC_BEGIN_FN("OEIsSDAIncluded");
|
|
|
|
// first check if we have SDA rects.
|
|
if (pddShm->ba.firstRect != BA_INVALID_RECT_INDEX) {
|
|
for (i=0 ; i < count; i++) {
|
|
for (uCurrentSDARect = pddShm->ba.firstRect;
|
|
uCurrentSDARect != BA_INVALID_RECT_INDEX;
|
|
uCurrentSDARect = pddShm->ba.bounds[uCurrentSDARect].iNext) {
|
|
|
|
pSDARect = &pddShm->ba.bounds[uCurrentSDARect].coord;
|
|
|
|
if (prc[i].top >= pSDARect->top &&
|
|
prc[i].bottom <= pSDARect->bottom &&
|
|
prc[i].left >= pSDARect->left &&
|
|
prc[i].right <= pSDARect->right) {
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
// We got to the end of the SDA array so that means
|
|
// we didn't find a rect that includes the target rect
|
|
if (uCurrentSDARect == BA_INVALID_RECT_INDEX) {
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
|
|
// We looped through all the rects and all were clipped
|
|
rc = TRUE;
|
|
DC_QUIT;
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
|
|
DC_END_FN();
|
|
return rc;
|
|
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************/
|
|
// OEGetClipRects
|
|
//
|
|
// Fills in *pEnumRects with up to COMPLEX_CLIP_RECT_COUNT clip rectangles,
|
|
// in standard GDI exclusive coordinates. The number of rects returned
|
|
// is zero if there is no clip object or the clipping is trivial.
|
|
// Returns FALSE if there are more than COMPLEX_CLIP_RECT_COUNT rects,
|
|
// indicating the clip object is too complex to encode.
|
|
/****************************************************************************/
|
|
BOOL RDPCALL OEGetClipRects(CLIPOBJ *pco, OE_ENUMRECTS *pEnumRects)
|
|
{
|
|
BOOL rc = TRUE;
|
|
|
|
DC_BEGIN_FN("OEGetClipRects");
|
|
|
|
// No clip obj or trivial are the most common.
|
|
if (pco == NULL || pco->iDComplexity == DC_TRIVIAL) {
|
|
TRC_DBG((TB,"No/trivial clipobj"));
|
|
pEnumRects->rects.c = 0;
|
|
}
|
|
else if (pco->iDComplexity == DC_RECT) {
|
|
// Single rect is easy, just grab it.
|
|
pEnumRects->rects.c = 1;
|
|
pEnumRects->rects.arcl[0] = pco->rclBounds;
|
|
}
|
|
else {
|
|
BOOL fMoreRects;
|
|
unsigned NumRects = 0;
|
|
OE_ENUMRECTS clip;
|
|
|
|
TRC_ASSERT((pco->iDComplexity == DC_COMPLEX),
|
|
(TB,"Unknown clipping %u", pco->iDComplexity));
|
|
|
|
// Enumerate all the rectangles involved in this drawing operation.
|
|
// The documentation for this function incorrectly states that the
|
|
// returned value is the total number of rectangles comprising the
|
|
// clip region. In fact, -1 is always returned, even when the final
|
|
// parameter is non-zero.
|
|
CLIPOBJ_cEnumStart(pco, FALSE, CT_RECTANGLES, CD_ANY, 0);
|
|
|
|
// Get the clip rectangles. We fetch these into the clip buffer which
|
|
// is big enough to get all the clip rectangles we expect + 1. The
|
|
// clip rectangle fetching is contained within a loop because, while
|
|
// we expect to call CLIPOBJ_bEnum once only, it is possible for this
|
|
// function to return zero rects and report that there are more to
|
|
// fetch (according to MSDN).
|
|
do {
|
|
fMoreRects = CLIPOBJ_bEnum(pco, sizeof(clip),
|
|
(ULONG *)&clip.rects);
|
|
|
|
// CLIPOBJ_bEnum can return a count of zero when there are still
|
|
// more rects.
|
|
if (clip.rects.c != 0) {
|
|
// Check to see if we have too many rects.
|
|
if ((NumRects + clip.rects.c) <= COMPLEX_CLIP_RECT_COUNT) {
|
|
// Copy the rects into the final destination.
|
|
memcpy(&pEnumRects->rects.arcl[NumRects],
|
|
&clip.rects.arcl[0],
|
|
sizeof(RECTL) * clip.rects.c);
|
|
NumRects += clip.rects.c;
|
|
}
|
|
else {
|
|
rc = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
} while (fMoreRects);
|
|
|
|
pEnumRects->rects.c = NumRects;
|
|
}
|
|
|
|
DC_END_FN();
|
|
return rc;
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
// OEGetIntersectingClipRects
|
|
//
|
|
// Fills in *pClipRects with up to COMPLEX_CLIP_RECT_COUNT clip rectangles,
|
|
// in standard GDI exclusive coordinates. Each result rectangle is clipped
|
|
// against the provided (exclusive) order rect. The number of rects
|
|
// returned is zero if there is no clip object or the clipping is trivial.
|
|
// Returns CLIPRECTS_TOO_COMPLEX if there are more than
|
|
// COMPLEX_CLIP_RECT_COUNT rects, CLIPRECTS_NO_INTERSECTIONS if there is
|
|
// no intersection between the order rect and the clip rects.
|
|
/****************************************************************************/
|
|
unsigned RDPCALL OEGetIntersectingClipRects(
|
|
CLIPOBJ *pco,
|
|
RECTL *pRect,
|
|
unsigned EnumType,
|
|
OE_ENUMRECTS *pClipRects)
|
|
{
|
|
unsigned rc = CLIPRECTS_OK;
|
|
RECTL OrderRect;
|
|
RECTL ClippedRect;
|
|
unsigned i;
|
|
unsigned NumIntersections;
|
|
OE_ENUMRECTS clip;
|
|
|
|
DC_BEGIN_FN("OEGetIntersectingClipRects");
|
|
|
|
// No clip obj or trivial are the most common.
|
|
if (pco == NULL || pco->iDComplexity == DC_TRIVIAL) {
|
|
TRC_DBG((TB,"No/trivial clipobj"));
|
|
pClipRects->rects.c = 0;
|
|
DC_QUIT;
|
|
}
|
|
|
|
OrderRect = *pRect;
|
|
|
|
if (pco->iDComplexity == DC_RECT) {
|
|
// Check for an intersection.
|
|
ClippedRect = pco->rclBounds;
|
|
if (ClippedRect.left < OrderRect.right &&
|
|
ClippedRect.bottom > OrderRect.top &&
|
|
ClippedRect.right > OrderRect.left &&
|
|
ClippedRect.top < OrderRect.bottom) {
|
|
// Get the intersection rect.
|
|
ClippedRect.left = max(ClippedRect.left, OrderRect.left);
|
|
ClippedRect.top = max(ClippedRect.top, OrderRect.top);
|
|
ClippedRect.bottom = min(ClippedRect.bottom, OrderRect.bottom);
|
|
ClippedRect.right = min(ClippedRect.right, OrderRect.right);
|
|
|
|
pClipRects->rects.c = 1;
|
|
pClipRects->rects.arcl[0] = ClippedRect;
|
|
}
|
|
else {
|
|
rc = CLIPRECTS_NO_INTERSECTIONS;
|
|
}
|
|
}
|
|
else {
|
|
BOOL fMoreRects;
|
|
unsigned NumRects = 0;
|
|
OE_ENUMRECTS clip;
|
|
|
|
TRC_ASSERT((pco->iDComplexity == DC_COMPLEX),
|
|
(TB,"Unknown clipping %u", pco->iDComplexity));
|
|
|
|
// Enumerate all the rectangles involved in this drawing operation.
|
|
// The documentation for this function incorrectly states that the
|
|
// returned value is the total number of rectangles comprising the
|
|
// clip region. In fact, -1 is always returned, even when the final
|
|
// parameter is non-zero.
|
|
CLIPOBJ_cEnumStart(pco, FALSE, CT_RECTANGLES, EnumType, 0);
|
|
|
|
// Get the clip rectangles. We fetch these into the clip buffer which
|
|
// is big enough to get all the clip rectangles we expect + 1. The
|
|
// clip rectangle fetching is contained within a loop because, while
|
|
// we expect to call CLIPOBJ_bEnum once only, it is possible for this
|
|
// function to return zero rects and report that there are more to
|
|
// fetch (according to MSDN).
|
|
NumIntersections = 0;
|
|
do {
|
|
fMoreRects = CLIPOBJ_bEnum(pco, sizeof(clip),
|
|
(ULONG *)&clip.rects);
|
|
|
|
// CLIPOBJ_bEnum can return a count of zero when there are still
|
|
// more rects.
|
|
if (clip.rects.c != 0) {
|
|
// Check to see if we have too many rects.
|
|
if ((NumIntersections + clip.rects.c) <=
|
|
COMPLEX_CLIP_RECT_COUNT) {
|
|
for (i = 0; i < clip.rects.c; i++) {
|
|
// Check for an intersection.
|
|
if (clip.rects.arcl[i].left < OrderRect.right &&
|
|
clip.rects.arcl[i].bottom > OrderRect.top &&
|
|
clip.rects.arcl[i].right > OrderRect.left &&
|
|
clip.rects.arcl[i].top < OrderRect.bottom) {
|
|
// Clip the intersection rect.
|
|
ClippedRect.left = max(clip.rects.arcl[i].left,
|
|
OrderRect.left);
|
|
ClippedRect.top = max(clip.rects.arcl[i].top,
|
|
OrderRect.top);
|
|
ClippedRect.right = min(clip.rects.arcl[i].right,
|
|
OrderRect.right);
|
|
ClippedRect.bottom = min(clip.rects.arcl[i].bottom,
|
|
OrderRect.bottom);
|
|
|
|
pClipRects->rects.arcl[NumIntersections] =
|
|
ClippedRect;
|
|
NumIntersections++;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
rc = CLIPRECTS_TOO_COMPLEX;
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
} while (fMoreRects);
|
|
|
|
if (NumIntersections > 0)
|
|
pClipRects->rects.c = NumIntersections;
|
|
else
|
|
rc = CLIPRECTS_NO_INTERSECTIONS;
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
DC_END_FN();
|
|
return rc;
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
// OEGetFontCacheInfo
|
|
//
|
|
// Gets the FCI for a font, allocating it if need be. Returns NULL on failure.
|
|
/****************************************************************************/
|
|
PFONTCACHEINFO RDPCALL OEGetFontCacheInfo(FONTOBJ *pfo)
|
|
{
|
|
PFONTCACHEINFO pfci;
|
|
PVOID pvConsumer;
|
|
|
|
DC_BEGIN_FN("OEGetFontCacheInfo");
|
|
|
|
pvConsumer = pfo->pvConsumer;
|
|
|
|
if (pvConsumer == NULL) {
|
|
pvConsumer = EngAllocMem(FL_ZERO_MEMORY, sizeof(FONTCACHEINFO),
|
|
DD_ALLOC_TAG);
|
|
|
|
if (pvConsumer != NULL && sbcFontCacheInfoList != NULL) {
|
|
// Save the pvConsumer data pointer so that on disconnect/logoff
|
|
// we can cleanup the memory.
|
|
if (sbcFontCacheInfoListIndex < sbcFontCacheInfoListSize) {
|
|
sbcFontCacheInfoList[sbcFontCacheInfoListIndex] =
|
|
(PFONTCACHEINFO)pvConsumer;
|
|
((PFONTCACHEINFO)pvConsumer)->listIndex = sbcFontCacheInfoListIndex;
|
|
sbcFontCacheInfoListIndex++;
|
|
}
|
|
else {
|
|
unsigned i, j;
|
|
PFONTCACHEINFO * tempList;
|
|
|
|
// We ran out of the preallocated memory, we have to
|
|
// reallocate the info list and recompact the list to the
|
|
// new one.
|
|
// Note: We need to update the list index now!
|
|
tempList = (PFONTCACHEINFO *) EngAllocMem(0,
|
|
sizeof(PFONTCACHEINFO) * sbcFontCacheInfoListSize * 2,
|
|
DD_ALLOC_TAG);
|
|
|
|
if (tempList != NULL) {
|
|
j = 0;
|
|
|
|
for (i = 0; i < sbcFontCacheInfoListIndex; i++) {
|
|
if (sbcFontCacheInfoList[i] != NULL) {
|
|
tempList[j] = sbcFontCacheInfoList[i];
|
|
((PFONTCACHEINFO)tempList[j])->listIndex = j;
|
|
j++;
|
|
}
|
|
}
|
|
|
|
EngFreeMem(sbcFontCacheInfoList);
|
|
sbcFontCacheInfoListSize = sbcFontCacheInfoListSize * 2;
|
|
sbcFontCacheInfoList = tempList;
|
|
|
|
sbcFontCacheInfoList[j] = (PFONTCACHEINFO)pvConsumer;
|
|
((PFONTCACHEINFO)pvConsumer)->listIndex = j;
|
|
sbcFontCacheInfoListIndex = ++j;
|
|
}
|
|
else {
|
|
EngFreeMem(pvConsumer);
|
|
pvConsumer = NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pvConsumer != NULL) {
|
|
pfci = (PFONTCACHEINFO)pvConsumer;
|
|
|
|
if (pfo->pvConsumer == NULL || pfci->shareId != pddShm->shareId ||
|
|
pfci->cacheHandle != pddShm->sbc.glyphCacheInfo[pfci->cacheId].cacheHandle) {
|
|
pfci->shareId = pddShm->shareId;
|
|
pfci->fontId = oeFontId++;
|
|
pfci->cacheId = -1;
|
|
}
|
|
|
|
pfo->pvConsumer = pvConsumer;
|
|
}
|
|
|
|
DC_END_FN();
|
|
return pvConsumer;
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
/* Worker function - encodes a delta from one rect to another in a minimal */
|
|
/* form in the MultiRectangle coded delta list. The encoding follows the */
|
|
/* following rules: */
|
|
/* 1. If a coordinate delta is zero, a flag is set saying so. This */
|
|
/* closely follows the data distribution which tends to have vertical */
|
|
/* and horizontal lines and so have a lot of zero deltas. */
|
|
/* 2. If we can pack the delta into 7 bits, do so, with the high bit */
|
|
/* cleared. This is similar to ASN.1 PER encoding; the high bit is a */
|
|
/* flag telling us whether the encoding is long. */
|
|
/* 3. Otherwise, we must be able to pack into 15 bits (assert if not), */
|
|
/* do so and set the high-order bit to indicate this is a long */
|
|
/* encoding. This differs from ASN.1 PER encoding in that we don't */
|
|
/* allow more than 15 bits of data. */
|
|
/* */
|
|
/* We usually see several small rectangles starting from about the same */
|
|
/* place but of wildly different shapes, so the delta between subsequent */
|
|
/* top-left's is small, and should normally fit in one byte, but the delta */
|
|
/* between bottom-rights may be large */
|
|
/* */
|
|
/* Thus we calculate the delta differently for the two corners: */
|
|
/* - the top left delta is the change from the last rectangle */
|
|
/* - the bottom right is the change from the top left of this rectangle */
|
|
/****************************************************************************/
|
|
void OEEncodeMultiRectangles(
|
|
BYTE **ppCurEncode,
|
|
unsigned *pNumDeltas,
|
|
unsigned *pDeltaSize,
|
|
BYTE *ZeroFlags,
|
|
RECTL *pFromRect,
|
|
RECTL *pToRect)
|
|
{
|
|
int Delta;
|
|
BYTE Zeros = 0;
|
|
BYTE *pBuffer;
|
|
unsigned EncodeLen = 0;
|
|
|
|
DC_BEGIN_FN("OEEncodeMultiRectangles");
|
|
|
|
pBuffer = *ppCurEncode;
|
|
|
|
// calculate the top-left x delta
|
|
Delta = pToRect->left - pFromRect->left;
|
|
TRC_DBG((TB, "Delta x-left %d", Delta));
|
|
if (Delta == 0) {
|
|
EncodeLen += 0;
|
|
Zeros |= ORD_CLIP_RECTS_XLDELTA_ZERO;
|
|
}
|
|
else if (Delta >= -64 && Delta <= 63) {
|
|
*pBuffer++ = (BYTE)(Delta & 0x7F);
|
|
EncodeLen += 1;
|
|
}
|
|
else {
|
|
// We can't encode deltas outside the range -16384 to +16383
|
|
if (Delta < -16384) {
|
|
TRC_ERR((TB,"X delta %d is too large to encode, clipping",Delta));
|
|
Delta = -16384;
|
|
}
|
|
else if (Delta > 16383) {
|
|
TRC_ERR((TB,"X delta %d is too large to encode, clipping",Delta));
|
|
Delta = 16383;
|
|
}
|
|
|
|
*pBuffer++ = (BYTE)((Delta >> 8) | ORD_CLIP_RECTS_LONG_DELTA);
|
|
*pBuffer++ = (BYTE)(Delta & 0xFF);
|
|
EncodeLen += 2;
|
|
}
|
|
|
|
// and the top-left y delta
|
|
Delta = pToRect->top - pFromRect->top;
|
|
TRC_DBG((TB, "Delta y-top %d", Delta));
|
|
if (Delta == 0) {
|
|
Zeros |= ORD_CLIP_RECTS_YTDELTA_ZERO;
|
|
}
|
|
else if (Delta >= -64 && Delta <= 63) {
|
|
*pBuffer++ = (BYTE)(Delta & 0x7F);
|
|
EncodeLen += 1;
|
|
}
|
|
else {
|
|
// See comments for the similar code above.
|
|
if (Delta < -16384) {
|
|
TRC_ERR((TB,"Y delta %d is too large to encode, clipping",Delta));
|
|
Delta = -16384;
|
|
}
|
|
else if (Delta > 16383) {
|
|
TRC_ERR((TB,"Y delta %d is too large to encode, clipping",Delta));
|
|
Delta = 16383;
|
|
}
|
|
|
|
*pBuffer++ = (BYTE)((Delta >> 8) | ORD_CLIP_RECTS_LONG_DELTA);
|
|
*pBuffer++ = (BYTE)(Delta & 0xFF);
|
|
EncodeLen += 2;
|
|
}
|
|
|
|
// Now the bottom-right x delta. Note this is relative to the current
|
|
// top left rather than the previous bottom right.
|
|
Delta = pToRect->right - pToRect->left;
|
|
TRC_DBG((TB, "Delta x-right %d", Delta));
|
|
if (Delta == 0) {
|
|
EncodeLen += 0;
|
|
Zeros |= ORD_CLIP_RECTS_XRDELTA_ZERO;
|
|
}
|
|
else if (Delta >= -64 && Delta <= 63) {
|
|
*pBuffer++ = (BYTE)(Delta & 0x7F);
|
|
EncodeLen += 1;
|
|
}
|
|
else {
|
|
// We can't encode deltas outside the range -16384 to +16383.
|
|
if (Delta < -16384) {
|
|
TRC_ERR((TB,"X delta %d is too large to encode, clipping",Delta));
|
|
Delta = -16384;
|
|
}
|
|
else if (Delta > 16383) {
|
|
TRC_ERR((TB,"X delta %d is too large to encode, clipping",Delta));
|
|
Delta = 16383;
|
|
}
|
|
|
|
*pBuffer++ = (BYTE)((Delta >> 8) | ORD_CLIP_RECTS_LONG_DELTA);
|
|
*pBuffer++ = (BYTE)(Delta & 0xFF);
|
|
EncodeLen += 2;
|
|
}
|
|
|
|
// and the bottom-right y delta.
|
|
Delta = pToRect->bottom - pToRect->top;
|
|
TRC_DBG((TB, "Delta y-bottom %d", Delta));
|
|
if (Delta == 0) {
|
|
Zeros |= ORD_CLIP_RECTS_YBDELTA_ZERO;
|
|
}
|
|
else if (Delta >= -64 && Delta <= 63) {
|
|
*pBuffer++ = (BYTE)(Delta & 0x7F);
|
|
EncodeLen += 1;
|
|
}
|
|
else {
|
|
// See comments for the similar code above.
|
|
if (Delta < -16384) {
|
|
TRC_ERR((TB,"Y delta %d is too large to encode, clipping",Delta));
|
|
Delta = -16384;
|
|
}
|
|
else if (Delta > 16383) {
|
|
TRC_ERR((TB,"Y delta %d is too large to encode, clipping",Delta));
|
|
Delta = 16383;
|
|
}
|
|
|
|
*pBuffer++ = (BYTE)((Delta >> 8) | ORD_CLIP_RECTS_LONG_DELTA);
|
|
*pBuffer++ = (BYTE)(Delta & 0xFF);
|
|
EncodeLen += 2;
|
|
}
|
|
|
|
// Set the zero flags by shifting the two bits we've accumulated.
|
|
ZeroFlags[(*pNumDeltas / 2)] |= (Zeros >> (4 * (*pNumDeltas & 0x01)));
|
|
|
|
*pNumDeltas += 1;
|
|
*pDeltaSize += EncodeLen;
|
|
*ppCurEncode = pBuffer;
|
|
|
|
DC_END_FN();
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
// OEBuildMultiClipOrder
|
|
//
|
|
// Creates a multi-clip-rect blob in intermediate format for multi-clip
|
|
// orders. Returns the number of clip rects in the blob.
|
|
/****************************************************************************/
|
|
unsigned OEBuildMultiClipOrder(
|
|
PDD_PDEV ppdev,
|
|
CLIP_RECT_VARIABLE_CODEDDELTALIST *pCodedDeltaList,
|
|
OE_ENUMRECTS *pClipRects)
|
|
{
|
|
unsigned NumRects;
|
|
unsigned i;
|
|
unsigned NumZeroFlagBytes;
|
|
BYTE Deltas[ORD_MAX_CLIP_RECTS_CODEDDELTAS_LEN] = { 0 };
|
|
BYTE ZeroFlags[ORD_MAX_CLIP_RECTS_ZERO_FLAGS_BYTES] = { 0 };
|
|
BYTE *pCurEncode;
|
|
unsigned NumDeltas = 0;
|
|
unsigned DeltaSize = 0;
|
|
RECTL nextRect = { 0 };
|
|
|
|
DC_BEGIN_FN("OEBuildMultiClipOrder");
|
|
|
|
#ifdef DRAW_NINEGRID
|
|
// Check that we actually have at least one clip rect.
|
|
TRC_ASSERT((pClipRects->rects.c > 0), (TB, "Got non-complex pClipObj"));
|
|
#else
|
|
// Check that we actually have more than one clip rect.
|
|
TRC_ASSERT((pClipRects->rects.c > 1), (TB, "Got non-complex pClipObj"));
|
|
#endif
|
|
|
|
// We expect no more than COMPLEX_CLIP_RECT_COUNT since
|
|
// somewhere up the encoding path we would have determined
|
|
// the number of clip rects already.
|
|
TRC_ASSERT((pClipRects->rects.c <= COMPLEX_CLIP_RECT_COUNT),
|
|
(TB, "Got %u rects but more exist", pClipRects->rects.c));
|
|
|
|
NumRects = pClipRects->rects.c;
|
|
pCurEncode = Deltas;
|
|
for (i = 0; i < NumRects; i++) {
|
|
// Add it to the delta array.
|
|
OEEncodeMultiRectangles(&pCurEncode, &NumDeltas, &DeltaSize,
|
|
ZeroFlags, &nextRect, &pClipRects->rects.arcl[i]);
|
|
nextRect = pClipRects->rects.arcl[i];
|
|
}
|
|
|
|
// Put the deltas into the supplied array.
|
|
NumZeroFlagBytes = (NumDeltas + 1) / 2;
|
|
TRC_NRM((TB, "Num zero flags %d", NumZeroFlagBytes));
|
|
pCodedDeltaList->len = DeltaSize + NumZeroFlagBytes;
|
|
|
|
// Copy the zero flags first.
|
|
memcpy(pCodedDeltaList->Deltas, ZeroFlags, NumZeroFlagBytes);
|
|
|
|
// Next copy the encoded deltas.
|
|
memcpy(pCodedDeltaList->Deltas + NumZeroFlagBytes, Deltas, DeltaSize);
|
|
|
|
TRC_NRM((TB, "num deltas %d in list len %d",
|
|
NumDeltas,
|
|
pCodedDeltaList->len));
|
|
|
|
TRC_DATA_NRM("zero flags", ZeroFlags, NumZeroFlagBytes);
|
|
TRC_DATA_NRM("deltas", Deltas, DeltaSize);
|
|
|
|
DC_END_FN();
|
|
return NumDeltas;
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
// OEBuildPrecodeMultiClipFields
|
|
//
|
|
// Given a CLIPOBJ, encodes the clip rects directly into the wire format for
|
|
// the nDeltaEntries and CLIP_RECT_VARIABLE_CODEDDELTALIST fields.
|
|
// Returns field flags for the nDeltaEntries and deltas fields:
|
|
// 0x01 for nDeltaEntries
|
|
// 0x02 for deltas
|
|
/****************************************************************************/
|
|
unsigned RDPCALL OEBuildPrecodeMultiClipFields(
|
|
OE_ENUMRECTS *pClipRects,
|
|
BYTE **ppBuffer,
|
|
UINT32 *pPrevNumDeltaEntries,
|
|
BYTE *pPrevCodedDeltas)
|
|
{
|
|
BYTE *pBuffer;
|
|
unsigned rc;
|
|
unsigned i;
|
|
unsigned NumRects;
|
|
unsigned NumZeroFlagBytes;
|
|
BYTE *pCurEncode;
|
|
unsigned NumDeltas = 0;
|
|
unsigned DeltaSize = 0;
|
|
unsigned TotalSize;
|
|
RECTL nextRect = { 0 };
|
|
BYTE Deltas[ORD_MAX_CLIP_RECTS_CODEDDELTAS_LEN] = { 0 };
|
|
BYTE ZeroFlags[ORD_MAX_CLIP_RECTS_ZERO_FLAGS_BYTES] = { 0 };
|
|
|
|
DC_BEGIN_FN("OEBuildPrecodeMultiClipFields");
|
|
|
|
// Check that we actually have more than one clip rect.
|
|
TRC_ASSERT((pClipRects->rects.c > 1), (TB, "Got non-complex clip"));
|
|
|
|
// We expect no more than COMPLEX_CLIP_RECT_COUNT since
|
|
// somewhere up the encoding path we would have determined
|
|
// the number of clip rects already.
|
|
TRC_ASSERT((pClipRects->rects.c <= COMPLEX_CLIP_RECT_COUNT),
|
|
(TB, "Got %u rects but more exist", pClipRects->rects.c));
|
|
|
|
NumRects = pClipRects->rects.c;
|
|
TRC_NRM((TB,"Encoding %u rects", NumRects));
|
|
pCurEncode = Deltas;
|
|
for (i = 0; i < NumRects; i++) {
|
|
// Add it to the delta array.
|
|
OEEncodeMultiRectangles(&pCurEncode, &NumDeltas, &DeltaSize,
|
|
ZeroFlags, &nextRect, &pClipRects->rects.arcl[i]);
|
|
TRC_DBG((TB," Added rect (%d,%d,%d,%d)",
|
|
pClipRects->rects.arcl[i].left,
|
|
pClipRects->rects.arcl[i].top,
|
|
pClipRects->rects.arcl[i].right,
|
|
pClipRects->rects.arcl[i].bottom));
|
|
nextRect = pClipRects->rects.arcl[i];
|
|
}
|
|
|
|
// Now use the accumulated information to encode the wire format.
|
|
pBuffer = *ppBuffer;
|
|
|
|
// nDeltaEntries - one-byte encoding if not same as previous.
|
|
if (NumDeltas == *pPrevNumDeltaEntries) {
|
|
rc = 0;
|
|
}
|
|
else {
|
|
rc = 0x01;
|
|
*pBuffer++ = (BYTE)NumDeltas;
|
|
*pPrevNumDeltaEntries = NumDeltas;
|
|
}
|
|
|
|
// The size is placed on the wire as 2 bytes, followed by the flag bytes
|
|
// and the deltas, as long as they are different from the previous.
|
|
NumZeroFlagBytes = (NumDeltas + 1) / 2;
|
|
TRC_DBG((TB, "Num flag bytes %d", NumZeroFlagBytes));
|
|
|
|
// Assemble the encoded rect deltas for comparison to the previous
|
|
// deltas in the last OE2 order encoding.
|
|
*((PUINT16_UA)pBuffer) = (UINT16)(DeltaSize + NumZeroFlagBytes);
|
|
memcpy(pBuffer + 2, ZeroFlags, NumZeroFlagBytes);
|
|
memcpy(pBuffer + 2 + NumZeroFlagBytes, Deltas, DeltaSize);
|
|
TotalSize = 2 + NumZeroFlagBytes + DeltaSize;
|
|
if (memcmp(pBuffer, pPrevCodedDeltas, TotalSize)) {
|
|
// Only send the deltas if the block is different from
|
|
// the previous block.
|
|
memcpy(pPrevCodedDeltas, pBuffer, TotalSize);
|
|
pBuffer += TotalSize;
|
|
rc |= 0x02;
|
|
}
|
|
|
|
*ppBuffer = pBuffer;
|
|
|
|
DC_END_FN();
|
|
return rc;
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
// OEGetIntersectionsWithClipRects
|
|
//
|
|
// Determines the rects that intersect between a set of (exclusive) clip
|
|
// rects and a single (exclusive) order rect. Clips the rects to the order
|
|
// rect while returning them; result rects are in exclusive coords.
|
|
// Returns the number of intersecting rects. Should only be called when
|
|
// there are more than zero clip rects.
|
|
/****************************************************************************/
|
|
unsigned OEGetIntersectionsWithClipRects(
|
|
RECTL *pRect,
|
|
OE_ENUMRECTS *pClipRects,
|
|
OE_ENUMRECTS *pResultRects)
|
|
{
|
|
RECTL OrderRect;
|
|
RECTL ClippedRect;
|
|
RECTL ClipRect;
|
|
unsigned i;
|
|
unsigned NumRects;
|
|
unsigned NumIntersections;
|
|
|
|
DC_BEGIN_FN("OEGetIntersectionsWithClipRects");
|
|
|
|
TRC_ASSERT((pClipRects->rects.c != 0),(TB,"Zero cliprects not allowed"));
|
|
|
|
OrderRect = *pRect;
|
|
NumRects = pClipRects->rects.c;
|
|
NumIntersections = 0;
|
|
for (i = 0; i < NumRects; i++) {
|
|
ClipRect = pClipRects->rects.arcl[i];
|
|
|
|
// Check for an intersection.
|
|
if (ClipRect.left < OrderRect.right &&
|
|
ClipRect.bottom > OrderRect.top &&
|
|
ClipRect.right > OrderRect.left &&
|
|
ClipRect.top < OrderRect.bottom) {
|
|
// Clip the intersection rect.
|
|
ClippedRect.left = max(ClipRect.left, OrderRect.left);
|
|
ClippedRect.bottom = min(ClipRect.bottom, OrderRect.bottom);
|
|
ClippedRect.right = min(ClipRect.right, OrderRect.right);
|
|
ClippedRect.top = max(ClipRect.top, OrderRect.top);
|
|
|
|
pResultRects->rects.arcl[NumIntersections] = ClippedRect;
|
|
NumIntersections++;
|
|
}
|
|
}
|
|
|
|
pResultRects->rects.c = NumIntersections;
|
|
|
|
DC_END_FN();
|
|
return NumIntersections;
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
// OEClipAndAddScreenDataAreaByIntersectRects
|
|
//
|
|
// Adds areas specified in intersect rect list to the SDA. If there are
|
|
// no intersect rects, adds the entire *pRect to the SDA.
|
|
/****************************************************************************/
|
|
void RDPCALL OEClipAndAddScreenDataAreaByIntersectRects(
|
|
PRECTL pRect,
|
|
OE_ENUMRECTS *pClipRects)
|
|
{
|
|
RECTL ClippedRect;
|
|
unsigned i;
|
|
unsigned NumRects;
|
|
|
|
DC_BEGIN_FN("OEClipAndAddScreenDataAreaByIntersectRects");
|
|
|
|
NumRects = pClipRects->rects.c;
|
|
|
|
if (NumRects == 0) {
|
|
// No clip rects; add the entire bounds.
|
|
// Use the inclusive rect. We make a copy because BA_AddScreenData
|
|
// can modify the rectangle.
|
|
ClippedRect = *pRect;
|
|
TRC_NRM((TB, "Adding SDA (%d,%d)(%d,%d)", ClippedRect.left,
|
|
ClippedRect.top, ClippedRect.right, ClippedRect.bottom));
|
|
BA_AddScreenData(&ClippedRect);
|
|
}
|
|
else {
|
|
for (i = 0; i < NumRects; i++) {
|
|
// Convert each rect to inclusive.
|
|
ClippedRect.left = pClipRects->rects.arcl[i].left;
|
|
ClippedRect.top = pClipRects->rects.arcl[i].top;
|
|
ClippedRect.right = pClipRects->rects.arcl[i].right;
|
|
ClippedRect.bottom = pClipRects->rects.arcl[i].bottom;
|
|
|
|
// Add the clipped rect into the SDA.
|
|
TRC_NRM((TB, "Adding SDA (%d,%d)(%d,%d)",
|
|
ClippedRect.left, ClippedRect.top,
|
|
ClippedRect.right, ClippedRect.bottom));
|
|
BA_AddScreenData(&ClippedRect);
|
|
}
|
|
}
|
|
|
|
DC_END_FN();
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
// OEClipAndAddScreenDataArea
|
|
//
|
|
// ClipObj version of OEClipAndAddScreenDataAreaByIntersectRects(), uses pco
|
|
// for enumeration since it may contain more than COMPLEX_CLIP_RECT_COUNT
|
|
// rects.
|
|
/****************************************************************************/
|
|
void RDPCALL OEClipAndAddScreenDataArea(PRECTL pRect, CLIPOBJ *pco)
|
|
{
|
|
BOOL fMoreRects;
|
|
RECTL clippedRect;
|
|
unsigned i;
|
|
OE_ENUMRECTS clip;
|
|
|
|
DC_BEGIN_FN("OEClipAndAddScreenDataArea");
|
|
|
|
if ((pco == NULL) || (pco->iDComplexity == DC_TRIVIAL)) {
|
|
// No clipping -- add the (exclusive) *pRect directly, making a copy
|
|
// since BA_AddScreenData() can modify the rect.
|
|
clippedRect = *pRect;
|
|
TRC_NRM((TB, "Adding SDA (%d,%d)(%d,%d)", clippedRect.left,
|
|
clippedRect.top, clippedRect.right, clippedRect.bottom));
|
|
BA_AddScreenData(&clippedRect);
|
|
}
|
|
else if (pco->iDComplexity == DC_RECT) {
|
|
// One clipping rectangle - use it directly. Make sure the rectangle
|
|
// is valid before adding to the SDA.
|
|
clippedRect.left = max(pco->rclBounds.left, pRect->left);
|
|
clippedRect.right = min(pco->rclBounds.right, pRect->right);
|
|
|
|
if (clippedRect.left < clippedRect.right) {
|
|
clippedRect.bottom = min(pco->rclBounds.bottom,
|
|
pRect->bottom);
|
|
clippedRect.top = max(pco->rclBounds.top, pRect->top);
|
|
|
|
if (clippedRect.bottom > clippedRect.top) {
|
|
// Add the clipped rect into the SDA.
|
|
TRC_NRM((TB, "Adding SDA RECT (%d,%d)(%d,%d)",
|
|
clippedRect.left, clippedRect.top,
|
|
clippedRect.right, clippedRect.bottom));
|
|
BA_AddScreenData(&clippedRect);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
// Enumerate all the rectangles involved in this drawing operation.
|
|
// The documentation for this function incorrectly states that
|
|
// the returned value is the total number of rectangles
|
|
// comprising the clip region. In fact, -1 is always returned,
|
|
// even when the final parameter is non-zero.
|
|
CLIPOBJ_cEnumStart(pco, FALSE, CT_RECTANGLES, CD_ANY, 0);
|
|
|
|
do {
|
|
// Get the next batch of clipping rectangles.
|
|
fMoreRects = CLIPOBJ_bEnum(pco, sizeof(clip),
|
|
(ULONG *)&clip.rects);
|
|
|
|
for (i = 0; i < clip.rects.c; i++) {
|
|
TRC_DBG((TB, " (%d,%d)(%d,%d)",
|
|
clip.rects.arcl[i].left, clip.rects.arcl[i].top,
|
|
clip.rects.arcl[i].right, clip.rects.arcl[i].bottom));
|
|
|
|
// Intersect the SDA rect with the clip rect, checking for
|
|
// no intersection. Convert clip.rects.arcl[i] to inclusive
|
|
// coords during comparisons.
|
|
clippedRect.left = max(clip.rects.arcl[i].left,
|
|
pRect->left);
|
|
clippedRect.right = min(clip.rects.arcl[i].right,
|
|
pRect->right);
|
|
|
|
// No horizontal intersection if the left boundary is to the
|
|
// right of the right boundary.
|
|
if (clippedRect.left < clippedRect.right) {
|
|
clippedRect.bottom = min(clip.rects.arcl[i].bottom,
|
|
pRect->bottom);
|
|
clippedRect.top = max(clip.rects.arcl[i].top, pRect->top);
|
|
|
|
// No vertical intersection if the top boundary is below
|
|
// the bottom boundary.
|
|
if (clippedRect.top < clippedRect.bottom) {
|
|
TRC_NRM((TB, "Adding SDA (%d,%d)(%d,%d)",
|
|
clippedRect.left, clippedRect.top,
|
|
clippedRect.right, clippedRect.bottom));
|
|
BA_AddScreenData(&clippedRect);
|
|
}
|
|
}
|
|
}
|
|
} while (fMoreRects);
|
|
}
|
|
|
|
DC_END_FN();
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
// OEEncodeLineToOrder
|
|
//
|
|
// Encodes a LineTo order to wire format.
|
|
/****************************************************************************/
|
|
BOOL RDPCALL OEEncodeLineToOrder(
|
|
PDD_PDEV ppdev,
|
|
PPOINTL startPoint,
|
|
PPOINTL endPoint,
|
|
UINT32 rop2,
|
|
UINT32 color,
|
|
OE_ENUMRECTS *pClipRects)
|
|
{
|
|
BOOL rc;
|
|
PINT_ORDER pOrder;
|
|
|
|
DC_BEGIN_FN("OEEncodeLineToOrder");
|
|
|
|
// 2 field flag bytes.
|
|
pOrder = OA_AllocOrderMem(ppdev, MAX_ORDER_SIZE(pClipRects->rects.c,
|
|
2, MAX_LINETO_FIELD_SIZE));
|
|
if (pOrder != NULL) {
|
|
BYTE *pControlFlags = pOrder->OrderData;
|
|
BYTE *pBuffer = pControlFlags + 1;
|
|
PUINT32_UA pFieldFlags;
|
|
short Delta, NormalCoordEncoding[4];
|
|
BOOLEAN bUseDeltaCoords;
|
|
unsigned NumFields;
|
|
DCCOLOR Color;
|
|
POINTL ClippedPoint;
|
|
|
|
memset(NormalCoordEncoding, 0, sizeof(NormalCoordEncoding));
|
|
// Direct-encode the primary order fields. 2 field flag bytes.
|
|
*pControlFlags = TS_STANDARD;
|
|
OE2_EncodeOrderType(pControlFlags, &pBuffer, TS_ENC_LINETO_ORDER);
|
|
pFieldFlags = (PUINT32_UA)pBuffer;
|
|
*pFieldFlags = 0;
|
|
*(pFieldFlags + 1) = 0;
|
|
pBuffer += 2;
|
|
if (pClipRects->rects.c != 0)
|
|
OE2_EncodeBounds(pControlFlags, &pBuffer,
|
|
&pClipRects->rects.arcl[0]);
|
|
|
|
// Start with the BackMode field.
|
|
// We only draw solid lines with no option as to what we do to the
|
|
// background, so this is always transparent. We will end up sending
|
|
// the field once with the first LineTo we send.
|
|
if (PrevLineTo.BackMode != TRANSPARENT) {
|
|
PrevLineTo.BackMode = TRANSPARENT;
|
|
*((unsigned short UNALIGNED *)pBuffer) =
|
|
(unsigned short)TRANSPARENT;
|
|
pBuffer += sizeof(short);
|
|
*pFieldFlags |= 0x0001;
|
|
}
|
|
|
|
// Simultaneously determine if each of the coordinate fields has
|
|
// changed, whether we can use delta coordinates, and save changed
|
|
// fields.
|
|
NumFields = 0;
|
|
bUseDeltaCoords = TRUE;
|
|
|
|
// Clip the start point coords.
|
|
ClippedPoint = *startPoint;
|
|
OEClipPoint(&ClippedPoint);
|
|
|
|
// nXStart
|
|
Delta = (short)(ClippedPoint.x - PrevLineTo.nXStart);
|
|
if (Delta) {
|
|
PrevLineTo.nXStart = ClippedPoint.x;
|
|
if (Delta != (short)(char)Delta)
|
|
bUseDeltaCoords = FALSE;
|
|
pBuffer[NumFields] = (char)Delta;
|
|
NormalCoordEncoding[NumFields] = (short)ClippedPoint.x;
|
|
NumFields++;
|
|
*pFieldFlags |= 0x0002;
|
|
}
|
|
|
|
// nYStart
|
|
Delta = (short)(ClippedPoint.y - PrevLineTo.nYStart);
|
|
if (Delta) {
|
|
PrevLineTo.nYStart = ClippedPoint.y;
|
|
if (Delta != (short)(char)Delta)
|
|
bUseDeltaCoords = FALSE;
|
|
pBuffer[NumFields] = (char)Delta;
|
|
NormalCoordEncoding[NumFields] = (short)ClippedPoint.y;
|
|
NumFields++;
|
|
*pFieldFlags |= 0x0004;
|
|
}
|
|
|
|
// Clip the end point coords.
|
|
ClippedPoint = *endPoint;
|
|
OEClipPoint(&ClippedPoint);
|
|
|
|
// nXEnd
|
|
Delta = (short)(ClippedPoint.x - PrevLineTo.nXEnd);
|
|
if (Delta) {
|
|
PrevLineTo.nXEnd = ClippedPoint.x;
|
|
if (Delta != (short)(char)Delta)
|
|
bUseDeltaCoords = FALSE;
|
|
pBuffer[NumFields] = (char)Delta;
|
|
NormalCoordEncoding[NumFields] = (short)ClippedPoint.x;
|
|
NumFields++;
|
|
*pFieldFlags |= 0x0008;
|
|
}
|
|
|
|
// nYEnd
|
|
Delta = (short)(ClippedPoint.y - PrevLineTo.nYEnd);
|
|
if (Delta) {
|
|
PrevLineTo.nYEnd = ClippedPoint.y;
|
|
if (Delta != (short)(char)Delta)
|
|
bUseDeltaCoords = FALSE;
|
|
pBuffer[NumFields] = (char)Delta;
|
|
NormalCoordEncoding[NumFields] = (short)ClippedPoint.y;
|
|
NumFields++;
|
|
*pFieldFlags |= 0x0010;
|
|
}
|
|
|
|
// Copy the final coordinates to the order.
|
|
if (bUseDeltaCoords) {
|
|
*pControlFlags |= TS_DELTA_COORDINATES;
|
|
pBuffer += NumFields;
|
|
}
|
|
else {
|
|
*((DWORD UNALIGNED *)pBuffer) = *((DWORD *)NormalCoordEncoding);
|
|
*((DWORD UNALIGNED *)(pBuffer + 4)) =
|
|
*((DWORD *)&NormalCoordEncoding[2]);
|
|
pBuffer += NumFields * sizeof(short);
|
|
}
|
|
|
|
// BackColor is a 3-byte color field.
|
|
// As it happens, we always draw solid lines, so we can choose any
|
|
// color. For convenience we choose black (0,0,0) so we never have to
|
|
// send this field at all. We skip encoding flag 0x0020.
|
|
|
|
// ROP2
|
|
if (rop2 != PrevLineTo.ROP2) {
|
|
PrevLineTo.ROP2 = rop2;
|
|
*pBuffer++ = (BYTE)rop2;
|
|
*pFieldFlags |= 0x0040;
|
|
}
|
|
|
|
// PenStyle
|
|
// The NT Display Driver is only called to accelerate simple solid
|
|
// lines. So we only support pen styles of PS_SOLID. Since PS_SOLID is
|
|
// zero, we never have to send this field. Skip encoding flag 0x0080.
|
|
|
|
// PenWidth
|
|
// We only accelerate width 1 fields. Which means we will send the
|
|
// 1 value only once in the first LineTo in the session.
|
|
if (PrevLineTo.PenWidth != 1) {
|
|
PrevLineTo.PenWidth = 1;
|
|
*pBuffer++ = 1;
|
|
*pFieldFlags |= 0x0100;
|
|
}
|
|
|
|
// PenColor is a 3-byte color field.
|
|
OEConvertColor(ppdev, &Color, color, NULL);
|
|
if (memcmp(&Color, &PrevLineTo.PenColor, sizeof(Color))) {
|
|
PrevLineTo.PenColor = Color;
|
|
*pBuffer++ = Color.u.rgb.red;
|
|
*pBuffer++ = Color.u.rgb.green;
|
|
*pBuffer++ = Color.u.rgb.blue;
|
|
*pFieldFlags |= 0x0200;
|
|
}
|
|
|
|
// Set final size.
|
|
pOrder->OrderLength = (unsigned)(pBuffer - pOrder->OrderData);
|
|
|
|
// See if we can save sending some of the order field bytes.
|
|
pOrder->OrderLength -= OE2_CheckTwoZeroFlagBytes(pControlFlags,
|
|
(BYTE *)pFieldFlags,
|
|
(unsigned)(pBuffer - (BYTE *)pFieldFlags - 2));
|
|
|
|
INC_OUTCOUNTER(OUT_LINETO_ORDR);
|
|
ADD_INCOUNTER(IN_LINETO_BYTES, pOrder->OrderLength);
|
|
OA_AppendToOrderList(pOrder);
|
|
|
|
// Flush the order.
|
|
if (pClipRects->rects.c < 2)
|
|
rc = TRUE;
|
|
else
|
|
rc = OEEmitReplayOrders(ppdev, 2, pClipRects);
|
|
|
|
TRC_NRM((TB, "LineTo: rop2=%02X, PenColor=%04X, start=(%d,%d), "
|
|
"end=(%d,%d)", rop2, Color.u.index,
|
|
startPoint->x, startPoint->y,
|
|
endPoint->x, endPoint->y));
|
|
}
|
|
else {
|
|
TRC_ERR((TB, "Failed to alloc order"));
|
|
rc = FALSE;
|
|
}
|
|
|
|
DC_END_FN();
|
|
return rc;
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
// OESendSwitchSurfacePDU
|
|
//
|
|
// If the last drawing surface changed since, we need to send a switch
|
|
// surface PDU to the client to switch to the new surface
|
|
// This PDU is added to support multisurface rendering.
|
|
/****************************************************************************/
|
|
BOOL RDPCALL OESendSwitchSurfacePDU(PDD_PDEV ppdev, PDD_DSURF pdsurf)
|
|
{
|
|
unsigned bitmapSurfaceId;
|
|
void *UserDefined;
|
|
PINT_ORDER pOrder;
|
|
PTS_SWITCH_SURFACE_ORDER pSurfSwitchOrder;
|
|
BOOL rc;
|
|
|
|
DC_BEGIN_FN("OESendSwitchSurfacePDU");
|
|
|
|
// Check if the surface has changed since the last drawing order.
|
|
// If not, then we don't need to send the switch surface order.
|
|
if (pdsurf != oeLastDstSurface) {
|
|
// set last surface to the new surface
|
|
oeLastDstSurface = pdsurf;
|
|
|
|
if (pdsurf == NULL) {
|
|
// Destination surface is the client screen
|
|
bitmapSurfaceId = SCREEN_BITMAP_SURFACE;
|
|
}
|
|
else {
|
|
if (pdsurf->shareId == pddShm->shareId) {
|
|
// Get the offscreen bitmap Id.
|
|
bitmapSurfaceId = pdsurf->bitmapId;
|
|
|
|
// Udate the mru list of the offscreen cache
|
|
CH_TouchCacheEntry(sbcOffscreenBitmapCacheHandle,
|
|
pdsurf->bitmapId);
|
|
}
|
|
else {
|
|
// This is the stale offscreen bitmap from last disconnected
|
|
// session. We need to turn off the offscreen flag on this
|
|
TRC_ALT((TB, "Need to turn off this offscreen bitmap"));
|
|
pdsurf->flags |= DD_NO_OFFSCREEN;
|
|
rc = FALSE;
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
// Return TRUE here since we didn't fail the send the order,
|
|
// there's just no need to send it.
|
|
rc = TRUE;
|
|
DC_QUIT;
|
|
}
|
|
|
|
pOrder = OA_AllocOrderMem(ppdev, sizeof(TS_SWITCH_SURFACE_ORDER));
|
|
if (pOrder != NULL) {
|
|
pSurfSwitchOrder = (PTS_SWITCH_SURFACE_ORDER)pOrder->OrderData;
|
|
pSurfSwitchOrder->ControlFlags = (TS_ALTSEC_SWITCH_SURFACE <<
|
|
TS_ALTSEC_ORDER_TYPE_SHIFT) | TS_SECONDARY;
|
|
pSurfSwitchOrder->BitmapID = (UINT16)bitmapSurfaceId;
|
|
|
|
INC_OUTCOUNTER(OUT_SWITCHSURFACE);
|
|
ADD_OUTCOUNTER(OUT_SWITCHSURFACE_BYTES,
|
|
sizeof(TS_SWITCH_SURFACE_ORDER));
|
|
OA_AppendToOrderList(pOrder);
|
|
rc = TRUE;
|
|
}
|
|
else {
|
|
TRC_ERR((TB, "Failed to add a switch surface PDU to the order heap"));
|
|
rc = FALSE;
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
DC_END_FN();
|
|
return rc;
|
|
}
|
|
|
|
#ifdef DRAW_NINEGRID
|
|
/****************************************************************************/
|
|
// OESendStreamBitmapOrder
|
|
//
|
|
// This is to stream the bitmap bits (either compressed or not compressed
|
|
// to the client in 4K block
|
|
/****************************************************************************/
|
|
BOOL RDPCALL OESendStreamBitmapOrder(PDD_PDEV ppdev, unsigned bitmapId,
|
|
SIZEL *sizl, unsigned bitmapBpp, PBYTE BitmapBuffer, unsigned BitmapSize,
|
|
BOOL compressed)
|
|
{
|
|
PINT_ORDER pIntOrder;
|
|
PTS_STREAM_BITMAP_FIRST_PDU pStreamBitmapFirstPDU;
|
|
PTS_STREAM_BITMAP_FIRST_PDU_REV2 pStreamBitmapFirstPDURev2;
|
|
PTS_STREAM_BITMAP_NEXT_PDU pStreamBitmapNextPDU;
|
|
BOOL rc = FALSE;
|
|
BOOL fEndOfStream = FALSE;
|
|
unsigned StreamBlockSize;
|
|
unsigned BitmapRemainingSize;
|
|
PBYTE BitmapRemainingBuffer;
|
|
|
|
DC_BEGIN_FN("OESendStreamBitmapOrder");
|
|
|
|
// Send the first stream block
|
|
BitmapRemainingBuffer = BitmapBuffer;
|
|
StreamBlockSize = min(BitmapSize, TS_STREAM_BITMAP_BLOCK);
|
|
BitmapRemainingSize = BitmapSize - StreamBlockSize;
|
|
|
|
if (pddShm->sbc.drawNineGridCacheInfo.supportLevel < TS_DRAW_NINEGRID_SUPPORTED_REV2) {
|
|
pIntOrder = OA_AllocOrderMem(ppdev, sizeof(TS_STREAM_BITMAP_FIRST_PDU) +
|
|
StreamBlockSize);
|
|
}
|
|
else {
|
|
// TS_STREAM_BITMAP REV2
|
|
pIntOrder = OA_AllocOrderMem(ppdev, sizeof(TS_STREAM_BITMAP_FIRST_PDU_REV2) +
|
|
StreamBlockSize);
|
|
}
|
|
|
|
if (BitmapRemainingSize == 0) {
|
|
fEndOfStream = TRUE;
|
|
}
|
|
|
|
if (pIntOrder != NULL) {
|
|
if (pddShm->sbc.drawNineGridCacheInfo.supportLevel < TS_DRAW_NINEGRID_SUPPORTED_REV2) {
|
|
pStreamBitmapFirstPDU = (PTS_STREAM_BITMAP_FIRST_PDU)pIntOrder->OrderData;
|
|
pStreamBitmapFirstPDU->ControlFlags = (TS_ALTSEC_STREAM_BITMAP_FIRST <<
|
|
TS_ALTSEC_ORDER_TYPE_SHIFT) | TS_SECONDARY;
|
|
pStreamBitmapFirstPDU->BitmapFlags = fEndOfStream ? TS_STREAM_BITMAP_END : 0;
|
|
pStreamBitmapFirstPDU->BitmapFlags |= compressed ? TS_STREAM_BITMAP_COMPRESSED : 0;
|
|
pStreamBitmapFirstPDU->BitmapId = (unsigned short)bitmapId;
|
|
pStreamBitmapFirstPDU->BitmapBpp = (TSUINT8)bitmapBpp;
|
|
pStreamBitmapFirstPDU->BitmapWidth = (TSUINT16)(sizl->cx);
|
|
pStreamBitmapFirstPDU->BitmapHeight = (TSUINT16)(sizl->cy);
|
|
pStreamBitmapFirstPDU->BitmapLength = (TSUINT16)BitmapSize;
|
|
pStreamBitmapFirstPDU->BitmapBlockLength = (TSUINT16)(StreamBlockSize);
|
|
|
|
memcpy(pStreamBitmapFirstPDU + 1, BitmapRemainingBuffer, StreamBlockSize);
|
|
}
|
|
else {
|
|
// TS_STREAM_BITMAP REV2
|
|
pStreamBitmapFirstPDURev2 = (PTS_STREAM_BITMAP_FIRST_PDU_REV2)pIntOrder->OrderData;
|
|
pStreamBitmapFirstPDURev2->ControlFlags = (TS_ALTSEC_STREAM_BITMAP_FIRST <<
|
|
TS_ALTSEC_ORDER_TYPE_SHIFT) | TS_SECONDARY;
|
|
pStreamBitmapFirstPDURev2->BitmapFlags = fEndOfStream ? TS_STREAM_BITMAP_END : 0;
|
|
pStreamBitmapFirstPDURev2->BitmapFlags |= compressed ? TS_STREAM_BITMAP_COMPRESSED : 0;
|
|
pStreamBitmapFirstPDURev2->BitmapFlags |= TS_STREAM_BITMAP_REV2;
|
|
pStreamBitmapFirstPDURev2->BitmapId = (unsigned short)bitmapId;
|
|
pStreamBitmapFirstPDURev2->BitmapBpp = (TSUINT8)bitmapBpp;
|
|
pStreamBitmapFirstPDURev2->BitmapWidth = (TSUINT16)(sizl->cx);
|
|
pStreamBitmapFirstPDURev2->BitmapHeight = (TSUINT16)(sizl->cy);
|
|
pStreamBitmapFirstPDURev2->BitmapLength = (TSUINT32)BitmapSize;
|
|
pStreamBitmapFirstPDURev2->BitmapBlockLength = (TSUINT16)(StreamBlockSize);
|
|
|
|
memcpy(pStreamBitmapFirstPDURev2 + 1, BitmapRemainingBuffer, StreamBlockSize);
|
|
}
|
|
|
|
BitmapRemainingBuffer += StreamBlockSize;
|
|
|
|
OA_AppendToOrderList(pIntOrder);
|
|
}
|
|
else {
|
|
TRC_ERR((TB, "Failed to allocated order for stream bitmap"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
|
|
// Send the subsequent streamblock
|
|
while (BitmapRemainingSize) {
|
|
StreamBlockSize = min(BitmapRemainingSize, TS_STREAM_BITMAP_BLOCK);
|
|
BitmapRemainingSize = BitmapRemainingSize - StreamBlockSize;
|
|
|
|
pIntOrder = OA_AllocOrderMem(ppdev, sizeof(TS_STREAM_BITMAP_NEXT_PDU) +
|
|
StreamBlockSize);
|
|
|
|
if (BitmapRemainingSize == 0) {
|
|
fEndOfStream = TRUE;
|
|
}
|
|
|
|
if (pIntOrder != NULL) {
|
|
pStreamBitmapNextPDU = (PTS_STREAM_BITMAP_NEXT_PDU)pIntOrder->OrderData;
|
|
pStreamBitmapNextPDU->ControlFlags = (TS_ALTSEC_STREAM_BITMAP_NEXT <<
|
|
TS_ALTSEC_ORDER_TYPE_SHIFT) | TS_SECONDARY;
|
|
pStreamBitmapNextPDU->BitmapFlags = fEndOfStream ? TS_STREAM_BITMAP_END : 0;
|
|
pStreamBitmapNextPDU->BitmapFlags |= compressed ? TS_STREAM_BITMAP_COMPRESSED : 0;
|
|
pStreamBitmapNextPDU->BitmapId = (unsigned short)bitmapId;
|
|
pStreamBitmapNextPDU->BitmapBlockLength = (TSUINT16)StreamBlockSize;
|
|
|
|
memcpy(pStreamBitmapNextPDU + 1, BitmapRemainingBuffer, StreamBlockSize);
|
|
BitmapRemainingBuffer += StreamBlockSize;
|
|
|
|
OA_AppendToOrderList(pIntOrder);
|
|
}
|
|
else {
|
|
TRC_ERR((TB, "Failed to allocated order for stream bitmap"));
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
|
|
rc = TRUE;
|
|
|
|
DC_EXIT_POINT:
|
|
|
|
DC_END_FN();
|
|
return rc;
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
// OESendCreateNineGridBitmapOrder
|
|
//
|
|
// Send the alternative secondary order to client to create the ninegrid bitmap
|
|
/****************************************************************************/
|
|
BOOL RDPCALL OESendCreateNineGridBitmapOrder(PDD_PDEV ppdev, unsigned nineGridBitmapId,
|
|
SIZEL *sizl, unsigned bitmapBpp, PNINEGRID png)
|
|
{
|
|
PINT_ORDER pOrder;
|
|
PTS_CREATE_NINEGRID_BITMAP_ORDER pCreateNineGridBitmapOrder;
|
|
BOOL rc = FALSE;
|
|
|
|
DC_BEGIN_FN("OESendCreateNineGridBitmapOrder");
|
|
|
|
pOrder = OA_AllocOrderMem(ppdev, sizeof(TS_CREATE_NINEGRID_BITMAP_ORDER));
|
|
|
|
if (pOrder != NULL) {
|
|
|
|
pCreateNineGridBitmapOrder = (PTS_CREATE_NINEGRID_BITMAP_ORDER)pOrder->OrderData;
|
|
pCreateNineGridBitmapOrder->ControlFlags = (TS_ALTSEC_CREATE_NINEGRID_BITMAP <<
|
|
TS_ALTSEC_ORDER_TYPE_SHIFT) | TS_SECONDARY;
|
|
pCreateNineGridBitmapOrder->BitmapID = (UINT16)nineGridBitmapId;
|
|
pCreateNineGridBitmapOrder->BitmapBpp = (BYTE)bitmapBpp;
|
|
pCreateNineGridBitmapOrder->cx = (TSUINT16)sizl->cx;
|
|
pCreateNineGridBitmapOrder->cy = (TSUINT16)sizl->cy;
|
|
pCreateNineGridBitmapOrder->nineGridInfo.crTransparent = png->crTransparent;
|
|
pCreateNineGridBitmapOrder->nineGridInfo.flFlags = png->flFlags;
|
|
pCreateNineGridBitmapOrder->nineGridInfo.ulLeftWidth = (TSUINT16)png->ulLeftWidth;
|
|
pCreateNineGridBitmapOrder->nineGridInfo.ulRightWidth = (TSUINT16)png->ulRightWidth;
|
|
pCreateNineGridBitmapOrder->nineGridInfo.ulTopHeight = (TSUINT16)png->ulTopHeight;
|
|
pCreateNineGridBitmapOrder->nineGridInfo.ulBottomHeight = (TSUINT16)png->ulBottomHeight;
|
|
|
|
//INC_OUTCOUNTER(OUT_SWITCHSURFACE);
|
|
//ADD_OUTCOUNTER(OUT_SWITCHSURFACE_BYTES,
|
|
// sizeof(TS_SWITCH_SURFACE_ORDER));
|
|
OA_AppendToOrderList(pOrder);
|
|
rc = TRUE;
|
|
}
|
|
else {
|
|
TRC_ERR((TB, "Failed to add a create drawninegrid order to the order heap"));
|
|
rc = FALSE;
|
|
}
|
|
|
|
DC_END_FN();
|
|
return rc;
|
|
}
|
|
|
|
/****************************************************************************/
|
|
// OECacheDrawNineGridBitmap
|
|
//
|
|
// Cache the draw ninegrid bitmap
|
|
/****************************************************************************/
|
|
BOOL RDPCALL OECacheDrawNineGridBitmap(PDD_PDEV ppdev, SURFOBJ *psoSrc,
|
|
PNINEGRID png, unsigned *bitmapId)
|
|
{
|
|
CHDataKeyContext CHContext;
|
|
void *UserDefined;
|
|
HSURF hWorkBitmap = NULL;
|
|
SURFOBJ *pWorkSurf = NULL;
|
|
SIZEL bitmapSize;
|
|
BOOL rc = FALSE;
|
|
PBYTE pWorkingBuffer = NULL;
|
|
PBYTE BitmapBuffer = NULL;
|
|
unsigned BitmapBufferSize = 0;
|
|
|
|
DC_BEGIN_FN("OECacheDrawNineGridBitmap");
|
|
|
|
CH_CreateKeyFromFirstData(&CHContext, psoSrc->pvBits, psoSrc->cjBits);
|
|
CH_CreateKeyFromNextData(&CHContext, png, sizeof(NINEGRID));
|
|
|
|
if (!CH_SearchCache(sbcDrawNineGridBitmapCacheHandle, CHContext.Key1, CHContext.Key2,
|
|
&UserDefined, bitmapId)) {
|
|
|
|
*bitmapId = CH_CacheKey(sbcDrawNineGridBitmapCacheHandle, CHContext.Key1,
|
|
CHContext.Key2, NULL);
|
|
|
|
if (*bitmapId != CH_KEY_UNCACHABLE) {
|
|
unsigned BitmapRawSize;
|
|
unsigned BitmapCompSize = 0;
|
|
unsigned BitmapBpp;
|
|
|
|
PBYTE BitmapRawBuffer;
|
|
|
|
unsigned paddedBitmapWidth;
|
|
BOOL ret;
|
|
// Get the protocol bitmap bpp
|
|
switch (psoSrc->iBitmapFormat)
|
|
{
|
|
case BMF_16BPP:
|
|
BitmapBpp = 16;
|
|
break;
|
|
|
|
case BMF_24BPP:
|
|
BitmapBpp = 24;
|
|
break;
|
|
|
|
case BMF_32BPP:
|
|
BitmapBpp = 32;
|
|
break;
|
|
|
|
default:
|
|
TRC_ASSERT((FALSE), (TB, "Invalid bitmap bpp: %d", psoSrc->iBitmapFormat));
|
|
BitmapBpp = 8;
|
|
}
|
|
|
|
paddedBitmapWidth = (psoSrc->sizlBitmap.cx + 3) & ~3;
|
|
bitmapSize.cx = paddedBitmapWidth;
|
|
bitmapSize.cy = psoSrc->sizlBitmap.cy;
|
|
|
|
// The bitmap width needs to be dword aligned
|
|
if (paddedBitmapWidth != psoSrc->sizlBitmap.cx) {
|
|
RECTL rect = { 0 };
|
|
POINTL origin = { 0 };
|
|
|
|
rect.right = psoSrc->sizlBitmap.cx;
|
|
rect.bottom = psoSrc->sizlBitmap.cy;
|
|
|
|
hWorkBitmap = (HSURF)EngCreateBitmap(bitmapSize,
|
|
TS_BYTES_IN_SCANLINE(bitmapSize.cx, BitmapBpp),
|
|
psoSrc->iBitmapFormat, 0, NULL);
|
|
|
|
if (hWorkBitmap) {
|
|
|
|
pWorkSurf = EngLockSurface(hWorkBitmap);
|
|
|
|
if (pWorkSurf) {
|
|
// Copy to a worker bitmap
|
|
if (EngCopyBits(pWorkSurf, psoSrc, NULL, NULL, &rect, &origin)) {
|
|
BitmapRawSize = pWorkSurf->cjBits;
|
|
BitmapRawBuffer = pWorkSurf->pvBits;
|
|
|
|
BitmapBuffer = EngAllocMem(0, BitmapRawSize, WD_ALLOC_TAG);
|
|
BitmapBufferSize = BitmapRawSize;
|
|
|
|
if (BitmapBuffer == NULL) {
|
|
ret = FALSE;
|
|
goto Post_Compression;
|
|
}
|
|
|
|
if (BitmapRawSize > MAX_UNCOMPRESSED_DATA_SIZE) {
|
|
pWorkingBuffer = EngAllocMem(0, BitmapRawSize, WD_ALLOC_TAG);
|
|
|
|
if (pWorkingBuffer == NULL) {
|
|
ret = FALSE;
|
|
goto Post_Compression;
|
|
}
|
|
}
|
|
|
|
ret = BC_CompressBitmap(pWorkSurf->pvBits, BitmapBuffer, pWorkingBuffer,
|
|
BitmapBufferSize, &BitmapCompSize, paddedBitmapWidth,
|
|
psoSrc->sizlBitmap.cy, BitmapBpp);
|
|
|
|
|
|
}
|
|
else {
|
|
TRC_ERR((TB, "Failed EngCopyBits"));
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
else {
|
|
TRC_ERR((TB, "Failed to lock the bitmap"));
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
else {
|
|
TRC_ERR((TB, "Failed to create the bitmap"));
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
else {
|
|
BitmapRawSize = psoSrc->cjBits;
|
|
BitmapRawBuffer = psoSrc->pvBits;
|
|
|
|
BitmapBuffer = EngAllocMem(0, BitmapRawSize, WD_ALLOC_TAG);
|
|
BitmapBufferSize = BitmapRawSize;
|
|
|
|
if (BitmapBuffer == NULL) {
|
|
ret = FALSE;
|
|
goto Post_Compression;
|
|
}
|
|
|
|
if (BitmapRawSize > MAX_UNCOMPRESSED_DATA_SIZE) {
|
|
pWorkingBuffer = EngAllocMem(0, BitmapRawSize, WD_ALLOC_TAG);
|
|
if (pWorkingBuffer == NULL) {
|
|
ret = FALSE;
|
|
goto Post_Compression;
|
|
}
|
|
}
|
|
|
|
ret = BC_CompressBitmap(psoSrc->pvBits, BitmapBuffer, pWorkingBuffer, BitmapBufferSize,
|
|
&BitmapCompSize, paddedBitmapWidth, psoSrc->sizlBitmap.cy,
|
|
BitmapBpp);
|
|
}
|
|
|
|
Post_Compression:
|
|
|
|
if (ret) {
|
|
// Send compressed bitmap
|
|
if (!OESendStreamBitmapOrder(ppdev, TS_DRAW_NINEGRID_BITMAP_CACHE, &bitmapSize, BitmapBpp,
|
|
BitmapBuffer, BitmapCompSize, TRUE)) {
|
|
TRC_ERR((TB, "Failed to send stream bitmap order"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
}
|
|
else {
|
|
// Send uncompressed bitmap
|
|
if (!OESendStreamBitmapOrder(ppdev, TS_DRAW_NINEGRID_BITMAP_CACHE, &bitmapSize, BitmapBpp,
|
|
BitmapRawBuffer, BitmapRawSize, FALSE))
|
|
{
|
|
TRC_ERR((TB, "Failed to send stream bitmap order"));
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
|
|
// send a create drawninegrid bitmap pdu
|
|
if (OESendCreateNineGridBitmapOrder(ppdev, *bitmapId,
|
|
&(psoSrc->sizlBitmap), BitmapBpp, png)) {
|
|
// Update the current offscreen cache size
|
|
//oeCurrentOffscreenCacheSize += bitmapSize;
|
|
//pdsurf->bitmapId = offscrBitmapId;
|
|
TRC_NRM((TB, "Created a drawninegrid bitmap"));
|
|
}
|
|
else {
|
|
TRC_ERR((TB, "Failed to send the create bitmap pdu"));
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
else {
|
|
TRC_ERR((TB, "Failed to cache the bitmap"));
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
else {
|
|
// bitmap already cached
|
|
}
|
|
|
|
rc = TRUE;
|
|
|
|
DC_EXIT_POINT:
|
|
|
|
if (pWorkSurf)
|
|
EngUnlockSurface(pWorkSurf);
|
|
|
|
if (hWorkBitmap)
|
|
EngDeleteSurface(hWorkBitmap);
|
|
|
|
if (pWorkingBuffer) {
|
|
EngFreeMem(pWorkingBuffer);
|
|
}
|
|
|
|
if (BitmapBuffer) {
|
|
EngFreeMem(BitmapBuffer);
|
|
}
|
|
|
|
if (rc != TRUE && *bitmapId != CH_KEY_UNCACHABLE)
|
|
CH_RemoveCacheEntry(sbcDrawNineGridBitmapCacheHandle, *bitmapId);
|
|
|
|
DC_END_FN();
|
|
return rc;
|
|
}
|
|
|
|
/****************************************************************************/
|
|
// OEEncodeDrawNineGrid
|
|
//
|
|
// Encodes the DrawNineGrid order. Returns FALSE on failure.
|
|
/****************************************************************************/
|
|
BOOL RDPCALL OEEncodeDrawNineGrid(
|
|
RECTL *pBounds,
|
|
RECTL *psrcRect,
|
|
unsigned bitmapId,
|
|
PDD_PDEV ppdev,
|
|
OE_ENUMRECTS *pClipRects)
|
|
{
|
|
BOOL rc = FALSE;
|
|
unsigned OrderSize;
|
|
unsigned NumFieldFlagBytes = 0;
|
|
BYTE OrderType = 0;
|
|
PINT_ORDER pOrder;
|
|
MULTI_DRAWNINEGRID_ORDER *pPrevDNG;
|
|
|
|
DC_BEGIN_FN("OEEncodeDrawNineGrid");
|
|
|
|
// Check whether we should use the multi-cliprect version.
|
|
if (pClipRects->rects.c == 0) {
|
|
// Non-multi version.
|
|
OrderType = TS_ENC_DRAWNINEGRID_ORDER;
|
|
OrderSize = MAX_DRAWNINEGRID_FIELD_SIZE;
|
|
pPrevDNG = (MULTI_DRAWNINEGRID_ORDER *)&PrevDrawNineGrid;
|
|
NumFieldFlagBytes = 1;
|
|
}
|
|
else {
|
|
// Multi version.
|
|
OrderType = TS_ENC_MULTI_DRAWNINEGRID_ORDER;
|
|
OrderSize = MAX_MULTI_DRAWNINEGRID_FIELD_SIZE_NCLIP(pClipRects->rects.c);
|
|
pPrevDNG = &PrevMultiDrawNineGrid;
|
|
NumFieldFlagBytes = 1;
|
|
}
|
|
|
|
// Encode and send the order
|
|
pOrder = OA_AllocOrderMem(ppdev, MAX_ORDER_SIZE(((NULL == pBounds) ? 0 : 1),
|
|
NumFieldFlagBytes, OrderSize));
|
|
|
|
if (pOrder != NULL) {
|
|
DRAWNINEGRID_ORDER *pDNG;
|
|
|
|
pDNG = (DRAWNINEGRID_ORDER *)oeTempOrderBuffer;
|
|
pDNG->srcLeft = psrcRect->left;
|
|
pDNG->srcTop = psrcRect->top;
|
|
pDNG->srcRight = psrcRect->right;
|
|
pDNG->srcBottom = psrcRect->bottom;
|
|
pDNG->bitmapId = (unsigned short)bitmapId;
|
|
|
|
// Need to increment this bound as we use it as cliprect and in the encode order
|
|
// code it'll make it includsive over the wire
|
|
pBounds->right += 1;
|
|
pBounds->bottom += 1;
|
|
|
|
if (OrderType == TS_ENC_DRAWNINEGRID_ORDER) {
|
|
// Slow-field-encode the order
|
|
pOrder->OrderLength = OE2_EncodeOrder(pOrder->OrderData,
|
|
TS_ENC_DRAWNINEGRID_ORDER, NUM_DRAWNINEGRID_FIELDS,
|
|
(BYTE *)pDNG, (BYTE *)pPrevDNG, etable_NG,
|
|
pBounds);
|
|
|
|
//INC_OUTCOUNTER(OUT_SCRBLT_ORDER);
|
|
//ADD_INCOUNTER(IN_SCRBLT_BYTES, pOrder->OrderLength);
|
|
OA_AppendToOrderList(pOrder);
|
|
rc = TRUE;
|
|
}
|
|
else {
|
|
MULTI_DRAWNINEGRID_ORDER *pMultiDNG = (MULTI_DRAWNINEGRID_ORDER *)
|
|
oeTempOrderBuffer;
|
|
|
|
// Encode the clip rects directly into the order.
|
|
pMultiDNG->nDeltaEntries = OEBuildMultiClipOrder(ppdev,
|
|
&pMultiDNG->codedDeltaList, pClipRects);
|
|
|
|
// Slow-field-encode the order with no clip rects.
|
|
pOrder->OrderLength = OE2_EncodeOrder(pOrder->OrderData,
|
|
TS_ENC_MULTI_DRAWNINEGRID_ORDER, NUM_MULTI_DRAWNINEGRID_FIELDS,
|
|
(BYTE *)pMultiDNG, (BYTE *)pPrevDNG, etable_MG,
|
|
pBounds);
|
|
|
|
//INC_OUTCOUNTER(OUT_MULTI_SCRBLT_ORDER);
|
|
//ADD_INCOUNTER(IN_MULTI_SCRBLT_BYTES, pOrder->OrderLength);
|
|
OA_AppendToOrderList(pOrder);
|
|
rc = TRUE;
|
|
}
|
|
}
|
|
else {
|
|
TRC_ERR((TB, "Failed to alloc order"));
|
|
INC_OUTCOUNTER(OUT_BITBLT_SDA_HEAPALLOCFAILED);
|
|
rc = FALSE;
|
|
}
|
|
|
|
DC_END_FN();
|
|
return rc;
|
|
}
|
|
|
|
|
|
#if 0
|
|
BOOL RDPCALL OESendCreateDrawStreamOrder(PDD_PDEV ppdev, unsigned bitmapId,
|
|
SIZEL *sizl, unsigned bitmapBpp)
|
|
{
|
|
PINT_ORDER pOrder;
|
|
PTS_CREATE_DRAW_STREAM_ORDER pCreateDrawStreamOrder;
|
|
BOOL rc = FALSE;
|
|
|
|
DC_BEGIN_FN("OESendCreateDrawStreamOrder");
|
|
|
|
pOrder = OA_AllocOrderMem(ppdev, sizeof(TS_CREATE_DRAW_STREAM_ORDER));
|
|
|
|
if (pOrder != NULL) {
|
|
|
|
pCreateDrawStreamOrder = (PTS_CREATE_DRAW_STREAM_ORDER)pOrder->OrderData;
|
|
pDrawStreamOrder->ControlFlags = (TS_ALTSEC_DRAW_STREAM <<
|
|
TS_ALTSEC_ORDER_TYPE_SHIFT) | TS_SECONDARY;
|
|
pCreateDrawStreamOrder->BitmapID = (UINT16)bitmapId;
|
|
pCreateDrawStreamOrder->bitmapBpp = (BYTE)bitmapBpp;
|
|
pCreateDrawStreamOrder->cx = (TSUINT16)sizl->cx;
|
|
pCreateDrawStreamOrder->cy = (TSUINT16)sizl->cy;
|
|
|
|
//INC_OUTCOUNTER(OUT_SWITCHSURFACE);
|
|
//ADD_OUTCOUNTER(OUT_SWITCHSURFACE_BYTES,
|
|
// sizeof(TS_SWITCH_SURFACE_ORDER));
|
|
OA_AppendToOrderList(pOrder);
|
|
rc = TRUE;
|
|
}
|
|
else {
|
|
TRC_ERR((TB, "Failed to add a create draw stream order to the order heap"));
|
|
rc = FALSE;
|
|
}
|
|
|
|
rc = TRUE;
|
|
|
|
DC_END_FN();
|
|
return rc;
|
|
}
|
|
|
|
VOID OEEncodeDrawStream(PVOID stream, ULONG streamSize, PPOINTL dstOffset,
|
|
PBYTE streamOut, unsigned* streamSizeOut)
|
|
{
|
|
ULONG * pul = (ULONG *) stream;
|
|
ULONG cjIn = streamSize;
|
|
|
|
DC_BEGIN_FN("OEEncodeDrawStream");
|
|
|
|
*streamSizeOut = 0;
|
|
|
|
while(cjIn >= sizeof(ULONG))
|
|
{
|
|
ULONG command = *pul;
|
|
ULONG commandSize;
|
|
|
|
switch(command)
|
|
{
|
|
case DS_COPYTILEID:
|
|
{
|
|
DS_COPYTILE * cmd = (DS_COPYTILE *) pul;
|
|
RDP_DS_COPYTILE * rdpcmd = (RDP_DS_COPYTILE *) streamOut;
|
|
|
|
commandSize = sizeof(*cmd);
|
|
|
|
if (cjIn < commandSize) {
|
|
DC_QUIT;
|
|
}
|
|
|
|
cmd->rclDst.left += dstOffset->x;
|
|
cmd->rclDst.right += dstOffset->x;
|
|
cmd->rclDst.top += dstOffset->y;
|
|
cmd->rclDst.bottom += dstOffset->y;
|
|
|
|
|
|
rdpcmd->ulCmdID = (BYTE)(DS_COPYTILEID);
|
|
|
|
OEClipRect(&(cmd->rclDst));
|
|
OEClipRect(&(cmd->rclSrc));
|
|
OEClipPoint(&(cmd->ptlOrigin));
|
|
|
|
RECTL_TO_TSRECT16(rdpcmd->rclDst, cmd->rclDst);
|
|
RECTL_TO_TSRECT16(rdpcmd->rclSrc, cmd->rclSrc);
|
|
POINTL_TO_TSPOINT16(rdpcmd->ptlOrigin, cmd->ptlOrigin);
|
|
|
|
*streamSizeOut += sizeof(RDP_DS_COPYTILE);
|
|
streamOut += sizeof(RDP_DS_COPYTILE);
|
|
}
|
|
break;
|
|
|
|
case DS_SOLIDFILLID:
|
|
{
|
|
DS_SOLIDFILL * cmd = (DS_SOLIDFILL *) pul;
|
|
RDP_DS_SOLIDFILL * rdpcmd = (RDP_DS_SOLIDFILL *) streamOut;
|
|
|
|
commandSize = sizeof(*cmd);
|
|
|
|
if (cjIn < commandSize) {
|
|
DC_QUIT;
|
|
}
|
|
|
|
cmd->rclDst.left += dstOffset->x;
|
|
cmd->rclDst.right += dstOffset->x;
|
|
cmd->rclDst.top += dstOffset->y;
|
|
cmd->rclDst.bottom += dstOffset->y;
|
|
|
|
|
|
rdpcmd->ulCmdID = (BYTE)(DS_SOLIDFILLID);
|
|
rdpcmd->crSolidColor = cmd->crSolidColor;
|
|
|
|
OEClipRect(&(cmd->rclDst));
|
|
RECTL_TO_TSRECT16(rdpcmd->rclDst, cmd->rclDst);
|
|
|
|
*streamSizeOut += sizeof(RDP_DS_SOLIDFILL);
|
|
streamOut += sizeof(RDP_DS_SOLIDFILL);
|
|
}
|
|
break;
|
|
|
|
case DS_TRANSPARENTTILEID:
|
|
{
|
|
DS_TRANSPARENTTILE * cmd = (DS_TRANSPARENTTILE *) pul;
|
|
RDP_DS_TRANSPARENTTILE * rdpcmd = (RDP_DS_TRANSPARENTTILE *) streamOut;
|
|
|
|
commandSize = sizeof(*cmd);
|
|
|
|
if (cjIn < commandSize) {
|
|
DC_QUIT;
|
|
}
|
|
|
|
cmd->rclDst.left += dstOffset->x;
|
|
cmd->rclDst.right += dstOffset->x;
|
|
cmd->rclDst.top += dstOffset->y;
|
|
cmd->rclDst.bottom += dstOffset->y;
|
|
|
|
|
|
rdpcmd->ulCmdID = (BYTE)(DS_TRANSPARENTTILEID);
|
|
rdpcmd->crTransparentColor = cmd->crTransparentColor;
|
|
|
|
OEClipRect(&(cmd->rclDst));
|
|
OEClipRect(&(cmd->rclSrc));
|
|
OEClipPoint(&(cmd->ptlOrigin));
|
|
|
|
RECTL_TO_TSRECT16(rdpcmd->rclDst, cmd->rclDst);
|
|
RECTL_TO_TSRECT16(rdpcmd->rclSrc, cmd->rclSrc);
|
|
POINTL_TO_TSPOINT16(rdpcmd->ptlOrigin, cmd->ptlOrigin);
|
|
|
|
*streamSizeOut += sizeof(RDP_DS_TRANSPARENTTILE);
|
|
streamOut += sizeof(RDP_DS_TRANSPARENTTILE);
|
|
|
|
}
|
|
break;
|
|
|
|
case DS_ALPHATILEID:
|
|
{
|
|
DS_ALPHATILE * cmd = (DS_ALPHATILE *) pul;
|
|
RDP_DS_ALPHATILE * rdpcmd = (RDP_DS_ALPHATILE *) streamOut;
|
|
|
|
commandSize = sizeof(*cmd);
|
|
|
|
if (cjIn < commandSize) {
|
|
DC_QUIT;
|
|
}
|
|
|
|
cmd->rclDst.left += dstOffset->x;
|
|
cmd->rclDst.right += dstOffset->x;
|
|
cmd->rclDst.top += dstOffset->y;
|
|
cmd->rclDst.bottom += dstOffset->y;
|
|
|
|
rdpcmd->ulCmdID = (BYTE)(DS_ALPHATILEID);
|
|
rdpcmd->blendFunction.AlphaFormat = cmd->blendFunction.AlphaFormat;
|
|
rdpcmd->blendFunction.BlendFlags = cmd->blendFunction.BlendFlags;
|
|
rdpcmd->blendFunction.BlendOp = cmd->blendFunction.BlendOp;
|
|
rdpcmd->blendFunction.SourceConstantAlpha = cmd->blendFunction.SourceConstantAlpha;
|
|
|
|
OEClipRect(&(cmd->rclDst));
|
|
OEClipRect(&(cmd->rclSrc));
|
|
OEClipPoint(&(cmd->ptlOrigin));
|
|
|
|
RECTL_TO_TSRECT16(rdpcmd->rclDst, cmd->rclDst);
|
|
RECTL_TO_TSRECT16(rdpcmd->rclSrc, cmd->rclSrc);
|
|
POINTL_TO_TSPOINT16(rdpcmd->ptlOrigin, cmd->ptlOrigin);
|
|
|
|
*streamSizeOut += sizeof(RDP_DS_ALPHATILE);
|
|
streamOut += sizeof(RDP_DS_ALPHATILE);
|
|
}
|
|
break;
|
|
|
|
case DS_STRETCHID:
|
|
{
|
|
DS_STRETCH * cmd = (DS_STRETCH *) pul;
|
|
RDP_DS_STRETCH * rdpcmd = (RDP_DS_STRETCH *) streamOut;
|
|
|
|
commandSize = sizeof(*cmd);
|
|
|
|
if (cjIn < commandSize) {
|
|
DC_QUIT;
|
|
}
|
|
|
|
cmd->rclDst.left += dstOffset->x;
|
|
cmd->rclDst.right += dstOffset->x;
|
|
cmd->rclDst.top += dstOffset->y;
|
|
cmd->rclDst.bottom += dstOffset->y;
|
|
|
|
rdpcmd->ulCmdID = (BYTE)(DS_STRETCHID);
|
|
|
|
OEClipRect(&(cmd->rclDst));
|
|
OEClipRect(&(cmd->rclSrc));
|
|
|
|
RECTL_TO_TSRECT16(rdpcmd->rclDst, cmd->rclDst);
|
|
RECTL_TO_TSRECT16(rdpcmd->rclSrc, cmd->rclSrc);
|
|
|
|
*streamSizeOut += sizeof(RDP_DS_STRETCH);
|
|
streamOut += sizeof(RDP_DS_STRETCH);
|
|
}
|
|
break;
|
|
|
|
case DS_TRANSPARENTSTRETCHID:
|
|
{
|
|
DS_TRANSPARENTSTRETCH * cmd = (DS_TRANSPARENTSTRETCH *) pul;
|
|
RDP_DS_TRANSPARENTSTRETCH * rdpcmd = (RDP_DS_TRANSPARENTSTRETCH *) streamOut;
|
|
|
|
commandSize = sizeof(*cmd);
|
|
|
|
if (cjIn < commandSize) {
|
|
DC_QUIT;
|
|
}
|
|
|
|
cmd->rclDst.left += dstOffset->x;
|
|
cmd->rclDst.right += dstOffset->x;
|
|
cmd->rclDst.top += dstOffset->y;
|
|
cmd->rclDst.bottom += dstOffset->y;
|
|
|
|
rdpcmd->ulCmdID = (BYTE)(DS_TRANSPARENTSTRETCHID);
|
|
rdpcmd->crTransparentColor = cmd->crTransparentColor;
|
|
|
|
OEClipRect(&(cmd->rclDst));
|
|
OEClipRect(&(cmd->rclSrc));
|
|
|
|
RECTL_TO_TSRECT16(rdpcmd->rclDst, cmd->rclDst);
|
|
RECTL_TO_TSRECT16(rdpcmd->rclSrc, cmd->rclSrc);
|
|
|
|
*streamSizeOut += sizeof(RDP_DS_TRANSPARENTSTRETCH);
|
|
streamOut += sizeof(RDP_DS_TRANSPARENTSTRETCH);
|
|
}
|
|
break;
|
|
|
|
case DS_ALPHASTRETCHID:
|
|
{
|
|
DS_ALPHASTRETCH * cmd = (DS_ALPHASTRETCH *) pul;
|
|
RDP_DS_ALPHASTRETCH * rdpcmd = (RDP_DS_ALPHASTRETCH *) streamOut;
|
|
|
|
commandSize = sizeof(*cmd);
|
|
|
|
if (cjIn < commandSize) {
|
|
DC_QUIT;
|
|
}
|
|
|
|
cmd->rclDst.left += dstOffset->x;
|
|
cmd->rclDst.right += dstOffset->x;
|
|
cmd->rclDst.top += dstOffset->y;
|
|
cmd->rclDst.bottom += dstOffset->y;
|
|
|
|
rdpcmd->ulCmdID = (BYTE)(DS_ALPHASTRETCHID);
|
|
rdpcmd->blendFunction.AlphaFormat = cmd->blendFunction.AlphaFormat;
|
|
rdpcmd->blendFunction.BlendFlags = cmd->blendFunction.BlendFlags;
|
|
rdpcmd->blendFunction.BlendOp = cmd->blendFunction.BlendOp;
|
|
rdpcmd->blendFunction.SourceConstantAlpha = cmd->blendFunction.SourceConstantAlpha;
|
|
|
|
OEClipRect(&(cmd->rclDst));
|
|
OEClipRect(&(cmd->rclSrc));
|
|
|
|
RECTL_TO_TSRECT16(rdpcmd->rclDst, cmd->rclDst);
|
|
RECTL_TO_TSRECT16(rdpcmd->rclSrc, cmd->rclSrc);
|
|
|
|
*streamSizeOut += sizeof(RDP_DS_ALPHASTRETCH);
|
|
streamOut += sizeof(RDP_DS_ALPHASTRETCH);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
{
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
|
|
cjIn -= commandSize;
|
|
pul += commandSize / 4;
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
|
|
DC_END_FN();
|
|
}
|
|
|
|
BOOL RDPCALL OESendDrawStreamOrder(PDD_PDEV ppdev, unsigned bitmapId, unsigned ulIn, PVOID pvIn,
|
|
PPOINTL dstOffset, RECTL *bounds, OE_ENUMRECTS *pclipRects)
|
|
{
|
|
PINT_ORDER pOrder;
|
|
PTS_DRAW_STREAM_ORDER pDrawStreamOrder;
|
|
unsigned cbOrderSize;
|
|
BOOL rc = FALSE;
|
|
|
|
DC_BEGIN_FN("OESendDrawStreamOrder");
|
|
|
|
cbOrderSize = sizeof(TS_DRAW_STREAM_ORDER) + ulIn +
|
|
sizeof(TS_RECTANGLE16) * pclipRects->rects.c;
|
|
|
|
pOrder = OA_AllocOrderMem(ppdev, cbOrderSize);
|
|
|
|
if (pOrder != NULL) {
|
|
unsigned i, streamSize;
|
|
TS_RECTANGLE16 *clipRects;
|
|
PBYTE stream;
|
|
|
|
pDrawStreamOrder = (PTS_DRAW_STREAM_ORDER)pOrder->OrderData;
|
|
pDrawStreamOrder->ControlFlags = (TS_ALTSEC_DRAW_STREAM <<
|
|
TS_ALTSEC_ORDER_TYPE_SHIFT) | TS_SECONDARY;
|
|
pDrawStreamOrder->Bounds.left = (TSINT16)(bounds->left);
|
|
pDrawStreamOrder->Bounds.top = (TSINT16)(bounds->top);
|
|
pDrawStreamOrder->Bounds.right = (TSINT16)(bounds->right);
|
|
pDrawStreamOrder->Bounds.bottom = (TSINT16)(bounds->bottom);
|
|
|
|
pDrawStreamOrder->nClipRects = (TSUINT8)(pclipRects->rects.c);
|
|
pDrawStreamOrder->BitmapID = (UINT16)bitmapId;
|
|
|
|
clipRects = (TS_RECTANGLE16 *)(pDrawStreamOrder + 1);
|
|
|
|
// add the cliprects here.
|
|
for (i = 0; i < pclipRects->rects.c; i++) {
|
|
clipRects[i].left = (TSINT16)pclipRects->rects.arcl[i].left;
|
|
clipRects[i].right = (TSINT16)pclipRects->rects.arcl[i].right;
|
|
clipRects[i].top = (TSINT16)pclipRects->rects.arcl[i].top;
|
|
clipRects[i].bottom = (TSINT16)pclipRects->rects.arcl[i].bottom;
|
|
}
|
|
|
|
// add the stream data
|
|
stream = (PBYTE)clipRects + sizeof(TS_RECTANGLE16) * pclipRects->rects.c;
|
|
|
|
OEEncodeDrawStream(pvIn, ulIn, dstOffset, stream, &streamSize);
|
|
pDrawStreamOrder->StreamLen = (TSUINT16)streamSize;
|
|
|
|
cbOrderSize = sizeof(TS_DRAW_STREAM_ORDER) + streamSize +
|
|
sizeof(TS_RECTANGLE16) * pclipRects->rects.c;
|
|
|
|
|
|
//INC_OUTCOUNTER(OUT_SWITCHSURFACE);
|
|
//ADD_OUTCOUNTER(OUT_SWITCHSURFACE_BYTES,
|
|
// sizeof(TS_SWITCH_SURFACE_ORDER));
|
|
|
|
OA_TruncateAllocatedOrder(pOrder, cbOrderSize);
|
|
OA_AppendToOrderList(pOrder);
|
|
rc = TRUE;
|
|
}
|
|
else {
|
|
TRC_ERR((TB, "Failed to add a draw stream order to the order heap"));
|
|
rc = FALSE;
|
|
}
|
|
|
|
rc = TRUE;
|
|
|
|
DC_END_FN();
|
|
return rc;
|
|
}
|
|
|
|
BOOL RDPCALL OESendDrawNineGridOrder(PDD_PDEV ppdev, unsigned bitmapId,
|
|
PRECTL prclSrc, RECTL *bounds, OE_ENUMRECTS *pclipRects)
|
|
{
|
|
PINT_ORDER pOrder;
|
|
PTS_DRAW_NINEGRID_ORDER pDrawNineGridOrder;
|
|
BOOL rc;
|
|
unsigned cbOrderSize;
|
|
unsigned srcRectIndex;
|
|
|
|
DC_BEGIN_FN("OESendDrawNineGridOrder");
|
|
|
|
cbOrderSize = sizeof(TS_DRAW_NINEGRID_ORDER) +
|
|
sizeof(TS_RECTANGLE16) * pclipRects->rects.c;
|
|
|
|
pOrder = OA_AllocOrderMem(ppdev, cbOrderSize);
|
|
|
|
if (pOrder != NULL) {
|
|
unsigned i;
|
|
TS_RECTANGLE16 *clipRects;
|
|
|
|
pDrawNineGridOrder = (PTS_DRAW_NINEGRID_ORDER)pOrder->OrderData;
|
|
pDrawNineGridOrder->ControlFlags = (TS_ALTSEC_DRAW_NINEGRID <<
|
|
TS_ALTSEC_ORDER_TYPE_SHIFT) | TS_SECONDARY;
|
|
pDrawNineGridOrder->Bounds.left = (TSINT16)(bounds->left);
|
|
pDrawNineGridOrder->Bounds.top = (TSINT16)(bounds->top);
|
|
pDrawNineGridOrder->Bounds.right = (TSINT16)(bounds->right);
|
|
pDrawNineGridOrder->Bounds.bottom = (TSINT16)(bounds->bottom);
|
|
|
|
pDrawNineGridOrder->nClipRects = (TSUINT8)(pclipRects->rects.c);
|
|
pDrawNineGridOrder->BitmapID = (TSUINT8)bitmapId;
|
|
|
|
pDrawNineGridOrder->srcBounds.left = (TSINT16)(prclSrc->left);
|
|
pDrawNineGridOrder->srcBounds.top = (TSINT16)(prclSrc->top);
|
|
pDrawNineGridOrder->srcBounds.right = (TSINT16)(prclSrc->right);
|
|
pDrawNineGridOrder->srcBounds.bottom = (TSINT16)(prclSrc->bottom);
|
|
|
|
clipRects = (TS_RECTANGLE16 *)(pDrawNineGridOrder + 1);
|
|
|
|
// add the cliprects here.
|
|
for (i = 0; i < pclipRects->rects.c; i++) {
|
|
clipRects[i].left = (TSINT16)pclipRects->rects.arcl[i].left;
|
|
clipRects[i].right = (TSINT16)pclipRects->rects.arcl[i].right;
|
|
clipRects[i].top = (TSINT16)pclipRects->rects.arcl[i].top;
|
|
clipRects[i].bottom = (TSINT16)pclipRects->rects.arcl[i].bottom;
|
|
}
|
|
|
|
OA_AppendToOrderList(pOrder);
|
|
rc = TRUE;
|
|
}
|
|
else {
|
|
TRC_ERR((TB, "Failed to add a draw stream order to the order heap"));
|
|
rc = FALSE;
|
|
}
|
|
|
|
rc = TRUE;
|
|
|
|
DC_END_FN();
|
|
return rc;
|
|
}
|
|
#endif
|
|
#endif //DRAW_NINEGRID
|
|
|
|
#ifdef DRAW_GDIPLUS
|
|
/****************************************************************************/
|
|
// OECreateDrawGdiplusOrder
|
|
//
|
|
// Create and Send DrawGdiplus order.
|
|
/****************************************************************************/
|
|
BOOL RDPCALL OECreateDrawGdiplusOrder(PDD_PDEV ppdev, RECTL *prcl, ULONG cjIn, PVOID pvIn)
|
|
{
|
|
BOOL rc = FALSE;
|
|
unsigned int sizeLeft;
|
|
unsigned int CopyDataSize, MoveDataSize;
|
|
PTSEmfPlusRecord pEmfRecord;
|
|
unsigned int NewRecordSize = 0;
|
|
unsigned int CacheID;
|
|
BYTE *pData;
|
|
BOOL bReturn;
|
|
BYTE *pGdipOrderBuffer = NULL;
|
|
unsigned int GdipOrderBufferOffset, GdipOrderBufferLeft;
|
|
unsigned int GdipOrderSize, NewEmfSize;
|
|
|
|
DC_BEGIN_FN("OECreateDrawGdiplusOrder");
|
|
|
|
sizeLeft = (int)cjIn;
|
|
pData = (BYTE*) pvIn;
|
|
|
|
// Allocate the draw order buffer
|
|
pGdipOrderBuffer = EngAllocMem(FL_ZERO_MEMORY, TS_GDIPLUS_ORDER_SIZELIMIT, DD_ALLOC_TAG);
|
|
if (NULL == pGdipOrderBuffer) {
|
|
rc = FALSE;
|
|
DC_QUIT;
|
|
}
|
|
GdipOrderBufferOffset = 0;
|
|
GdipOrderBufferLeft = TS_GDIPLUS_ORDER_SIZELIMIT;
|
|
GdipOrderSize = 0;
|
|
NewEmfSize = 0;
|
|
|
|
while (sizeLeft >= sizeof(TSEmfPlusRecord)) {
|
|
bReturn = FALSE;
|
|
CacheID = CH_KEY_UNCACHABLE;
|
|
pEmfRecord = (PTSEmfPlusRecord)pData;
|
|
if ((pEmfRecord->Size > sizeLeft) ||
|
|
(pEmfRecord->Size == 0)) {
|
|
TRC_ERR((TB, "GDI+ EMF record size %d is not correct", pEmfRecord->Size));
|
|
rc = FALSE;
|
|
DC_QUIT;
|
|
}
|
|
if (pddShm->sbc.drawGdiplusInfo.GdipCacheLevel > TS_DRAW_GDIPLUS_CACHE_LEVEL_DEFAULT) {
|
|
// Cache this record
|
|
bReturn = OECacheDrawGdiplus(ppdev, pEmfRecord, &CacheID);
|
|
}
|
|
if (bReturn && (CacheID != CH_KEY_UNCACHABLE)) {
|
|
//This record is cached
|
|
MoveDataSize = pEmfRecord->Size; // This is used data size in pData
|
|
CopyDataSize = sizeof(TSEmfPlusRecord) + sizeof(TSUINT16);
|
|
|
|
// If the order buffer can't hold more data, send this order first
|
|
if (CopyDataSize > GdipOrderBufferLeft) {
|
|
OESendDrawGdiplusOrder(ppdev, prcl, GdipOrderSize, pGdipOrderBuffer, NewEmfSize);
|
|
GdipOrderBufferOffset = 0;
|
|
GdipOrderBufferLeft = TS_GDIPLUS_ORDER_SIZELIMIT;
|
|
GdipOrderSize = 0;
|
|
NewEmfSize = 0;
|
|
}
|
|
|
|
// Copy the data to the order buffer
|
|
memcpy(pGdipOrderBuffer + GdipOrderBufferOffset, pData, CopyDataSize);
|
|
pEmfRecord = (PTSEmfPlusRecord)(pGdipOrderBuffer + GdipOrderBufferOffset);
|
|
GdipOrderBufferOffset += CopyDataSize;
|
|
GdipOrderBufferLeft -= CopyDataSize;
|
|
GdipOrderSize += CopyDataSize;
|
|
NewEmfSize += MoveDataSize;
|
|
|
|
pEmfRecord->Size = CopyDataSize;
|
|
// set the cache flag
|
|
pEmfRecord->Size |= 0x80000000;
|
|
// CacheID follows
|
|
*(TSUINT16 *)(pEmfRecord +1) = (TSUINT16)CacheID;
|
|
}
|
|
else {
|
|
// Not cachable, just copy the data
|
|
MoveDataSize = pEmfRecord->Size; // This is used data size in pData
|
|
|
|
// If the order buffer can't hold more data, send this order first
|
|
if ((MoveDataSize > GdipOrderBufferLeft) && (GdipOrderSize != 0)) {
|
|
OESendDrawGdiplusOrder(ppdev, prcl, GdipOrderSize, pGdipOrderBuffer, NewEmfSize);
|
|
GdipOrderBufferOffset = 0;
|
|
GdipOrderBufferLeft = TS_GDIPLUS_ORDER_SIZELIMIT;
|
|
GdipOrderSize = 0;
|
|
NewEmfSize = 0;
|
|
}
|
|
|
|
if (MoveDataSize > GdipOrderBufferLeft) {
|
|
// This single EMF record is larger than the order sizelimit, send it
|
|
OESendDrawGdiplusOrder(ppdev, prcl, MoveDataSize, pData, MoveDataSize);
|
|
}
|
|
else {
|
|
// Copy the data to the order buffer
|
|
memcpy(pGdipOrderBuffer + GdipOrderBufferOffset, pData, MoveDataSize);
|
|
GdipOrderBufferOffset += MoveDataSize;
|
|
GdipOrderBufferLeft -= MoveDataSize;
|
|
GdipOrderSize += MoveDataSize;
|
|
NewEmfSize += MoveDataSize;
|
|
}
|
|
}
|
|
sizeLeft -= (int)MoveDataSize;
|
|
pData += MoveDataSize;
|
|
}
|
|
// Temporarily remove this assert since GDI+ might send incorrect EMF record size
|
|
//TRC_ASSERT((sizeLeft == 0), (TB, "Gdiplus EMF+ record has invalid data size"));
|
|
|
|
// Send the remaining in the order buffer
|
|
if (GdipOrderSize != 0) {
|
|
OESendDrawGdiplusOrder(ppdev, prcl, GdipOrderSize, pGdipOrderBuffer, NewEmfSize);
|
|
}
|
|
|
|
rc = TRUE;
|
|
DC_END_FN();
|
|
|
|
DC_EXIT_POINT:
|
|
if (pGdipOrderBuffer) {
|
|
EngFreeMem(pGdipOrderBuffer);
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
// OECacheDrawGdiplus
|
|
//
|
|
// Cache the Gdiplus EMF+ record
|
|
/****************************************************************************/
|
|
BOOL RDPCALL OECacheDrawGdiplus(PDD_PDEV ppdev, PVOID pvIn, unsigned int *CacheID)
|
|
{
|
|
BOOL rc = FALSE;
|
|
CHDataKeyContext CHContext;
|
|
PTSEmfPlusRecord pEmfRecord = (PTSEmfPlusRecord)pvIn;
|
|
CHCACHEHANDLE CacheHandle;
|
|
TSUINT16 CacheType;
|
|
void *UserDefined;
|
|
BYTE *CacheBuffer;
|
|
unsigned CacheBufferSize;
|
|
unsigned Temp, RemoveCacheID;
|
|
TSUINT16 RemoveCacheNum = 0, *RemoveCacheIDList = NULL;
|
|
TSUINT16 CacheSize; // used for image cache, in number of chunks
|
|
unsigned MaxCacheSize; // used for other caches, in bytes
|
|
|
|
DC_BEGIN_FN("OECacheDrawGdiplus");
|
|
|
|
TRC_NRM((TB, "EmfPlusRecord type is %d", pEmfRecord->Type));
|
|
if (pEmfRecord->Type == EmfPlusRecordTypeSetTSGraphics) {
|
|
CacheHandle = sbcGdipGraphicsCacheHandle;
|
|
CacheType = GDIP_CACHE_GRAPHICS_DATA;
|
|
MaxCacheSize = sbcGdipGraphicsCacheChunkSize;
|
|
}
|
|
else if (pEmfRecord->Type == EmfPlusRecordTypeObject) {
|
|
switch ((enum ObjectType)(pEmfRecord->Flags >> 8) ) {
|
|
case ObjectTypeBrush:
|
|
CacheHandle = sbcGdipObjectBrushCacheHandle;
|
|
CacheType = GDIP_CACHE_OBJECT_BRUSH;
|
|
MaxCacheSize = sbcGdipObjectBrushCacheChunkSize;
|
|
break;
|
|
case ObjectTypePen:
|
|
CacheHandle = sbcGdipObjectPenCacheHandle;
|
|
CacheType = GDIP_CACHE_OBJECT_PEN;
|
|
MaxCacheSize = sbcGdipObjectPenCacheChunkSize;
|
|
break;
|
|
case ObjectTypeImage:
|
|
CacheHandle = sbcGdipObjectImageCacheHandle;
|
|
CacheType = GDIP_CACHE_OBJECT_IMAGE;
|
|
break;
|
|
case ObjectTypeImageAttributes:
|
|
CacheHandle = sbcGdipObjectImageAttributesCacheHandle;
|
|
CacheType = GDIP_CACHE_OBJECT_IMAGEATTRIBUTES;
|
|
MaxCacheSize = sbcGdipObjectImageAttributesCacheChunkSize;
|
|
break;
|
|
default:
|
|
*CacheID = CH_KEY_UNCACHABLE;
|
|
goto NO_CACHE;
|
|
}
|
|
}
|
|
else {
|
|
*CacheID = CH_KEY_UNCACHABLE;
|
|
goto NO_CACHE;
|
|
}
|
|
// Data size needs to be multiple of DWORD to calculate cache key
|
|
Temp = (pEmfRecord->Size - sizeof(TSEmfPlusRecord)) % sizeof(UINT32);
|
|
if (Temp != 0) {
|
|
// Not multiple of DWORD, need to craete a new buffer to hold the data
|
|
CacheBufferSize = (((pEmfRecord->Size - sizeof(TSEmfPlusRecord)) / sizeof(UINT32) + 1) * sizeof(UINT32));
|
|
CacheBuffer = (BYTE *)EngAllocMem(FL_ZERO_MEMORY, CacheBufferSize, DD_ALLOC_TAG);
|
|
if (CacheBuffer == NULL) {
|
|
*CacheID = CH_KEY_UNCACHABLE;
|
|
goto NO_CACHE;
|
|
}
|
|
memcpy(CacheBuffer, (BYTE *)(pEmfRecord + 1), (pEmfRecord->Size - sizeof(TSEmfPlusRecord)));
|
|
CH_CreateKeyFromFirstData(&CHContext, CacheBuffer, CacheBufferSize);
|
|
EngFreeMem(CacheBuffer);
|
|
}
|
|
else {
|
|
CH_CreateKeyFromFirstData(&CHContext, (pEmfRecord + 1), (pEmfRecord->Size - sizeof(TSEmfPlusRecord)));
|
|
}
|
|
|
|
if (!CH_SearchCache(CacheHandle, CHContext.Key1, CHContext.Key2, &UserDefined, CacheID)) {
|
|
// This record is not cached, need to create a new one
|
|
if (CacheType == GDIP_CACHE_OBJECT_IMAGE) {
|
|
// Convert the size in bytes to the number of cache chunks
|
|
CacheSize = (TSUINT16)ActualSizeToChunkSize(pEmfRecord->Size, sbcGdipObjectImageCacheChunkSize);
|
|
if (CacheSize > sbcGdipObjectImageCacheMaxSize) {
|
|
TRC_NRM((TB, ("Image Cache Size %d too big, will not cache it"), CacheSize));
|
|
*CacheID = CH_KEY_UNCACHABLE;
|
|
goto NO_CACHE;
|
|
}
|
|
// The total cache cap is sbcGdipObjectImageCacheTotalSize
|
|
// if sbcGdipObjectImageCacheSizeUsed plus the new cache size exceeds the cap
|
|
// we remove the cache entry in LRU list until cache cap won't be exceeded
|
|
RemoveCacheID = CH_GetLRUCacheEntry(CacheHandle);
|
|
//TRC_ERR((TB, ("Shoud discard cache type: %d, ID: %d"), CacheType, CH_GetLRUCacheEntry(CacheHandle)));
|
|
*CacheID = CH_CacheKey(CacheHandle, CHContext.Key1, CHContext.Key2, NULL);
|
|
|
|
if ((RemoveCacheID == *CacheID) &&
|
|
(RemoveCacheID != CH_KEY_UNCACHABLE)) {
|
|
// New cache entry will replace an old one, need to update sbcGdipObjectImageCacheSizeUsed
|
|
sbcGdipObjectImageCacheSizeUsed -= sbcGdipObjectImageCacheSizeList[RemoveCacheID];
|
|
}
|
|
TRC_NRM((TB, ("Size used is %d, will add %d"), sbcGdipObjectImageCacheSizeUsed, CacheSize));
|
|
if ((sbcGdipObjectImageCacheSizeUsed + CacheSize) > sbcGdipObjectImageCacheTotalSize) {
|
|
RemoveCacheNum = 0;
|
|
RemoveCacheIDList = EngAllocMem(FL_ZERO_MEMORY,
|
|
sizeof(TSUINT16) * pddShm->sbc.drawGdiplusInfo.GdipCacheEntries.GdipObjectImageCacheEntries, DD_ALLOC_TAG);
|
|
if (RemoveCacheIDList == NULL) {
|
|
TRC_ERR((TB, "Can't allocate the memory for RemoveCacheIDList"));
|
|
goto NO_CACHE;
|
|
}
|
|
while ((sbcGdipObjectImageCacheSizeUsed + CacheSize) > sbcGdipObjectImageCacheTotalSize) {
|
|
RemoveCacheID = CH_GetLRUCacheEntry(CacheHandle);
|
|
sbcGdipObjectImageCacheSizeUsed -= sbcGdipObjectImageCacheSizeList[RemoveCacheID];
|
|
TRC_NRM((TB, ("Remove cacheId %d, minus size %d, Used size is %d"), RemoveCacheID,
|
|
sbcGdipObjectImageCacheSizeList[RemoveCacheID], sbcGdipObjectImageCacheSizeUsed));
|
|
CH_RemoveCacheEntry(CacheHandle, RemoveCacheID);
|
|
// Add the RemoveCacheID to the list, will send it with the cache order
|
|
RemoveCacheIDList[RemoveCacheNum] = (TSUINT16)RemoveCacheID;
|
|
RemoveCacheNum++;
|
|
sbcGdipObjectImageCacheSizeList[RemoveCacheID] = 0;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if (pEmfRecord->Size > MaxCacheSize) {
|
|
TRC_NRM((TB, ("Cache Size %d with type %d too big, will not cache it"), pEmfRecord->Size, CacheType));
|
|
*CacheID = CH_KEY_UNCACHABLE;
|
|
goto NO_CACHE;
|
|
}
|
|
*CacheID = CH_CacheKey(CacheHandle, CHContext.Key1, CHContext.Key2, NULL);
|
|
}
|
|
|
|
if (CacheType == GDIP_CACHE_OBJECT_IMAGE)
|
|
TRC_NRM((TB, ("new cache: type %d, ID: %d, size: %d"), CacheType, *CacheID, CacheSize));
|
|
if (*CacheID != CH_KEY_UNCACHABLE) {
|
|
if (!OESendDrawGdiplusCacheOrder(ppdev, pEmfRecord, CacheID, CacheType, RemoveCacheNum, RemoveCacheIDList))
|
|
{
|
|
TRC_ERR((TB, ("OESendDrawGdiplusCacheOrder failed to send cache order")));
|
|
DC_QUIT;
|
|
}
|
|
if (CacheType == GDIP_CACHE_OBJECT_IMAGE) {
|
|
// Update the used image cache size
|
|
sbcGdipObjectImageCacheSizeList[*CacheID] = CacheSize;
|
|
sbcGdipObjectImageCacheSizeUsed += CacheSize;
|
|
TRC_NRM((TB, ("add size %d, new used size is %d"), CacheSize, sbcGdipObjectImageCacheSizeUsed));
|
|
}
|
|
}
|
|
else {
|
|
TRC_ERR((TB, "Failed to cache the Gdiplus object"));
|
|
goto NO_CACHE;
|
|
}
|
|
}
|
|
else {
|
|
TRC_NRM((TB, ("Already cached: type %d, ID: %d"), CacheType, *CacheID));
|
|
}
|
|
|
|
NO_CACHE:
|
|
rc = TRUE;
|
|
DC_EXIT_POINT:
|
|
if (rc != TRUE && *CacheID != CH_KEY_UNCACHABLE)
|
|
CH_RemoveCacheEntry(CacheHandle, *CacheID);
|
|
if (NULL != RemoveCacheIDList) {
|
|
EngFreeMem(RemoveCacheIDList);
|
|
}
|
|
DC_END_FN();
|
|
return rc;
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
// OESendDrawGdiplusCacheOrder
|
|
//
|
|
// Send the Gdiplus EMF+ record cache order
|
|
/****************************************************************************/
|
|
BOOL RDPCALL OESendDrawGdiplusCacheOrder(PDD_PDEV ppdev, PVOID pvIn, unsigned int *CacheID, TSUINT16 CacheType,
|
|
TSUINT16 RemoveCacheNum, TSUINT16 * RemoveCacheIDList)
|
|
{
|
|
BOOL rc = FALSE;
|
|
PINT_ORDER pIntOrder;
|
|
PTSEmfPlusRecord pEmfRecord = (PTSEmfPlusRecord)pvIn;
|
|
PTS_DRAW_GDIPLUS_CACHE_ORDER_FIRST pDrawGdiplusCachePDUFirst;
|
|
PTS_DRAW_GDIPLUS_CACHE_ORDER_NEXT pDrawGdiplusCachePDUNext;
|
|
PTS_DRAW_GDIPLUS_CACHE_ORDER_END pDrawGdiplusCachePDUEnd;
|
|
unsigned sizeLeft, sizeOrder, sizeTotal, sizeUsed;
|
|
BOOL bFirst = TRUE;
|
|
BOOL bAddRemoveCacheList = FALSE;
|
|
BYTE *pDataOffset;
|
|
TSUINT16 *pImageCacheData, i;
|
|
|
|
DC_BEGIN_FN("OESendDrawGdiplusCacheOrder");
|
|
|
|
sizeTotal = pEmfRecord->Size - sizeof(TSEmfPlusRecord);
|
|
sizeLeft = sizeTotal;
|
|
pDataOffset = (BYTE *)(pEmfRecord +1);
|
|
while (sizeLeft > 0) {
|
|
sizeUsed = (sizeLeft <= TS_GDIPLUS_ORDER_SIZELIMIT) ? sizeLeft : TS_GDIPLUS_ORDER_SIZELIMIT;
|
|
sizeLeft -= sizeUsed;
|
|
|
|
if (bFirst && (RemoveCacheNum != 0) &&
|
|
(CacheType == GDIP_CACHE_OBJECT_IMAGE)) {
|
|
// Need to add the RemoveCacheList to this order
|
|
sizeOrder = sizeUsed + sizeof(TSUINT16) * (RemoveCacheNum + 1);
|
|
bAddRemoveCacheList = TRUE;
|
|
}
|
|
else {
|
|
sizeOrder = sizeUsed;
|
|
}
|
|
|
|
if (bFirst) {
|
|
pIntOrder = OA_AllocOrderMem(ppdev, sizeof(TS_DRAW_GDIPLUS_CACHE_ORDER_FIRST) + sizeOrder);
|
|
if (pIntOrder != NULL) {
|
|
// First block of the order data
|
|
pDrawGdiplusCachePDUFirst = (PTS_DRAW_GDIPLUS_CACHE_ORDER_FIRST)pIntOrder->OrderData;
|
|
pDrawGdiplusCachePDUFirst->ControlFlags = (TS_ALTSEC_GDIP_CACHE_FIRST <<
|
|
TS_ALTSEC_ORDER_TYPE_SHIFT) | TS_SECONDARY;
|
|
bFirst = FALSE;
|
|
if (bAddRemoveCacheList) {
|
|
// Need to add the RemoveCacheList to this order
|
|
pImageCacheData = (TSUINT16 *)(pDrawGdiplusCachePDUFirst + 1);
|
|
*pImageCacheData = RemoveCacheNum;
|
|
pImageCacheData ++;
|
|
for (i=0; i<RemoveCacheNum; i++) {
|
|
TRC_NRM((TB, "Remove Cache ID: %d", *(RemoveCacheIDList + i)));
|
|
*pImageCacheData = *(RemoveCacheIDList + i);
|
|
pImageCacheData++;
|
|
}
|
|
}
|
|
pDrawGdiplusCachePDUFirst->Flags = 0;
|
|
pDrawGdiplusCachePDUFirst->CacheID = (TSUINT16)*CacheID;
|
|
pDrawGdiplusCachePDUFirst->CacheType = CacheType;
|
|
pDrawGdiplusCachePDUFirst->cbTotalSize = sizeTotal;
|
|
pDrawGdiplusCachePDUFirst->cbSize = (TSUINT16)sizeOrder;
|
|
|
|
if (!bAddRemoveCacheList) {
|
|
memcpy(pDrawGdiplusCachePDUFirst + 1, pDataOffset, sizeUsed);
|
|
}
|
|
else {
|
|
// Set the flag to indicate there's RemoveCacheIDList in this order
|
|
pDrawGdiplusCachePDUFirst->Flags |= TS_GDIPLUS_CACHE_ORDER_REMOVE_CACHEENTRY;
|
|
memcpy((BYTE *)pImageCacheData, pDataOffset, sizeUsed);
|
|
bAddRemoveCacheList = FALSE;
|
|
}
|
|
}
|
|
else {
|
|
TRC_ERR((TB, "Failed to allocated order for drawgdiplus cache"));
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
else {
|
|
if (sizeLeft == 0) {
|
|
// Last block of the order data
|
|
pIntOrder = OA_AllocOrderMem(ppdev, sizeof(TS_DRAW_GDIPLUS_CACHE_ORDER_END) + sizeOrder);
|
|
if (pIntOrder != NULL) {
|
|
pDrawGdiplusCachePDUEnd = (PTS_DRAW_GDIPLUS_CACHE_ORDER_END)pIntOrder->OrderData;
|
|
pDrawGdiplusCachePDUEnd->ControlFlags = (TS_ALTSEC_GDIP_CACHE_END <<
|
|
TS_ALTSEC_ORDER_TYPE_SHIFT) | TS_SECONDARY;
|
|
pDrawGdiplusCachePDUEnd->Flags = 0;
|
|
pDrawGdiplusCachePDUEnd->CacheID = (TSUINT16)*CacheID;
|
|
pDrawGdiplusCachePDUEnd->CacheType = CacheType;
|
|
pDrawGdiplusCachePDUEnd->cbSize = (TSUINT16)sizeOrder;
|
|
pDrawGdiplusCachePDUEnd->cbTotalSize = sizeTotal;
|
|
memcpy(pDrawGdiplusCachePDUEnd + 1, pDataOffset, sizeUsed);
|
|
}
|
|
else {
|
|
TRC_ERR((TB, "Failed to allocated order for drawgdiplus cache"));
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
else {
|
|
// subsequent block of the order data
|
|
pIntOrder = OA_AllocOrderMem(ppdev, sizeof(TS_DRAW_GDIPLUS_CACHE_ORDER_NEXT) + sizeOrder);
|
|
if (pIntOrder != NULL) {
|
|
pDrawGdiplusCachePDUNext = (PTS_DRAW_GDIPLUS_CACHE_ORDER_NEXT)pIntOrder->OrderData;
|
|
pDrawGdiplusCachePDUNext->ControlFlags = (TS_ALTSEC_GDIP_CACHE_NEXT <<
|
|
TS_ALTSEC_ORDER_TYPE_SHIFT) | TS_SECONDARY;
|
|
pDrawGdiplusCachePDUNext->Flags = 0;
|
|
pDrawGdiplusCachePDUNext->CacheID = (TSUINT16)*CacheID;
|
|
pDrawGdiplusCachePDUNext->CacheType = CacheType;
|
|
pDrawGdiplusCachePDUNext->cbSize = (TSUINT16)sizeOrder;
|
|
memcpy(pDrawGdiplusCachePDUNext + 1, pDataOffset, sizeUsed);
|
|
}
|
|
else {
|
|
TRC_ERR((TB, "Failed to allocated order for drawgdiplus cache"));
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
}
|
|
pDataOffset += sizeUsed;
|
|
|
|
OA_AppendToOrderList(pIntOrder);
|
|
}
|
|
rc = TRUE;
|
|
DC_EXIT_POINT:
|
|
DC_END_FN();
|
|
return rc;
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
// OESendDrawGdiplusOrder
|
|
//
|
|
// Send the Gdiplus EMF+ record order
|
|
/****************************************************************************/
|
|
BOOL RDPCALL OESendDrawGdiplusOrder(PDD_PDEV ppdev, RECTL *prcl, ULONG cjIn, PVOID pvIn, ULONG TotalEmfSize)
|
|
{
|
|
BOOL rc = FALSE;
|
|
PINT_ORDER pIntOrder;
|
|
PTS_DRAW_GDIPLUS_ORDER_FIRST pDrawGdiplusPDUFirst;
|
|
PTS_DRAW_GDIPLUS_ORDER_NEXT pDrawGdiplusPDUNext;
|
|
PTS_DRAW_GDIPLUS_ORDER_END pDrawGdiplusPDUEnd;
|
|
unsigned sizeLeft, sizeOrder, sizeTotal;
|
|
BOOL bFirst = TRUE;
|
|
BYTE *pDataOffset;
|
|
|
|
DC_BEGIN_FN("OESendDrawGdiplusOrder");
|
|
|
|
sizeTotal = cjIn;
|
|
sizeLeft = sizeTotal;
|
|
pDataOffset = pvIn;
|
|
while (sizeLeft > 0) {
|
|
sizeOrder = (sizeLeft <= TS_GDIPLUS_ORDER_SIZELIMIT) ? sizeLeft : TS_GDIPLUS_ORDER_SIZELIMIT;
|
|
sizeLeft -= sizeOrder;
|
|
|
|
if (bFirst) {
|
|
// First block of the order data
|
|
pIntOrder = OA_AllocOrderMem(ppdev, sizeof(TS_DRAW_GDIPLUS_ORDER_FIRST) + sizeOrder);
|
|
if (pIntOrder != NULL) {
|
|
pDrawGdiplusPDUFirst = (PTS_DRAW_GDIPLUS_ORDER_FIRST)pIntOrder->OrderData;
|
|
pDrawGdiplusPDUFirst->ControlFlags = (TS_ALTSEC_GDIP_FIRST <<
|
|
TS_ALTSEC_ORDER_TYPE_SHIFT) | TS_SECONDARY;
|
|
pDrawGdiplusPDUFirst->cbTotalSize = sizeTotal;
|
|
pDrawGdiplusPDUFirst->cbSize = (TSUINT16)sizeOrder;
|
|
pDrawGdiplusPDUFirst->cbTotalEmfSize = TotalEmfSize;
|
|
|
|
memcpy(pDrawGdiplusPDUFirst + 1, pDataOffset, sizeOrder);
|
|
bFirst = FALSE;
|
|
}
|
|
else {
|
|
TRC_ERR((TB, "Failed to allocated order for drawgdiplus"));
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
else {
|
|
if (sizeLeft == 0) {
|
|
// Last block of the order data
|
|
pIntOrder = OA_AllocOrderMem(ppdev, sizeof(TS_DRAW_GDIPLUS_ORDER_END) + sizeOrder);
|
|
if (pIntOrder != NULL) {
|
|
pDrawGdiplusPDUEnd = (PTS_DRAW_GDIPLUS_ORDER_END)pIntOrder->OrderData;
|
|
pDrawGdiplusPDUEnd->ControlFlags = (TS_ALTSEC_GDIP_END <<
|
|
TS_ALTSEC_ORDER_TYPE_SHIFT) | TS_SECONDARY;
|
|
pDrawGdiplusPDUEnd->cbTotalSize = sizeTotal;
|
|
pDrawGdiplusPDUEnd->cbSize = (TSUINT16)sizeOrder;
|
|
pDrawGdiplusPDUEnd->cbTotalEmfSize = TotalEmfSize;
|
|
|
|
memcpy(pDrawGdiplusPDUEnd + 1, pDataOffset, sizeOrder);
|
|
}
|
|
else {
|
|
TRC_ERR((TB, "Failed to allocated order for drawgdiplus"));
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
else {
|
|
// subsequent block of the order data
|
|
pIntOrder = OA_AllocOrderMem(ppdev, sizeof(TS_DRAW_GDIPLUS_ORDER_NEXT) + sizeOrder);
|
|
if (pIntOrder != NULL) {
|
|
pDrawGdiplusPDUNext = (PTS_DRAW_GDIPLUS_ORDER_NEXT)pIntOrder->OrderData;
|
|
pDrawGdiplusPDUNext->ControlFlags = (TS_ALTSEC_GDIP_NEXT <<
|
|
TS_ALTSEC_ORDER_TYPE_SHIFT) | TS_SECONDARY;
|
|
pDrawGdiplusPDUNext->cbSize = (TSUINT16)sizeOrder;
|
|
|
|
memcpy(pDrawGdiplusPDUNext + 1, pDataOffset, sizeOrder);
|
|
}
|
|
else {
|
|
TRC_ERR((TB, "Failed to allocated order for drawgdiplus"));
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
}
|
|
OA_AppendToOrderList(pIntOrder);
|
|
pDataOffset += sizeOrder;
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
DC_END_FN();
|
|
return rc;
|
|
}
|
|
#endif // DRAW_GDIPLUS
|
|
|
|
/****************************************************************************/
|
|
// OEEncodeDstBlt
|
|
//
|
|
// Performs all encoding steps required to encode a DstBlt order, then adds
|
|
// to order list. Returns FALSE if the order needs to be added to the screen
|
|
// data area.
|
|
/****************************************************************************/
|
|
BOOL RDPCALL OEEncodeDstBlt(
|
|
RECTL *pBounds,
|
|
BYTE Rop3,
|
|
PDD_PDEV ppdev,
|
|
OE_ENUMRECTS *pClipRects)
|
|
{
|
|
BOOL rc;
|
|
unsigned OrderSize;
|
|
BYTE OrderType;
|
|
PINT_ORDER pOrder;
|
|
MULTI_DSTBLT_ORDER *pPrevDB;
|
|
|
|
DC_BEGIN_FN("OEEncodeDstBlt");
|
|
|
|
// Check whether we should use the multi-cliprect version. Must be a
|
|
// complex clip region and the client must support the order.
|
|
if (pClipRects->rects.c < 2 ||
|
|
!OE_SendAsOrder(TS_ENC_MULTIDSTBLT_ORDER)) {
|
|
// Non-multi version.
|
|
OrderType = TS_ENC_DSTBLT_ORDER;
|
|
OrderSize = MAX_DSTBLT_FIELD_SIZE;
|
|
pPrevDB = (MULTI_DSTBLT_ORDER *)&PrevDstBlt;
|
|
}
|
|
else {
|
|
// Multi version.
|
|
OrderType = TS_ENC_MULTIDSTBLT_ORDER;
|
|
OrderSize = MAX_MULTI_DSTBLT_FIELD_SIZE_NCLIP(pClipRects->rects.c);
|
|
pPrevDB = &PrevMultiDstBlt;
|
|
}
|
|
|
|
if (OE_SendAsOrder(OrderType)) {
|
|
// Alloc the order memory.
|
|
// 1 field flag byte for both regular and Multi.
|
|
pOrder = OA_AllocOrderMem(ppdev, MAX_ORDER_SIZE(pClipRects->rects.c,
|
|
1, OrderSize));
|
|
if (pOrder != NULL) {
|
|
BYTE *pControlFlags = pOrder->OrderData;
|
|
BYTE *pBuffer = pControlFlags + 1;
|
|
BYTE *pFieldFlags;
|
|
|
|
// Direct-encode the primary order fields. 1 field flag byte.
|
|
*pControlFlags = TS_STANDARD;
|
|
OE2_EncodeOrderType(pControlFlags, &pBuffer, OrderType);
|
|
pFieldFlags = pBuffer;
|
|
*pFieldFlags = 0;
|
|
pBuffer++;
|
|
|
|
// Only set boundrect for non-multi order.
|
|
if (pClipRects->rects.c != 0 && OrderType == TS_ENC_DSTBLT_ORDER)
|
|
OE2_EncodeBounds(pControlFlags, &pBuffer,
|
|
&pClipRects->rects.arcl[0]);
|
|
|
|
// Simultaneously determine if each of the coordinate fields has
|
|
// changed, whether we can use delta coordinates, and save changed
|
|
// fields.
|
|
*pFieldFlags |= (BYTE)OEDirectEncodeRect(pBounds,
|
|
(RECT *)&pPrevDB->nLeftRect, &pBuffer,
|
|
pControlFlags);
|
|
|
|
// bRop
|
|
if (Rop3 != pPrevDB->bRop) {
|
|
pPrevDB->bRop = Rop3;
|
|
*pBuffer++ = Rop3;
|
|
*pFieldFlags |= 0x10;
|
|
}
|
|
|
|
// Add the order to the list.
|
|
if (OrderType == TS_ENC_DSTBLT_ORDER) {
|
|
pOrder->OrderLength = (unsigned)(pBuffer - pOrder->OrderData);
|
|
|
|
// See if we can save sending the order field bytes.
|
|
pOrder->OrderLength -= OE2_CheckOneZeroFlagByte(pControlFlags,
|
|
pFieldFlags, (unsigned)(pBuffer - pFieldFlags - 1));
|
|
|
|
INC_OUTCOUNTER(OUT_DSTBLT_ORDER);
|
|
ADD_INCOUNTER(IN_DSTBLT_BYTES, pOrder->OrderLength);
|
|
OA_AppendToOrderList(pOrder);
|
|
|
|
// Flush the order.
|
|
if (pClipRects->rects.c < 2)
|
|
rc = TRUE;
|
|
else
|
|
rc = OEEmitReplayOrders(ppdev, 1, pClipRects);
|
|
}
|
|
else {
|
|
// Append the cliprect info.
|
|
*pFieldFlags |= (BYTE)(OEBuildPrecodeMultiClipFields(
|
|
pClipRects, &pBuffer, &pPrevDB->nDeltaEntries,
|
|
(BYTE *)&pPrevDB->codedDeltaList) << 5);
|
|
|
|
pOrder->OrderLength = (unsigned)(pBuffer - pOrder->OrderData);
|
|
|
|
// See if we can save sending the order field bytes.
|
|
pOrder->OrderLength -= OE2_CheckOneZeroFlagByte(pControlFlags,
|
|
pFieldFlags, (unsigned)(pBuffer - pFieldFlags - 1));
|
|
|
|
INC_OUTCOUNTER(OUT_MULTI_DSTBLT_ORDER);
|
|
ADD_INCOUNTER(IN_MULTI_DSTBLT_BYTES, pOrder->OrderLength);
|
|
OA_AppendToOrderList(pOrder);
|
|
rc = TRUE;
|
|
}
|
|
|
|
TRC_NRM((TB, "%sDstBlt X %d Y %d w %d h %d rop %02X",
|
|
(OrderType == TS_ENC_DSTBLT_ORDER ? "" : "Multi"),
|
|
pBounds->left, pBounds->top, pBounds->right,
|
|
pBounds->bottom, Rop3));
|
|
}
|
|
else {
|
|
TRC_ERR((TB, "Failed to alloc order"));
|
|
INC_OUTCOUNTER(OUT_BITBLT_SDA_HEAPALLOCFAILED);
|
|
rc = FALSE;
|
|
}
|
|
}
|
|
else {
|
|
TRC_NRM((TB,"(Multi)DstBlt order not supported"));
|
|
INC_OUTCOUNTER(OUT_BITBLT_SDA_UNSUPPORTED);
|
|
rc = FALSE;
|
|
}
|
|
|
|
DC_END_FN();
|
|
return rc;
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
// OEEncodeOpaqueRect
|
|
//
|
|
// Encodes the OpaqueRect order. Returns FALSE on failure.
|
|
/****************************************************************************/
|
|
BOOL RDPCALL OEEncodeOpaqueRect(
|
|
RECTL *pBounds,
|
|
BRUSHOBJ *pbo,
|
|
PDD_PDEV ppdev,
|
|
OE_ENUMRECTS *pClipRects)
|
|
{
|
|
BOOL rc;
|
|
unsigned OrderSize = 0;
|
|
unsigned NumFieldFlagBytes = 0;
|
|
BYTE OrderType = 0;
|
|
PINT_ORDER pOrder;
|
|
MULTI_OPAQUERECT_ORDER *pPrevOR = NULL;
|
|
|
|
DC_BEGIN_FN("OEEncodeOpaqueRect");
|
|
|
|
// Check whether we should use the multi-cliprect version. Must be a
|
|
// complex clip region and the client must support the order.
|
|
if (pClipRects->rects.c < 2 ||
|
|
!OE_SendAsOrder(TS_ENC_MULTIOPAQUERECT_ORDER)) {
|
|
// The single version is implied by PatBlt, so we check for that
|
|
// order support instead.
|
|
if (OE_SendAsOrder(TS_ENC_PATBLT_ORDER)) {
|
|
// Non-multi version.
|
|
OrderType = TS_ENC_OPAQUERECT_ORDER;
|
|
OrderSize = MAX_OPAQUERECT_FIELD_SIZE;
|
|
pPrevOR = (MULTI_OPAQUERECT_ORDER *)&PrevOpaqueRect;
|
|
NumFieldFlagBytes = 1;
|
|
}
|
|
else {
|
|
TRC_NRM((TB,"OpaqueRect/PatBlt order not supported"));
|
|
INC_OUTCOUNTER(OUT_BITBLT_SDA_UNSUPPORTED);
|
|
rc = FALSE;
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
else {
|
|
// Multi version.
|
|
OrderType = TS_ENC_MULTIOPAQUERECT_ORDER;
|
|
OrderSize = MAX_MULTI_OPAQUERECT_FIELD_SIZE_NCLIP(pClipRects->rects.c);
|
|
pPrevOR = &PrevMultiOpaqueRect;
|
|
NumFieldFlagBytes = 2;
|
|
}
|
|
|
|
pOrder = OA_AllocOrderMem(ppdev, MAX_ORDER_SIZE(pClipRects->rects.c,
|
|
NumFieldFlagBytes, OrderSize));
|
|
if (pOrder != NULL) {
|
|
BYTE *pControlFlags = pOrder->OrderData;
|
|
BYTE *pBuffer = pControlFlags + 1;
|
|
PUINT32_UA pFieldFlags;
|
|
DCCOLOR Color;
|
|
|
|
// Direct-encode the primary order fields.
|
|
*pControlFlags = TS_STANDARD;
|
|
OE2_EncodeOrderType(pControlFlags, &pBuffer, OrderType);
|
|
pFieldFlags = (PUINT32_UA)pBuffer;
|
|
*pFieldFlags = 0;
|
|
pBuffer += NumFieldFlagBytes;
|
|
|
|
// Only set boundrect for non-multi order.
|
|
if (pClipRects->rects.c != 0 && OrderType == TS_ENC_OPAQUERECT_ORDER)
|
|
OE2_EncodeBounds(pControlFlags, &pBuffer,
|
|
&pClipRects->rects.arcl[0]);
|
|
|
|
*pFieldFlags |= OEDirectEncodeRect(pBounds,
|
|
(RECT *)&pPrevOR->nLeftRect, &pBuffer, pControlFlags);
|
|
|
|
// Copy non-coordinate fields, saving copies as needed.
|
|
|
|
OEConvertColor(ppdev, &Color, pbo->iSolidColor, NULL);
|
|
if (Color.u.rgb.red != pPrevOR->Color.u.rgb.red) {
|
|
pPrevOR->Color.u.rgb.red = Color.u.rgb.red;
|
|
*pBuffer++ = Color.u.rgb.red;
|
|
*pFieldFlags |= 0x10;
|
|
}
|
|
if (Color.u.rgb.green != pPrevOR->Color.u.rgb.green) {
|
|
pPrevOR->Color.u.rgb.green = Color.u.rgb.green;
|
|
*pBuffer++ = Color.u.rgb.green;
|
|
*pFieldFlags |= 0x20;
|
|
}
|
|
if (Color.u.rgb.blue != pPrevOR->Color.u.rgb.blue) {
|
|
pPrevOR->Color.u.rgb.blue = Color.u.rgb.blue;
|
|
*pBuffer++ = Color.u.rgb.blue;
|
|
*pFieldFlags |= 0x40;
|
|
}
|
|
|
|
// Different handling based on the type.
|
|
if (OrderType == TS_ENC_OPAQUERECT_ORDER) {
|
|
pOrder->OrderLength = (unsigned)(pBuffer - pOrder->OrderData);
|
|
|
|
// See if we can save sending the order field bytes.
|
|
pOrder->OrderLength -= OE2_CheckOneZeroFlagByte(pControlFlags,
|
|
(BYTE *)pFieldFlags,
|
|
(unsigned)(pBuffer - (BYTE *)pFieldFlags - 1));
|
|
|
|
// Flush the order.
|
|
INC_OUTCOUNTER(OUT_OPAQUERECT_ORDER);
|
|
ADD_INCOUNTER(IN_OPAQUERECT_BYTES, pOrder->OrderLength);
|
|
OA_AppendToOrderList(pOrder);
|
|
if (pClipRects->rects.c < 2)
|
|
rc = TRUE;
|
|
else
|
|
rc = OEEmitReplayOrders(ppdev, 1, pClipRects);
|
|
}
|
|
else {
|
|
// Append the cliprect info.
|
|
*pFieldFlags |= (OEBuildPrecodeMultiClipFields(pClipRects,
|
|
&pBuffer, &pPrevOR->nDeltaEntries,
|
|
(BYTE *)&pPrevOR->codedDeltaList) << 7);
|
|
|
|
pOrder->OrderLength = (unsigned)(pBuffer - pOrder->OrderData);
|
|
|
|
// See if we can save sending the order field bytes.
|
|
pOrder->OrderLength -= OE2_CheckTwoZeroFlagBytes(pControlFlags,
|
|
(BYTE *)pFieldFlags,
|
|
(unsigned)(pBuffer - (BYTE *)pFieldFlags - 2));
|
|
|
|
// Flush the order.
|
|
INC_OUTCOUNTER(OUT_MULTI_OPAQUERECT_ORDER);
|
|
ADD_INCOUNTER(IN_MULTI_OPAQUERECT_BYTES, pOrder->OrderLength);
|
|
OA_AppendToOrderList(pOrder);
|
|
rc = TRUE;
|
|
}
|
|
|
|
TRC_NRM((TB, "%sOpaqueRect x(%d) y(%d) w(%d) h(%d) c(%#02x)",
|
|
(OrderType == TS_ENC_OPAQUERECT_ORDER ? "" : "Multi"),
|
|
pBounds->left, pBounds->top,
|
|
pBounds->right - pBounds->left,
|
|
pBounds->bottom - pBounds->top,
|
|
Color.u.index));
|
|
}
|
|
else {
|
|
TRC_ERR((TB, "Failed to alloc order"));
|
|
INC_OUTCOUNTER(OUT_BITBLT_SDA_HEAPALLOCFAILED);
|
|
rc = FALSE;
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
DC_END_FN();
|
|
return rc;
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
// OEDirectEncodeRect
|
|
//
|
|
// Common code for handling the various XxxBlt and MultiXxxBlt direct
|
|
// encoding to wire format. Encodes the left, top, width, and height
|
|
// of the exclusive bound rect. Assumes that the bound rect values are
|
|
// encoded as 2-byte coords on the wire and that they are the only coords
|
|
// in the target order. Returns the field encoding flags.
|
|
/****************************************************************************/
|
|
unsigned OEDirectEncodeRect(
|
|
RECTL *pBounds,
|
|
RECT *pPrevBounds,
|
|
BYTE **ppBuf,
|
|
BYTE *pControlFlags)
|
|
{
|
|
BYTE *pBuf = *ppBuf;
|
|
long Temp;
|
|
BOOL bUseDeltaCoords;
|
|
short Delta, NormalCoordEncoding[4];
|
|
unsigned NumFields;
|
|
unsigned FieldFlags;
|
|
|
|
DC_BEGIN_FN("OEDirectEncodeRect");
|
|
|
|
memset(NormalCoordEncoding, 0, sizeof(NormalCoordEncoding));
|
|
// Simultaneously determine if each of the coordinate fields
|
|
// has changed, whether we can use delta coordinates, and
|
|
// save changed fields. Note bounds are in exclusive coords.
|
|
FieldFlags = 0;
|
|
NumFields = 0;
|
|
bUseDeltaCoords = TRUE;
|
|
|
|
// Left
|
|
Delta = (short)(pBounds->left - pPrevBounds->left);
|
|
if (Delta) {
|
|
pPrevBounds->left = pBounds->left;
|
|
if (Delta != (short)(char)Delta)
|
|
bUseDeltaCoords = FALSE;
|
|
pBuf[NumFields] = (char)Delta;
|
|
NormalCoordEncoding[NumFields] = (short)pBounds->left;
|
|
NumFields++;
|
|
FieldFlags |= 0x01;
|
|
}
|
|
|
|
// Top
|
|
Delta = (short)(pBounds->top - pPrevBounds->top);
|
|
if (Delta) {
|
|
pPrevBounds->top = pBounds->top;
|
|
if (Delta != (short)(char)Delta)
|
|
bUseDeltaCoords = FALSE;
|
|
pBuf[NumFields] = (char)Delta;
|
|
NormalCoordEncoding[NumFields] = (short)pBounds->top;
|
|
NumFields++;
|
|
FieldFlags |= 0x02;
|
|
}
|
|
|
|
// Width -- we use pPrevBounds->right as prev value.
|
|
Temp = pBounds->right - pBounds->left;
|
|
Delta = (short)(Temp - pPrevBounds->right);
|
|
if (Delta) {
|
|
pPrevBounds->right = Temp;
|
|
if (Delta != (short)(char)Delta)
|
|
bUseDeltaCoords = FALSE;
|
|
pBuf[NumFields] = (char)Delta;
|
|
NormalCoordEncoding[NumFields] = (short)Temp;
|
|
NumFields++;
|
|
FieldFlags |= 0x04;
|
|
}
|
|
|
|
// Height -- we use pPrevBounds->bottom as prev value.
|
|
Temp = pBounds->bottom - pBounds->top;
|
|
Delta = (short)(Temp - pPrevBounds->bottom);
|
|
if (Delta) {
|
|
pPrevBounds->bottom = Temp;
|
|
if (Delta != (short)(char)Delta)
|
|
bUseDeltaCoords = FALSE;
|
|
pBuf[NumFields] = (char)Delta;
|
|
NormalCoordEncoding[NumFields] = (short)Temp;
|
|
NumFields++;
|
|
FieldFlags |= 0x08;
|
|
}
|
|
|
|
// Copy the final coordinates to the order.
|
|
if (bUseDeltaCoords) {
|
|
*pControlFlags |= TS_DELTA_COORDINATES;
|
|
pBuf += NumFields;
|
|
}
|
|
else {
|
|
*((DWORD UNALIGNED *)pBuf) = *((DWORD *)NormalCoordEncoding);
|
|
*((DWORD UNALIGNED *)(pBuf + 4)) = *((DWORD *)&NormalCoordEncoding[2]);
|
|
pBuf += NumFields * sizeof(short);
|
|
}
|
|
|
|
*ppBuf = pBuf;
|
|
|
|
DC_END_FN();
|
|
return FieldFlags;
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
// OEEncodePatBlt
|
|
//
|
|
// Encodes a PatBlt order. Retruns FALSE on failure.
|
|
/****************************************************************************/
|
|
BOOL RDPCALL OEEncodePatBlt(
|
|
PDD_PDEV ppdev,
|
|
BRUSHOBJ *pbo,
|
|
RECTL *pBounds,
|
|
POINTL *pptlBrush,
|
|
BYTE Rop3,
|
|
OE_ENUMRECTS *pClipRects)
|
|
{
|
|
BOOL rc;
|
|
unsigned OrderSize;
|
|
BYTE OrderType;
|
|
PINT_ORDER pOrder;
|
|
MULTI_PATBLT_ORDER *pPrevPB;
|
|
POE_BRUSH_DATA pCurrentBrush;
|
|
|
|
DC_BEGIN_FN("OEEncodePatBlt");
|
|
|
|
// Check for a simple brush pattern.
|
|
if (OECheckBrushIsSimple(ppdev, pbo, &pCurrentBrush)) {
|
|
// Check whether we should use the multi-cliprect version. Must be a
|
|
// complex clip region and the client must support the order.
|
|
if (pClipRects->rects.c < 2 ||
|
|
!OE_SendAsOrder(TS_ENC_MULTIPATBLT_ORDER)) {
|
|
// Non-multi version.
|
|
OrderType = TS_ENC_PATBLT_ORDER;
|
|
OrderSize = MAX_PATBLT_FIELD_SIZE;
|
|
pPrevPB = (MULTI_PATBLT_ORDER *)&PrevPatBlt;
|
|
}
|
|
else {
|
|
// Multi version.
|
|
OrderType = TS_ENC_MULTIPATBLT_ORDER;
|
|
OrderSize = MAX_MULTI_PATBLT_FIELD_SIZE_NCLIP(pClipRects->rects.c);
|
|
pPrevPB = &PrevMultiPatBlt;
|
|
}
|
|
|
|
// Make sure we don't have orders turned off.
|
|
if (OE_SendAsOrder(OrderType)) {
|
|
// 2 field flag bytes for regular and multi.
|
|
pOrder = OA_AllocOrderMem(ppdev, MAX_ORDER_SIZE(
|
|
pClipRects->rects.c, 2, OrderSize));
|
|
if (pOrder != NULL) {
|
|
BYTE *pControlFlags = pOrder->OrderData;
|
|
BYTE *pBuffer = pControlFlags + 1;
|
|
PUINT32_UA pFieldFlags;
|
|
DCCOLOR Color;
|
|
POINTL ClippedBrushOrg;
|
|
|
|
// Direct-encode the primary order fields.
|
|
*pControlFlags = TS_STANDARD;
|
|
OE2_EncodeOrderType(pControlFlags, &pBuffer, OrderType);
|
|
pFieldFlags = (PUINT32_UA)pBuffer;
|
|
*pFieldFlags = 0;
|
|
pBuffer += 2;
|
|
|
|
// Only set boundrect for non-multi order.
|
|
if (pClipRects->rects.c != 0 &&
|
|
OrderType == TS_ENC_PATBLT_ORDER)
|
|
OE2_EncodeBounds(pControlFlags, &pBuffer,
|
|
&pClipRects->rects.arcl[0]);
|
|
|
|
// Inline field encoding to wire format.
|
|
*pFieldFlags |= OEDirectEncodeRect(pBounds,
|
|
(RECT *)&pPrevPB->nLeftRect, &pBuffer, pControlFlags);
|
|
|
|
// bRop
|
|
if (Rop3 != pPrevPB->bRop) {
|
|
pPrevPB->bRop = Rop3;
|
|
*pBuffer++ = (BYTE)Rop3;
|
|
*pFieldFlags |= 0x0010;
|
|
}
|
|
|
|
// BackColor is a 3-byte color field.
|
|
if (memcmp(&pCurrentBrush->back, &pPrevPB->BackColor,
|
|
sizeof(pCurrentBrush->back))) {
|
|
pPrevPB->BackColor = pCurrentBrush->back;
|
|
*pBuffer++ = pCurrentBrush->back.u.rgb.red;
|
|
*pBuffer++ = pCurrentBrush->back.u.rgb.green;
|
|
*pBuffer++ = pCurrentBrush->back.u.rgb.blue;
|
|
*pFieldFlags |= 0x0020;
|
|
}
|
|
|
|
// ForeColor is a 3-byte color field.
|
|
if (memcmp(&pCurrentBrush->fore, &pPrevPB->ForeColor,
|
|
sizeof(pCurrentBrush->fore))) {
|
|
pPrevPB->ForeColor = pCurrentBrush->fore;
|
|
*pBuffer++ = pCurrentBrush->fore.u.rgb.red;
|
|
*pBuffer++ = pCurrentBrush->fore.u.rgb.green;
|
|
*pBuffer++ = pCurrentBrush->fore.u.rgb.blue;
|
|
*pFieldFlags |= 0x0040;
|
|
}
|
|
|
|
// The protocol brush origin is the point on the screen where
|
|
// we want the brush to start being drawn from (tiling where
|
|
// necessary).
|
|
ClippedBrushOrg = *pptlBrush;
|
|
OEClipPoint(&ClippedBrushOrg);
|
|
|
|
// BrushOrgX
|
|
if (ClippedBrushOrg.x != pPrevPB->BrushOrgX) {
|
|
pPrevPB->BrushOrgX = ClippedBrushOrg.x;
|
|
*pBuffer++ = (BYTE)ClippedBrushOrg.x;
|
|
*pFieldFlags |= 0x0080;
|
|
}
|
|
|
|
// BrushOrgY
|
|
if (ClippedBrushOrg.y != pPrevPB->BrushOrgY) {
|
|
pPrevPB->BrushOrgY = ClippedBrushOrg.y;
|
|
*pBuffer++ = (BYTE)ClippedBrushOrg.y;
|
|
*pFieldFlags |= 0x0100;
|
|
}
|
|
|
|
// BrushStyle
|
|
if (pCurrentBrush->style != pPrevPB->BrushStyle) {
|
|
pPrevPB->BrushStyle = pCurrentBrush->style;
|
|
*pBuffer++ = (BYTE)pCurrentBrush->style;
|
|
*pFieldFlags |= 0x0200;
|
|
}
|
|
|
|
// BrushHatch
|
|
if (pCurrentBrush->hatch != pPrevPB->BrushHatch) {
|
|
pPrevPB->BrushHatch = pCurrentBrush->hatch;
|
|
*pBuffer++ = (BYTE)pCurrentBrush->hatch;
|
|
*pFieldFlags |= 0x0400;
|
|
}
|
|
|
|
// BrushExtra, a 7-byte field.
|
|
if (memcmp(pCurrentBrush->brushData, pPrevPB->BrushExtra, 7)) {
|
|
memcpy(pPrevPB->BrushExtra, pCurrentBrush->brushData, 7);
|
|
memcpy(pBuffer, pCurrentBrush->brushData, 7);
|
|
pBuffer += 7;
|
|
*pFieldFlags |= 0x0800;
|
|
}
|
|
|
|
// Different handling based on the order type.
|
|
if (OrderType == TS_ENC_PATBLT_ORDER) {
|
|
pOrder->OrderLength = (unsigned)(pBuffer -
|
|
pOrder->OrderData);
|
|
|
|
// See if we can save sending the order field bytes.
|
|
pOrder->OrderLength -= OE2_CheckTwoZeroFlagBytes(
|
|
pControlFlags, (BYTE *)pFieldFlags,
|
|
(unsigned)(pBuffer - (BYTE *)pFieldFlags - 2));
|
|
|
|
INC_OUTCOUNTER(OUT_PATBLT_ORDER);
|
|
ADD_INCOUNTER(IN_PATBLT_BYTES, pOrder->OrderLength);
|
|
OA_AppendToOrderList(pOrder);
|
|
|
|
// Flush the order.
|
|
if (pClipRects->rects.c < 2)
|
|
rc = TRUE;
|
|
else
|
|
rc = OEEmitReplayOrders(ppdev, 2, pClipRects);
|
|
}
|
|
else {
|
|
// Append the cliprect info.
|
|
*pFieldFlags |= (OEBuildPrecodeMultiClipFields(pClipRects,
|
|
&pBuffer, &pPrevPB->nDeltaEntries,
|
|
(BYTE *)&pPrevPB->codedDeltaList) << 12);
|
|
|
|
pOrder->OrderLength = (unsigned)(pBuffer -
|
|
pOrder->OrderData);
|
|
|
|
// See if we can save sending the order field bytes.
|
|
pOrder->OrderLength -= OE2_CheckTwoZeroFlagBytes(
|
|
pControlFlags, (BYTE *)pFieldFlags,
|
|
(unsigned)(pBuffer - (BYTE *)pFieldFlags - 2));
|
|
|
|
INC_OUTCOUNTER(OUT_MULTI_PATBLT_ORDER);
|
|
ADD_INCOUNTER(IN_MULTI_PATBLT_BYTES, pOrder->OrderLength);
|
|
OA_AppendToOrderList(pOrder);
|
|
rc = TRUE;
|
|
}
|
|
|
|
TRC_NRM((TB, "%sPatBlt BC %02x FC %02x "
|
|
"Brush %02X %02X X %d Y %d w %d h %d rop %02X",
|
|
(OrderType == TS_ENC_PATBLT_ORDER ? "" : "Multi"),
|
|
pCurrentBrush->back.u.index,
|
|
pCurrentBrush->fore.u.index,
|
|
pCurrentBrush->style,
|
|
pCurrentBrush->hatch,
|
|
pBounds->left,
|
|
pBounds->top,
|
|
pBounds->right - pBounds->left,
|
|
pBounds->bottom = pBounds->top,
|
|
Rop3));
|
|
}
|
|
else {
|
|
TRC_ERR((TB, "Failed to alloc order"));
|
|
INC_OUTCOUNTER(OUT_BITBLT_SDA_HEAPALLOCFAILED);
|
|
rc = FALSE;
|
|
}
|
|
}
|
|
else {
|
|
TRC_NRM((TB,"(Multi)PatBlt order not supported"));
|
|
INC_OUTCOUNTER(OUT_BITBLT_SDA_UNSUPPORTED);
|
|
rc = FALSE;
|
|
}
|
|
}
|
|
else {
|
|
TRC_NRM((TB, "Brush is not simple"));
|
|
rc = FALSE;
|
|
}
|
|
|
|
DC_END_FN();
|
|
return rc;
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
// OEDirectEncodeMemBlt
|
|
//
|
|
// Handles all steps required to encode MemBlt. Assumes the order data
|
|
// has been placed in oeTempMemBlt.
|
|
/****************************************************************************/
|
|
BOOL OEDirectEncodeMemBlt(PDD_PDEV ppdev, OE_ENUMRECTS *pClipRects)
|
|
{
|
|
BOOL rc;
|
|
BYTE DeltaEncoding2[2];
|
|
short Delta, NormalEncoding1[4], NormalEncoding2[2];
|
|
BOOLEAN bUseDeltaCoords;
|
|
unsigned NumFields1, NumFields2;
|
|
PINT_ORDER pOrder;
|
|
|
|
DC_BEGIN_FN("OEDirectEncodeMemBlt");
|
|
|
|
TRC_NRM((TB,"MemBlt: Dst=(%d,%d),w=%d,h=%d, Src=(%d,%d), "
|
|
"clip=%s (%d,%d,%d,%d)",
|
|
oeTempMemBlt.Common.nLeftRect,
|
|
oeTempMemBlt.Common.nTopRect,
|
|
oeTempMemBlt.Common.nWidth,
|
|
oeTempMemBlt.Common.nHeight,
|
|
oeTempMemBlt.Common.nXSrc,
|
|
oeTempMemBlt.Common.nYSrc,
|
|
pClipRects->rects.c == 0 ? "n/a" : "present",
|
|
pClipRects->rects.arcl[0].left,
|
|
pClipRects->rects.arcl[0].top,
|
|
pClipRects->rects.arcl[0].right,
|
|
pClipRects->rects.arcl[0].bottom));
|
|
|
|
// 2 field flag bytes.
|
|
pOrder = OA_AllocOrderMem(ppdev, MAX_ORDER_SIZE(pClipRects->rects.c, 2,
|
|
MAX_MEMBLT_FIELD_SIZE));
|
|
if (pOrder != NULL) {
|
|
BYTE *pControlFlags = pOrder->OrderData;
|
|
BYTE *pBuffer = pControlFlags + 1;
|
|
PUINT32_UA pFieldFlags;
|
|
|
|
// Direct-encode the primary order fields. 2 field flag bytes.
|
|
*pControlFlags = TS_STANDARD;
|
|
OE2_EncodeOrderType(pControlFlags, &pBuffer, TS_ENC_MEMBLT_R2_ORDER);
|
|
pFieldFlags = (PUINT32_UA)pBuffer;
|
|
*pFieldFlags = 0;
|
|
pBuffer += 2;
|
|
if (pClipRects->rects.c != 0)
|
|
OE2_EncodeBounds(pControlFlags, &pBuffer,
|
|
&pClipRects->rects.arcl[0]);
|
|
|
|
if (oeTempMemBlt.Common.cacheId != PrevMemBlt.Common.cacheId) {
|
|
PrevMemBlt.Common.cacheId = oeTempMemBlt.Common.cacheId;
|
|
*((UNALIGNED unsigned short *)pBuffer) =
|
|
oeTempMemBlt.Common.cacheId;
|
|
pBuffer += sizeof(unsigned short);
|
|
*pFieldFlags |= 0x0001;
|
|
}
|
|
|
|
// Simultaneously determine if each of the coordinate fields has changed,
|
|
// whether we can use delta coordinates, and save changed fields.
|
|
NumFields1 = NumFields2 = 0;
|
|
bUseDeltaCoords = TRUE;
|
|
|
|
Delta = (short)(oeTempMemBlt.Common.nLeftRect -
|
|
PrevMemBlt.Common.nLeftRect);
|
|
if (Delta) {
|
|
PrevMemBlt.Common.nLeftRect = oeTempMemBlt.Common.nLeftRect;
|
|
if (Delta != (short)(char)Delta)
|
|
bUseDeltaCoords = FALSE;
|
|
pBuffer[NumFields1] = (char)Delta;
|
|
NormalEncoding1[NumFields1] = (short)oeTempMemBlt.Common.nLeftRect;
|
|
NumFields1++;
|
|
*pFieldFlags |= 0x0002;
|
|
}
|
|
|
|
Delta = (short)(oeTempMemBlt.Common.nTopRect -
|
|
PrevMemBlt.Common.nTopRect);
|
|
if (Delta) {
|
|
PrevMemBlt.Common.nTopRect = oeTempMemBlt.Common.nTopRect;
|
|
if (Delta != (short)(char)Delta)
|
|
bUseDeltaCoords = FALSE;
|
|
pBuffer[NumFields1] = (char)Delta;
|
|
NormalEncoding1[NumFields1] = (short)oeTempMemBlt.Common.nTopRect;
|
|
NumFields1++;
|
|
*pFieldFlags |= 0x0004;
|
|
}
|
|
|
|
Delta = (short)(oeTempMemBlt.Common.nWidth - PrevMemBlt.Common.nWidth);
|
|
if (Delta) {
|
|
PrevMemBlt.Common.nWidth = oeTempMemBlt.Common.nWidth;
|
|
if (Delta != (short)(char)Delta)
|
|
bUseDeltaCoords = FALSE;
|
|
pBuffer[NumFields1] = (char)Delta;
|
|
NormalEncoding1[NumFields1] = (short)oeTempMemBlt.Common.nWidth;
|
|
NumFields1++;
|
|
*pFieldFlags |= 0x0008;
|
|
}
|
|
|
|
Delta = (short)(oeTempMemBlt.Common.nHeight -
|
|
PrevMemBlt.Common.nHeight);
|
|
if (Delta) {
|
|
PrevMemBlt.Common.nHeight = oeTempMemBlt.Common.nHeight;
|
|
if (Delta != (short)(char)Delta)
|
|
bUseDeltaCoords = FALSE;
|
|
pBuffer[NumFields1] = (char)Delta;
|
|
NormalEncoding1[NumFields1] = (short)oeTempMemBlt.Common.nHeight;
|
|
NumFields1++;
|
|
*pFieldFlags |= 0x0010;
|
|
}
|
|
|
|
Delta = (short)(oeTempMemBlt.Common.nXSrc - PrevMemBlt.Common.nXSrc);
|
|
if (Delta) {
|
|
PrevMemBlt.Common.nXSrc = oeTempMemBlt.Common.nXSrc;
|
|
if (Delta != (short)(char)Delta)
|
|
bUseDeltaCoords = FALSE;
|
|
DeltaEncoding2[NumFields2] = (char)Delta;
|
|
NormalEncoding2[NumFields2] = (short)oeTempMemBlt.Common.nXSrc;
|
|
NumFields2++;
|
|
*pFieldFlags |= 0x0040;
|
|
}
|
|
|
|
Delta = (short)(oeTempMemBlt.Common.nYSrc - PrevMemBlt.Common.nYSrc);
|
|
if (Delta) {
|
|
PrevMemBlt.Common.nYSrc = oeTempMemBlt.Common.nYSrc;
|
|
if (Delta != (short)(char)Delta)
|
|
bUseDeltaCoords = FALSE;
|
|
DeltaEncoding2[NumFields2] = (char)Delta;
|
|
NormalEncoding2[NumFields2] = (short)oeTempMemBlt.Common.nYSrc;
|
|
NumFields2++;
|
|
*pFieldFlags |= 0x0080;
|
|
}
|
|
|
|
// Begin copying the final coordinates to the order.
|
|
if (bUseDeltaCoords) {
|
|
*pControlFlags |= TS_DELTA_COORDINATES;
|
|
pBuffer += NumFields1;
|
|
}
|
|
else {
|
|
memcpy(pBuffer, NormalEncoding1, NumFields1 * sizeof(short));
|
|
pBuffer += NumFields1 * sizeof(short);
|
|
}
|
|
|
|
// Copy the intervening bRop field.
|
|
if (oeTempMemBlt.Common.bRop != PrevMemBlt.Common.bRop) {
|
|
PrevMemBlt.Common.bRop = oeTempMemBlt.Common.bRop;
|
|
*pBuffer++ = (BYTE)oeTempMemBlt.Common.bRop;
|
|
*pFieldFlags |= 0x0020;
|
|
}
|
|
|
|
// Copy the src coords.
|
|
if (bUseDeltaCoords) {
|
|
memcpy(pBuffer, DeltaEncoding2, NumFields2);
|
|
pBuffer += NumFields2;
|
|
}
|
|
else {
|
|
memcpy(pBuffer, NormalEncoding2, NumFields2 * sizeof(short));
|
|
pBuffer += NumFields2 * sizeof(short);
|
|
}
|
|
|
|
// Finish with the cache index.
|
|
if (oeTempMemBlt.Common.cacheIndex != PrevMemBlt.Common.cacheIndex) {
|
|
PrevMemBlt.Common.cacheIndex = oeTempMemBlt.Common.cacheIndex;
|
|
*((UNALIGNED unsigned short *)pBuffer) =
|
|
oeTempMemBlt.Common.cacheIndex;
|
|
pBuffer += sizeof(unsigned short);
|
|
*pFieldFlags |= 0x0100;
|
|
}
|
|
|
|
pOrder->OrderLength = (unsigned)(pBuffer - pOrder->OrderData);
|
|
|
|
// See if we can save sending the order field bytes.
|
|
pOrder->OrderLength -= OE2_CheckTwoZeroFlagBytes(pControlFlags,
|
|
(BYTE *)pFieldFlags,
|
|
(unsigned)(pBuffer - (BYTE *)pFieldFlags - 2));
|
|
|
|
INC_OUTCOUNTER(OUT_MEMBLT_ORDER);
|
|
ADD_INCOUNTER(IN_MEMBLT_BYTES, pOrder->OrderLength);
|
|
OA_AppendToOrderList(pOrder);
|
|
|
|
// Flush the order.
|
|
if (pClipRects->rects.c < 2)
|
|
rc = TRUE;
|
|
else
|
|
rc = OEEmitReplayOrders(ppdev, 2, pClipRects);
|
|
}
|
|
else {
|
|
TRC_ERR((TB,"Failed alloc MemBlt order on heap"));
|
|
INC_OUTCOUNTER(OUT_BITBLT_SDA_HEAPALLOCFAILED);
|
|
rc = FALSE;
|
|
}
|
|
|
|
DC_END_FN();
|
|
return rc;
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
// OEAllocAndSendMem3BltOrder
|
|
//
|
|
// Performs steps needed to launch a Mem3Blt order. Assumes the order data
|
|
// has been placed in oeTempMemBlt.
|
|
/****************************************************************************/
|
|
BOOL OEAllocAndSendMem3BltOrder(PDD_PDEV ppdev, OE_ENUMRECTS *pClipRects)
|
|
{
|
|
BOOL rc;
|
|
PINT_ORDER pOrder;
|
|
|
|
DC_BEGIN_FN("OEAllocAndSendMem3BltOrder");
|
|
|
|
TRC_NRM((TB,"Mem3Blt: Dst=(%d,%d),w=%d,h=%d, Src=(%d,%d), "
|
|
"clip=%s (%d,%d,%d,%d)",
|
|
oeTempMemBlt.Common.nLeftRect,
|
|
oeTempMemBlt.Common.nTopRect,
|
|
oeTempMemBlt.Common.nWidth,
|
|
oeTempMemBlt.Common.nHeight,
|
|
oeTempMemBlt.Common.nXSrc,
|
|
oeTempMemBlt.Common.nYSrc,
|
|
pClipRects->rects.c == 0 ? "n/a" : "present",
|
|
pClipRects->rects.arcl[0].left,
|
|
pClipRects->rects.arcl[0].top,
|
|
pClipRects->rects.arcl[0].right,
|
|
pClipRects->rects.arcl[0].bottom));
|
|
|
|
// 3 field flag bytes.
|
|
pOrder = OA_AllocOrderMem(ppdev, MAX_ORDER_SIZE(pClipRects->rects.c,
|
|
3, MAX_MEM3BLT_FIELD_SIZE));
|
|
if (pOrder != NULL) {
|
|
// Slow-field-encode the order with the first clip rect
|
|
// (if present).
|
|
pOrder->OrderLength = OE2_EncodeOrder(pOrder->OrderData,
|
|
TS_ENC_MEM3BLT_R2_ORDER, NUM_MEM3BLT_FIELDS,
|
|
(BYTE *)&oeTempMemBlt, (BYTE *)&PrevMem3Blt, etable_3C,
|
|
(pClipRects->rects.c == 0 ? NULL :
|
|
&pClipRects->rects.arcl[0]));
|
|
|
|
INC_OUTCOUNTER(OUT_MEM3BLT_ORDER);
|
|
ADD_INCOUNTER(IN_MEM3BLT_BYTES, pOrder->OrderLength);
|
|
OA_AppendToOrderList(pOrder);
|
|
|
|
// Flush the order.
|
|
if (pClipRects->rects.c < 2)
|
|
rc = TRUE;
|
|
else
|
|
rc = OEEmitReplayOrders(ppdev, 3, pClipRects);
|
|
}
|
|
else {
|
|
TRC_ERR((TB,"Failed alloc Mem3Blt order on heap"));
|
|
INC_OUTCOUNTER(OUT_BITBLT_SDA_HEAPALLOCFAILED);
|
|
rc = FALSE;
|
|
}
|
|
|
|
DC_END_FN();
|
|
return rc;
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
// OETileBitBltOrder
|
|
//
|
|
// Divides a single large BitBlt order into a series of small, "tiled"
|
|
// BitBlt orders, each of which is added to the order queue. Returns FALSE
|
|
// on failure (alloc failure on offscr target).
|
|
/****************************************************************************/
|
|
BOOL OETileBitBltOrder(
|
|
PDD_PDEV ppdev,
|
|
PPOINTL pptlSrc,
|
|
RECTL *pBounds,
|
|
unsigned OrderType,
|
|
unsigned ColorTableIndex,
|
|
PMEMBLT_ORDER_EXTRA_INFO pExInfo,
|
|
OE_ENUMRECTS *pClipRects)
|
|
{
|
|
int srcRight, srcBottom;
|
|
int xTile, yTile;
|
|
unsigned xFirstTile, yFirstTile;
|
|
unsigned Width, Height;
|
|
BOOL rc = TRUE;
|
|
RECTL SrcRect, DestRect;
|
|
OE_ENUMRECTS TileClipRects;
|
|
POINTL SrcPt;
|
|
SIZEL SrcSize;
|
|
#ifdef PERF_SPOILING
|
|
BOOL bDiscardTile;
|
|
#endif
|
|
|
|
DC_BEGIN_FN("OETileBitBltOrder");
|
|
|
|
Width = pBounds->right - pBounds->left;
|
|
Height = pBounds->bottom - pBounds->top;
|
|
SrcSize = pExInfo->pSource->sizlBitmap;
|
|
|
|
// Find out what the tile size and ID will be.
|
|
pExInfo->TileID = SBC_DDQueryBitmapTileSize(SrcSize.cx, SrcSize.cy,
|
|
pptlSrc, Width, Height);
|
|
pExInfo->TileSize = (unsigned)(SBC_CACHE_0_DIMENSION << pExInfo->TileID);
|
|
|
|
// Tile the order. If an individual tile fails to go as an order,
|
|
// it's up to OEAddTiledBitBltOrder to add the tile's destination as
|
|
// screen data.
|
|
SrcPt = *pptlSrc;
|
|
TRC_NRM((TB, "Tiling order"));
|
|
TRC_DBG((TB, "l=%u, t=%u, w=%u, h=%u, tile=%u",
|
|
SrcPt.x, SrcPt.y, Width, Height, pExInfo->TileSize));
|
|
xFirstTile = SrcPt.x - (SrcPt.x & (pExInfo->TileSize - 1));
|
|
yFirstTile = SrcPt.y - (SrcPt.y & (pExInfo->TileSize - 1));
|
|
TRC_DBG((TB, "xStart=%hd, yStart=%hd", xFirstTile, yFirstTile));
|
|
|
|
// Note we are creating exclusive bounds now.
|
|
srcRight = (int)(SrcPt.x + Width);
|
|
srcBottom = (int)(SrcPt.y + Height);
|
|
|
|
// Enumerate all tiles, left-to-right, top-to-bottom, and send
|
|
// Cache Bitmap and Mem(3)Blt orders for each tile.
|
|
for (yTile = yFirstTile; (yTile < srcBottom && yTile < SrcSize.cy);
|
|
yTile += pExInfo->TileSize) {
|
|
for (xTile = xFirstTile; (xTile < srcRight && xTile < SrcSize.cx);
|
|
xTile += pExInfo->TileSize) {
|
|
// SrcRect and DestRect are exclusive rects.
|
|
SrcRect.left = SrcPt.x;
|
|
SrcRect.top = SrcPt.y;
|
|
SrcRect.right = SrcRect.left + Width;
|
|
SrcRect.bottom = SrcRect.top + Height;
|
|
DestRect.left = pBounds->left;
|
|
DestRect.top = pBounds->top;
|
|
|
|
// Intersect source and tile rects, and set up destination rect
|
|
// accordingly.
|
|
TRC_DBG((TB, "pre: xTile(%d) yTile(%d) src.left(%d) src.top(%d)",
|
|
xTile, yTile, SrcRect.left, SrcRect.top));
|
|
|
|
// Modify srcRect to contain the tile's left and top if they are
|
|
// within the full blt rect. Also move the destRect left and top
|
|
// out the same amount to match.
|
|
if (xTile > SrcRect.left) {
|
|
DestRect.left += (xTile - SrcRect.left);
|
|
SrcRect.left = xTile;
|
|
}
|
|
if (yTile > SrcRect.top) {
|
|
DestRect.top += (yTile - SrcRect.top);
|
|
SrcRect.top = yTile;
|
|
}
|
|
|
|
TRC_DBG((TB, "post: xTile(%d) yTile(%d) src.left(%d) src.top(%d)",
|
|
xTile, yTile, SrcRect.left, SrcRect.top));
|
|
|
|
// Find the right and bottom of the tile, making sure not to
|
|
// overrun the actual blt boundaries and the screen boundaries,
|
|
// and remaining in exclusive coords.
|
|
SrcRect.right = min((unsigned)SrcRect.right, (unsigned)xTile +
|
|
pExInfo->TileSize);
|
|
SrcRect.bottom = min((unsigned)SrcRect.bottom, (unsigned)yTile +
|
|
pExInfo->TileSize);
|
|
DestRect.right = DestRect.left + (SrcRect.right - SrcRect.left);
|
|
DestRect.bottom = DestRect.top + (SrcRect.bottom - SrcRect.top);
|
|
|
|
// Now that we have the exclusive dest rect, find out if the
|
|
// overall DrvBitBlt() clip rects intersect with the tile. If not,
|
|
// no need to either cache the tile or send the order.
|
|
|
|
TileClipRects.rects.c = 0;
|
|
|
|
#ifndef PERF_SPOILING
|
|
if (pClipRects->rects.c == 0 ||
|
|
OEGetIntersectionsWithClipRects(&DestRect, pClipRects,
|
|
&TileClipRects)) {
|
|
#else
|
|
// Normally, we send the tile to the client to be rendered and
|
|
// added to the bitmap cache. However, there are two cases where
|
|
// this can be avoided:
|
|
// 1) The tile doesn't intersect with the current clipping-region.
|
|
// In this case, the bitmap won't paint anyway.
|
|
// 2) The tile lies completely within our SDA bounds, meaning that
|
|
// it is already being sent as SDA. In this case, sending it
|
|
// as a cached-item would just be sending it twice over the wire!
|
|
if (pClipRects->rects.c == 0) {
|
|
bDiscardTile = pExInfo->bIsPrimarySurface &&
|
|
OEIsSDAIncluded(&DestRect,1);
|
|
} else {
|
|
if (OEGetIntersectionsWithClipRects(&DestRect,
|
|
pClipRects,
|
|
&TileClipRects)) {
|
|
|
|
bDiscardTile = pExInfo->bIsPrimarySurface &&
|
|
OEIsSDAIncluded(&(TileClipRects.rects.arcl[0]), TileClipRects.rects.c);
|
|
} else {
|
|
bDiscardTile = TRUE;
|
|
}
|
|
}
|
|
|
|
if (!bDiscardTile) {
|
|
#endif //PERF_SPOILING
|
|
// First step is to make sure the source data tile is in the
|
|
// bitmap cache. If we fail this, we simply add the
|
|
// intersected clip rects to the SDA.
|
|
if (SBC_CacheBitmapTile(ppdev, pExInfo, &SrcRect, &DestRect)) {
|
|
// nXSrc and nYSrc are the source within the tile.
|
|
// Since we've cached tiles sitting at TileSize
|
|
// boundaries, we simply use the offset into the tile by
|
|
// taking the modulo.
|
|
oeTempMemBlt.Common.nXSrc = SrcRect.left &
|
|
(pExInfo->TileSize - 1);
|
|
oeTempMemBlt.Common.nYSrc = SrcRect.top &
|
|
(pExInfo->TileSize - 1);
|
|
oeTempMemBlt.Common.nLeftRect = DestRect.left;
|
|
oeTempMemBlt.Common.nTopRect = DestRect.top;
|
|
oeTempMemBlt.Common.nWidth = SrcRect.right - SrcRect.left;
|
|
oeTempMemBlt.Common.nHeight = SrcRect.bottom - SrcRect.top;
|
|
oeTempMemBlt.Common.cacheId = (UINT16)
|
|
((ColorTableIndex << 8) | pExInfo->CacheID);
|
|
oeTempMemBlt.Common.cacheIndex =
|
|
(UINT16)pExInfo->CacheIndex;
|
|
|
|
if (OrderType == TS_ENC_MEMBLT_R2_ORDER)
|
|
rc = OEDirectEncodeMemBlt(ppdev, &TileClipRects);
|
|
else
|
|
rc = OEAllocAndSendMem3BltOrder(ppdev, &TileClipRects);
|
|
|
|
if (!rc) {
|
|
if (oeLastDstSurface == NULL) {
|
|
// If this is a screen target, send screen data and
|
|
// continue trying to create tiles. Reset the
|
|
// return value to TRUE to indicate the tile
|
|
// was handled.
|
|
OEClipAndAddScreenDataAreaByIntersectRects(
|
|
&DestRect, &TileClipRects);
|
|
rc = TRUE;
|
|
}
|
|
else {
|
|
// On failure for offscreen rendering, we forget
|
|
// the rest of the tiles and return FALSE.
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
TRC_ERR((TB, "Failed cache bitmap order"));
|
|
INC_OUTCOUNTER(OUT_BITBLT_SDA_HEAPALLOCFAILED);
|
|
|
|
// If this is a screen target, send screen data, but still
|
|
// return TRUE to indicate we handled the tile. If an
|
|
// offscreen target, return FALSE to force the target
|
|
// surface to become uncacheable.
|
|
if (oeLastDstSurface == NULL) {
|
|
OEClipAndAddScreenDataAreaByIntersectRects(&DestRect,
|
|
&TileClipRects);
|
|
}
|
|
else {
|
|
// On failure for offscreen rendering, we forget
|
|
// the rest of the tiles and return FALSE.
|
|
rc = FALSE;
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
// We still succeed here -- the tile was handled OK.
|
|
TRC_NRM((TB,"Dropping tile - no intersections w/clip rects"));
|
|
}
|
|
}
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
DC_END_FN();
|
|
return rc;
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
// OEEncodeMemBlt
|
|
//
|
|
// Performs all encoding steps required to encode a Mem(3)Blt order, then adds
|
|
// to order list. Returns FALSE if the order needs to be added to the screen
|
|
// data area.
|
|
/****************************************************************************/
|
|
BOOL RDPCALL OEEncodeMemBlt(
|
|
RECTL *pBounds,
|
|
MEMBLT_ORDER_EXTRA_INFO *pMemBltExtraInfo,
|
|
unsigned OrderType,
|
|
unsigned SrcSurfaceId,
|
|
BYTE Rop3,
|
|
POINTL *pptlSrc,
|
|
POINTL *pptlBrush,
|
|
BRUSHOBJ *pbo,
|
|
PDD_PDEV ppdev,
|
|
OE_ENUMRECTS *pClipRects)
|
|
{
|
|
BOOL rc;
|
|
unsigned ColorTableIndex;
|
|
PINT_ORDER pOrder;
|
|
MEMBLT_COMMON *pCommon;
|
|
MEM3BLT_R2_ORDER *pMem3Blt;
|
|
|
|
DC_BEGIN_FN("OEEncodeMemBlt");
|
|
|
|
// Make sure we can cache the blt -- caching must be enabled too.
|
|
if (SrcSurfaceId == CH_KEY_UNCACHABLE) {
|
|
rc = SBC_DDIsMemScreenBltCachable(pMemBltExtraInfo);
|
|
}
|
|
else {
|
|
// The surface bitmap bits already cached at the client.
|
|
// There is no need to send the bits
|
|
rc = (sbcEnabled & SBC_BITMAP_CACHE_ENABLED);
|
|
}
|
|
|
|
if (rc) {
|
|
// We have to cache the color table first.
|
|
if (SBC_SendCacheColorTableOrder(ppdev, &ColorTableIndex)) {
|
|
TRC_ASSERT((ColorTableIndex < SBC_NUM_COLOR_TABLE_CACHE_ENTRIES),
|
|
(TB, "Invalid ColorTableIndex(%u)", ColorTableIndex));
|
|
|
|
// Set up only the ROP common MemBlt field here; the rest need
|
|
// to wait until we have tile information (if retrived).
|
|
oeTempMemBlt.Common.bRop = Rop3;
|
|
|
|
if (OrderType == TS_ENC_MEMBLT_R2_ORDER) {
|
|
TRC_NRM((TB, "MemBlt dx %d dy %d w %d h %d sx %d sy %d "
|
|
"rop %04X", pBounds->left, pBounds->top,
|
|
pBounds->right - pBounds->left,
|
|
pBounds->bottom - pBounds->top,
|
|
pptlSrc->x, pptlSrc->y, Rop3));
|
|
}
|
|
else {
|
|
POE_BRUSH_DATA pCurrentBrush;
|
|
|
|
// For Mem3Blt, create the extra brush-related fields
|
|
// in oeTempMemBlt so it will be set up properly
|
|
// when we encode the order later.
|
|
|
|
// Check that the brush pattern is simple.
|
|
if (OECheckBrushIsSimple(ppdev, pbo, &pCurrentBrush)) {
|
|
// The protocol brush origin is the point on the screen
|
|
// where we want the brush to start being drawn from
|
|
// (tiling where necessary).
|
|
oeTempMemBlt.BrushOrgX = pptlBrush->x;
|
|
oeTempMemBlt.BrushOrgY = pptlBrush->y;
|
|
OEClipPoint((PPOINTL)&oeTempMemBlt.BrushOrgX);
|
|
|
|
// Pattern data.
|
|
oeTempMemBlt.BackColor = pCurrentBrush->back;
|
|
oeTempMemBlt.ForeColor = pCurrentBrush->fore;
|
|
|
|
// Realized brush data.
|
|
oeTempMemBlt.BrushStyle = pCurrentBrush->style;
|
|
oeTempMemBlt.BrushHatch = pCurrentBrush->hatch;
|
|
memcpy(oeTempMemBlt.BrushExtra, pCurrentBrush->brushData,
|
|
sizeof(oeTempMemBlt.BrushExtra));
|
|
|
|
TRC_NRM((TB, "Mem3Blt brush %02X %02X dx %d dy %d "
|
|
"w %d h %d sx %d sy %d rop %04X",
|
|
oeTempMemBlt.BrushStyle,
|
|
oeTempMemBlt.BrushHatch,
|
|
oeTempMemBlt.Common.nLeftRect,
|
|
oeTempMemBlt.Common.nTopRect,
|
|
oeTempMemBlt.Common.nWidth,
|
|
oeTempMemBlt.Common.nHeight,
|
|
oeTempMemBlt.Common.nXSrc,
|
|
oeTempMemBlt.Common.nYSrc,
|
|
oeTempMemBlt.Common.bRop));
|
|
}
|
|
else {
|
|
TRC_NRM((TB, "Mem3Blt brush is not simple"));
|
|
INC_OUTCOUNTER(OUT_BITBLT_SDA_M3BCOMPLEXBRUSH);
|
|
rc = FALSE;
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
|
|
// Send the order to be cached.
|
|
if (SrcSurfaceId == CH_KEY_UNCACHABLE) {
|
|
rc = OETileBitBltOrder(ppdev, pptlSrc, pBounds, OrderType,
|
|
ColorTableIndex, pMemBltExtraInfo, pClipRects);
|
|
}
|
|
else {
|
|
// Set up the order fields not set up above. We rely on the
|
|
// Common field in MEMBLT_ORDER and MEM3BLT_ORDER orders
|
|
// being in the same position.
|
|
oeTempMemBlt.Common.nLeftRect = pBounds->left;
|
|
oeTempMemBlt.Common.nTopRect = pBounds->top;
|
|
oeTempMemBlt.Common.nWidth = pBounds->right - pBounds->left;
|
|
oeTempMemBlt.Common.nHeight = pBounds->bottom - pBounds->top;
|
|
|
|
// Store the source bitmap origin.
|
|
oeTempMemBlt.Common.nXSrc = pptlSrc->x;
|
|
oeTempMemBlt.Common.nYSrc = pptlSrc->y;
|
|
|
|
// Store the color table cache index and cache ID.
|
|
// The source bitmap is at client offscreen bitmap, we
|
|
// use 0xff as bitmap ID to indicate that. The cache index
|
|
// is used to indicate the client offscreen bitmap Id.
|
|
oeTempMemBlt.Common.cacheId = (UINT16)((ColorTableIndex << 8) |
|
|
TS_BITMAPCACHE_SCREEN_ID);
|
|
oeTempMemBlt.Common.cacheIndex = (UINT16)SrcSurfaceId;
|
|
|
|
if (OrderType == TS_ENC_MEMBLT_R2_ORDER)
|
|
rc = OEDirectEncodeMemBlt(ppdev, pClipRects);
|
|
else
|
|
rc = OEAllocAndSendMem3BltOrder(ppdev, pClipRects);
|
|
}
|
|
}
|
|
else {
|
|
TRC_ALT((TB, "Unable to send color table for MemBlt"));
|
|
INC_OUTCOUNTER(OUT_BITBLT_SDA_NOCOLORTABLE);
|
|
rc = FALSE;
|
|
}
|
|
}
|
|
else {
|
|
TRC_NRM((TB, "MemBlt is not cachable"));
|
|
INC_OUTCOUNTER(OUT_BITBLT_SDA_MBUNCACHEABLE);
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
DC_END_FN();
|
|
return rc;
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
// OEIntersectScrBltWithSDA
|
|
//
|
|
// Intersects a ScrBlt order (given the source point and exclusive dest rect)
|
|
// with the current SDA. Returns FALSE if the entire ScrBlt order should be
|
|
// spoiled because its source rect is entirely within the current SDA.
|
|
//
|
|
// Algorithm notes:
|
|
//
|
|
// OLD (ORIGINAL) SCRBLT SCHEME
|
|
// ----------------------------
|
|
// If the source rectangle intersects the current SDA then the src rectangle
|
|
// is modified so that no there is no intersection with the SDA, and the dst
|
|
// rectangle adjusted accordingly (this is the theory - in practice the
|
|
// operation remains the same and we just adjust the dst clip rectangle).
|
|
// The destination area that is removed is added into the SDA. The code
|
|
// works, but can result in more screen data being sent than is required.
|
|
// E.g. for the following operation:
|
|
//
|
|
// SSSSSS DDDDDD | S = src rect
|
|
// SSSSSS -> DDDDDD | D = dst rect
|
|
// SSSSSS DDDDDD | x = SDA overlap
|
|
// SxSSSS DDDDDD |
|
|
//
|
|
// The bottom edge of the blt is trimmed off, and the corresponding
|
|
// destination area added into the SDA.
|
|
//
|
|
// SSSSSS DDDDDD
|
|
// SSSSSS -> DDDDDD
|
|
// SSSSSS DDDDDD
|
|
// xxxxxx
|
|
//
|
|
// NEW SCRBLT SCHEME
|
|
// -----------------
|
|
// The new scheme does not modify the blt rectangles, and just maps the SDA
|
|
// overlap to the destination rect and adds that area back into the SDA.
|
|
// E.g. (as above):
|
|
//
|
|
// SSSSSS DDDDDD
|
|
// SSSSSS -> DDDDDD
|
|
// SSSSSS DDDDDD
|
|
// SxSSSS DDDDDD
|
|
//
|
|
// The blt operation remains the same, but the overlap area is mapped to the
|
|
// destination rectangle and added into the SDA.
|
|
//
|
|
// SSSSSS DDDDDD
|
|
// SSSSSS -> DDDDDD
|
|
// SSSSSS DDDDDD
|
|
// SxSSSS DxDDDD
|
|
//
|
|
// This scheme results in a smaller SDA area. However, this scheme does blt
|
|
// potentially invalid data to the destination - which may briefly be visible
|
|
// at the remote machine (because orders are replayed before Screen Data).
|
|
// This has not (yet) proved to be a problem. The main benefit of the new
|
|
// scheme vs. the old is when scrolling an area that includes a small SDA:
|
|
//
|
|
// new old
|
|
// AAAAAAAA AAAAAAAA AAAAAAAA
|
|
// AAAAAAAA AAAxAAAA xxxxxxxx
|
|
// AAAAAAAA scroll up 3 times -> AAAxAAAA xxxxxxxx
|
|
// AAAAAAAA AAAxAAAA xxxxxxxx
|
|
// AAAxAAAA AAAxAAAA xxxxxxxx
|
|
/****************************************************************************/
|
|
BOOL OEIntersectScrBltWithSDA(
|
|
PPOINTL pSrcPt,
|
|
RECTL *pDestRect,
|
|
OE_ENUMRECTS *pClipRects)
|
|
{
|
|
BOOL rc = TRUE;
|
|
unsigned NumSDA;
|
|
unsigned totalBounds;
|
|
unsigned i, j;
|
|
unsigned NumClipRects;
|
|
int dx;
|
|
int dy;
|
|
RECTL SrcRect;
|
|
RECTL TempRect;
|
|
RECTL InvalidDestRect;
|
|
RECTL SDARects[BA_MAX_ACCUMULATED_RECTS];
|
|
|
|
DC_BEGIN_FN("OEIntersectScrBltWithSDA");
|
|
|
|
// Calculate the full source rect (exclusive coords).
|
|
SrcRect.left = pSrcPt->x;
|
|
SrcRect.top = pSrcPt->y;
|
|
SrcRect.right = SrcRect.left + pDestRect->right - pDestRect->left;
|
|
SrcRect.bottom = SrcRect.top + pDestRect->bottom - pDestRect->top;
|
|
|
|
// Calculate the offset from the src to the dest.
|
|
dx = pDestRect->left - SrcRect.left;
|
|
dy = pDestRect->top - SrcRect.top;
|
|
|
|
NumClipRects = pClipRects->rects.c;
|
|
|
|
// Get the current SDA rects.
|
|
BA_QueryBounds(SDARects, &NumSDA);
|
|
|
|
for (i = 0; i < NumSDA; i++) {
|
|
if (SrcRect.left < SDARects[i].left ||
|
|
SrcRect.right > SDARects[i].right ||
|
|
SrcRect.top < SDARects[i].top ||
|
|
SrcRect.bottom > SDARects[i].bottom) {
|
|
// Intersect the src rect with the SDA rect and offset to
|
|
// get the invalid dest rect.
|
|
InvalidDestRect.left = max(SrcRect.left, SDARects[i].left) + dx;
|
|
InvalidDestRect.right = min(SrcRect.right, SDARects[i].right) + dx;
|
|
InvalidDestRect.top = max(SrcRect.top, SDARects[i].top) + dy;
|
|
InvalidDestRect.bottom = min(SrcRect.bottom,
|
|
SDARects[i].bottom) + dy;
|
|
|
|
// Walk through each of the dest clip rects (or the entire
|
|
// dest rect if there are no clip rects), intersecting with the
|
|
// invalid dest rect, and adding the intersections into the SDA.
|
|
if (NumClipRects == 0) {
|
|
// DestRect is already in inclusive coords.
|
|
TempRect.left = max(InvalidDestRect.left,
|
|
pDestRect->left);
|
|
TempRect.top = max(InvalidDestRect.top,
|
|
pDestRect->top);
|
|
TempRect.right = min(InvalidDestRect.right,
|
|
pDestRect->right);
|
|
TempRect.bottom = min(InvalidDestRect.bottom,
|
|
pDestRect->bottom);
|
|
|
|
// If there is a 3-way intersection, add the rect to the SDA.
|
|
if (TempRect.left < TempRect.right &&
|
|
TempRect.top < TempRect.bottom)
|
|
BA_AddScreenData(&TempRect);
|
|
}
|
|
else {
|
|
// Clip rects are in exclusive coords, we have to take
|
|
// this into account when getting the intersection.
|
|
for (j = 0; j < NumClipRects; j++) {
|
|
TempRect.left = max(InvalidDestRect.left,
|
|
pClipRects->rects.arcl[j].left);
|
|
TempRect.top = max(InvalidDestRect.top,
|
|
pClipRects->rects.arcl[j].top);
|
|
TempRect.right = min(InvalidDestRect.right,
|
|
pClipRects->rects.arcl[j].right);
|
|
TempRect.bottom = min(InvalidDestRect.bottom,
|
|
pClipRects->rects.arcl[j].bottom);
|
|
|
|
// If there is a 3-way intersection, add the rect to the
|
|
// SDA.
|
|
if (TempRect.left < TempRect.right &&
|
|
TempRect.top < TempRect.bottom)
|
|
BA_AddScreenData(&TempRect);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
// The src of the ScrBlt is completely within the SDA. We
|
|
// must add each of the dest clip rects (or the entire
|
|
// dest rect if there are no clip rects) into the SDA and
|
|
// spoil the ScrBlt.
|
|
TRC_NRM((TB, "ScrBlt src within SDA - spoil it"));
|
|
|
|
if (NumClipRects == 0) {
|
|
// We can just add DestRect to SDA.
|
|
InvalidDestRect = *pDestRect;
|
|
BA_AddScreenData(&InvalidDestRect);
|
|
}
|
|
else {
|
|
for (j = 0; j < NumClipRects; j++) {
|
|
InvalidDestRect = pClipRects->rects.arcl[j];
|
|
BA_AddScreenData(&InvalidDestRect);
|
|
}
|
|
}
|
|
|
|
rc = FALSE;
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
DC_END_FN();
|
|
return rc;
|
|
}
|
|
|
|
/****************************************************************************/
|
|
// OEDeviceBitmapCachable
|
|
//
|
|
// Check if we can cache this device bitmap in the client side offscreen
|
|
// bitmap memory
|
|
/****************************************************************************/
|
|
BOOL RDPCALL OEDeviceBitmapCachable(PDD_PDEV ppdev,SIZEL sizl, ULONG iFormat)
|
|
{
|
|
BOOL rc = FALSE;
|
|
unsigned bitmapSize, minBitmapSize;
|
|
|
|
DC_BEGIN_FN("OEDeviceBitmapCachable");
|
|
|
|
// Return 0 if client doesn't support offscreen rendering
|
|
if (pddShm != NULL && sbcOffscreenBitmapCacheHandle != NULL &&
|
|
pddShm->sbc.offscreenCacheInfo.supportLevel > TS_OFFSCREEN_DEFAULT) {
|
|
|
|
// We only support device bitmaps that are the same color depth
|
|
// as our display.
|
|
// Actually, those are the only kind GDI will ever call us with,
|
|
// but we may as well check. Note that this implies you'll never
|
|
// get a crack at 1bpp bitmaps.
|
|
if (iFormat == ppdev->iBitmapFormat) {
|
|
// Get the bitmap size
|
|
// The assumption here is that iFormat is > 1BPP, i << iFormat
|
|
// gives the actual bits per pel. bitmapSize is in bytes.
|
|
|
|
if (iFormat < 5) {
|
|
bitmapSize = sizl.cx * sizl.cy * (1 << iFormat) / 8;
|
|
minBitmapSize = MIN_OFFSCREEN_BITMAP_PIXELS * (1 << iFormat) / 8;
|
|
}
|
|
else if (iFormat == 5) {
|
|
bitmapSize = sizl.cx * sizl.cy * 24 / 8;
|
|
minBitmapSize = MIN_OFFSCREEN_BITMAP_PIXELS * 24 / 8;
|
|
|
|
}
|
|
else if (iFormat == 6) {
|
|
bitmapSize = sizl.cx * sizl.cy * 32 / 8;
|
|
minBitmapSize = MIN_OFFSCREEN_BITMAP_PIXELS * 32 / 8;
|
|
}
|
|
else {
|
|
minBitmapSize = 0;
|
|
TRC_NRM((TB, "Bitmap format not supported"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
// From Winbench99 Business Graphics benchmark, we found
|
|
// creating offscreen bitmaps of 2K or smaller does not
|
|
// improve our bandwidth. This parameter needs to be highly
|
|
// tuned.
|
|
// We also don't want to cache any cursor bitmaps for offscreen
|
|
// the maximum cursor size is 32x32, which is less than the
|
|
// minBitmapSize.
|
|
if (bitmapSize > minBitmapSize) {
|
|
SURFOBJ *psoDevice;
|
|
SIZEL screenSize;
|
|
|
|
// Get the bitmap size for the primary device.
|
|
psoDevice = EngLockSurface(ppdev->hsurfDevice);
|
|
TRC_ERR((TB,"Null device surfac"));
|
|
if (NULL == psoDevice) {
|
|
TRC_ERR((TB, "Failed to lock ppdev surface"));
|
|
DC_QUIT;
|
|
}
|
|
screenSize = psoDevice->sizlBitmap;
|
|
EngUnlockSurface(psoDevice);
|
|
|
|
// We only support bitmap of size less than the primary device
|
|
if ((sizl.cx <= screenSize.cx) && (sizl.cy <= screenSize.cy)) {
|
|
// If adding this offscreen bitmap exceeds the client total
|
|
// offscreen bitmap memory, we have to let GDI to manage
|
|
// this bitmap
|
|
if (oeCurrentOffscreenCacheSize + bitmapSize <=
|
|
(pddShm->sbc.offscreenCacheInfo.cacheSize * 1024)) {
|
|
rc = TRUE;
|
|
}
|
|
else {
|
|
TRC_NRM((TB, "run out of offscreen memory"));
|
|
DC_QUIT;
|
|
}
|
|
} else {
|
|
TRC_NRM((TB, "offscreen bitmap size too big"));
|
|
DC_QUIT;
|
|
}
|
|
} else {
|
|
TRC_NRM((TB, "Offscreen bitmap size is 2K or less"));
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
else {
|
|
TRC_NRM((TB, "offscreen bitmap iFormat different from ppdev"));
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
else {
|
|
TRC_NRM((TB, "Offscreen bitmap rendering not supported"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
return rc;
|
|
}
|
|
|
|
/****************************************************************************/
|
|
// OETransformClipRectsForScrBlt
|
|
//
|
|
// Transforms the CD_ANY cliprect ordering to a particular order depending
|
|
// on the direction of the scrblt.
|
|
/****************************************************************************/
|
|
void OETransformClipRectsForScrBlt(
|
|
OE_ENUMRECTS *pClipRects,
|
|
PPOINTL pSrcPt,
|
|
RECTL *pDestRect,
|
|
CLIPOBJ *pco)
|
|
{
|
|
unsigned EnumType;
|
|
unsigned RetVal;
|
|
|
|
DC_BEGIN_FN("OESendScrBltAsOrder");
|
|
|
|
// If there are zero or one clip rectangles then we can send it OK.
|
|
TRC_ASSERT((pClipRects->rects.c > 1),(TB,"Called with too few cliprects"));
|
|
|
|
// Check common cases and re-enumerate the rectangles as needed to
|
|
// get an ordering compatible with the direction of scroll.
|
|
if (pDestRect->top <= pSrcPt->y) {
|
|
// Upward/horizontal cases.
|
|
if (pDestRect->left <= pSrcPt->x) {
|
|
// Vertical up (most common case), horizontal to the left,
|
|
// or up and to the left. Enumerate rects left-to-right,
|
|
// top-to-bottom.
|
|
EnumType = CD_RIGHTDOWN;
|
|
}
|
|
else {
|
|
// Up and to the right or horizontal right. Enumerate
|
|
// right-to-left, top-to-bottom.
|
|
EnumType = CD_LEFTDOWN;
|
|
}
|
|
}
|
|
else {
|
|
// Downward cases.
|
|
if (pDestRect->left <= pSrcPt->x) {
|
|
// Vertical down or down and to the left. Enumerate left-to-right,
|
|
// bottom-to-top.
|
|
EnumType = CD_RIGHTUP;
|
|
}
|
|
else {
|
|
// Down and to the right. Enumerate right-to-left, bottom-to-top.
|
|
EnumType = CD_LEFTUP;
|
|
}
|
|
}
|
|
|
|
RetVal = OEGetIntersectingClipRects(pco, pDestRect, EnumType, pClipRects);
|
|
TRC_ASSERT((RetVal == CLIPRECTS_OK),
|
|
(TB,"Re-enumeration of clip rects produced err %u", RetVal));
|
|
|
|
DC_END_FN();
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
// OEEncodeScrBlt
|
|
//
|
|
// Performs all encoding steps required to encode a ScrBlt order, then adds
|
|
// to order list. Returns FALSE if the order needs to be added to the screen
|
|
// data area.
|
|
/****************************************************************************/
|
|
BOOL RDPCALL OEEncodeScrBlt(
|
|
RECTL *pBounds,
|
|
BYTE Rop3,
|
|
POINTL *pptlSrc,
|
|
PDD_PDEV ppdev,
|
|
OE_ENUMRECTS *pClipRects,
|
|
CLIPOBJ *pco)
|
|
{
|
|
unsigned i;
|
|
unsigned OrderSize;
|
|
unsigned NumFieldFlagBytes;
|
|
POINTL Origin;
|
|
BYTE OrderType;
|
|
BOOL rc = TRUE;
|
|
RECTL SrcRect;
|
|
PINT_ORDER pOrder;
|
|
SCRBLT_ORDER *pScrBlt;
|
|
|
|
DC_BEGIN_FN("OEEncodeScrBlt");
|
|
|
|
// Check whether we should use the multi-cliprect version. Must be a
|
|
// complex clip region and the client must support the order.
|
|
if (pClipRects->rects.c < 2 ||
|
|
!OE_SendAsOrder(TS_ENC_MULTISCRBLT_ORDER)) {
|
|
// Non-multi version.
|
|
OrderType = TS_ENC_SCRBLT_ORDER;
|
|
OrderSize = MAX_SCRBLT_FIELD_SIZE;
|
|
NumFieldFlagBytes = 1;
|
|
}
|
|
else {
|
|
// Multi version.
|
|
OrderType = TS_ENC_MULTISCRBLT_ORDER;
|
|
OrderSize = MAX_MULTI_SCRBLT_FIELD_SIZE_NCLIP(pClipRects->rects.c);
|
|
NumFieldFlagBytes = 2;
|
|
}
|
|
|
|
// Make sure we don't have orders turned off.
|
|
if (OE_SendAsOrder(OrderType)) {
|
|
// Clip source point.
|
|
Origin = *pptlSrc;
|
|
OEClipPoint(&Origin);
|
|
|
|
// Where there are multiple clipping rectangles, it is
|
|
// difficult to calculate the correct order to move the various
|
|
// bits of target surface around - we might move the bottom left
|
|
// to the middle before we moved the middle up to the top right.
|
|
//
|
|
// We make an exception where:
|
|
// - there is only horizontal or vertical movement
|
|
// - there is no overlap between the different clipping
|
|
// rectangles (source or destination)
|
|
// - there are 3 or fewer clipping rectangles.
|
|
//
|
|
// This takes care of several important cases - particularly
|
|
// scrolling in Excel.
|
|
if (pClipRects->rects.c > 1)
|
|
OETransformClipRectsForScrBlt(pClipRects, &Origin, pBounds, pco);
|
|
|
|
// For screen targets, we have to take into account the existing
|
|
// screen data areas. The problem here arises from the fact that
|
|
// SDA is bltted to the screen *after* all orders in a packet
|
|
// are drawn. If we do not take this into account, we can end
|
|
// up ScrBltting pieces of the screen around that are wrong
|
|
// because the SDA should have been written first, but won't
|
|
// have been.
|
|
if (oeLastDstSurface == NULL) {
|
|
if (!OEIntersectScrBltWithSDA(&Origin, pBounds, pClipRects)) {
|
|
TRC_NRM((TB,"ScrBlt entirely contained within SDA, "
|
|
"not sending"));
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
|
|
// By this point either we've got no clip rects, so we simply send
|
|
// the order straight, or some clip rects that might have been
|
|
// intersected with the SDA if the target is the screen, so we
|
|
// send one or more copies of the order, one for each clip rect.
|
|
|
|
// 1 or 2 field flag bytes.
|
|
pOrder = OA_AllocOrderMem(ppdev, MAX_ORDER_SIZE(
|
|
pClipRects->rects.c, NumFieldFlagBytes, OrderSize));
|
|
if (pOrder != NULL) {
|
|
pScrBlt = (SCRBLT_ORDER *)oeTempOrderBuffer;
|
|
pScrBlt->nLeftRect = pBounds->left;
|
|
pScrBlt->nTopRect = pBounds->top;
|
|
pScrBlt->nWidth = pBounds->right - pBounds->left;
|
|
pScrBlt->nHeight = pBounds->bottom - pBounds->top;
|
|
pScrBlt->bRop = Rop3;
|
|
pScrBlt->nXSrc = Origin.x;
|
|
pScrBlt->nYSrc = Origin.y;
|
|
|
|
if (OrderType == TS_ENC_SCRBLT_ORDER) {
|
|
// Slow-field-encode the order with the first clip rect
|
|
// (if present).
|
|
pOrder->OrderLength = OE2_EncodeOrder(pOrder->OrderData,
|
|
TS_ENC_SCRBLT_ORDER, NUM_SCRBLT_FIELDS,
|
|
(BYTE *)pScrBlt, (BYTE *)&PrevScrBlt, etable_SB,
|
|
(pClipRects->rects.c == 0 ? NULL :
|
|
&pClipRects->rects.arcl[0]));
|
|
|
|
INC_OUTCOUNTER(OUT_SCRBLT_ORDER);
|
|
ADD_INCOUNTER(IN_SCRBLT_BYTES, pOrder->OrderLength);
|
|
OA_AppendToOrderList(pOrder);
|
|
|
|
// Flush the order.
|
|
if (pClipRects->rects.c < 2)
|
|
rc = TRUE;
|
|
else
|
|
rc = OEEmitReplayOrders(ppdev, 1, pClipRects);
|
|
}
|
|
else {
|
|
MULTI_SCRBLT_ORDER *pMultiSB = (MULTI_SCRBLT_ORDER *)
|
|
oeTempOrderBuffer;
|
|
|
|
// Encode the clip rects directly into the order.
|
|
pMultiSB->nDeltaEntries = OEBuildMultiClipOrder(ppdev,
|
|
&pMultiSB->codedDeltaList, pClipRects);
|
|
|
|
// Slow-field-encode the order with no clip rects.
|
|
pOrder->OrderLength = OE2_EncodeOrder(pOrder->OrderData,
|
|
TS_ENC_MULTISCRBLT_ORDER, NUM_MULTI_SCRBLT_FIELDS,
|
|
(BYTE *)pMultiSB, (BYTE *)&PrevMultiScrBlt, etable_MS,
|
|
NULL);
|
|
|
|
INC_OUTCOUNTER(OUT_MULTI_SCRBLT_ORDER);
|
|
ADD_INCOUNTER(IN_MULTI_SCRBLT_BYTES, pOrder->OrderLength);
|
|
OA_AppendToOrderList(pOrder);
|
|
}
|
|
|
|
TRC_NRM((TB, "%sScrBlt x %d y %d w %d h %d sx %d sy %d rop %02X",
|
|
(OrderType == TS_ENC_SCRBLT_ORDER ? "" : "Multi"),
|
|
pScrBlt->nLeftRect, pScrBlt->nTopRect,
|
|
pScrBlt->nWidth, pScrBlt->nHeight,
|
|
pScrBlt->nXSrc, pScrBlt->nYSrc, pScrBlt->bRop));
|
|
}
|
|
else {
|
|
TRC_ERR((TB, "Failed to alloc order"));
|
|
INC_OUTCOUNTER(OUT_BITBLT_SDA_HEAPALLOCFAILED);
|
|
|
|
// On failure with a screen target, add all of the clip
|
|
// destination rects to SDA. Clip rects are in exclusive
|
|
// coords so convert before adding.
|
|
if (oeLastDstSurface == NULL)
|
|
OEClipAndAddScreenDataAreaByIntersectRects(pBounds,
|
|
pClipRects);
|
|
|
|
rc = FALSE;
|
|
}
|
|
}
|
|
else {
|
|
TRC_NRM((TB, "(Multi)ScrBlt order not allowed"));
|
|
INC_OUTCOUNTER(OUT_BITBLT_SDA_UNSUPPORTED);
|
|
rc = FALSE;
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
DC_END_FN();
|
|
return rc;
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
// OECacheGlyphs
|
|
//
|
|
// Caches glyphs as presented in the given font and string objects. Returns
|
|
// FALSE on failure to cache.
|
|
/****************************************************************************/
|
|
BOOL RDPCALL OECacheGlyphs(
|
|
STROBJ *pstro,
|
|
FONTOBJ *pfo,
|
|
PFONTCACHEINFO pfci,
|
|
PGLYPHCONTEXT pglc)
|
|
{
|
|
unsigned i;
|
|
unsigned j;
|
|
BOOL fMore;
|
|
UINT32 cGlyphs;
|
|
unsigned cbDataSize;
|
|
GLYPHPOS *pGlyphPos;
|
|
UINT32 Key1, Key2;
|
|
void *UserDefined;
|
|
unsigned cx;
|
|
unsigned cy;
|
|
unsigned dx;
|
|
unsigned dy;
|
|
unsigned x;
|
|
unsigned y;
|
|
FONTINFO fi;
|
|
|
|
DC_BEGIN_FN("OECacheGlyphs");
|
|
|
|
// Determine appropriate glyph cache if we haven't already done so.
|
|
if (pfci->cacheId < 0) {
|
|
FONTOBJ_vGetInfo(pfo, sizeof(fi), &fi);
|
|
cbDataSize = (fi.cjMaxGlyph1 + 3) & ~3;
|
|
if (SBCSelectGlyphCache(cbDataSize, &pfci->cacheId)) {
|
|
pfci->cacheHandle =
|
|
pddShm->sbc.glyphCacheInfo[pfci->cacheId].cacheHandle;
|
|
pddShm->sbc.glyphCacheInfo[pfci->cacheId].cbUseCount++;
|
|
}
|
|
else {
|
|
TRC_NRM((TB, "Failed to determine glyph cache"));
|
|
goto FailCache;
|
|
}
|
|
}
|
|
|
|
// Establish our cache context.
|
|
CH_SetCacheContext(pfci->cacheHandle, pglc);
|
|
|
|
// Loop through each glyph, caching it appropriately.
|
|
if (pstro->pgp == NULL)
|
|
STROBJ_vEnumStart(pstro);
|
|
dx = 0;
|
|
dy = 0;
|
|
j = 0;
|
|
fMore = TRUE;
|
|
while (fMore) {
|
|
if (pstro->pgp != NULL) {
|
|
fMore = FALSE;
|
|
cGlyphs = pstro->cGlyphs;
|
|
pGlyphPos = pstro->pgp;
|
|
}
|
|
else {
|
|
fMore = STROBJ_bEnum(pstro, &cGlyphs, &pGlyphPos);
|
|
if (cGlyphs == 0) {
|
|
TRC_NRM((TB, "STROBJ_bEnum - 0 glyphs"));
|
|
goto FailCache;
|
|
}
|
|
}
|
|
|
|
if (j == 0) {
|
|
x = pGlyphPos->ptl.x;
|
|
y = pGlyphPos->ptl.y;
|
|
}
|
|
|
|
for (i = 0; i < cGlyphs; i++) {
|
|
// GDI never sets the SO_VERTICAL bit, and there are cases where
|
|
// it does not properly set the SO_HORIZONTAL bit either. As a
|
|
// result, when GDI is silent we need to look for ourselves if
|
|
// we plan on catching these cases.
|
|
if ((pstro->flAccel & SO_HORIZONTAL) == 0) {
|
|
dx += (x - pGlyphPos->ptl.x);
|
|
dy += (y - pGlyphPos->ptl.y);
|
|
if (dx && dy) {
|
|
TRC_NRM((TB, "Can't process horizertical text"));
|
|
goto FailCache;
|
|
}
|
|
}
|
|
|
|
// Search for cache entry.
|
|
Key1 = pGlyphPos->hg; // Key1 has to be the most variable for the hash.
|
|
Key2 = pfci->fontId;
|
|
if (CH_SearchCache(pfci->cacheHandle, Key1, Key2, &UserDefined,
|
|
&pglc->rgCacheIndex[j])) {
|
|
// If the cache entry already existed, then flag our index
|
|
// item as such so we know later on not to send the glyph
|
|
// Set the entry tag for this DrvTextOut.
|
|
CH_SetUserDefined(pfci->cacheHandle, pglc->rgCacheIndex[j],
|
|
(void *)pglc->cacheTag);
|
|
pddCacheStats[GLYPH].CacheHits++;
|
|
pglc->rgCacheIndex[j] = ~pglc->rgCacheIndex[j];
|
|
pglc->nCacheHit++;
|
|
}
|
|
else {
|
|
// Cache the key.
|
|
pglc->rgCacheIndex[j] = CH_CacheKey(pfci->cacheHandle, Key1,
|
|
Key2, (void *)pglc->cacheTag);
|
|
if (pglc->rgCacheIndex[j] != CH_KEY_UNCACHABLE) {
|
|
// Keep a running total of the glyph data size for
|
|
// later use.
|
|
cx = pGlyphPos->pgdf->pgb->sizlBitmap.cx;
|
|
cy = pGlyphPos->pgdf->pgb->sizlBitmap.cy;
|
|
|
|
cbDataSize = ((cx + 7) / 8) * cy;
|
|
cbDataSize = (cbDataSize + 3) & ~3;
|
|
|
|
pglc->cbTotalDataSize += cbDataSize;
|
|
|
|
pglc->cbTotalDataSize += sizeof(TS_CACHE_GLYPH_DATA) -
|
|
FIELDSIZE(TS_CACHE_GLYPH_DATA, aj);
|
|
}
|
|
else {
|
|
TRC_NRM((TB, "Glyph could not be added to cache"));
|
|
goto FailCache;
|
|
}
|
|
}
|
|
|
|
pglc->nCacheIndex = ++j;
|
|
pGlyphPos++;
|
|
}
|
|
}
|
|
|
|
// Establish text orientation when GDI is silent (see above comment).
|
|
if ((pstro->flAccel & SO_HORIZONTAL) == 0) {
|
|
if (dx != 0)
|
|
pstro->flAccel |= SO_HORIZONTAL;
|
|
else if (dy != 0)
|
|
pstro->flAccel |= SO_VERTICAL;
|
|
}
|
|
|
|
// De-establish our context to be used for the cache callback.
|
|
if (pfci->cacheId >= 0)
|
|
CH_SetCacheContext(pfci->cacheHandle, NULL);
|
|
|
|
DC_END_FN();
|
|
return TRUE;
|
|
|
|
FailCache:
|
|
// De-establish our context to be used for the cache callback.
|
|
if (pfci->cacheId >= 0)
|
|
CH_SetCacheContext(pfci->cacheHandle, NULL);
|
|
|
|
// Remove any entries we did cache.
|
|
for (i = 0; i < pglc->nCacheIndex; i++)
|
|
if (pglc->rgCacheIndex[i] < SBC_NUM_GLYPH_CACHE_ENTRIES)
|
|
CH_RemoveCacheEntry(pfci->cacheHandle, pglc->rgCacheIndex[i]);
|
|
|
|
DC_END_FN();
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
// OEFlushCacheGlyphOrder
|
|
//
|
|
// Flushes a buffered Cache Glyph order.
|
|
/****************************************************************************/
|
|
void OEFlushCacheGlyphOrder(
|
|
STROBJ *pstro,
|
|
PINT_ORDER pOrder,
|
|
PGLYPHCONTEXT pglc)
|
|
{
|
|
unsigned cbOrderSize;
|
|
PTS_CACHE_GLYPH_ORDER pGlyphOrder;
|
|
unsigned i, cGlyphs;
|
|
UINT16 UNALIGNED *pUnicode;
|
|
UINT16 UNALIGNED *pUnicodeEnd;
|
|
|
|
DC_BEGIN_FN("OEFlushCacheGlyphOrder");
|
|
|
|
if (pOrder != NULL) {
|
|
TRC_ASSERT((pglc->cbDataSize > 0),
|
|
(TB, "Bad pglc->cbDataSize"));
|
|
|
|
pGlyphOrder = (PTS_CACHE_GLYPH_ORDER)pOrder->OrderData;
|
|
|
|
if (pddShm->sbc.caps.GlyphSupportLevel >= CAPS_GLYPH_SUPPORT_ENCODE) {
|
|
cGlyphs = (pGlyphOrder->header.extraFlags &
|
|
TS_CacheGlyphRev2_cGlyphs_Mask) >> 8;
|
|
|
|
cbOrderSize = sizeof(TS_CACHE_GLYPH_ORDER_REV2) -
|
|
FIELDSIZE(TS_CACHE_GLYPH_ORDER_REV2, glyphData) +
|
|
pglc->cbDataSize;
|
|
}
|
|
else {
|
|
cGlyphs = pGlyphOrder->cGlyphs;
|
|
|
|
cbOrderSize = sizeof(TS_CACHE_GLYPH_ORDER) -
|
|
FIELDSIZE(TS_CACHE_GLYPH_ORDER, glyphData) +
|
|
pglc->cbDataSize;
|
|
}
|
|
|
|
pUnicode = (UINT16 UNALIGNED *)&pOrder->OrderData[cbOrderSize];
|
|
pUnicodeEnd = pUnicode + cGlyphs;
|
|
|
|
for (i = pglc->indexNextSend; pUnicode < pUnicodeEnd; i++)
|
|
if (pglc->rgCacheIndex[i] < SBC_NUM_GLYPH_CACHE_ENTRIES)
|
|
*pUnicode++ = pstro->pwszOrg[i];
|
|
|
|
cbOrderSize += cGlyphs * sizeof(UINT16);
|
|
pGlyphOrder->header.orderLength = (USHORT)
|
|
TS_CALCULATE_SECONDARY_ORDER_ORDERLENGTH(cbOrderSize);
|
|
OA_TruncateAllocatedOrder(pOrder, cbOrderSize);
|
|
INC_OUTCOUNTER(OUT_CACHEGLYPH);
|
|
ADD_OUTCOUNTER(OUT_CACHEGLYPH_BYTES, cbOrderSize);
|
|
OA_AppendToOrderList(pOrder);
|
|
|
|
pglc->cbDataSize = 0;
|
|
pglc->cbBufferSize = 0;
|
|
}
|
|
|
|
DC_END_FN();
|
|
}
|
|
|
|
__inline void Encode2ByteFields(
|
|
BYTE **pEncode,
|
|
unsigned Val,
|
|
unsigned *pOrderSize)
|
|
{
|
|
if (Val <= 127) {
|
|
**pEncode = (BYTE) Val;
|
|
(*pOrderSize)++;
|
|
(*pEncode)++;
|
|
}
|
|
else {
|
|
**pEncode = (BYTE)(((Val & 0x7F00) >> 8) | 0x80);
|
|
*(*pEncode + 1) = (BYTE)(Val & 0x00FF);
|
|
(*pOrderSize) += 2;
|
|
(*pEncode) += 2;
|
|
}
|
|
}
|
|
|
|
__inline void Encode2ByteSignedFields(
|
|
BYTE **pEncode,
|
|
int Val,
|
|
unsigned *pOrderSize)
|
|
{
|
|
if (Val < 0) {
|
|
**pEncode = 0x40;
|
|
Val = - Val;
|
|
}
|
|
else {
|
|
**pEncode = 0;
|
|
}
|
|
|
|
if (Val <= 63) {
|
|
**pEncode |= (BYTE)Val;
|
|
(*pOrderSize)++;
|
|
(*pEncode)++;
|
|
}
|
|
else {
|
|
**pEncode |= ((BYTE)(((Val & 0x3F00) >> 8) | 0x80));
|
|
*((*pEncode) + 1) = (BYTE)(Val & 0x00FF);
|
|
(*pOrderSize) += 2;
|
|
(*pEncode) += 2;
|
|
}
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
// OESendCacheGlyphRev2
|
|
//
|
|
// Allocates and sends a Cache Glyph Rev2 secondary order. REturns FALSE on
|
|
// failure.
|
|
/****************************************************************************/
|
|
BOOL OESendCacheGlyphRev2(
|
|
PDD_PDEV ppdev,
|
|
STROBJ *pstro,
|
|
FONTOBJ *pfo,
|
|
PFONTCACHEINFO pfci,
|
|
PGLYPHCONTEXT pglc,
|
|
unsigned index,
|
|
GLYPHPOS *pGlyphPos,
|
|
PINT_ORDER *ppOrder)
|
|
{
|
|
BOOL rc = TRUE;
|
|
unsigned cbDataSize, cbGlyphSize;
|
|
unsigned cx;
|
|
unsigned cy;
|
|
PTS_CACHE_GLYPH_ORDER_REV2 pGlyphOrder;
|
|
PBYTE pGlyphData;
|
|
PINT_ORDER pOrder = *ppOrder;
|
|
|
|
DC_BEGIN_FN("OESendCacheGlyphRev2");
|
|
|
|
// Calculate and allocate glyph order buffer.
|
|
cx = pGlyphPos->pgdf->pgb->sizlBitmap.cx;
|
|
cy = pGlyphPos->pgdf->pgb->sizlBitmap.cy;
|
|
|
|
cbGlyphSize = ((cx + 7) / 8) * cy;
|
|
cbGlyphSize = (cbGlyphSize + 3) & ~3;
|
|
|
|
cbDataSize = cbGlyphSize;
|
|
|
|
if (pglc->cbBufferSize < (TS_GLYPH_DATA_REV2_HDR_MAX_SIZE + cbDataSize +
|
|
sizeof(UINT16))) {
|
|
if (pOrder != NULL) {
|
|
pglc->cbTotalDataSize += pglc->cbBufferSize;
|
|
OEFlushCacheGlyphOrder(pstro, pOrder, pglc);
|
|
pglc->indexNextSend = index;
|
|
}
|
|
|
|
pglc->cbBufferSize = min(pglc->cbTotalDataSize, 4096);
|
|
pglc->cbTotalDataSize -= pglc->cbBufferSize;
|
|
|
|
pOrder = OA_AllocOrderMem(ppdev, sizeof(TS_CACHE_GLYPH_ORDER_REV2) -
|
|
FIELDSIZE(TS_CACHE_GLYPH_ORDER_REV2, glyphData) +
|
|
pglc->cbBufferSize);
|
|
if (pOrder != NULL) {
|
|
pGlyphOrder = (PTS_CACHE_GLYPH_ORDER_REV2)pOrder->OrderData;
|
|
pGlyphOrder->header.extraFlags = TS_EXTRA_GLYPH_UNICODE |
|
|
TS_CacheGlyphRev2_Mask;
|
|
pGlyphOrder->header.orderType = TS_CACHE_GLYPH;
|
|
pGlyphOrder->header.orderHdr.controlFlags = TS_STANDARD |
|
|
TS_SECONDARY;
|
|
pGlyphOrder->header.extraFlags |= (((char)pfci->cacheId) &
|
|
TS_CacheGlyphRev2_CacheID_Mask);
|
|
}
|
|
else {
|
|
TRC_ERR((TB, "Failed to allocate glyph order"));
|
|
rc = FALSE;
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
|
|
pGlyphOrder = (PTS_CACHE_GLYPH_ORDER_REV2)pOrder->OrderData;
|
|
pGlyphData = (PBYTE)(pGlyphOrder->glyphData) + pglc->cbDataSize;
|
|
|
|
*pGlyphData++ = (BYTE)pglc->rgCacheIndex[index];
|
|
cbDataSize++;
|
|
|
|
Encode2ByteSignedFields(&pGlyphData, (INT16)pGlyphPos->pgdf->pgb->ptlOrigin.x,
|
|
&cbDataSize);
|
|
Encode2ByteSignedFields(&pGlyphData, (INT16)pGlyphPos->pgdf->pgb->ptlOrigin.y,
|
|
&cbDataSize);
|
|
Encode2ByteFields(&pGlyphData, (UINT16)cx, &cbDataSize);
|
|
Encode2ByteFields(&pGlyphData, (UINT16)cy, &cbDataSize);
|
|
RtlCopyMemory(pGlyphData, pGlyphPos->pgdf->pgb->aj, cbGlyphSize);
|
|
|
|
// number of glyphs. cGlyphs is the upper byte in extraflag
|
|
pGlyphOrder->header.extraFlags += 0x100;
|
|
|
|
TRC_ASSERT((pglc->cbBufferSize >= cbDataSize),
|
|
(TB, "Bad pglc->cbBufferSize"));
|
|
|
|
pglc->cbDataSize += cbDataSize;
|
|
pglc->cbBufferSize -= (cbDataSize + sizeof(UINT16));
|
|
|
|
DC_EXIT_POINT:
|
|
*ppOrder = pOrder;
|
|
DC_END_FN();
|
|
return rc;
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
// OESendCacheGlyph
|
|
//
|
|
// Allocates and sends glyph orders. Returns FALSE on failure.
|
|
/****************************************************************************/
|
|
BOOL OESendCacheGlyph(
|
|
PDD_PDEV ppdev,
|
|
STROBJ *pstro,
|
|
FONTOBJ *pfo,
|
|
PFONTCACHEINFO pfci,
|
|
PGLYPHCONTEXT pglc,
|
|
unsigned index,
|
|
GLYPHPOS *pGlyphPos,
|
|
PINT_ORDER *ppOrder)
|
|
{
|
|
BOOL rc = TRUE;
|
|
unsigned cbDataSize;
|
|
unsigned cbOrderSize;
|
|
unsigned cx;
|
|
unsigned cy;
|
|
PTS_CACHE_GLYPH_DATA pGlyphData;
|
|
PTS_CACHE_GLYPH_ORDER pGlyphOrder;
|
|
PINT_ORDER pOrder = *ppOrder;
|
|
|
|
DC_BEGIN_FN("OESendCacheGlyph");
|
|
|
|
// Calculate and allocate glyph order buffer.
|
|
cx = pGlyphPos->pgdf->pgb->sizlBitmap.cx;
|
|
cy = pGlyphPos->pgdf->pgb->sizlBitmap.cy;
|
|
|
|
cbDataSize = ((cx + 7) / 8) * cy;
|
|
cbDataSize = (cbDataSize + 3) & ~3;
|
|
|
|
cbOrderSize = (sizeof(TS_CACHE_GLYPH_DATA) -
|
|
FIELDSIZE(TS_CACHE_GLYPH_DATA, aj) + cbDataSize);
|
|
|
|
if (pglc->cbBufferSize < cbOrderSize + sizeof(UINT16)) {
|
|
if (*ppOrder != NULL) {
|
|
pglc->cbTotalDataSize += pglc->cbBufferSize;
|
|
OEFlushCacheGlyphOrder(pstro, pOrder, pglc);
|
|
pglc->indexNextSend = index;
|
|
}
|
|
|
|
pglc->cbBufferSize = min(pglc->cbTotalDataSize, 4096);
|
|
pglc->cbTotalDataSize -= pglc->cbBufferSize;
|
|
|
|
pOrder = OA_AllocOrderMem(ppdev, sizeof(TS_CACHE_GLYPH_ORDER) -
|
|
FIELDSIZE(TS_CACHE_GLYPH_ORDER, glyphData) +
|
|
pglc->cbBufferSize);
|
|
if (pOrder != NULL) {
|
|
pGlyphOrder = (PTS_CACHE_GLYPH_ORDER)pOrder->OrderData;
|
|
pGlyphOrder->header.extraFlags = TS_EXTRA_GLYPH_UNICODE;
|
|
pGlyphOrder->header.orderType = TS_CACHE_GLYPH;
|
|
pGlyphOrder->header.orderHdr.controlFlags = TS_STANDARD |
|
|
TS_SECONDARY;
|
|
pGlyphOrder->cacheId = (char)pfci->cacheId;
|
|
pGlyphOrder->cGlyphs = 0;
|
|
}
|
|
else {
|
|
TRC_ERR((TB, "Failed to allocate glyph order"));
|
|
rc = FALSE;
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
|
|
pGlyphOrder = (PTS_CACHE_GLYPH_ORDER)pOrder->OrderData;
|
|
pGlyphData = (PTS_CACHE_GLYPH_DATA)
|
|
((PBYTE)(pGlyphOrder->glyphData) + pglc->cbDataSize);
|
|
|
|
pGlyphData->cacheIndex = (UINT16)pglc->rgCacheIndex[index];
|
|
pGlyphData->x = (INT16)pGlyphPos->pgdf->pgb->ptlOrigin.x;
|
|
pGlyphData->y = (INT16)pGlyphPos->pgdf->pgb->ptlOrigin.y;
|
|
pGlyphData->cx = (INT16)cx;
|
|
pGlyphData->cy = (INT16)cy;
|
|
|
|
RtlCopyMemory(pGlyphData->aj, pGlyphPos->pgdf->pgb->aj, cbDataSize);
|
|
pGlyphOrder->cGlyphs++;
|
|
|
|
TRC_ASSERT((pglc->cbBufferSize >= cbOrderSize),
|
|
(TB, "Bad pglc->cbBufferSize"));
|
|
|
|
pglc->cbDataSize += cbOrderSize;
|
|
pglc->cbBufferSize -= (cbOrderSize + sizeof(UINT16));
|
|
|
|
DC_EXIT_POINT:
|
|
*ppOrder = pOrder;
|
|
DC_END_FN();
|
|
return rc;
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
// OESendGlyphs
|
|
//
|
|
// Sends glyphs. Returns FALSE on failure.
|
|
/****************************************************************************/
|
|
BOOL RDPCALL OESendGlyphs(
|
|
SURFOBJ *pso,
|
|
STROBJ *pstro,
|
|
FONTOBJ *pfo,
|
|
PFONTCACHEINFO pfci,
|
|
PGLYPHCONTEXT pglc)
|
|
{
|
|
BOOL rc = TRUE;
|
|
unsigned i;
|
|
unsigned j;
|
|
BOOL fMore;
|
|
UINT32 cGlyphs;
|
|
UINT32 dwSize;
|
|
GLYPHPOS *pGlyphPos;
|
|
PDD_PDEV ppdev;
|
|
PINT_ORDER pOrder;
|
|
|
|
DC_BEGIN_FN("OESendGlyphs");
|
|
|
|
j = 0;
|
|
pOrder = NULL;
|
|
|
|
// If we don't have to send ANY glyphs, then just exit.
|
|
if (pglc->nCacheHit < pstro->cGlyphs) {
|
|
ppdev = (PDD_PDEV)pso->dhpdev;
|
|
pglc->cbTotalDataSize += ((pstro->cGlyphs - pglc->nCacheHit) *
|
|
(sizeof(UINT16)));
|
|
|
|
// Loop through all glyphs, sending those that have not yet been sent.
|
|
if (pstro->pgp == NULL)
|
|
STROBJ_vEnumStart(pstro);
|
|
|
|
fMore = TRUE;
|
|
while (rc && fMore) {
|
|
if (pstro->pgp != NULL) {
|
|
fMore = FALSE;
|
|
cGlyphs = pstro->cGlyphs;
|
|
pGlyphPos = pstro->pgp;
|
|
}
|
|
else {
|
|
fMore = STROBJ_bEnum(pstro, &cGlyphs, &pGlyphPos);
|
|
if (cGlyphs == 0) {
|
|
TRC_NRM((TB, "STROBJ_bEnum - 0 glyphs"));
|
|
goto SucceedEncode;
|
|
}
|
|
}
|
|
|
|
// Send all current retrieved glyphs.
|
|
for (i = 0; i < cGlyphs; i++) {
|
|
if (pglc->rgCacheIndex[j] < SBC_NUM_GLYPH_CACHE_ENTRIES) {
|
|
if (pddShm->sbc.caps.GlyphSupportLevel >=
|
|
CAPS_GLYPH_SUPPORT_ENCODE) {
|
|
rc = OESendCacheGlyphRev2(ppdev, pstro, pfo, pfci,
|
|
pglc, j, pGlyphPos, &pOrder);
|
|
}
|
|
else {
|
|
rc = OESendCacheGlyph(ppdev, pstro, pfo, pfci, pglc,
|
|
j, pGlyphPos, &pOrder);
|
|
}
|
|
|
|
if (!rc)
|
|
goto FailEncode;
|
|
}
|
|
|
|
j++;
|
|
pGlyphPos++;
|
|
}
|
|
}
|
|
}
|
|
|
|
SucceedEncode:
|
|
// All is well, make sure we flush out any buffered glyphs.
|
|
if (pOrder != NULL)
|
|
OEFlushCacheGlyphOrder(pstro, pOrder, pglc);
|
|
|
|
DC_END_FN();
|
|
return TRUE;
|
|
|
|
FailEncode:
|
|
// If we could not send all the required glyphs, then remove them from
|
|
// the cache (as future hits on this entry will be invalid).
|
|
if (pOrder != NULL)
|
|
OA_FreeOrderMem(pOrder);
|
|
|
|
for (i = 0; i < pglc->nCacheIndex; i++) {
|
|
if (pglc->rgCacheIndex[i] < SBC_NUM_GLYPH_CACHE_ENTRIES)
|
|
CH_RemoveCacheEntry(pfci->cacheHandle, pglc->rgCacheIndex[i]);
|
|
}
|
|
|
|
DC_END_FN();
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
// OESendGlyphAndIndexOrder
|
|
//
|
|
// Sends FastGlyph order. Returns FALSE on failure.
|
|
/****************************************************************************/
|
|
BOOL OESendGlyphAndIndexOrder(
|
|
PDD_PDEV ppdev,
|
|
STROBJ *pstro,
|
|
OE_ENUMRECTS *pClipRects,
|
|
PRECTL prclOpaque,
|
|
POE_BRUSH_DATA pCurrentBrush,
|
|
PFONTCACHEINFO pfci,
|
|
PGLYPHCONTEXT pglc)
|
|
{
|
|
BOOL rc = TRUE;
|
|
GLYPHPOS *pGlyphPos;
|
|
PINT_ORDER pOrder;
|
|
unsigned tempVar, OpEncodeFlags;
|
|
unsigned cx, cy, cbDataSize, cbGlyphSize;
|
|
PBYTE pGlyphData;
|
|
LPFAST_GLYPH_ORDER pFastGlyphOrder;
|
|
RECTL BoundRect;
|
|
OE_ENUMRECTS IntersectRects;
|
|
|
|
DC_BEGIN_FN("OESendGlyphIndexOrder");
|
|
|
|
pOrder = NULL;
|
|
|
|
// First determine if this order is clipped out by the clip rects.
|
|
// If so, no need to allocate and send it.
|
|
if (prclOpaque != NULL) {
|
|
// Bounded by the opaque rect. Clip it to our max first.
|
|
if (prclOpaque->right > OE_MAX_COORD)
|
|
prclOpaque->right = OE_MAX_COORD;
|
|
|
|
if (prclOpaque->bottom > OE_MAX_COORD)
|
|
prclOpaque->bottom = OE_MAX_COORD;
|
|
|
|
// If the rect is inverted or null, we use the target string rect
|
|
// instead.
|
|
if (prclOpaque->top < prclOpaque->bottom)
|
|
BoundRect = *prclOpaque;
|
|
else
|
|
BoundRect = pstro->rclBkGround;
|
|
}
|
|
else {
|
|
// Bounded by the string target rect.
|
|
BoundRect = pstro->rclBkGround;
|
|
}
|
|
|
|
IntersectRects.rects.c = 0;
|
|
if (pClipRects->rects.c == 0 ||
|
|
OEGetIntersectionsWithClipRects(&BoundRect, pClipRects,
|
|
&IntersectRects)) {
|
|
if (pstro->pgp != NULL) {
|
|
pGlyphPos = pstro->pgp;
|
|
}
|
|
else {
|
|
STROBJ_vEnumStart(pstro);
|
|
STROBJ_bEnum(pstro, &tempVar, &pGlyphPos);
|
|
if (tempVar == 0) {
|
|
TRC_NRM((TB, "STROBJ_bEnum - 0 glyphs"));
|
|
rc = FALSE;
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
TRC_NRM((TB,"Order bounds do not intersect with clip, not sending"));
|
|
rc = FALSE;
|
|
DC_QUIT;
|
|
}
|
|
|
|
pFastGlyphOrder = (LPFAST_GLYPH_ORDER)oeTempOrderBuffer;
|
|
|
|
if (pglc->nCacheHit == 0) {
|
|
// We don't have a cache hit, so need to send the glyph.
|
|
// First create the variable-size data in the temp order buf
|
|
// to determine its size.
|
|
pGlyphData = (PBYTE)(pFastGlyphOrder->variableBytes.glyphData);
|
|
|
|
*pGlyphData++ = (BYTE)pglc->rgCacheIndex[0];
|
|
cbDataSize = 1;
|
|
Encode2ByteSignedFields(&pGlyphData,
|
|
(INT16)pGlyphPos->pgdf->pgb->ptlOrigin.x, &cbDataSize);
|
|
Encode2ByteSignedFields(&pGlyphData,
|
|
(INT16)pGlyphPos->pgdf->pgb->ptlOrigin.y, &cbDataSize);
|
|
|
|
cx = pGlyphPos->pgdf->pgb->sizlBitmap.cx;
|
|
cy = pGlyphPos->pgdf->pgb->sizlBitmap.cy;
|
|
cbGlyphSize = ((cx + 7) / 8) * cy;
|
|
cbGlyphSize = (cbGlyphSize + 3) & ~3;
|
|
cbDataSize += cbGlyphSize;
|
|
|
|
*pGlyphData++ = (BYTE)cx;
|
|
*pGlyphData++ = (BYTE)cy;
|
|
cbDataSize += 2;
|
|
|
|
memcpy(pGlyphData, pGlyphPos->pgdf->pgb->aj, cbGlyphSize);
|
|
|
|
// append unicode to the end of glyph data
|
|
*((UINT16 UNALIGNED *)(pGlyphData + cbGlyphSize)) = pstro->pwszOrg[0];
|
|
cbDataSize += 2;
|
|
|
|
pFastGlyphOrder->variableBytes.len = cbDataSize;
|
|
}
|
|
else {
|
|
// We have a cache hit. We only need to send a 1-byte cache index
|
|
// in the variable size data.
|
|
cbDataSize = 1;
|
|
|
|
// Store the variable data in the order data
|
|
if (pglc->rgCacheIndex[0] > SBC_GL_MAX_CACHE_ENTRIES)
|
|
pFastGlyphOrder->variableBytes.glyphData[0] =
|
|
(BYTE)(~pglc->rgCacheIndex[0]);
|
|
else
|
|
pFastGlyphOrder->variableBytes.glyphData[0] =
|
|
(BYTE)pglc->rgCacheIndex[0];
|
|
|
|
pFastGlyphOrder->variableBytes.len = 1;
|
|
}
|
|
|
|
// 2 field flag bytes, plus the variable data size.
|
|
pOrder = OA_AllocOrderMem(ppdev, MAX_ORDER_SIZE(IntersectRects.rects.c,
|
|
2, MAX_FAST_GLYPH_FIELD_SIZE_DATASIZE(cbDataSize)));
|
|
if (pOrder != NULL) {
|
|
// Establish per order settings.
|
|
pFastGlyphOrder->cacheId = (BYTE)pfci->cacheId;
|
|
pFastGlyphOrder->fDrawing = (((BYTE) pstro->flAccel) << 8) |
|
|
((BYTE)pstro->ulCharInc);
|
|
pFastGlyphOrder->BackColor = pCurrentBrush->back;
|
|
pFastGlyphOrder->ForeColor = pCurrentBrush->fore;
|
|
|
|
// Establish bounding rect left and right values.
|
|
pFastGlyphOrder->BkTop = pstro->rclBkGround.top;
|
|
pFastGlyphOrder->BkBottom = pstro->rclBkGround.bottom;
|
|
pFastGlyphOrder->BkLeft = pstro->rclBkGround.left;
|
|
pFastGlyphOrder->BkRight = pstro->rclBkGround.right;
|
|
|
|
// Set up x, y coordinates
|
|
if (pGlyphPos->ptl.x == pFastGlyphOrder->BkLeft)
|
|
pFastGlyphOrder->x = INT16_MIN;
|
|
else
|
|
pFastGlyphOrder->x = pGlyphPos->ptl.x;
|
|
|
|
if (pGlyphPos->ptl.y == pFastGlyphOrder->BkTop)
|
|
pFastGlyphOrder->y = INT16_MIN;
|
|
else
|
|
pFastGlyphOrder->y = pGlyphPos->ptl.y;
|
|
|
|
// Setup Opaque rect coordinates. Note we clipped to OE_MAX_COORD
|
|
// above.
|
|
if (prclOpaque) {
|
|
pFastGlyphOrder->OpTop = prclOpaque->top;
|
|
pFastGlyphOrder->OpBottom = prclOpaque->bottom;
|
|
pFastGlyphOrder->OpLeft = prclOpaque->left;
|
|
pFastGlyphOrder->OpRight = prclOpaque->right;
|
|
}
|
|
else {
|
|
pFastGlyphOrder->OpTop = 0;
|
|
pFastGlyphOrder->OpBottom = 0;
|
|
pFastGlyphOrder->OpLeft = 0;
|
|
pFastGlyphOrder->OpRight = 0;
|
|
}
|
|
|
|
// Is the Opaque rect redundant?
|
|
OpEncodeFlags =
|
|
((pFastGlyphOrder->OpLeft == pFastGlyphOrder->BkLeft) << 3) |
|
|
((pFastGlyphOrder->OpTop == pFastGlyphOrder->BkTop) << 2) |
|
|
((pFastGlyphOrder->OpRight == pFastGlyphOrder->BkRight) << 1) |
|
|
(pFastGlyphOrder->OpBottom == pFastGlyphOrder->BkBottom);
|
|
|
|
// For Fast Index order, we can encode even better for x, y and
|
|
// opaque rect.
|
|
if (OpEncodeFlags == 0xf) {
|
|
// All 4 bits present, Opaque rect is same as Bk rect.
|
|
pFastGlyphOrder->OpLeft = 0;
|
|
pFastGlyphOrder->OpTop = OpEncodeFlags;
|
|
pFastGlyphOrder->OpRight = 0;
|
|
pFastGlyphOrder->OpBottom = INT16_MIN;
|
|
}
|
|
else if (OpEncodeFlags == 0xd) {
|
|
// Bit 1 is 0, others are 1.
|
|
// Opaque rect matches Bk rect except OpRight
|
|
// we store OpRight at OpRight field
|
|
pFastGlyphOrder->OpLeft = 0;
|
|
pFastGlyphOrder->OpTop = OpEncodeFlags;
|
|
pFastGlyphOrder->OpRight = pFastGlyphOrder->OpRight;
|
|
pFastGlyphOrder->OpBottom = INT16_MIN;
|
|
}
|
|
|
|
// Slow-field-encode the order with the first clip rect
|
|
// (if present).
|
|
pOrder->OrderLength = OE2_EncodeOrder(pOrder->OrderData,
|
|
TS_ENC_FAST_GLYPH_ORDER, NUM_FAST_GLYPH_FIELDS,
|
|
(BYTE *)pFastGlyphOrder, (BYTE *)&PrevFastGlyph, etable_FG,
|
|
(IntersectRects.rects.c == 0 ? NULL :
|
|
&IntersectRects.rects.arcl[0]));
|
|
|
|
INC_OUTCOUNTER(OUT_TEXTOUT_FAST_GLYPH);
|
|
ADD_INCOUNTER(IN_FASTGLYPH_BYTES, pOrder->OrderLength);
|
|
OA_AppendToOrderList(pOrder);
|
|
|
|
// Flush the order.
|
|
if (IntersectRects.rects.c < 2)
|
|
rc = TRUE;
|
|
else
|
|
rc = OEEmitReplayOrders(ppdev, 2, &IntersectRects);
|
|
}
|
|
else {
|
|
rc = FALSE;
|
|
TRC_ERR((TB, "Failed to alloc Fast Index order"));
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
|
|
// If we could not send all the required glyphs, then remove them from
|
|
// the cache (as future hits on this entry will be invalid).
|
|
if (!rc && pglc->nCacheHit == 0)
|
|
CH_RemoveCacheEntry(pfci->cacheHandle, pglc->rgCacheIndex[0]);
|
|
|
|
DC_END_FN();
|
|
return rc;
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
// OESendIndexOrder
|
|
//
|
|
// Sends GlyphIndex and FastIndex orders. Returns FALSE on failure.
|
|
/****************************************************************************/
|
|
unsigned RDPCALL OESendIndexOrder(
|
|
PDD_PDEV ppdev,
|
|
STROBJ *pstro,
|
|
OE_ENUMRECTS *pClipRects,
|
|
PRECTL prclOpaque,
|
|
POE_BRUSH_DATA pCurrentBrush,
|
|
PFONTCACHEINFO pfci,
|
|
PGLYPHCONTEXT pglc,
|
|
unsigned iGlyph,
|
|
unsigned cGlyphs,
|
|
int x,
|
|
int y,
|
|
int cx,
|
|
int cy,
|
|
int cxLast,
|
|
int cyLast,
|
|
PBYTE pjData,
|
|
unsigned cbData)
|
|
{
|
|
UINT32 dwSize;
|
|
LPINDEX_ORDER pIndexOrder;
|
|
LPFAST_INDEX_ORDER pFastIndexOrder;
|
|
PINT_ORDER pOrder;
|
|
BOOL fFastIndex;
|
|
unsigned fStatus, OpEncodeFlags;
|
|
unsigned NumFieldFlagBytes;
|
|
RECTL *pBoundRect;
|
|
RECTL OpaqueRect;
|
|
RECTL BkRect;
|
|
OE_ENUMRECTS IntersectRects;
|
|
|
|
DC_BEGIN_FN("OESendIndexOrder");
|
|
|
|
fStatus = GH_STATUS_SUCCESS;
|
|
|
|
// First determine the opaque rect and background rects we will send
|
|
// on the wire. We'll use these to determine the target bound rect for the
|
|
// GlyphIndex order, to see if it is clipped out by the cliprects.
|
|
if (pstro->flAccel & SO_HORIZONTAL) {
|
|
BkRect.top = pstro->rclBkGround.top;
|
|
BkRect.bottom = pstro->rclBkGround.bottom;
|
|
|
|
OpaqueRect.top = prclOpaque->top;
|
|
OpaqueRect.bottom = prclOpaque->bottom;
|
|
|
|
// Left to right
|
|
if (x <= cx) {
|
|
if (iGlyph == 0) {
|
|
BkRect.left = pstro->rclBkGround.left;
|
|
OpaqueRect.left = prclOpaque->left;
|
|
}
|
|
else {
|
|
BkRect.left = min(cxLast, x);
|
|
if (OpaqueRect.top == OpaqueRect.bottom)
|
|
OpaqueRect.left = 0;
|
|
else
|
|
OpaqueRect.left = cxLast;
|
|
}
|
|
|
|
if (iGlyph + cGlyphs >= pglc->nCacheIndex) {
|
|
BkRect.right = pstro->rclBkGround.right;
|
|
OpaqueRect.right = prclOpaque->right;
|
|
}
|
|
else {
|
|
BkRect.right = cx;
|
|
if (OpaqueRect.top == OpaqueRect.bottom)
|
|
OpaqueRect.right = 0;
|
|
else
|
|
OpaqueRect.right = cx;
|
|
}
|
|
}
|
|
|
|
// Right to left
|
|
else {
|
|
if (iGlyph == 0) {
|
|
BkRect.right = pstro->rclBkGround.right;
|
|
OpaqueRect.right = prclOpaque->right;
|
|
}
|
|
else {
|
|
BkRect.right = x;
|
|
if (OpaqueRect.top == OpaqueRect.bottom)
|
|
OpaqueRect.right = 0;
|
|
else
|
|
OpaqueRect.right = x;
|
|
}
|
|
|
|
if (iGlyph + cGlyphs >= pglc->nCacheIndex) {
|
|
BkRect.left = pstro->rclBkGround.left;
|
|
OpaqueRect.left = prclOpaque->left;
|
|
}
|
|
else {
|
|
BkRect.left = cx;
|
|
if (prclOpaque->top == prclOpaque->bottom)
|
|
OpaqueRect.left = 0;
|
|
else
|
|
OpaqueRect.left = cx;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
BkRect.left = pstro->rclBkGround.left;
|
|
BkRect.right = pstro->rclBkGround.right;
|
|
|
|
OpaqueRect.left = prclOpaque->left;
|
|
OpaqueRect.right = prclOpaque->right;
|
|
|
|
// Top to bottom
|
|
if (y <= cy) {
|
|
if (iGlyph == 0) {
|
|
BkRect.top = pstro->rclBkGround.top;
|
|
OpaqueRect.top = prclOpaque->top;
|
|
}
|
|
else {
|
|
BkRect.top = cyLast;
|
|
if (prclOpaque->top == prclOpaque->bottom)
|
|
OpaqueRect.top = 0;
|
|
else
|
|
OpaqueRect.top = cyLast;
|
|
}
|
|
|
|
if (iGlyph + cGlyphs >= pglc->nCacheIndex) {
|
|
BkRect.bottom = pstro->rclBkGround.bottom;
|
|
OpaqueRect.bottom = prclOpaque->bottom;
|
|
}
|
|
else {
|
|
BkRect.bottom = cy;
|
|
if (prclOpaque->top == prclOpaque->bottom)
|
|
OpaqueRect.bottom = 0;
|
|
else
|
|
OpaqueRect.bottom = cy;
|
|
}
|
|
}
|
|
else {
|
|
// Bottom to top
|
|
if (iGlyph == 0) {
|
|
BkRect.bottom = pstro->rclBkGround.bottom;
|
|
OpaqueRect.bottom = prclOpaque->bottom;
|
|
}
|
|
else {
|
|
BkRect.bottom = y;
|
|
if (prclOpaque->top == prclOpaque->bottom)
|
|
OpaqueRect.bottom = 0;
|
|
else
|
|
OpaqueRect.bottom = y;
|
|
}
|
|
|
|
if (iGlyph + cGlyphs >= pglc->nCacheIndex) {
|
|
BkRect.top = pstro->rclBkGround.top;
|
|
OpaqueRect.top = prclOpaque->top;
|
|
}
|
|
else {
|
|
BkRect.top = cy;
|
|
if (prclOpaque->top == prclOpaque->bottom)
|
|
OpaqueRect.top = 0;
|
|
else
|
|
OpaqueRect.top = cy;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If the opaque rect is normally-ordered, it is our bound rect.
|
|
// Otherwise use the target background string rect.
|
|
if (OpaqueRect.top < OpaqueRect.bottom)
|
|
pBoundRect = &OpaqueRect;
|
|
else
|
|
pBoundRect = &BkRect;
|
|
|
|
IntersectRects.rects.c = 0;
|
|
if (pClipRects->rects.c == 0 ||
|
|
OEGetIntersectionsWithClipRects(pBoundRect, pClipRects,
|
|
&IntersectRects) > 0) {
|
|
fFastIndex = OE_SendAsOrder(TS_ENC_FAST_INDEX_ORDER);
|
|
|
|
// Calculate and allocate the req memory for this order.
|
|
if (fFastIndex) {
|
|
dwSize = MAX_FAST_INDEX_FIELD_SIZE_DATASIZE(cbData);
|
|
NumFieldFlagBytes = 2;
|
|
}
|
|
else {
|
|
dwSize = MAX_INDEX_FIELD_SIZE_DATASIZE(cbData);
|
|
NumFieldFlagBytes = 3;
|
|
}
|
|
|
|
pOrder = OA_AllocOrderMem(ppdev, MAX_ORDER_SIZE(IntersectRects.rects.c,
|
|
NumFieldFlagBytes, dwSize));
|
|
|
|
if (pOrder != NULL) {
|
|
// Since most of the fields are the same between Index order and
|
|
// fast index order. We arrange them in such a way that they can
|
|
// both be cast to the Index order in a lot of cases.
|
|
pIndexOrder = (LPINDEX_ORDER)oeTempOrderBuffer;
|
|
pFastIndexOrder = (LPFAST_INDEX_ORDER)oeTempOrderBuffer;
|
|
|
|
pIndexOrder->cacheId = (BYTE)pfci->cacheId;
|
|
pIndexOrder->ForeColor = pCurrentBrush->fore;
|
|
pIndexOrder->BackColor = pCurrentBrush->back;
|
|
pIndexOrder->x = x;
|
|
pIndexOrder->y = y;
|
|
|
|
|
|
pIndexOrder->BkLeft = BkRect.left;
|
|
pIndexOrder->BkTop = BkRect.top;
|
|
pIndexOrder->BkRight = BkRect.right;
|
|
pIndexOrder->BkBottom = BkRect.bottom;
|
|
|
|
pIndexOrder->OpLeft = OpaqueRect.left;
|
|
pIndexOrder->OpTop = OpaqueRect.top;
|
|
pIndexOrder->OpRight = OpaqueRect.right;
|
|
pIndexOrder->OpBottom = OpaqueRect.bottom;
|
|
|
|
// Is the Opaque rect redundant?
|
|
// We use 4 bits in OpTop field to encode Opaque Rect. 1 means
|
|
// a field is same as BkRect's field. 0 means a field is supplied
|
|
// in OpLeft or OpRight.
|
|
// bit 0: OpBottom
|
|
// bit 1: OpRight
|
|
// bit 2: OpTop
|
|
// bit 3: OpLeft
|
|
OpEncodeFlags = ((OpaqueRect.left == BkRect.left) << 3) |
|
|
((OpaqueRect.top == BkRect.top) << 2) |
|
|
((OpaqueRect.right == BkRect.right) << 1) |
|
|
(OpaqueRect.bottom == BkRect.bottom);
|
|
|
|
if (fFastIndex) {
|
|
pFastIndexOrder->fDrawing = (((BYTE) pstro->flAccel) << 8) |
|
|
((BYTE) pstro->ulCharInc);
|
|
|
|
// For Fast Index order, we can encode even better for x, y and
|
|
// opaque rect. We use INT16_MIN when possible to let the
|
|
// field encoder not send that field more often.
|
|
if (OpEncodeFlags == 0xf) {
|
|
// All 4 bits present, Opaque rect is same as Bk rect.
|
|
pFastIndexOrder->OpLeft = 0;
|
|
pFastIndexOrder->OpTop = OpEncodeFlags;
|
|
pFastIndexOrder->OpRight = 0;
|
|
pFastIndexOrder->OpBottom = INT16_MIN;
|
|
}
|
|
else if (OpEncodeFlags == 0xd) {
|
|
// Bit 1 is 0, others are 1.
|
|
// Opaque rect matches Bk rect except OpRight
|
|
// we store OpRight at OpRight field
|
|
pFastIndexOrder->OpLeft = 0;
|
|
pFastIndexOrder->OpTop = OpEncodeFlags;
|
|
pFastIndexOrder->OpRight = pFastIndexOrder->OpRight;
|
|
pFastIndexOrder->OpBottom = INT16_MIN;
|
|
}
|
|
|
|
// Set to same val if x coordinate same as BkLeft or y same as
|
|
// BkTop. This lets field encoding not send the value more often.
|
|
if (pFastIndexOrder->x == pFastIndexOrder->BkLeft)
|
|
pFastIndexOrder->x = INT16_MIN;
|
|
if (pFastIndexOrder->y == pFastIndexOrder->BkTop)
|
|
pFastIndexOrder->y = INT16_MIN;
|
|
|
|
// Store the order data and encode the order.
|
|
memcpy(pFastIndexOrder->variableBytes.arecs, pjData, cbData);
|
|
pFastIndexOrder->variableBytes.len = cbData;
|
|
|
|
// Slow-field-encode the order with the first clip rect
|
|
// (if present).
|
|
pOrder->OrderLength = OE2_EncodeOrder(pOrder->OrderData,
|
|
TS_ENC_FAST_INDEX_ORDER, NUM_FAST_INDEX_FIELDS,
|
|
(BYTE *)pFastIndexOrder, (BYTE *)&PrevFastIndex,
|
|
etable_FI,
|
|
(IntersectRects.rects.c == 0 ? NULL :
|
|
&IntersectRects.rects.arcl[0]));
|
|
|
|
INC_OUTCOUNTER(OUT_TEXTOUT_FAST_INDEX);
|
|
ADD_INCOUNTER(IN_FASTINDEX_BYTES, pOrder->OrderLength);
|
|
OA_AppendToOrderList(pOrder);
|
|
|
|
// Flush the order.
|
|
if (IntersectRects.rects.c >= 2)
|
|
if (!OEEmitReplayOrders(ppdev, 2, &IntersectRects))
|
|
fStatus = GH_STATUS_CLIPPED;
|
|
}
|
|
else {
|
|
pIndexOrder->flAccel = (BYTE)pstro->flAccel;
|
|
pIndexOrder->ulCharInc = (BYTE)pstro->ulCharInc;
|
|
pIndexOrder->BrushStyle = pCurrentBrush->style;
|
|
TRC_ASSERT((pIndexOrder->BrushStyle == BS_SOLID),
|
|
(TB,"Non solid brush"));
|
|
|
|
if (OpEncodeFlags == 0xf) {
|
|
pIndexOrder->OpTop = 0;
|
|
pIndexOrder->OpRight = 0;
|
|
pIndexOrder->OpBottom = 0;
|
|
pIndexOrder->OpLeft = 0;
|
|
pIndexOrder->fOpRedundant = TRUE;
|
|
}
|
|
else {
|
|
pIndexOrder->fOpRedundant = FALSE;
|
|
}
|
|
|
|
// Store the order data and encode the order.
|
|
memcpy(pIndexOrder->variableBytes.arecs, pjData, cbData);
|
|
pIndexOrder->variableBytes.len = cbData;
|
|
|
|
// Slow-field-encode the order with the first clip rect
|
|
// (if present).
|
|
pOrder->OrderLength = OE2_EncodeOrder(pOrder->OrderData,
|
|
TS_ENC_INDEX_ORDER, NUM_INDEX_FIELDS,
|
|
(BYTE *)pIndexOrder, (BYTE *)&PrevGlyphIndex,
|
|
etable_GI,
|
|
(IntersectRects.rects.c == 0 ? NULL :
|
|
&IntersectRects.rects.arcl[0]));
|
|
|
|
INC_OUTCOUNTER(OUT_TEXTOUT_GLYPH_INDEX);
|
|
ADD_INCOUNTER(IN_GLYPHINDEX_BYTES, pOrder->OrderLength);
|
|
OA_AppendToOrderList(pOrder);
|
|
|
|
// Flush the order.
|
|
if (IntersectRects.rects.c >= 2)
|
|
if (!OEEmitReplayOrders(ppdev, 3, &IntersectRects))
|
|
fStatus = GH_STATUS_CLIPPED;
|
|
}
|
|
}
|
|
else {
|
|
fStatus = GH_STATUS_NO_MEMORY;
|
|
TRC_ERR((TB, "Failed to alloc Index order"));
|
|
}
|
|
}
|
|
else {
|
|
TRC_NRM((TB,"(Fast)Index order completely clipped, not sending"));
|
|
fStatus = GH_STATUS_CLIPPED;
|
|
}
|
|
|
|
DC_END_FN();
|
|
return fStatus;
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
// OEGetFragment
|
|
//
|
|
// Retrieves text fragments (run of contig glyphs). Returns number of bytes
|
|
// copied into fragment buffer.
|
|
/****************************************************************************/
|
|
unsigned RDPCALL OEGetFragment(
|
|
STROBJ *pstro,
|
|
FONTOBJ *pfo,
|
|
GLYPHPOS **ppGlyphPos,
|
|
PGLYPHCONTEXT pglc,
|
|
PUINT pcGlyphs,
|
|
PUINT pcCurGlyphs,
|
|
PINT px,
|
|
PINT py,
|
|
PINT pcx,
|
|
PINT pcy,
|
|
PBYTE pjFrag,
|
|
unsigned maxFrag)
|
|
{
|
|
unsigned cbFrag;
|
|
unsigned cbEntrySize;
|
|
unsigned cacheIndex;
|
|
int delta;
|
|
BOOL fMore;
|
|
|
|
DC_BEGIN_FN("OEGetFragment");
|
|
|
|
// Loop through each glyph index accumulating the fragment.
|
|
cbFrag = 0;
|
|
cbEntrySize = (pstro->flAccel & SO_CHAR_INC_EQUAL_BM_BASE) ? 1 : 2;
|
|
|
|
while (*pcGlyphs < pglc->nCacheIndex) {
|
|
// If we have exhausted our fragment space, then exit.
|
|
if (cbFrag + cbEntrySize >= maxFrag)
|
|
break;
|
|
|
|
// We may need to get a new batch of current glyphs.
|
|
if (*pcCurGlyphs == 0) {
|
|
fMore = STROBJ_bEnum(pstro, pcCurGlyphs, ppGlyphPos);
|
|
if (*pcCurGlyphs == 0) {
|
|
cbFrag = 0;
|
|
|
|
TRC_NRM((TB, "STROBJ_bEnum - 0 glyphs"));
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
|
|
// Place the glyph cache index into the fragment.
|
|
cacheIndex = pglc->rgCacheIndex[*pcGlyphs];
|
|
if (cacheIndex > SBC_GL_MAX_CACHE_ENTRIES)
|
|
cacheIndex = ~cacheIndex;
|
|
|
|
if (!(pstro->flAccel & SO_GLYPHINDEX_TEXTOUT) && (cbFrag > 0) &&
|
|
(pstro->pwszOrg[*pcGlyphs] == 0x20)) {
|
|
if (pstro->ulCharInc || (pstro->flAccel & SO_CHAR_INC_EQUAL_BM_BASE))
|
|
pjFrag[cbFrag++] = (BYTE) cacheIndex;
|
|
}
|
|
else {
|
|
pjFrag[cbFrag++] = (BYTE) cacheIndex;
|
|
|
|
// If we do not have a mono-spaced font, nor an equal base font,
|
|
// then we need to also provide a delta coordinate.
|
|
if (pstro->ulCharInc == 0) {
|
|
if ((pstro->flAccel & SO_CHAR_INC_EQUAL_BM_BASE) == 0) {
|
|
// The delta coordinate is either the x-delta or the
|
|
// y-delta, based upon whether the text is horizontal
|
|
// or vertical.
|
|
if (pstro->flAccel & SO_HORIZONTAL)
|
|
delta = ((*ppGlyphPos)->ptl.x - *px);
|
|
else
|
|
delta = ((*ppGlyphPos)->ptl.y - *py);
|
|
|
|
if (delta >= 0 && delta <= 127) {
|
|
pjFrag[cbFrag++] = (char) delta;
|
|
}
|
|
else {
|
|
pjFrag[cbFrag++] = 0x80;
|
|
*(UNALIGNED short *)(&pjFrag[cbFrag]) = (SHORT)delta;
|
|
cbFrag += sizeof(INT16);
|
|
}
|
|
}
|
|
|
|
// Return the new glyph spacing coordinates to the main
|
|
// routine.
|
|
*px = (*ppGlyphPos)->ptl.x;
|
|
*py = (*ppGlyphPos)->ptl.y;
|
|
*pcx = (*ppGlyphPos)->ptl.x +
|
|
(*ppGlyphPos)->pgdf->pgb->ptlOrigin.x +
|
|
(*ppGlyphPos)->pgdf->pgb->sizlBitmap.cx;
|
|
*pcy = (*ppGlyphPos)->ptl.y +
|
|
(*ppGlyphPos)->pgdf->pgb->ptlOrigin.y +
|
|
(*ppGlyphPos)->pgdf->pgb->sizlBitmap.cy;
|
|
}
|
|
}
|
|
|
|
// Next glyph.
|
|
(*pcGlyphs)++;
|
|
(*ppGlyphPos)++;
|
|
(*pcCurGlyphs)--;
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
DC_END_FN();
|
|
return cbFrag;
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
// OEMatchFragment
|
|
//
|
|
// Matches text fragments with cached fragments. Returns the number of bytes
|
|
// in the fragment index returned in *pNewFragIndex.
|
|
/****************************************************************************/
|
|
unsigned RDPCALL OEMatchFragment(
|
|
STROBJ *pstro,
|
|
FONTOBJ *pfo,
|
|
PFONTCACHEINFO pfci,
|
|
PFRAGCONTEXT pfgc,
|
|
PBYTE pjFrag,
|
|
unsigned cbFrag,
|
|
PUINT pNewFragIndex,
|
|
unsigned cx,
|
|
unsigned cy,
|
|
unsigned cxLast,
|
|
unsigned cyLast)
|
|
{
|
|
unsigned cacheIndex;
|
|
UINT16 delta;
|
|
unsigned i;
|
|
void *UserDefined;
|
|
CHDataKeyContext CHContext;
|
|
INT16 dx, dy;
|
|
|
|
DC_BEGIN_FN("OEMatchFragment");
|
|
|
|
if (pfgc->cacheHandle) {
|
|
// If this is not a mono-spaced font, nor an equal base font, then
|
|
// we need to normalize the first delta, and the trailing padding.
|
|
if (pstro->ulCharInc == 0) {
|
|
if ((pstro->flAccel & SO_CHAR_INC_EQUAL_BM_BASE) == 0) {
|
|
if (pjFrag[1] != 0x80) {
|
|
delta = pjFrag[1];
|
|
pjFrag[1] = (BYTE) (pfci->cacheId);
|
|
}
|
|
else {
|
|
delta = *(UNALIGNED short *)(&pjFrag[2]);
|
|
pjFrag[2] = (BYTE) (pfci->cacheId);
|
|
pjFrag[3] = (BYTE) (pfci->cacheId);
|
|
}
|
|
}
|
|
}
|
|
|
|
i = (cbFrag + 3) & ~3;
|
|
|
|
memset(&pjFrag[cbFrag], (0xff), i - cbFrag);
|
|
|
|
// Multiple fonts can fall into the same cacheId, so two fragments
|
|
// of different fonts may collide if we use cacheId instead of fontId.
|
|
// memset(&pjFrag[i], (BYTE) (pfci->cacheId), sizeof(DWORD));
|
|
*(PUINT32)(&pjFrag[i]) = pfci->fontId;
|
|
|
|
i += sizeof(UINT32);
|
|
|
|
// Restore the normalized first delta.
|
|
if (pstro->ulCharInc == 0) {
|
|
if ((pstro->flAccel & SO_CHAR_INC_EQUAL_BM_BASE) == 0) {
|
|
if (delta >= 0 && delta <= 127)
|
|
pjFrag[1] = (char) delta;
|
|
else
|
|
*(UNALIGNED short *)(&pjFrag[2]) = delta;
|
|
}
|
|
}
|
|
|
|
// Make the default key for this fragment, and then search the
|
|
// fragment cache for a match.
|
|
CH_CreateKeyFromFirstData(&CHContext, pjFrag, i);
|
|
|
|
// Check if it is a fragment cache key collision by verifying the
|
|
// bounding background rectangle.
|
|
if (pstro->flAccel & SO_HORIZONTAL) {
|
|
dy = (INT16) (pstro->rclBkGround.bottom - pstro->rclBkGround.top);
|
|
if (cxLast == 0)
|
|
dx = (INT16)(cx - pstro->rclBkGround.left);
|
|
else
|
|
dx = (INT16)(cx - cxLast);
|
|
}
|
|
else {
|
|
dx = (INT16) (pstro->rclBkGround.right - pstro->rclBkGround.left);
|
|
if (cyLast == 0)
|
|
dy = (INT16)(cy - pstro->rclBkGround.top);
|
|
else
|
|
dy = (INT16)(cy - cyLast);
|
|
}
|
|
|
|
if (CH_SearchCache(pfgc->cacheHandle, CHContext.Key1,
|
|
CHContext.Key2, &UserDefined, &cacheIndex)) {
|
|
if (dx == (INT16) HIWORD((UINT32)(UINT_PTR)UserDefined) &&
|
|
dy == (INT16) LOWORD((UINT32)(UINT_PTR)UserDefined)) {
|
|
// If the entry already exists, then we can use it.
|
|
for (i = 0; i < pfgc->nCacheIndex; i++) {
|
|
if (cacheIndex == pfgc->rgCacheIndex[i])
|
|
DC_QUIT;
|
|
}
|
|
|
|
cbFrag = 0;
|
|
pjFrag[cbFrag++] = ORD_INDEX_FRAGMENT_USE;
|
|
pjFrag[cbFrag++] = (BYTE)cacheIndex;
|
|
|
|
if (pstro->ulCharInc == 0) {
|
|
if ((pstro->flAccel & SO_CHAR_INC_EQUAL_BM_BASE) == 0) {
|
|
if (delta >= 0 && delta <= 127) {
|
|
pjFrag[cbFrag++] = (char) delta;
|
|
}
|
|
else {
|
|
pjFrag[cbFrag++] = 0x80;
|
|
*(UNALIGNED short *)(&pjFrag[cbFrag]) = delta;
|
|
cbFrag += sizeof(INT16);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
TRC_ALT((TB, "Fragment cache Key collision at index %d",
|
|
cacheIndex));
|
|
UserDefined = (void *) ULongToPtr((((((UINT32) ((UINT16) dx)) << 16) |
|
|
(UINT32) ((UINT16) dy))));
|
|
CH_SetUserDefined(pfgc->cacheHandle, cacheIndex, UserDefined);
|
|
|
|
// Pass the entry along to the client.
|
|
i = cbFrag;
|
|
|
|
pjFrag[cbFrag++] = ORD_INDEX_FRAGMENT_ADD;
|
|
pjFrag[cbFrag++] = (BYTE)cacheIndex;
|
|
pjFrag[cbFrag++] = (BYTE)i;
|
|
|
|
*pNewFragIndex = cacheIndex;
|
|
}
|
|
}
|
|
else {
|
|
UserDefined = (void *) ULongToPtr((((((UINT32) ((UINT16) dx)) << 16) |
|
|
(UINT32) ((UINT16) dy))));
|
|
cacheIndex = CH_CacheKey(pfgc->cacheHandle, CHContext.Key1,
|
|
CHContext.Key2, UserDefined);
|
|
|
|
// If we could not add the cache entry, then bail.
|
|
if (cacheIndex != CH_KEY_UNCACHABLE) {
|
|
// Pass the entry along to the client.
|
|
i = cbFrag;
|
|
|
|
pjFrag[cbFrag++] = ORD_INDEX_FRAGMENT_ADD;
|
|
pjFrag[cbFrag++] = (BYTE)cacheIndex;
|
|
pjFrag[cbFrag++] = (BYTE)i;
|
|
|
|
*pNewFragIndex = cacheIndex;
|
|
}
|
|
else {
|
|
TRC_NRM((TB, "Fragment could not be added to cache"));
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
DC_END_FN();
|
|
return cbFrag;
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
// OEClearFragments
|
|
//
|
|
// Clears the newly added cache fragments.
|
|
/****************************************************************************/
|
|
void RDPCALL OEClearFragments(PFRAGCONTEXT pfgc)
|
|
{
|
|
unsigned i;
|
|
|
|
DC_BEGIN_FN("OEClearFragments");
|
|
|
|
// Remove all fragment cache entries that were being newly defined to
|
|
// to the client.
|
|
for (i = 0; i < pfgc->nCacheIndex; i++)
|
|
CH_RemoveCacheEntry(pfgc->cacheHandle, pfgc->rgCacheIndex[i]);
|
|
|
|
pfgc->nCacheIndex = 0;
|
|
|
|
DC_END_FN();
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
// OEMatchFragment
|
|
//
|
|
// Matches text fragments with cached fragments. Returns FALSE on failure.
|
|
/****************************************************************************/
|
|
BOOL RDPCALL OESendIndexes(
|
|
SURFOBJ *pso,
|
|
STROBJ *pstro,
|
|
FONTOBJ *pfo,
|
|
OE_ENUMRECTS *pClipRects,
|
|
PRECTL prclOpaque,
|
|
POE_BRUSH_DATA pbdOpaque,
|
|
POINTL *pptlOrg,
|
|
PFONTCACHEINFO pfci,
|
|
PGLYPHCONTEXT pglc)
|
|
{
|
|
BOOL rc;
|
|
unsigned iGlyph;
|
|
UINT32 cGlyphs;
|
|
RECTL rclOpaque;
|
|
LPINDEX_ORDER pIndexOrder;
|
|
GLYPHPOS *pGlyphPos;
|
|
FRAGCONTEXT fgc;
|
|
unsigned cCurGlyphs;
|
|
unsigned cbData;
|
|
unsigned cbFrag;
|
|
BYTE ajFrag[255];
|
|
BYTE ajData[255];
|
|
int x, y;
|
|
int cx, cy;
|
|
int cxPre, cyPre;
|
|
int cxLast, cyLast;
|
|
int dx, dy;
|
|
int cdx, cdy;
|
|
int xFrag, yFrag;
|
|
unsigned maxFrag;
|
|
unsigned minFrag;
|
|
unsigned fStatus;
|
|
PDD_PDEV ppdev;
|
|
BOOL fMore;
|
|
unsigned newFragIndex;
|
|
|
|
DC_BEGIN_FN("OEMatchFragment");
|
|
|
|
rc = FALSE;
|
|
fStatus = GH_STATUS_NO_MEMORY;
|
|
|
|
ppdev = (PDD_PDEV)pso->dhpdev;
|
|
|
|
// If no opaque rect is specified, default to the null rect.
|
|
if (prclOpaque == NULL) {
|
|
prclOpaque = &rclOpaque;
|
|
prclOpaque->left = 0;
|
|
prclOpaque->top = 0;
|
|
prclOpaque->right = 0;
|
|
prclOpaque->bottom = 0;
|
|
}
|
|
else {
|
|
if (prclOpaque->right > OE_MAX_COORD)
|
|
prclOpaque->right = OE_MAX_COORD;
|
|
|
|
if (prclOpaque->bottom > OE_MAX_COORD)
|
|
prclOpaque->bottom = OE_MAX_COORD;
|
|
}
|
|
|
|
// Establish min and max fragment limits.
|
|
fgc.nCacheIndex = 0;
|
|
fgc.cacheHandle = pddShm->sbc.fragCacheInfo[0].cacheHandle;
|
|
fgc.cbCellSize = pddShm->sbc.fragCacheInfo[0].cbCellSize;
|
|
|
|
maxFrag = fgc.cacheHandle ? fgc.cbCellSize : sizeof(ajFrag);
|
|
maxFrag = min(maxFrag, sizeof(ajFrag) - 2 * sizeof(DWORD) - 4);
|
|
minFrag = 3 * ((pstro->flAccel & SO_CHAR_INC_EQUAL_BM_BASE) ? 1 : 2);
|
|
|
|
// Loop through each glyph index, sending as many entries as
|
|
// possible per each order.
|
|
if (pstro->pgp != NULL) {
|
|
pGlyphPos = pstro->pgp;
|
|
cCurGlyphs = pglc->nCacheIndex;
|
|
}
|
|
else {
|
|
STROBJ_vEnumStart(pstro);
|
|
fMore = STROBJ_bEnum(pstro, &cCurGlyphs, &pGlyphPos);
|
|
if (cCurGlyphs == 0) {
|
|
TRC_NRM((TB, "STROBJ_bEnum - 0 glyphs"));
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
|
|
cbData = 0;
|
|
iGlyph = 0;
|
|
cGlyphs = 0;
|
|
|
|
x = dx = pGlyphPos->ptl.x;
|
|
y = dy = pGlyphPos->ptl.y;
|
|
cx = cy = cxLast = cyLast = 0;
|
|
|
|
while (cGlyphs < pglc->nCacheIndex) {
|
|
xFrag = pGlyphPos->ptl.x;
|
|
yFrag = pGlyphPos->ptl.y;
|
|
|
|
// Get the next available fragment.
|
|
cbFrag = OEGetFragment(pstro, pfo, &pGlyphPos,
|
|
pglc, &cGlyphs, &cCurGlyphs,
|
|
&dx, &dy, &cdx, &cdy, ajFrag, maxFrag);
|
|
|
|
if (cbFrag == 0) {
|
|
if (fgc.nCacheIndex > 0)
|
|
OEClearFragments(&fgc);
|
|
|
|
TRC_NRM((TB, "Fragment could not be gotten"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
// Keep track of the running coordinates.
|
|
cxPre = cx;
|
|
cyPre = cy;
|
|
|
|
if (pstro->ulCharInc == 0) {
|
|
cx = cdx;
|
|
cy = cdy;
|
|
}
|
|
else {
|
|
if (pstro->flAccel & SO_HORIZONTAL)
|
|
cx = x + (pstro->ulCharInc * (cGlyphs - iGlyph));
|
|
else
|
|
cy = y + (pstro->ulCharInc * (cGlyphs - iGlyph));
|
|
}
|
|
|
|
// If the fragment size is within limits, then attempt to match it
|
|
// with a previously defined fragment.
|
|
newFragIndex = BAD_FRAG_INDEX;
|
|
if (cbFrag >= minFrag)
|
|
cbFrag = OEMatchFragment(pstro, pfo, pfci, &fgc,
|
|
ajFrag, cbFrag, &newFragIndex, cx, cy, cxPre, cyPre);
|
|
|
|
// If this fragment will not fit into the current index order, then
|
|
// send the current buffered index data.
|
|
if (cbData + cbFrag > sizeof(pIndexOrder->variableBytes.arecs)) {
|
|
fStatus = OESendIndexOrder(ppdev, pstro, pClipRects,
|
|
prclOpaque, pbdOpaque, pfci, pglc,
|
|
iGlyph, cGlyphs - iGlyph, x, y,
|
|
cx, cy, cxLast, cyLast, ajData, cbData);
|
|
if (fStatus != GH_STATUS_SUCCESS) {
|
|
if (fgc.nCacheIndex > 0)
|
|
OEClearFragments(&fgc);
|
|
|
|
if (fStatus == GH_STATUS_NO_MEMORY) {
|
|
if (newFragIndex != BAD_FRAG_INDEX)
|
|
CH_RemoveCacheEntry(fgc.cacheHandle, newFragIndex);
|
|
|
|
TRC_NRM((TB, "Index order could not be sent - no memory"));
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
|
|
// Reset the process.
|
|
cbData = 0;
|
|
iGlyph += cGlyphs;
|
|
|
|
cxLast = cxPre;
|
|
cyLast = cyPre;
|
|
|
|
if (pstro->ulCharInc == 0) {
|
|
x = xFrag;
|
|
y = yFrag;
|
|
}
|
|
else {
|
|
if (pstro->flAccel & SO_HORIZONTAL)
|
|
x = cxLast;
|
|
else
|
|
y = cyLast;
|
|
}
|
|
|
|
if (pstro->ulCharInc == 0) {
|
|
if ((pstro->flAccel & SO_CHAR_INC_EQUAL_BM_BASE) == 0) {
|
|
if (ajFrag[1] != 0x80) {
|
|
ajFrag[1] = 0;
|
|
}
|
|
else {
|
|
ajFrag[2] = 0;
|
|
ajFrag[3] = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
fgc.nCacheIndex = 0;
|
|
}
|
|
|
|
// Copy the fragment into the order data buffer.
|
|
memcpy(&ajData[cbData], ajFrag, cbFrag);
|
|
cbData += cbFrag;
|
|
|
|
if (newFragIndex != BAD_FRAG_INDEX)
|
|
fgc.rgCacheIndex[fgc.nCacheIndex++] = newFragIndex;
|
|
}
|
|
|
|
// Flush out any remaining buffered fragments.
|
|
if (cbData > 0) {
|
|
fStatus = OESendIndexOrder(ppdev, pstro, pClipRects,
|
|
prclOpaque, pbdOpaque, pfci, pglc,
|
|
iGlyph, cGlyphs - iGlyph, x, y, cx, cy, cxLast, cyLast,
|
|
ajData, cbData);
|
|
if (fStatus != GH_STATUS_SUCCESS) {
|
|
if (fgc.nCacheIndex > 0)
|
|
OEClearFragments(&fgc);
|
|
}
|
|
}
|
|
|
|
rc = TRUE;
|
|
|
|
DC_EXIT_POINT:
|
|
DC_END_FN();
|
|
return rc;
|
|
}
|
|
|