/****************************************************************************/ // noeint.c // // RDP Order Encoder Display Driver internal functions // // Copyright (C) 1996-2000 Microsoft Corporation /****************************************************************************/ #include #pragma hdrstop #include #define TRC_FILE "noeint" #include #include #include #include #include #include #include #include #define DC_INCLUDE_DATA #include #include #include #undef DC_INCLUDE_DATA #include #include #include #include #include #include #include #include #include #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; iFlags = 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; }