Leaked source code of windows server 2003
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

/****************************************************************************/
// 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;
}