|
|
/****************************************************************************/ // noeapi.c
//
// RDP Order Encoder functions
//
// Copyright (C) 1996-2000 Microsoft Corporation
/****************************************************************************/
#include <precmpdd.h>
#pragma hdrstop
#define TRC_FILE "noeapi"
#include <adcg.h>
#include <atrcapi.h>
#include <nshmapi.h>
#include <tsrvexp.h>
#include <nschdisp.h>
#include <noadisp.h>
#include <nsbcdisp.h>
#include <nprcount.h>
#include <noedisp.h>
#include <oe2.h>
#define DC_INCLUDE_DATA
#include <ndddata.c>
#include <noedata.c>
#include <nsbcddat.c>
#include <oe2data.c>
#undef DC_INCLUDE_DATA
#include <nbadisp.h>
#include <nbainl.h>
#include <noeinl.h>
#include <nsbcinl.h>
#include <nchdisp.h>
#include <tsgdiplusenums.h>
/****************************************************************************/ // OE_InitShm
//
// Alloc-time SHM init.
/****************************************************************************/ void OE_InitShm(void) { DC_BEGIN_FN("OE_InitShm");
memset(&pddShm->oe, 0, sizeof(OE_SHARED_DATA));
DC_END_FN(); }
/****************************************************************************/ // OE_Reset
//
// Reset oe components as necessary
/****************************************************************************/ void OE_Reset(void) { DC_BEGIN_FN("OE_Reset");
oeLastDstSurface = 0;
DC_END_FN(); }
/****************************************************************************/ // OE_Update
//
// Called when the share is reset on logon or reconnect.
// Sets new capabilities.
/****************************************************************************/ void RDPCALL OE_Update() { if (pddShm->oe.newCapsData) { oeSendSolidPatternBrushOnly = pddShm->oe.sendSolidPatternBrushOnly; oeColorIndexSupported = pddShm->oe.colorIndices;
// The share core has passed down a pointer to its copy of the order
// support array. We take a copy for the kernel here.
memcpy(&oeOrderSupported, pddShm->oe.orderSupported, sizeof(oeOrderSupported));
pddShm->oe.newCapsData = FALSE; }
DC_END_FN(); }
/****************************************************************************/ // OE_ClearOrderEncoding
//
// Called on a share state toggle to clear the order encoding states held
// in the DD data segment.
/****************************************************************************/ void OE_ClearOrderEncoding() { DC_BEGIN_FN("OE_ClearOrderEncoding");
memset(&PrevMemBlt, 0, sizeof(PrevMemBlt)); memset(&PrevMem3Blt, 0, sizeof(PrevMem3Blt)); memset(&PrevDstBlt, 0, sizeof(PrevDstBlt)); memset(&PrevMultiDstBlt, 0, sizeof(PrevMultiDstBlt)); memset(&PrevPatBlt, 0, sizeof(PrevPatBlt)); memset(&PrevMultiPatBlt, 0, sizeof(PrevMultiPatBlt)); memset(&PrevScrBlt, 0, sizeof(PrevScrBlt)); memset(&PrevMultiScrBlt, 0, sizeof(PrevMultiScrBlt)); memset(&PrevOpaqueRect, 0, sizeof(PrevOpaqueRect)); memset(&PrevMultiOpaqueRect, 0, sizeof(PrevMultiOpaqueRect));
memset(&PrevLineTo, 0, sizeof(PrevLineTo)); memset(&PrevPolyLine, 0, sizeof(PrevPolyLine)); memset(&PrevPolygonSC, 0, sizeof(PrevPolygonSC)); memset(&PrevPolygonCB, 0, sizeof(PrevPolygonCB)); memset(&PrevEllipseSC, 0, sizeof(PrevEllipseSC)); memset(&PrevEllipseCB, 0, sizeof(PrevEllipseCB));
memset(&PrevFastIndex, 0, sizeof(PrevFastIndex)); memset(&PrevFastGlyph, 0, sizeof(PrevFastGlyph)); memset(&PrevGlyphIndex, 0, sizeof(PrevGlyphIndex));
#ifdef DRAW_NINEGRID
memset(&PrevDrawNineGrid, 0, sizeof(PrevDrawNineGrid)); memset(&PrevMultiDrawNineGrid, 0, sizeof(PrevMultiDrawNineGrid)); #endif
DC_END_FN(); }
/****************************************************************************/ /* OE_SendGlyphs - Send text glyphs to the client */ /****************************************************************************/ BOOL RDPCALL OE_SendGlyphs( SURFOBJ *pso, STROBJ *pstro, FONTOBJ *pfo, OE_ENUMRECTS *pClipRects, RECTL *prclOpaque, BRUSHOBJ *pboFore, BRUSHOBJ *pboOpaque, POINTL *pptlOrg, PFONTCACHEINFO pfci) { BOOL rc; GLYPHCONTEXT glc; POE_BRUSH_DATA pbdOpaque; PDD_PDEV ppdev;
DC_BEGIN_FN("OE_SendGlyphs");
rc = FALSE;
// Rasterized fonts are bitmaps - others are PATHOBJ structures.
if (pfo->flFontType & RASTER_FONTTYPE) { ppdev = (PDD_PDEV)pso->dhpdev; pddCacheStats[GLYPH].CacheReads += pstro->cGlyphs;
// Make sure we don't exceed our max glyph_out capacity.
if (pstro->cGlyphs <= OE_GL_MAX_INDEX_ENTRIES) { // The system can only handle 'simple' brushes.
if (OECheckBrushIsSimple(ppdev, pboOpaque, &pbdOpaque)) { // Get the text fore color.
OEConvertColor(ppdev, &pbdOpaque->back, pboFore->iSolidColor, NULL);
// Initialize our glyph context structure.
glc.fontId = pfci->fontId; glc.cacheTag = oeTextOut++;
glc.nCacheHit = 0; glc.nCacheIndex = 0; glc.indexNextSend = 0; glc.cbDataSize = 0; glc.cbTotalDataSize = 0; glc.cbBufferSize = 0;
// Cache all glyphs for this message .
if (OECacheGlyphs(pstro, pfo, pfci, &glc)) { // Send all newly cached glyphs to the client.
// If this is single glyph and we support fast glyph order
// and the glyph data length can fit in one byte. We will
// send the glyph index and data in one order. Note we
// can bypass fragment caching in this case since we don't
// cache fragment of length less than 3 glyphs.
if (OE_SendAsOrder(TS_ENC_FAST_GLYPH_ORDER) && (pstro->cGlyphs == 1) && (glc.cbTotalDataSize + sizeof(UINT16)) <= FIELDSIZE(VARIABLE_GLYPHBYTES, glyphData)) { rc = OESendGlyphAndIndexOrder(ppdev, pstro, pClipRects, prclOpaque, pbdOpaque, pfci, &glc); } else { if (OESendGlyphs(pso, pstro, pfo, pfci, &glc)) { // Send glyph index orders to the client.
rc = OESendIndexes(pso, pstro, pfo, pClipRects, prclOpaque, pbdOpaque, pptlOrg, pfci, &glc); } } } } } }
DC_END_FN(); return rc; }
/****************************************************************************/ // DrvBitBlt - see NT DDK documentation.
/****************************************************************************/ BOOL RDPCALL DrvBitBlt( SURFOBJ *psoTrg, SURFOBJ *psoSrc, SURFOBJ *psoMask, CLIPOBJ *pco, XLATEOBJ *pxlo, RECTL *prclTrg, POINTL *pptlSrc, POINTL *pptlMask, BRUSHOBJ *pbo, POINTL *pptlBrush, ROP4 rop4) { PDD_PDEV ppdev = (PDD_PDEV)psoTrg->dhpdev; PDD_DSURF pdsurfTrg = NULL; PDD_DSURF pdsurfSrc = NULL; SURFOBJ *psoSrcArg = NULL; SURFOBJ *psoTrgArg = NULL; BOOL rc; BYTE rop3; RECTL bounds; OE_ENUMRECTS ClipRects;
DC_BEGIN_FN("DrvBitBlt");
// Sometimes we're called after being disconnected.
if (ddConnected && pddShm != NULL) { psoSrcArg = psoSrc; psoTrgArg = psoTrg; psoTrg = OEGetSurfObjBitmap(psoTrg, &pdsurfTrg); if (psoSrc != NULL) psoSrc = OEGetSurfObjBitmap(psoSrc, &pdsurfSrc);
DD_UPD_STATE(DD_BITBLT); INC_OUTCOUNTER(OUT_BITBLT_ALL);
if (((pco == NULL) && (psoTrg->sizlBitmap.cx >= prclTrg->right) && (psoTrg->sizlBitmap.cy >= prclTrg->bottom)) || ((pco != NULL) && (psoTrg->sizlBitmap.cx >= pco->rclBounds.right) && (psoTrg->sizlBitmap.cy >= pco->rclBounds.bottom))) { // Punt the call back to GDI to do the drawing.
rc = EngBitBlt(psoTrg, psoSrc, psoMask, pco, pxlo, prclTrg, pptlSrc, pptlMask, pbo, pptlBrush, rop4); } else { // If the bounding rectangle is greater the frame buffer, something
// is really wrong here. This means the desktop surface size and
// the framebuffer is not matched up. We need to bail out here.
rc = FALSE; }
if (rc) { TRC_DBG((TB, "ppdev(%p) psoSrc(%p) psoTrg(%p)" "s[sizl(%d,%d) format(%d) type(%d)]" "d[sizl(%d,%d) format(%d) type(%d)]" "src(%d,%d) dst(%d,%d,%d,%d), rop %#x", ppdev, psoSrc, psoTrg, (psoSrc != NULL) ? psoSrc->sizlBitmap.cx : -1, (psoSrc != NULL) ? psoSrc->sizlBitmap.cy : -1, (psoSrc != NULL) ? psoSrc->iBitmapFormat : -1, (psoSrc != NULL) ? psoSrc->iType : -1, (psoTrg != NULL) ? psoTrg->sizlBitmap.cx : -1, (psoTrg != NULL) ? psoTrg->sizlBitmap.cy : -1, (psoTrg != NULL) ? psoTrg->iBitmapFormat : -1, (psoTrg != NULL) ? psoTrg->iType : -1, (pptlSrc != NULL) ? pptlSrc->x : -1, (pptlSrc != NULL) ? pptlSrc->y : -1, prclTrg->left, prclTrg->top, prclTrg->right, prclTrg->bottom, rop4));
// If ppdev is NULL then this is a blt to GDI managed memory bitmap,
// so there is no need to accumulate any output.
if (ppdev != NULL) { // Get the bounding rectangle for the operation. According to
// the DDK, this rectangle is always well-ordered and does not
// need to be rearranged. Clip it to 16 bits.
bounds = *prclTrg; OEClipRect(&bounds);
// If this function is changed, need to know that psoTrg points
// to the GDI DIB bitmap in offscreen bitmap case.
} else { // if ppdev is NULL, we are blt to GDI managed bitmap,
// so, the dhurf of the target surface should be NULL
TRC_ASSERT((pdsurfTrg == NULL), (TB, "NULL ppdev - psoTrg has non NULL dhsurf"));
TRC_NRM((TB, "NULL ppdev - blt to GDI managed bitmap")); DC_QUIT; } } else { TRC_ERR((TB, "EngBitBlt failed")); DC_QUIT; } } else { if (psoTrg->iType == STYPE_DEVBITMAP) { psoTrg = OEGetSurfObjBitmap(psoTrg, &pdsurfTrg); if (psoSrc != NULL) psoSrc = OEGetSurfObjBitmap(psoSrc, &pdsurfSrc); // Punt the call back to GDI to do the drawing.
rc = EngBitBlt(psoTrg, psoSrc, psoMask, pco, pxlo, prclTrg, pptlSrc, pptlMask, pbo, pptlBrush, rop4); } else { TRC_ERR((TB, "Called when disconnected")); rc = TRUE; }
goto CalledOnDisconnected; }
if ((psoTrg->hsurf == ppdev->hsurfFrameBuf) || (!(pdsurfTrg->flags & DD_NO_OFFSCREEN))) { // Send a switch surface PDU if the destination surface is different
// from last drawing order. If we failed to send the PDU, we will
// just have to bail on this drawing order.
if (!OESendSwitchSurfacePDU(ppdev, pdsurfTrg)) { TRC_ERR((TB, "failed to send the switch surface PDU")); DC_QUIT; } } else { // If noOffscreen flag is on, we will bail on sending
// the client any further offscreen rendering. And we'll send the
// final offscreen to screen blt as regular memblt.
TRC_NRM((TB, "Offscreen blt bail")); INC_OUTCOUNTER(OUT_BITBLT_NOOFFSCR); DC_QUIT; }
// Check if this 4-way ROP simplifies to a 3-way ROP. A 4-way ROP
// contains two 3-way ROPS, one for each setting of a mask bit - the
// high ROP3 corresponds to a value of zero in the mask bit. If the two
// 3-way ROPs are the same, we know the 4-way ROP is a 3-way ROP.
rop3 = ROP3_HIGH_FROM_ROP4(rop4); if (ROP3_LOW_FROM_ROP4(rop4) == rop3) { // Take the high byte as the 3-way ROP.
TRC_DBG((TB, "4-way ROP %04x is really 3-way %02x", rop4, rop3));
// Check if we are allowed to send the ROP.
if (OESendRop3AsOrder(rop3)) { unsigned RetVal;
// Get the intersection between the dest rect and the
// clip rects. Check for overcomplicated or nonintersecting
// clipping.
RetVal = OEGetIntersectingClipRects(pco, &bounds, CD_ANY, &ClipRects); if (RetVal == CLIPRECTS_TOO_COMPLEX) { TRC_NRM((TB, "Clipping is too complex")); INC_OUTCOUNTER(OUT_BITBLT_SDA_COMPLEXCLIP); if (oeLastDstSurface == NULL) ADD_INCOUNTER(IN_SDA_BITBLT_COMPLEXCLIP_AREA, COM_SIZEOF_RECT(bounds)); goto SendScreenData; } else if (RetVal == CLIPRECTS_NO_INTERSECTIONS) { TRC_NRM((TB, "Clipping does not intersect destrect")); DC_QUIT; } #ifdef PERF_SPOILING
else if (psoTrg->hsurf == ppdev->hsurfFrameBuf) { // This is the case where the bitblt lies completely within
// the current screen-data dirty-rect, so we can just send
// it as screendata. (Actually, it's benign to goto
// SendScreenData, since the new RECT will just be collapsed
// into the current screen-data dirty-rects.)
if (ClipRects.rects.c==0) { if (OEIsSDAIncluded(&bounds, 1)) { goto SendScreenData; } } else { if (OEIsSDAIncluded(&ClipRects.rects.arcl[0], ClipRects.rects.c)) { goto SendScreenData; } } } #endif
} else { TRC_NRM((TB, "Cannot send ROP %d", rop3)); INC_OUTCOUNTER(OUT_BITBLT_SDA_NOROP3); if (oeLastDstSurface == NULL) ADD_INCOUNTER(IN_SDA_BITBLT_NOROP3_AREA, COM_SIZEOF_RECT(bounds)); goto SendScreenData; } } else { TRC_NRM((TB, "4-way ROP %08x", rop4)); INC_OUTCOUNTER(OUT_BITBLT_SDA_ROP4); if (oeLastDstSurface == NULL) ADD_INCOUNTER(IN_SDA_BITBLT_ROP4_AREA, COM_SIZEOF_RECT(bounds)); goto SendScreenData; }
// Determine the blt type. It can be one of the following. Note that the
// following if statements are carefully tuned based on the most common
// blt types seen in WinStone/WinBench, to minimize mispredictions.
// In order of frequency:
//
// OpaqueRect: A destination-only blt where the output bits are overlaid
// on the output screen and the pattern is a solid color.
// PatBlt: An OpaqueRect with a non-solid-color pattern.
// MemBlt: A memory-to-memory/screen blt with no pattern.
// Mem3Blt: A memory-to-memory/screen blt with an accompanying pattern.
// DstBlt: Destination-only blt; the output is dependent on the screen
// contents.
// ScrBlt: A screen-to-screen blt (copy screen contents).
// Check for destination only BLTs (ie. independent of source bits).
if ((psoSrc == NULL) || ROP3_NO_SOURCE(rop3)) { // Check for a pattern or true destination BLT.
if (!ROP3_NO_PATTERN(rop3)) { // Check whether we can encode the PatBlt as an OpaqueRect.
// It must be solid with a PATCOPY rop.
if (pbo->iSolidColor != -1 && rop3 == OE_PATCOPY_ROP) { if (!OEEncodeOpaqueRect(&bounds, pbo, ppdev, &ClipRects)) { // Something went wrong with the encoding, so skip
// to the end to add this operation to the SDA.
if (oeLastDstSurface == NULL) ADD_INCOUNTER(IN_SDA_OPAQUERECT_AREA, COM_SIZEOF_RECT(bounds)); goto SendScreenData; } } else if (!OEEncodePatBlt(ppdev, pbo, &bounds, pptlBrush, rop3, &ClipRects)) { // Something went wrong with the encoding, so skip to
// the end to add this operation to the SDA.
if (oeLastDstSurface == NULL) ADD_INCOUNTER(IN_SDA_PATBLT_AREA, COM_SIZEOF_RECT(bounds)); goto SendScreenData; } } else { if (!OEEncodeDstBlt(&bounds, rop3, ppdev, &ClipRects)) { if (oeLastDstSurface == NULL) ADD_INCOUNTER(IN_SDA_DSTBLT_AREA, COM_SIZEOF_RECT(bounds)); goto SendScreenData; } } } else { // We have a source BLT, check whether we have screen or memory BLTs.
if (psoSrc->hsurf != ppdev->hsurfFrameBuf) { // The source surface is memory, so this is either memory to screen
// blt or memory to offscreen blt
if (psoTrg->hsurf == ppdev->hsurfFrameBuf || pdsurfTrg != NULL) { // We only support destination surface as the screen surface
// or driver managed offscreen surface
unsigned OffscrBitmapId = 0; MEMBLT_ORDER_EXTRA_INFO MemBltExtraInfo;
// Fill in extra info structure.
MemBltExtraInfo.pSource = psoSrc; MemBltExtraInfo.pDest = psoTrg; MemBltExtraInfo.pXlateObj = pxlo; MemBltExtraInfo.bNoFastPathCaching = FALSE; MemBltExtraInfo.iDeviceUniq = psoSrcArg ? (psoSrcArg->iUniq) : 0; #ifdef PERF_SPOILING
MemBltExtraInfo.bIsPrimarySurface = (psoTrg->hsurf == ppdev->hsurfFrameBuf); #endif
if (pdsurfSrc != NULL && (psoTrg->hsurf == ppdev->hsurfFrameBuf || pdsurfSrc == pdsurfTrg)) { if ((pddShm->sbc.offscreenCacheInfo.supportLevel > TS_OFFSCREEN_DEFAULT) && (sbcEnabled & SBC_OFFSCREEN_CACHE_ENABLED)) {
if (pdsurfSrc->shareId == pddShm->shareId) { // We are blting from an offscreen surface to client screen,
// Or from one area of an offscreen to another area of the
// same offscreen bitmap
if (!(pdsurfSrc->flags & DD_NO_OFFSCREEN)) { OffscrBitmapId = pdsurfSrc->bitmapId; CH_TouchCacheEntry(sbcOffscreenBitmapCacheHandle, OffscrBitmapId); } else { // If the source surface is offscreen surface, and we
// have the noOffscreen flag on, this means we will
// send the bitmap bits as regular memory bitmap bits
// This means that the offscreen bitmap has been evicted
// out of the offscreen cache or screen data needs to be
// sent for the offscreen bitmap
TRC_ALT((TB, "noOffscreen flag is on for %p", pdsurfSrc)); OffscrBitmapId = CH_KEY_UNCACHABLE; } } 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")); pdsurfSrc->flags |= DD_NO_OFFSCREEN; OffscrBitmapId = CH_KEY_UNCACHABLE; } } else { // These are offscreen bitmaps from the disconnected session
// or client has sent an error pdu,
// We have to treat them as memory bitmap now since the client
// doesn't have the offscreen bitmap locally
TRC_ALT((TB, "Need to turn off this offscreen bitmap")); pdsurfSrc->flags |= DD_NO_OFFSCREEN; OffscrBitmapId = CH_KEY_UNCACHABLE; } } else { OffscrBitmapId = CH_KEY_UNCACHABLE; }
// We send MemBlts for clients that allow offscreen rendering
// or if iUniq is nonzero. Zero iUniq is supposed to be a GDI
// hack in NT5 that tells us that Windows Layering for window
// borders is being used. We would want to send screen data
// for these cases to prevent flushing the bitmap cache.
// Unfortunately, quite a few bitmaps seem to also have
// iUniq==0, so for 5.1 clients using offscreen we save some
// bandwidth instead of sending screen data. The Windows
// Layering stuff uses offscreen rendering anyway...
if (psoSrcArg->iUniq != 0) { // We have a memory to screen BLT, check which type.
if (ROP3_NO_PATTERN(rop3)) { // Make sure orders are not turned off.
if (OE_SendAsOrder(TS_ENC_MEMBLT_R2_ORDER)) { if (!OEEncodeMemBlt(&bounds, &MemBltExtraInfo, TS_ENC_MEMBLT_R2_ORDER, OffscrBitmapId, rop3, pptlSrc, pptlBrush, pbo, ppdev, &ClipRects)) { if (oeLastDstSurface == NULL) ADD_INCOUNTER(IN_SDA_MEMBLT_AREA, COM_SIZEOF_RECT(bounds)); goto SendScreenData; } } else { TRC_NRM((TB, "MemBlt order not allowed")); INC_OUTCOUNTER(OUT_BITBLT_SDA_UNSUPPORTED); goto SendScreenData; } } else { // Make sure orders are not turned off.
if (OE_SendAsOrder(TS_ENC_MEM3BLT_R2_ORDER)) { if (!OEEncodeMemBlt(&bounds, &MemBltExtraInfo, TS_ENC_MEM3BLT_R2_ORDER, OffscrBitmapId, rop3, pptlSrc, pptlBrush, pbo, ppdev, &ClipRects)) { if (oeLastDstSurface == NULL) ADD_INCOUNTER(IN_SDA_MEM3BLT_AREA, COM_SIZEOF_RECT(bounds)); goto SendScreenData; } } else { TRC_NRM((TB, "Mem3Blt order not allowed")); INC_OUTCOUNTER(OUT_BITBLT_SDA_UNSUPPORTED); goto SendScreenData; } } } else { // To avoid Windows Layering Mem to Mem Blt flush bitmap caches
// on the client, we have to send it as screen data instead
TRC_NRM((TB, "Get a windows layering mem-mem blt, " "send as screen data")); INC_OUTCOUNTER(OUT_BITBLT_SDA_WINDOWSAYERING); goto SendScreenData; } } else { TRC_ALT((TB, "Unsupported MEM to MEM blt!")); DC_QUIT; } } else { // The source surface is screen, so this is either screen to screen
// blt or screen to offscreen memory blt
if (psoTrg->hsurf == ppdev->hsurfFrameBuf || pdsurfTrg != NULL) { // We only support destination only screen BLTs (ie. no
// patterns allowed).
if (ROP3_NO_PATTERN(rop3)) { if (!OEEncodeScrBlt(&bounds, rop3, pptlSrc, ppdev, &ClipRects, pco)) { if (oeLastDstSurface == NULL) ADD_INCOUNTER(IN_SDA_SCRBLT_AREA, COM_SIZEOF_RECT(bounds)); goto SendScreenData; } } else { TRC_ALT((TB, "Unsupported screen ROP %x", rop3)); if (oeLastDstSurface == NULL) ADD_INCOUNTER(IN_SDA_SCRSCR_FAILROP_AREA, COM_SIZEOF_RECT(bounds)); INC_OUTCOUNTER(OUT_BITBLT_SDA_UNSUPPORTED); goto SendScreenData; } } else { TRC_NRM((TB, "Unsupported SCR to MEM blt!")); DC_QUIT; } } }
// We added an order to the list, increment global counts.
goto PostSDA;
SendScreenData: if (psoTrg->hsurf == ppdev->hsurfFrameBuf) { INC_OUTCOUNTER(OUT_BITBLT_SDA); OEClipAndAddScreenDataArea(&bounds, pco); } else { // if we can't send orders for offscreen rendering, we will
// bail offscreen support for this bitmap
TRC_ALT((TB, "screen data call for offscreen rendering"));
// Remove the bitmap from the offscreen bitmap cache
if (!(pdsurfTrg->flags & DD_NO_OFFSCREEN)) CH_RemoveCacheEntry(sbcOffscreenBitmapCacheHandle, pdsurfTrg->bitmapId);
DC_QUIT; }
PostSDA: SCH_DDOutputAvailable(ppdev, FALSE);
DC_EXIT_POINT: // EngStretchBlt called from DrvStretchBlt sometimes calls DrvBitBlt to
// do its drawing. Clear the flag here to tell DrvStretchBlt that it
// doesn't need to send any output.
oeAccumulateStretchBlt = FALSE;
CalledOnDisconnected: DC_END_FN(); return rc; }
/****************************************************************************/ // DrvStretchBlt - see NT DDK documentation.
/****************************************************************************/ BOOL RDPCALL DrvStretchBlt( SURFOBJ *psoTrg, SURFOBJ *psoSrc, SURFOBJ *psoMask, CLIPOBJ *pco, XLATEOBJ *pxlo, COLORADJUSTMENT *pca, POINTL *pptlHTOrg, RECTL *prclTrg, RECTL *prclSrc, POINTL *pptlMask, ULONG iMode) { PDD_PDEV ppdev = (PDD_PDEV)psoTrg->dhpdev; PDD_DSURF pdsurfTrg = NULL; PDD_DSURF pdsurfSrc = NULL; SURFOBJ *psoTrgBitmap, *psoSrcBitmap; BOOL rc = TRUE; POINTL ptlSrc; RECTL rclTrg; int bltWidth; int bltHeight; OE_ENUMRECTS ClipRects; MEMBLT_ORDER_EXTRA_INFO MemBltExtraInfo;
DC_BEGIN_FN("DrvStretchBlt");
// psoTrg and psoSrc should not be NULL.
psoTrgBitmap = OEGetSurfObjBitmap(psoTrg, &pdsurfTrg); psoSrcBitmap = OEGetSurfObjBitmap(psoSrc, &pdsurfSrc);
// Sometimes, we're called after being disconnected. This is a pain,
// but we can trap it here.
if (ddConnected && pddShm != NULL) { INC_OUTCOUNTER(OUT_STRTCHBLT_ALL);
// Get the destination rectangle, ordering it properly if necessary.
// Note we don't have to order the src rect -- according to the
// DDK it is guaranteed well-ordered. Clip the result to 16 bits.
RECT_FROM_RECTL(rclTrg, (*prclTrg)); OEClipRect(&rclTrg);
if ((psoTrgBitmap->hsurf == ppdev->hsurfFrameBuf) || (!(pdsurfTrg->flags & DD_NO_OFFSCREEN))) { // Send a switch surface PDU if the destination surface is different
// from last drawing order. If we failed to send the PDU, we will
// just have to bail on this drawing order.
if (!OESendSwitchSurfacePDU(ppdev, pdsurfTrg)) { TRC_ERR((TB, "failed to send the switch surface PDU")); goto SendSDA; } } else { // if noOffscreen flag is on, we will bail on sending
// the client any further offscreen rendering. And will send the
// final offscreen to screen blt as regular memblt.
TRC_NRM((TB, "Offscreen blt bail")); goto SendSDA; }
// Check that we have a valid ROP code. The NT DDK states that
// the ROP code for the StretchBlt is implicit in the mask
// specification. If a mask is specified, we have an implicit
// ROP4 of 0xCCAA, otherwise the code is 0xCCCC.
//
// Our BitBlt code only encodes orders for ROP3s, so we must
// throw any StretchBlts with a mask.
if (psoMask == NULL) { unsigned RetVal;
// Get the intersection between the dest rect and the
// clip rects. Check for overcomplicated or nonintersecting
// clipping.
RetVal = OEGetIntersectingClipRects(pco, &rclTrg, CD_ANY, &ClipRects); if (RetVal == CLIPRECTS_TOO_COMPLEX) { TRC_NRM((TB, "Clipping is too complex")); INC_OUTCOUNTER(OUT_STRTCHBLT_SDA_COMPLEXCLIP); goto SendSDA; } else if (RetVal == CLIPRECTS_NO_INTERSECTIONS) { TRC_NRM((TB, "Clipping does not intersect destrect")); goto PostSDA; } } else { TRC_NRM((TB, "Mask specified")); INC_OUTCOUNTER(OUT_STRTCHBLT_SDA_MASK); goto SendSDA; } } else { if (psoTrg->iType == STYPE_DEVBITMAP) { rc = EngStretchBlt(psoTrgBitmap, psoSrcBitmap, psoMask, pco, pxlo, pca, pptlHTOrg, prclTrg, prclSrc, pptlMask, iMode); } else { TRC_ERR((TB, "Called when disconnected")); } goto CalledOnDisconnected; }
// DrvStretchBlt can be called with unclipped coords, but we need clipped
// coords. We must therefore perform clipping here to avoid faults in
// callbacks to EngBitBlt from DrvBitBlt.
// First clip the destination rect to the destination surface.
ptlSrc.x = prclSrc->left; ptlSrc.y = prclSrc->top; if (rclTrg.left < 0) { ptlSrc.x += (-rclTrg.left); rclTrg.left = 0; TRC_NRM((TB, "Clip trg left")); } if (rclTrg.top < 0) { ptlSrc.y += (-rclTrg.top); rclTrg.top = 0; TRC_NRM((TB, "Clip trg top")); }
// We need to clip to the screen size instead of the size of psoTrg
// (the screen surface) here - after reconnection at lower resolution
// psoTrg->sizlBitmap can be larger than the real screen size.
rclTrg.right = min(rclTrg.right, ppdev->cxScreen); rclTrg.bottom = min(rclTrg.bottom, ppdev->cyScreen);
// Check if we have a degenerate (ie. no stretch) case. Use the
// original coords, because it is possible for one of the rects to
// be flipped to perform an inverted blt.
if ((prclSrc->right - prclSrc->left == prclTrg->right - prclTrg->left) && (prclSrc->bottom - prclSrc->top == prclTrg->bottom - prclTrg->top)) { // Adjust the destination blt size to keep the source rect within
// the source bitmap, if necessary. Note this should be done here
// instead of before determining 1:1 stretch since the amount to
// change rclTrg by would vary according to the stretch ratio.
if (ptlSrc.x < 0) { rclTrg.left += (-ptlSrc.x); ptlSrc.x = 0; TRC_NRM((TB, "Clip src left")); } if (ptlSrc.y < 0) { rclTrg.top += (-ptlSrc.y); ptlSrc.y = 0; TRC_NRM((TB, "Clip src top")); }
bltWidth = rclTrg.right - rclTrg.left; if ((ptlSrc.x + bltWidth) > psoSrcBitmap->sizlBitmap.cx) { rclTrg.right -= ((ptlSrc.x + bltWidth) - psoSrcBitmap->sizlBitmap.cx); TRC_NRM((TB, "Clip src right")); }
bltHeight = rclTrg.bottom - rclTrg.top; if ((ptlSrc.y + bltHeight) > psoSrcBitmap->sizlBitmap.cy) { rclTrg.bottom -= ((ptlSrc.y + bltHeight) - psoSrcBitmap->sizlBitmap.cy); TRC_NRM((TB, "Clip src bottom")); }
// Check again for complete clipping out.
if (rclTrg.right > rclTrg.left && rclTrg.bottom > rclTrg.top) { INC_OUTCOUNTER(OUT_STRTCHBLT_BITBLT); rc = DrvBitBlt(psoTrg, psoSrc, psoMask, pco, pxlo, &rclTrg, &ptlSrc, pptlMask, NULL, NULL, 0xCCCC); } else { TRC_NRM((TB, "StretchBlt completely clipped")); }
goto PostSDA; } else { // Non-degenerate case -- we are really stretching.
// Here we simply blt to the screen, then do a bitblt specifying the
// destination rect to the screen as the source rect.
// EngStretchBlt sometimes calls DrvBitBlt to do its drawing. Set
// the flag here before we call to default to sending SDA output.
// If DrvBitBlt has done all the processing it needs to it will
// clear the flag.
oeAccumulateStretchBlt = TRUE;
rc = EngStretchBlt(psoTrgBitmap, psoSrcBitmap, psoMask, pco, pxlo, pca, pptlHTOrg, prclTrg, prclSrc, pptlMask, iMode); if (rc && oeAccumulateStretchBlt) { // DrvBitBlt was not called already, and we're drawing to our
// screen surface.
// Fill in extra info structure. Note NULL pxlo meaning no
// color translation -- we're drawing from screen to screen.
// Also, because we are caching directly from the screen
// we need to turn off fast-path caching -- sometimes we
// get multiple StretchBlts to nearby areas of the screen,
// where we may fast-path cache a block that is drawn again
// on a successive StretchBlt, thereby drawing the wrong tile
// at the client.
MemBltExtraInfo.pSource = psoTrgBitmap; MemBltExtraInfo.pDest = psoTrgBitmap; MemBltExtraInfo.pXlateObj = NULL; MemBltExtraInfo.bNoFastPathCaching = TRUE; MemBltExtraInfo.iDeviceUniq = psoTrg ? (psoTrg->iUniq) : 0; #ifdef PERF_SPOILING
MemBltExtraInfo.bIsPrimarySurface = (psoTrgBitmap->hsurf == ppdev->hsurfFrameBuf); #endif
// Make sure orders are not turned off.
if (OE_SendAsOrder(TS_ENC_MEMBLT_R2_ORDER)) { // Note pco is clip obj for destination and so is applicable
// here. We also use ROP3 of 0xCC meaning copy src->dest.
if (!OEEncodeMemBlt(&rclTrg, &MemBltExtraInfo, TS_ENC_MEMBLT_R2_ORDER, CH_KEY_UNCACHABLE, 0xCC, (PPOINTL)&rclTrg.left, NULL, NULL, ppdev, &ClipRects)) goto SendSDAPostEngStretchBlt; } else { TRC_NRM((TB, "MemBlt order not allowed")); INC_OUTCOUNTER(OUT_BITBLT_SDA_UNSUPPORTED); goto SendSDAPostEngStretchBlt; } }
goto PostSDA; }
SendSDA: // Accumulate screen data if necessary. EngStretchBlt may have
// called DrvCopyBits or DrvBitblt to do the work. These two will
// have accumulated the data, so no need to do it here.
TRC_NRM((TB, "***Add SDA for STRETCHBLT"));
// EngStretchBlt sometimes calls DrvBitBlt to do its drawing. Set
// the flag here before we call to default to sending SDA output.
// If DrvBitBlt has done all the processing it needs to it will
// clear the flag.
oeAccumulateStretchBlt = TRUE; rc = EngStretchBlt(psoTrgBitmap, psoSrcBitmap, psoMask, pco, pxlo, pca, pptlHTOrg, prclTrg, prclSrc, pptlMask, iMode);
if (oeAccumulateStretchBlt) {
SendSDAPostEngStretchBlt:
if (psoTrgBitmap->hsurf == ppdev->hsurfFrameBuf) { INC_OUTCOUNTER(OUT_STRTCHBLT_SDA); SCH_DDOutputAvailable(ppdev, FALSE); } else { // if we can't send orders for offscreen rendering, we will
// bail offscreen support for this bitmap
TRC_ALT((TB, "screen data call for offscreen rendering")); if (!(pdsurfTrg->flags & DD_NO_OFFSCREEN)) CH_RemoveCacheEntry(sbcOffscreenBitmapCacheHandle, pdsurfTrg->bitmapId); } } PostSDA: CalledOnDisconnected: DC_END_FN(); return rc; }
/****************************************************************************/ // DrvCopyBits - see NT DDK documentation.
/****************************************************************************/ BOOL RDPCALL DrvCopyBits( SURFOBJ *psoTrg, SURFOBJ *psoSrc, CLIPOBJ *pco, XLATEOBJ *pxlo, RECTL *prclTrg, POINTL *pptlSrc) { BOOL rc;
DC_BEGIN_FN("DrvCopyBits");
if (ddConnected) { INC_OUTCOUNTER(OUT_COPYBITS_ALL);
// CopyBits is a fast path for the NT display drivers. In our case it
// can always be processed as a BitBlt with copy ROP.
rc = DrvBitBlt(psoTrg, psoSrc, NULL, pco, pxlo, prclTrg, pptlSrc, NULL, NULL, NULL, 0xCCCC); } else { PDD_DSURF pdsurfTrg; PDD_DSURF pdsurfSrc;
TRC_NRM((TB, "Called when disconnected"));
TRC_ASSERT((psoSrc != NULL),(TB,"NULL source surface!"));
// We can get called by GDI after disconnection to translate offscreen
// bitmap surfaces from the DD-specific representation to a GDI surface,
// for reuse with a different DD. Most often this occurs with Personal
// TS switching between a remote DD, the disconnected DD (tsddd.dll),
// and a hardware DD. For this case we really do need to do the
// CopyBits action. So, we have GDI do it for us since we're really
// already having GDI manage our "internal" representation.
psoTrg = OEGetSurfObjBitmap(psoTrg, &pdsurfTrg); psoSrc = OEGetSurfObjBitmap(psoSrc, &pdsurfSrc);
rc = EngCopyBits(psoTrg, psoSrc, pco, pxlo, prclTrg, pptlSrc); if (!rc) { TRC_ERR((TB,"Post-disc copy: rc=FALSE")); }
// Must return TRUE to ensure that the PTS console will reconnect
// properly, otherwise the user's machine gets stuck in limbo.
rc = TRUE; }
DC_END_FN(); return rc; }
/***************************************************************************/ // OE_SendCreateOffscrBitmapOrder
//
// Send create offscreen bitmap request to client
/***************************************************************************/ BOOL RDPCALL OE_SendCreateOffscrBitmapOrder( PDD_PDEV ppdev, SIZEL sizl, ULONG iFormat, unsigned clientBitmapId) { BOOL rc; unsigned cbOrderSize, bitmapSize; PINT_ORDER pOrder; PTS_CREATE_OFFSCR_BITMAP_ORDER pOffscrBitmapOrder;
DC_BEGIN_FN("OE_SendCreateOffscrBitmapOrder");
// Get the current bitmap Size
if (iFormat < 5) { bitmapSize = sizl.cx * sizl.cy * (1 << iFormat) / 8; } else if (iFormat == 5) { bitmapSize = sizl.cx * sizl.cy * 24 / 8; } else if (iFormat == 6) { bitmapSize = sizl.cx * sizl.cy * 32 / 8; } else { TRC_NRM((TB, "Bitmap format not supported")); return FALSE; }
// The last entry in the delete list will always be the entry we are using
// for create this bitmap. So, remove this from the delete list.
if (sbcNumOffscrBitmapsToDelete) { TRC_ASSERT((sbcOffscrBitmapsDelList[sbcNumOffscrBitmapsToDelete-1].bitmapId == clientBitmapId), (TB, "different bitmap id")); sbcOffscrBitmapsToDeleteSize -= sbcOffscrBitmapsDelList[sbcNumOffscrBitmapsToDelete - 1].bitmapSize; sbcNumOffscrBitmapsToDelete--; }
// check if we need to send the delete bitmap list. We only need to
// send the list if we are about to exceed client offscreen cache size
// limit.
if (bitmapSize + oeCurrentOffscreenCacheSize + sbcOffscrBitmapsToDeleteSize <= (pddShm->sbc.offscreenCacheInfo.cacheSize * 1024)) { cbOrderSize = sizeof(TS_CREATE_OFFSCR_BITMAP_ORDER) - sizeof(pOffscrBitmapOrder->variableBytes); } else { // Note we use the UINT16 at variableBytes for the number of
// bitmaps. Hence, we don't subtract the size of variableBytes here.
cbOrderSize = sizeof(TS_CREATE_OFFSCR_BITMAP_ORDER) + sizeof(UINT16) * sbcNumOffscrBitmapsToDelete; }
pOrder = OA_AllocOrderMem(ppdev, cbOrderSize); if (pOrder != NULL) { // Fill in the details. This is an alternate secondary order
// type.
pOffscrBitmapOrder = (PTS_CREATE_OFFSCR_BITMAP_ORDER)pOrder->OrderData; pOffscrBitmapOrder->ControlFlags = (TS_ALTSEC_CREATE_OFFSCR_BITMAP << TS_ALTSEC_ORDER_TYPE_SHIFT) | TS_SECONDARY; pOffscrBitmapOrder->Flags = (UINT16)clientBitmapId; pOffscrBitmapOrder->cx = (UINT16)sizl.cx; pOffscrBitmapOrder->cy = (UINT16)sizl.cy;
// Send the delete bitmap list
if (cbOrderSize > sizeof(TS_CREATE_OFFSCR_BITMAP_ORDER)) { PUINT16_UA pData; unsigned i;
// This flag indicates that the delete bitmap list is appended.
pOffscrBitmapOrder->Flags |= 0x8000; pData = (PUINT16_UA)pOffscrBitmapOrder->variableBytes; *pData++ = (UINT16)sbcNumOffscrBitmapsToDelete;
for (i = 0; i < sbcNumOffscrBitmapsToDelete; i++) *pData++ = (UINT16)sbcOffscrBitmapsDelList[i].bitmapId;
// Reset the flags.
sbcNumOffscrBitmapsToDelete = 0; sbcOffscrBitmapsToDeleteSize = 0; }
INC_OUTCOUNTER(OUT_OFFSCREEN_BITMAP_ORDER); ADD_OUTCOUNTER(OUT_OFFSCREEN_BITMAP_ORDER_BYTES, cbOrderSize); OA_AppendToOrderList(pOrder); rc = TRUE; } else { TRC_ERR((TB,"Unable to alloc heap space")); rc = FALSE; }
DC_END_FN(); return rc; }
/****************************************************************************/ // DrvCreateDeviceBitmap - See NT DDK for documentation
/****************************************************************************/ HBITMAP DrvCreateDeviceBitmap(DHPDEV dhpdev, SIZEL sizl, ULONG iFormat) { PDD_PDEV ppdev; PDD_DSURF pdsurf; HBITMAP hbmDevice = NULL; HBITMAP hbmDib; FLONG flHooks; SURFOBJ *pso; unsigned bitmapSize; ULONG iFormatArg = iFormat;
DC_BEGIN_FN("DrvCreateDeviceBitmap"); if (ddConnected && pddShm != NULL) { INC_OUTCOUNTER(OUT_OFFSCREEN_BITMAP_ALL);
ppdev = (PDD_PDEV) dhpdev;
if (ddConsole) { //
// For the console case, since we accept any Format by overwritting it,
// we accept Format=1 (1bpp). This case is not fully supported by GDI.
// So skip it and do as a regular remote session.
//
if (iFormat == 1) DC_QUIT;
iFormat = ppdev->iBitmapFormat; }
if (OEDeviceBitmapCachable(ppdev, sizl, iFormat)) { if (iFormat < 5) bitmapSize = sizl.cx * sizl.cy * (1 << iFormat) / 8; else if (iFormat == 5) bitmapSize = sizl.cx * sizl.cy * 24 / 8; else if (iFormat == 6) bitmapSize = sizl.cx * sizl.cy * 32 / 8; else { TRC_NRM((TB, "Bitmap format not supported")); DC_QUIT; } goto CreateBitmap; } else { TRC_DBG((TB, "OEDeviceBitmapCachable returns FALSE")); DC_QUIT; } } else { //TRC_DBG((TB, "Call on disconnected"));
DC_QUIT; }
CreateBitmap:
// Create the device surface for this offscreen bitmap.
// This device surface handle is used to identify the offscreen
// bitmap in all DrvXXX calls.
pdsurf = EngAllocMem(FL_ZERO_MEMORY, sizeof(DD_DSURF), DD_ALLOC_TAG);
if (pdsurf != NULL) { // initialize the DD_DSURF fields
memset(pdsurf, 0, sizeof(DD_DSURF));
// Create a device bitmap for this offscreen bitmap
hbmDevice = EngCreateDeviceBitmap((DHSURF) pdsurf, sizl, iFormat);
if (hbmDevice != NULL) { // Get the flHooks flag from the PDEV struct
flHooks = ppdev->flHooks;
// Associate the bitmap to the PDEV device
if (EngAssociateSurface((HSURF) hbmDevice, ppdev->hdevEng, flHooks)) { // Create a DIB backup bitmap for this offscreen bitmap
hbmDib = EngCreateBitmap(sizl, TS_BYTES_IN_SCANLINE(sizl.cx, ppdev->cClientBitsPerPel), ppdev->iBitmapFormat, BMF_TOPDOWN, NULL);
if (hbmDib) { // Associate the bitmap to the PDEV device
if (EngAssociateSurface((HSURF) hbmDib, ppdev->hdevEng, 0)) { // Lock the surface to get the surf obj
pso = EngLockSurface((HSURF) hbmDib);
if (pso != NULL) { int i; unsigned clientBitmapId; CHDataKeyContext CHContext;
// Setup offscreen device surface struct
pdsurf->shareId = pddShm->shareId; pdsurf->sizl = sizl; pdsurf->iBitmapFormat = iFormat; pdsurf->ppdev = ppdev; pdsurf->pso = pso; pdsurf->flags = 0;
CH_CreateKeyFromFirstData(&CHContext, (BYTE *)(&pdsurf), sizeof(pdsurf));
// Cache the offscreen bitmap in the cache
clientBitmapId = CH_CacheKey( sbcOffscreenBitmapCacheHandle, CHContext.Key1, CHContext.Key2, (VOID *)pdsurf);
if (clientBitmapId != CH_KEY_UNCACHABLE) { // send a create offscreen bitmap pdu
if (OE_SendCreateOffscrBitmapOrder(ppdev, sizl, iFormat, clientBitmapId)) { // Update the current offscreen cache size
oeCurrentOffscreenCacheSize += bitmapSize; pdsurf->bitmapId = clientBitmapId; TRC_NRM((TB, "Created an offscreen bitmap")); DC_QUIT; } else { TRC_ERR((TB, "Failed to send the create bitmap pdu")); CH_RemoveCacheEntry( sbcOffscreenBitmapCacheHandle, clientBitmapId); EngDeleteSurface((HSURF)hbmDevice); hbmDevice = NULL; DC_QUIT; } } else { TRC_ERR((TB, "Failed to cache the bitmap")); EngDeleteSurface((HSURF)hbmDevice); hbmDevice = NULL; DC_QUIT; }
} else { TRC_ERR((TB, "Failed to lock the surfac")); EngDeleteSurface((HSURF)hbmDib); EngDeleteSurface((HSURF)hbmDevice); hbmDevice = NULL; DC_QUIT; } } else { TRC_ERR((TB, "Failed to associate the surface to device")); EngDeleteSurface((HSURF)hbmDib); EngDeleteSurface((HSURF)hbmDevice); hbmDevice = NULL; DC_QUIT; } } else { TRC_ERR((TB, "Failed to create backup DIB bitmap")); EngDeleteSurface((HSURF)hbmDevice); hbmDevice = NULL; DC_QUIT; } } else { TRC_ERR((TB, "Failed to associate the device surface to the device")); EngDeleteSurface((HSURF)hbmDevice); hbmDevice = NULL; DC_QUIT; } } else { TRC_ERR((TB, "Failed to allocate memory for the device surface")); EngFreeMem(pdsurf); DC_QUIT; } } else { TRC_ERR((TB, "Failed to allocate memory for the device surface")); DC_QUIT; }
DC_EXIT_POINT:
DC_END_FN(); return hbmDevice; }
/****************************************************************************/ // DrvDeleteDeviceBitmap - See NT DDK for documentation
/****************************************************************************/ VOID DrvDeleteDeviceBitmap(DHSURF dhsurf) { PDD_DSURF pdsurf; PDD_PDEV ppdev; SURFOBJ *psoDib; HSURF hsurfDib;
DC_BEGIN_FN("DrvDeleteDeviceBitmap");
pdsurf = (PDD_DSURF)dhsurf; ppdev = pdsurf->ppdev;
if (ddConnected && pddShm != NULL) { if ((pddShm->sbc.offscreenCacheInfo.supportLevel > TS_OFFSCREEN_DEFAULT) && (sbcEnabled & SBC_OFFSCREEN_CACHE_ENABLED)) { if (!(pdsurf->flags & DD_NO_OFFSCREEN) && (pdsurf->shareId == pddShm->shareId)) { CH_RemoveCacheEntry(sbcOffscreenBitmapCacheHandle, pdsurf->bitmapId); } else { // This is when the bitmap is bumped out of the cache
TRC_NRM((TB, "Failed to find the offscreen bitmap in the cache")); DC_QUIT; } } else { TRC_ERR((TB, "offscreen rendering is not supported")); DC_QUIT; } } else { TRC_ERR((TB, "Call on disconnected")); DC_QUIT; }
DC_EXIT_POINT:
// Get the hsurf from the SURFOBJ before we unlock it (it's not
// legal to dereference psoDib when it's unlocked):
psoDib = pdsurf->pso;
if (psoDib) { hsurfDib = psoDib->hsurf; EngUnlockSurface(psoDib); EngDeleteSurface(hsurfDib); }
EngFreeMem(pdsurf);
DC_END_FN(); }
#ifdef DRAW_NINEGRID
/****************************************************************************/ // DrvNineGrid - This is to support Whistler Luna Draw9Grid operation
/****************************************************************************/ BOOL DrvNineGrid( SURFOBJ *psoTrg, SURFOBJ *psoSrc, CLIPOBJ *pco, XLATEOBJ *pxlo, PRECTL prclTrg, PRECTL prclSrc, PNINEGRID png, BLENDOBJ* pBlendObj, PVOID pvReserved) { BOOL rc = TRUE; SURFOBJ *psoTrgArg; SURFOBJ *psoSrcArg; PDD_PDEV ppdev = (PDD_PDEV)psoTrg->dhpdev; PDD_DSURF pdsurfTrg = NULL; PDD_DSURF pdsurfSrc = NULL; RECTL bounds; OE_ENUMRECTS clipRects; unsigned nineGridBitmapId = 0; unsigned clipVal; DC_BEGIN_FN("DrvNineGrid")
if (ddConnected && pddShm != NULL) { psoTrgArg = psoTrg; psoSrcArg = psoSrc; // Get the GDI format source and destination bitmaps
psoTrg = OEGetSurfObjBitmap(psoTrg, &pdsurfTrg); if (psoSrc != NULL) psoSrc = OEGetSurfObjBitmap(psoSrc, &pdsurfSrc);
// if the client doesn't support drawninegrid, return false to reroute
if (sbcDrawNineGridBitmapCacheHandle == NULL || (pddShm != NULL && pddShm->sbc.drawNineGridCacheInfo.supportLevel <= TS_DRAW_NINEGRID_DEFAULT) || !OE_SendAsOrder(TS_ENC_DRAWNINEGRID_ORDER) || !OE_SendAsOrder(TS_ENC_MULTI_DRAWNINEGRID_ORDER) || (ppdev != NULL && !((psoTrg->hsurf == ppdev->hsurfFrameBuf) || (!(pdsurfTrg->flags & DD_NO_OFFSCREEN))))) { return EngNineGrid(psoTrgArg, psoSrcArg, pco, pxlo, prclTrg, prclSrc, png, pBlendObj, pvReserved); }
//DD_UPD_STATE(DD_BITBLT);
//INC_OUTCOUNTER(OUT_BITBLT_ALL);
// Punt the call back to GDI to do the drawing.
rc = EngNineGrid(psoTrg, psoSrc, pco, pxlo, prclTrg, prclSrc, png, pBlendObj, pvReserved);
if (rc) { // If ppdev is NULL then this is a blt to GDI managed memory bitmap,
// so there is no need to accumulate any output.
if (ppdev != NULL) { BOOL bMirror;
// The following is true for DrvBitBlt, need to find out for
// get the bounding rectangle for the operation. According to
// the DDK, this rectangle is always well-ordered and does not
// need to be rearranged.
// Clip it to 16 bits.
bounds = *prclTrg; OEClipRect(&bounds);
// If the bound is right to left, we need to switch it.
bMirror = bounds.left > bounds.right; if (bMirror) { LONG lRight = bounds.left; bounds.left = bounds.right; bounds.right = lRight; } } else { // if ppdev is NULL, we are blt to GDI managed bitmap,
// so, the dhurf of the target surface should be NULL
TRC_ASSERT((pdsurfTrg == NULL), (TB, "NULL ppdev - psoTrg has non NULL dhsurf"));
TRC_NRM((TB, "NULL ppdev - blt to GDI managed bitmap")); DC_QUIT; } } else { TRC_ERR((TB, "EngBitBlt failed")); DC_QUIT; } } else { if (psoTrg->iType == STYPE_DEVBITMAP) { psoTrg = OEGetSurfObjBitmap(psoTrg, &pdsurfTrg); if (psoSrc != NULL) psoSrc = OEGetSurfObjBitmap(psoSrc, &pdsurfSrc); // Punt the call back to GDI to do the drawing.
rc = EngNineGrid(psoTrg, psoSrc, pco, pxlo, prclTrg, prclSrc, png, pBlendObj, pvReserved); } else { TRC_ERR((TB, "Called when disconnected")); rc = TRUE; }
DC_QUIT; }
if (!((psoTrg->hsurf == ppdev->hsurfFrameBuf) || (!(pdsurfTrg->flags & DD_NO_OFFSCREEN)))) { // If noOffscreen flag is on, we will bail on sending
// the client any further offscreen rendering. And we'll send the
// final offscreen to screen blt as regular memblt.
TRC_NRM((TB, "Offscreen blt bail"));
//INC_OUTCOUNTER(OUT_BITBLT_NOOFFSCR);
DC_QUIT; } // Get the intersection between the dest rect and the
// clip rects. Check for overcomplicated or nonintersecting
// clipping.
clipVal = OEGetIntersectingClipRects(pco, &bounds, CD_ANY, &clipRects);
if (clipVal == CLIPRECTS_TOO_COMPLEX) { TRC_NRM((TB, "Clipping is too complex")); //INC_OUTCOUNTER(OUT_BITBLT_SDA_COMPLEXCLIP);
//if (oeLastDstSurface == NULL)
// ADD_INCOUNTER(IN_SDA_BITBLT_COMPLEXCLIP_AREA,
// COM_SIZEOF_RECT(bounds));
goto SendScreenData; } else if (clipVal == CLIPRECTS_NO_INTERSECTIONS) { TRC_NRM((TB, "Clipping does not intersect destrect")); DC_QUIT; }
// Cache the source bitmap
TRC_ASSERT((psoSrcArg->iUniq != 0), (TB, "Source bitmap should be cachable")); TRC_ASSERT((pdsurfSrc == NULL), (TB, "The source bitmap for this should be GDI managed bitmap")); TRC_ASSERT((psoSrc->iBitmapFormat == BMF_32BPP), (TB, "for now, we always get 32bpp bitmap")); TRC_ASSERT((pBlendObj->BlendFunction.BlendOp == 0 && pBlendObj->BlendFunction.BlendFlags == 0 && pBlendObj->BlendFunction.SourceConstantAlpha == 255 && pBlendObj->BlendFunction.AlphaFormat == 1), (TB, "Received unknown blend function"));
if (!OECacheDrawNineGridBitmap(ppdev, psoSrc, png, &nineGridBitmapId)) { TRC_ERR((TB, "Failed to cache drawninegrid bitmap")); goto SendScreenData; } // Switch drawing surface if needed
if ((psoTrg->hsurf == ppdev->hsurfFrameBuf) || (!(pdsurfTrg->flags & DD_NO_OFFSCREEN))) { // Send a switch surface PDU if the destination surface is different
// from last drawing order. If we failed to send the PDU, we will
// just have to bail on this drawing order.
if (!OESendSwitchSurfacePDU(ppdev, pdsurfTrg)) { TRC_ERR((TB, "failed to send the switch surface PDU")); goto SendScreenData; } } else { // if noOffscreen flag is on, we will bail on sending
// the client any further offscreen rendering. And will send the
// final offscreen to screen blt as regular memblt.
TRC_NRM((TB, "Offscreen blt bail")); goto SendScreenData; }
// Send the drawninegrid encoded primary order
if (OEEncodeDrawNineGrid(&bounds, prclSrc, nineGridBitmapId, ppdev, &clipRects)) { // We added an order to the list, increment global counts.
goto PostSDA; } else { goto SendScreenData; } SendScreenData: if ((psoTrg->hsurf == ppdev->hsurfFrameBuf) || (!(pdsurfTrg->flags & DD_NO_OFFSCREEN))) { // Send a switch surface PDU if the destination surface is different
// from last drawing order. If we failed to send the PDU, we will
// just have to bail on this drawing order.
if (!OESendSwitchSurfacePDU(ppdev, pdsurfTrg)) { TRC_ERR((TB, "failed to send the switch surface PDU")); DC_QUIT; } } else { // If noOffscreen flag is on, we will bail on sending
// the client any further offscreen rendering. And we'll send the
// final offscreen to screen blt as regular memblt.
TRC_NRM((TB, "Offscreen blt bail"));
//INC_OUTCOUNTER(OUT_BITBLT_NOOFFSCR);
DC_QUIT; }
if (psoTrg->hsurf == ppdev->hsurfFrameBuf) { //INC_OUTCOUNTER(OUT_BITBLT_SDA);
OEClipAndAddScreenDataArea(&bounds, pco); } else { // if we can't send orders for offscreen rendering, we will
// bail offscreen support for this bitmap
TRC_ALT((TB, "screen data call for offscreen rendering"));
// Remove the bitmap from the offscreen bitmap cache
if (!(pdsurfTrg->flags & DD_NO_OFFSCREEN)) CH_RemoveCacheEntry(sbcOffscreenBitmapCacheHandle, pdsurfTrg->bitmapId);
DC_QUIT; }
PostSDA: SCH_DDOutputAvailable(ppdev, FALSE);
DC_EXIT_POINT:
DC_END_FN(); return rc; }
#if 0
BOOL DrvDrawStream( SURFOBJ *psoTrg, SURFOBJ *psoSrc, CLIPOBJ *pco, XLATEOBJ *pxlo, RECTL *prclTrg, POINTL *pptlDstOffset, ULONG ulIn, PVOID pvIn, PVOID pvReserved) { BOOL rc = TRUE; SURFOBJ *psoTrgArg; SURFOBJ *psoSrcArg; PDD_PDEV ppdev = (PDD_PDEV)psoTrg->dhpdev; PDD_DSURF pdsurfTrg = NULL; PDD_DSURF pdsurfSrc = NULL; RECTL bounds; OE_ENUMRECTS clipRects; unsigned drawStreamBitmapId = 0; unsigned offscrBitmapId = 0; unsigned RetVal;
DC_BEGIN_FN("DrvDrawStream")
if (ddConnected) { psoTrgArg = psoTrg; psoSrcArg = psoSrc; // Get the GDI format source and destination bitmaps
psoTrg = OEGetSurfObjBitmap(psoTrg, &pdsurfTrg); if (psoSrc != NULL) psoSrc = OEGetSurfObjBitmap(psoSrc, &pdsurfSrc);
//DD_UPD_STATE(DD_BITBLT);
//INC_OUTCOUNTER(OUT_BITBLT_ALL);
// Punt the call back to GDI to do the drawing.
rc = EngDrawStream(psoTrg, psoSrc, pco, pxlo, prclTrg, pptlDstOffset, ulIn, pvIn, pvReserved);
if (rc) { // If ppdev is NULL then this is a blt to GDI managed memory bitmap,
// so there is no need to accumulate any output.
if (ppdev != NULL) { // The following is true for DrvBitBlt, need to find out for Get
// the bounding rectangle for the operation. According to
// the DDK, this rectangle is always well-ordered and does not
// need to be rearranged.
// Clip it to 16 bits.
bounds = *prclTrg; OEClipRect(&bounds); } else { // if ppdev is NULL, we are blt to GDI managed bitmap,
// so, the dhurf of the target surface should be NULL
TRC_ASSERT((pdsurfTrg == NULL), (TB, "NULL ppdev - psoTrg has non NULL dhsurf"));
TRC_NRM((TB, "NULL ppdev - blt to GDI managed bitmap")); DC_QUIT; } } else { TRC_ERR((TB, "EngBitBlt failed")); DC_QUIT; } } else { if (psoTrg->iType == STYPE_DEVBITMAP) { psoTrg = OEGetSurfObjBitmap(psoTrg, &pdsurfTrg); if (psoSrc != NULL) psoSrc = OEGetSurfObjBitmap(psoSrc, &pdsurfSrc); // Punt the call back to GDI to do the drawing.
rc = EngDrawStream(psoTrg, psoSrc, pco, pxlo, prclTrg, pptlDstOffset, ulIn, pvIn, pvReserved); } else { TRC_ERR((TB, "Called when disconnected")); rc = TRUE; }
DC_QUIT; }
if (!((psoTrg->hsurf == ppdev->hsurfFrameBuf) || (!(pdsurfTrg->flags & DD_NO_OFFSCREEN)))) { // If noOffscreen flag is on, we will bail on sending
// the client any further offscreen rendering. And we'll send the
// final offscreen to screen blt as regular memblt.
TRC_NRM((TB, "Offscreen blt bail"));
//INC_OUTCOUNTER(OUT_BITBLT_NOOFFSCR);
DC_QUIT; } // Get the intersection between the dest rect and the
// clip rects. Check for overcomplicated or nonintersecting
// clipping.
RetVal = OEGetIntersectingClipRects(pco, &bounds, CD_ANY, &clipRects);
if (RetVal == CLIPRECTS_TOO_COMPLEX) { TRC_NRM((TB, "Clipping is too complex")); //INC_OUTCOUNTER(OUT_BITBLT_SDA_COMPLEXCLIP);
//if (oeLastDstSurface == NULL)
// ADD_INCOUNTER(IN_SDA_BITBLT_COMPLEXCLIP_AREA,
// COM_SIZEOF_RECT(bounds));
goto SendScreenData; } else if (RetVal == CLIPRECTS_NO_INTERSECTIONS) { TRC_NRM((TB, "Clipping does not intersect destrect")); DC_QUIT; }
// Cache the source bitmap
TRC_ASSERT((psoSrcArg->iUniq != 0), (TB, "Source bitmap should be cachable"));
// For the source bitmap
//
// Case 1: This is an RDP managed device bitmap and the bitmap
// is still cached at the client and we can just use the bitmapId
//
// Case 2: This is an RDP managed device bitmap, but no longer
// cached at the client side
//
// Case 3: This is a GDI managed bitmap
//
// For case 2 and 3, we need to cache the bitmap first
//
if (pdsurfSrc != NULL) { if ((pddShm->sbc.offscreenCacheInfo.supportLevel > TS_OFFSCREEN_DEFAULT) && (sbcEnabled & SBC_OFFSCREEN_CACHE_ENABLED)) { if (pdsurfSrc->shareId == pddShm->shareId) { // The client has the offscreen bitmap in cache
if (!(pdsurfSrc->flags & DD_NO_OFFSCREEN)) { offscrBitmapId = pdsurfSrc->bitmapId; CH_TouchCacheEntry(sbcOffscreenBitmapCacheHandle, offscrBitmapId); } else { // If the source surface is offscreen surface, and we
// have the noOffscreen flag on, this means we will
// send the bitmap bits as regular memory bitmap bits
// This means that the offscreen bitmap has been evicted
// out of the offscreen cache or screen data needs to be
// sent for the offscreen bitmap
TRC_ALT((TB, "noOffscreen flag is on for %p", pdsurfSrc)); offscrBitmapId = CH_KEY_UNCACHABLE; } } 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")); pdsurfSrc->flags |= DD_NO_OFFSCREEN; offscrBitmapId = CH_KEY_UNCACHABLE; } } else { // These are offscreen bitmaps from the disconnected session
// or client has sent an error pdu,
// We have to treat them as memory bitmap now since the client
// doesn't have the offscreen bitmap locally
TRC_ALT((TB, "Need to turn off this offscreen bitmap")); pdsurfSrc->flags |= DD_NO_OFFSCREEN; offscrBitmapId = CH_KEY_UNCACHABLE; } } else { if ((pddShm->sbc.offscreenCacheInfo.supportLevel > TS_OFFSCREEN_DEFAULT) && (sbcEnabled & SBC_OFFSCREEN_CACHE_ENABLED)) { offscrBitmapId = CH_KEY_UNCACHABLE; } else { TRC_NRM((TB, "No offscreen support, can't support draw stream")); goto SendScreenData; } }
// Need to create an offscreen
if (offscrBitmapId == CH_KEY_UNCACHABLE) { MEMBLT_ORDER_EXTRA_INFO MemBltExtraInfo; POINTL ptlSrc; CHDataKeyContext CHContext; void *UserDefined; drawStreamBitmapId = CH_KEY_UNCACHABLE;
CH_CreateKeyFromFirstData(&CHContext, psoSrc->pvBits, psoSrc->cjBits); if (!CH_SearchCache(sbcDrawStreamBitmapCacheHandle, CHContext.Key1, CHContext.Key2, &UserDefined, &drawStreamBitmapId)) { drawStreamBitmapId = CH_CacheKey( sbcDrawStreamBitmapCacheHandle, CHContext.Key1, CHContext.Key2, NULL);
if (drawStreamBitmapId != CH_KEY_UNCACHABLE) {
unsigned BitmapRawSize; unsigned BitmapBufferSize; unsigned BitmapCompSize; unsigned BitmapBpp; SIZEL size; PBYTE BitmapBuffer; PBYTE BitmapRawBuffer; PINT_ORDER pOrder = NULL; unsigned paddedBitmapWidth; HSURF hWorkBitmap; SURFOBJ *pWorkSurf; BOOL rc;
BitmapBuffer = oeTempBitmapBuffer; BitmapBufferSize = TS_MAX_STREAM_BITMAP_SIZE; // Convert to the protocol wire bitmap bpp format
switch (psoSrc->iBitmapFormat) { case BMF_16BPP: BitmapBpp = 16; break;
case BMF_24BPP: BitmapBpp = 24; break;
case BMF_32BPP: BitmapBpp = 32; break;
default: BitmapBpp = 8; } paddedBitmapWidth = (psoSrc->sizlBitmap.cx + 3) & ~3; size.cx = paddedBitmapWidth; size.cy = psoSrc->sizlBitmap.cy;
// We need to copy to a worker bitmap if the bitmap width is
// not dword aligned, or the color depth is not 32bpp and
// doesn't match the frame buffer color depth
if (paddedBitmapWidth != psoSrc->sizlBitmap.cx || (psoSrc->iBitmapFormat != ppdev->iBitmapFormat && psoSrc->iBitmapFormat != BMF_32BPP)) { RECTL rect; POINTL origin;
rect.left = 0; rect.top = 0;
rect.right = paddedBitmapWidth; rect.bottom = psoSrc->sizlBitmap.cy; origin.x = 0; origin.y = 0;
hWorkBitmap = (HSURF)EngCreateBitmap(size, TS_BYTES_IN_SCANLINE(size.cx, BitmapBpp), psoSrc->iBitmapFormat, 0, NULL);
pWorkSurf = EngLockSurface(hWorkBitmap);
if (EngCopyBits(pWorkSurf, psoSrc, NULL, NULL, &rect, &origin)) { BitmapRawSize = pWorkSurf->cjBits; BitmapRawBuffer = pWorkSurf->pvBits;
rc = BC_CompressBitmap(BitmapRawBuffer, BitmapBuffer, BitmapBufferSize, &BitmapCompSize, paddedBitmapWidth, psoSrc->sizlBitmap.cy, BitmapBpp);
EngUnlockSurface(pWorkSurf); EngDeleteSurface(hWorkBitmap); } else { EngUnlockSurface(pWorkSurf); EngDeleteSurface(hWorkBitmap); goto SendScreenData; }
EngUnlockSurface(pWorkSurf); EngDeleteSurface(hWorkBitmap); } else { BitmapRawSize = psoSrc->cjBits; BitmapRawBuffer = psoSrc->pvBits;
rc = BC_CompressBitmap(BitmapRawBuffer, BitmapBuffer, BitmapBufferSize, &BitmapCompSize, paddedBitmapWidth, psoSrc->sizlBitmap.cy, BitmapBpp); }
if (rc) { if (!OESendStreamBitmapOrder(ppdev, TS_DRAW_NINEGRID_BITMAP_CACHE, &size, BitmapBpp, BitmapBuffer, BitmapCompSize, TRUE)) { goto SendScreenData; } } else { // Send uncompressed bitmap
if (!OESendStreamBitmapOrder(ppdev, TS_DRAW_NINEGRID_BITMAP_CACHE, &size, BitmapBpp, BitmapRawBuffer, BitmapRawSize, FALSE)) { goto SendScreenData; } }
// send a create drawStream bitmap pdu
if (OESendCreateDrawStreamOrder(ppdev,drawStreamBitmapId, &(psoSrc->sizlBitmap), BitmapBpp)) { // Update the current offscreen cache size
//oeCurrentOffscreenCacheSize += bitmapSize;
TRC_NRM((TB, "Created an offscreen bitmap")); } else { TRC_ERR((TB, "Failed to send the create bitmap pdu")); CH_RemoveCacheEntry( sbcDrawStreamBitmapCacheHandle, drawStreamBitmapId); goto SendScreenData; } } else { TRC_ERR((TB, "Failed to cache the bitmap")); goto SendScreenData; } #if 0
if (!OESendSwitchSurfacePDU(ppdev, (PDD_DSURF)(&drawStreamBitmapId), DRAW_STREAM_SURFACE)) { TRC_ERR((TB, "failed to send the switch surface PDU")); goto SendScreenData; } // Fill in extra info structure.
MemBltExtraInfo.pSource = psoSrc; MemBltExtraInfo.pDest = psoSrc; MemBltExtraInfo.pXlateObj = NULL; MemBltExtraInfo.bNoFastPathCaching = FALSE; MemBltExtraInfo.iDeviceUniq = psoSrcArg ? (psoSrcArg->iUniq) : 0;
ptlSrc.x = 0; ptlSrc.y = 0; // Make sure orders are not turned off.
if (OE_SendAsOrder(TS_ENC_MEMBLT_R2_ORDER)) { RECTL srcBound; OE_ENUMRECTS srcClipRect; // Get the intersection rect.
srcBound.left = 0; srcBound.top = 0; srcBound.right = psoSrc->sizlBitmap.cx; srcBound.bottom = psoSrc->sizlBitmap.cy; srcClipRect.rects.c = 1; srcClipRect.rects.arcl[0] = srcBound; if (!OEEncodeMemBlt(&srcBound, &MemBltExtraInfo, TS_ENC_MEMBLT_R2_ORDER, CH_KEY_UNCACHABLE, 0xCC, &ptlSrc, NULL, NULL, ppdev, &srcClipRect)) { //if (oeLastDstSurface == NULL)
// ADD_INCOUNTER(IN_SDA_MEMBLT_AREA,
// COM_SIZEOF_RECT(bounds));
goto SendScreenData; } } else { TRC_NRM((TB, "MemBlt order not allowed")); //INC_OUTCOUNTER(OUT_BITBLT_SDA_UNSUPPORTED);
goto SendScreenData; } #endif
} else { //DrvDebugPrint("JOYC: drawstream source already cached\n");
} } else { // The client already has the bitmap cached in offscreen bitmap cache
//DrvDebugPrint("JOYC: offscreen already cached!\n");
}
if ((psoTrg->hsurf == ppdev->hsurfFrameBuf) || (!(pdsurfTrg->flags & DD_NO_OFFSCREEN))) { // Send a switch surface PDU if the destination surface is different
// from last drawing order. If we failed to send the PDU, we will
// just have to bail on this drawing order.
if (!OESendSwitchSurfacePDU(ppdev, pdsurfTrg, 0)) { TRC_ERR((TB, "failed to send the switch surface PDU")); goto SendScreenData; } } else { // if noOffscreen flag is on, we will bail on sending
// the client any further offscreen rendering. And will send the
// final offscreen to screen blt as regular memblt.
TRC_NRM((TB, "Offscreen blt bail")); goto SendScreenData; }
// Send the drawstream bits, first round sending as secondary order
if (OESendDrawStreamOrder(ppdev, drawStreamBitmapId, ulIn, pvIn, pptlDstOffset, &bounds, &clipRects)) { //DrvDebugPrint("JOYC: Send DrawStream Order\n");
// We added an order to the list, increment global counts.
goto PostSDA; } else { goto SendScreenData; }
SendScreenData: DrvDebugPrint("JOYC: DrawStream using SendScreenData\n");
if ((psoTrg->hsurf == ppdev->hsurfFrameBuf) || (!(pdsurfTrg->flags & DD_NO_OFFSCREEN))) { // Send a switch surface PDU if the destination surface is different
// from last drawing order. If we failed to send the PDU, we will
// just have to bail on this drawing order.
if (!OESendSwitchSurfacePDU(ppdev, pdsurfTrg, 0)) { TRC_ERR((TB, "failed to send the switch surface PDU")); DC_QUIT; } } else { // If noOffscreen flag is on, we will bail on sending
// the client any further offscreen rendering. And we'll send the
// final offscreen to screen blt as regular memblt.
TRC_NRM((TB, "Offscreen blt bail"));
//INC_OUTCOUNTER(OUT_BITBLT_NOOFFSCR);
DC_QUIT; }
if (psoTrg->hsurf == ppdev->hsurfFrameBuf) { //INC_OUTCOUNTER(OUT_BITBLT_SDA);
OEClipAndAddScreenDataArea(&bounds, pco); } else { // if we can't send orders for offscreen rendering, we will
// bail offscreen support for this bitmap
TRC_ALT((TB, "screen data call for offscreen rendering"));
// Remove the bitmap from the offscreen bitmap cache
if (!(pdsurfTrg->flags & DD_NO_OFFSCREEN)) CH_RemoveCacheEntry(sbcOffscreenBitmapCacheHandle, pdsurfTrg->bitmapId);
DC_QUIT; }
PostSDA: SCH_DDOutputAvailable(ppdev, FALSE);
DC_EXIT_POINT:
DC_END_FN(); return rc; } #endif
#endif //DRAW_NINEGRID
#ifdef DRAW_GDIPLUS
// DrawGdiPlus
ULONG DrawGdiPlus( IN SURFOBJ *pso, IN ULONG iEsc, IN CLIPOBJ *pco, IN RECTL *prcl, IN ULONG cjIn, IN PVOID pvIn) { SURFOBJ *psoTrgArg; SURFOBJ *psoSrcArg; PDD_PDEV ppdev = (PDD_PDEV)pso->dhpdev; PDD_DSURF pdsurf = NULL; BOOL rc = TRUE;
DC_BEGIN_FN("DrawGdiplus"); // The callers should check this. Asserting they do.
TRC_ASSERT((pddShm != NULL),(TB, "DrawGdiPlus called when pddShm is NULL")) // Sometimes, we're called after being disconnected.
if (ddConnected) { // Surface is non-NULL.
pso = OEGetSurfObjBitmap(pso, &pdsurf);
if ((pso->hsurf == ppdev->hsurfFrameBuf) || (!(pdsurf->flags & DD_NO_OFFSCREEN))) { // Send a switch surface PDU if the destination surface is
// different from last drawing order. If we failed to send the
// PDU, we will just have to bail on this drawing order.
if (!OESendSwitchSurfacePDU(ppdev, pdsurf)) { TRC_ERR((TB, "failed to send the switch surface PDU")); DC_QUIT; } } else { // if noOffscreen flag is on, we will bail on sending
// the client any further offscreen rendering. And will send the final
// offscreen to screen blt as regular memblt.
TRC_NRM((TB, "Offscreen blt bail")); DC_QUIT; } } else { TRC_ERR((TB, "Called when disconnected")); DC_QUIT; } // Create and Send DrawGdiplus Order
if (OECreateDrawGdiplusOrder(ppdev, prcl, cjIn, pvIn)) { goto PostSDA; } // Send screen data when OECreateDrawGdiplusOrder fails
OEClipAndAddScreenDataArea(prcl, NULL); PostSDA: // All done: consider sending the output.
SCH_DDOutputAvailable(ppdev, FALSE);
DC_EXIT_POINT: DC_END_FN(); return rc; }
/****************************************************************************/ // DrvDrawEscape - see NT DDK documentation.
/****************************************************************************/ ULONG DrvDrawEscape( IN SURFOBJ *pso, IN ULONG iEsc, IN CLIPOBJ *pco, IN RECTL *prcl, IN ULONG cjIn, IN PVOID pvIn) { PDD_PDEV ppdev = (PDD_PDEV)pso->dhpdev;
DC_BEGIN_FN("DrvDrawEscape"); TRC_NRM((TB, "DrvDrawEscape %d", iEsc)); switch (iEsc) { case GDIPLUS_TS_QUERYVER: // Query the gdiplus version
// DDraw only support 8, 16, 24, 32 bpp
if ((ppdev->cClientBitsPerPel != 8) && (ppdev->cClientBitsPerPel != 16) && (ppdev->cClientBitsPerPel != 24) && (ppdev->cClientBitsPerPel != 32)) { TRC_ERR((TB, "The DDrawColor does not support the color depth %d", ppdev->cClientBitsPerPel)); return 0; } if (ppdev->SectionObject == NULL) { TRC_ERR((TB, "The section memory is not allocated")); return 0; }
if (pddShm == NULL) { TRC_ERR((TB, "The pddShm is NULL")); return 0; } if (pddShm->sbc.drawGdiplusInfo.supportLevel > TS_DRAW_GDIPLUS_DEFAULT) { TRC_NRM((TB, "Gdiplus version is %d", pddShm->sbc.drawGdiplusInfo.GdipVersion)); return pddShm->sbc.drawGdiplusInfo.GdipVersion; } else { TRC_ERR((TB, "TSDrawGdip not supported")); return 0; } break; case GDIPLUS_TS_RECORD: // Send out the Gdiplus EMF+ record
// DDraw only support 8, 16, 24, 32 bpp
if ((ppdev->cClientBitsPerPel != 8) && (ppdev->cClientBitsPerPel != 16) && (ppdev->cClientBitsPerPel != 24) && (ppdev->cClientBitsPerPel != 32)) { TRC_ERR((TB, "The DDrawColor does not support the color depth %d", ppdev->cClientBitsPerPel)); return 0; } if (pddShm == NULL) { TRC_ERR((TB, "The pddShm is NULL !")); return 0; } if (ppdev->SectionObject == NULL) { TRC_ERR((TB, "Called when Gdiplus is not supported!")); return 0; } if (pddShm->sbc.drawGdiplusInfo.supportLevel > TS_DRAW_GDIPLUS_DEFAULT) { TRC_ASSERT((pvIn != NULL), (TB, "DrvDrawEscape gets NULL data")) TRC_ASSERT((cjIn != 0), (TB, "DrvDrawEscape gets data with size 0")) return DrawGdiPlus(pso, iEsc, pco, prcl, cjIn, pvIn); } else { TRC_ERR((TB, "TSDrawGdip not supported")); return 0; } default : TRC_ERR((TB, "DrvDrawEscape %d not supported", iEsc)); return 0; } DC_END_FN() } #endif // DRAW_GDIPLUS
/****************************************************************************/ // DrvTextOut - see NT DDK documentation.
/****************************************************************************/ BOOL RDPCALL DrvTextOut( SURFOBJ *pso, STROBJ *pstro, FONTOBJ *pfo, CLIPOBJ *pco, RECTL *prclExtra, RECTL *prclOpaque, BRUSHOBJ *pboFore, BRUSHOBJ *pboOpaque, POINTL *pptlOrg, MIX mix) { BOOL rc; RECTL rectTrg; PDD_PDEV ppdev = (PDD_PDEV)pso->dhpdev; PDD_DSURF pdsurf = NULL; PFONTCACHEINFO pfci; OE_ENUMRECTS ClipRects;
DC_BEGIN_FN("DrvTextOut");
rc = TRUE;
// Sometimes, we're called after being disconnected.
if (ddConnected && pddShm != NULL) { // Surface is non-NULL.
pso = OEGetSurfObjBitmap(pso, &pdsurf); INC_OUTCOUNTER(OUT_TEXTOUT_ALL);
if (((pco == NULL) && (pso->sizlBitmap.cx >= prclOpaque->right) && (pso->sizlBitmap.cy >= prclOpaque->bottom)) || ((pco != NULL) && (pso->sizlBitmap.cx >= pco->rclBounds.right) && (pso->sizlBitmap.cy >= pco->rclBounds.bottom))) { // Let GDI to do the local drawing.
rc = EngTextOut(pso, pstro, pfo, pco, prclExtra, prclOpaque, pboFore, pboOpaque, pptlOrg, mix); } else { // If the bounding rectangle is greater the frame buffer, something
// is really wrong here. This means the desktop surface size and
// the framebuffer is not matched up. We need to bail out here.
rc = FALSE; }
if (rc) { if ((pso->hsurf == ppdev->hsurfFrameBuf) || (!(pdsurf->flags & DD_NO_OFFSCREEN))) { // Send a switch surface PDU if the destination surface is different
// from last drawing order. If we failed to send the PDU, we will
// just have to bail on this drawing order.
if (!OESendSwitchSurfacePDU(ppdev, pdsurf)) { TRC_ERR((TB, "failed to send the switch surface PDU")); DC_QUIT; } } else { // if noOffscreen flag is on, we will bail on sending the
// client any further offscreen rendering. And will send the
// final offscreen to screen blt as regular memblt.
TRC_NRM((TB, "Offscreen blt bail")); DC_QUIT; }
// Check we have a valid string.
if (pstro->pwszOrg != NULL) { if (OEGetClipRects(pco, &ClipRects)) {
// Special case when the clipobj is not correct.
// When rdpdd is used as a mirroring driver the Mul layer
// will modify the CLIPOBJ and in some cases we get a complex
// CLIPOBJ but the enumeration gives no rectangles.
// If it happens then don't draw anything.
// We test only the DC_COMPLEX case because in that case we
// are supposed to always get at least one rect.
// If it's DC_RECT we always have one rect without enumeration,
// so no need to test it (see OEGetClipRects).
// If it's DC_TRIVIAL we have to draw it, so don't test it.
if ((pco != NULL) && (pco->iDComplexity == DC_COMPLEX) && (ClipRects.rects.c == 0)) {
TRC_NRM((TB, "Complex CLIPOBJ without any rects"));
DC_QUIT; }
// Check that we don't have any modifier rects on the
// font.
if (prclExtra == NULL) { // Get a ptr to this font's cached information.
pfci = OEGetFontCacheInfo(pfo); if (pfci == NULL) { TRC_NRM((TB, "Cannot allocate font cache info " "struct")); INC_OUTCOUNTER(OUT_TEXTOUT_SDA_NOFCI); goto SendAsSDA; } } else { TRC_NRM((TB, "Unsupported rects")); INC_OUTCOUNTER(OUT_TEXTOUT_SDA_EXTRARECTS); goto SendAsSDA; } } else { TRC_NRM((TB, "Clipping is too complex")); INC_OUTCOUNTER(OUT_TEXTOUT_SDA_COMPLEXCLIP); goto SendAsSDA; } } else { TRC_NRM((TB, "No string - opaque %p", prclOpaque)); INC_OUTCOUNTER(OUT_TEXTOUT_SDA_NOSTRING); goto SendAsSDA; } } else { TRC_ERR((TB, "EngTextOut failed")); DC_QUIT; } } else { if (pso->iType == STYPE_DEVBITMAP) { pso = OEGetSurfObjBitmap(pso, &pdsurf); // Let GDI to do the local drawing.
rc = EngTextOut(pso, pstro, pfo, pco, prclExtra, prclOpaque, pboFore, pboOpaque, pptlOrg, mix); } else { TRC_ERR((TB, "Called when disconnected")); } goto CalledOnDisconnected; }
// Process the request according to the glyph support level setting
// we can attempt to send a Glyph order
if (pddShm->sbc.caps.GlyphSupportLevel >= CAPS_GLYPH_SUPPORT_PARTIAL) { if (OE_SendGlyphs(pso, pstro, pfo, &ClipRects, prclOpaque, pboFore, pboOpaque, pptlOrg, pfci)) goto PostSDA; }
SendAsSDA: // We reach here in the case we could not send for some reason.
// Accumulate in screen data area.
if (pso->hsurf == ppdev->hsurfFrameBuf) { INC_OUTCOUNTER(OUT_TEXTOUT_SDA);
// Get bounding rectangle, convert to a RECT, and convert to
// inclusive coordinates.
if (prclOpaque != NULL) { RECT_FROM_RECTL(rectTrg, (*prclOpaque)); } else { RECT_FROM_RECTL(rectTrg, pstro->rclBkGround); TRC_DBG((TB, "Using STROBJ bgd for size")); }
OEClipRect(&rectTrg); ADD_INCOUNTER(IN_SDA_TEXTOUT_AREA, COM_SIZEOF_RECT(rectTrg)); // Output to SDA
OEClipAndAddScreenDataArea(&rectTrg, pco); } else { // If we can't send orders for offscreen rendering, we will
// bail offscreen support for this bitmap.
TRC_ALT((TB, "screen data call for offscreen rendering")); if (!(pdsurf->flags & DD_NO_OFFSCREEN)) CH_RemoveCacheEntry(sbcOffscreenBitmapCacheHandle, pdsurf->bitmapId);
DC_QUIT; }
PostSDA: // All done: consider sending the output.
SCH_DDOutputAvailable(ppdev, FALSE);
CalledOnDisconnected: DC_EXIT_POINT:
DC_END_FN(); return rc; }
/****************************************************************************/ // DrvDestroyFont - see NT DDK documentation.
/****************************************************************************/ VOID DrvDestroyFont(FONTOBJ *pfo) { FONTCACHEINFO *pfci;
DC_BEGIN_FN("DrvDestroyFont");
pfci = pfo->pvConsumer;
if (pfci != NULL) { if (pddShm != NULL && pfci->cacheHandle) pddShm->sbc.glyphCacheInfo[pfci->cacheId].cbUseCount--;
if (sbcFontCacheInfoList != NULL && sbcFontCacheInfoList[pfci->listIndex] == pfci) { sbcFontCacheInfoList[pfci->listIndex] = NULL; } EngFreeMem(pfci); pfo->pvConsumer = NULL; }
DC_END_FN(); }
/****************************************************************************/ // DrvLineTo - see NT DDK documentation.
/****************************************************************************/ BOOL RDPCALL DrvLineTo( SURFOBJ *pso, CLIPOBJ *pco, BRUSHOBJ *pbo, LONG x1, LONG y1, LONG x2, LONG y2, RECTL *prclBounds, MIX mix) { PDD_PDEV ppdev = (PDD_PDEV)pso->dhpdev; PDD_DSURF pdsurf = NULL; BOOL rc = TRUE; RECTL rectTrg; POINTL startPoint; POINTL endPoint; OE_ENUMRECTS ClipRects;
DC_BEGIN_FN("DrvLineTo");
// Sometimes, we're called after being disconnected.
if (ddConnected && pddShm != NULL) { // Surface is non-NULL.
pso = OEGetSurfObjBitmap(pso, &pdsurf); INC_OUTCOUNTER(OUT_LINETO_ALL);
// Get bounding rectangle and clip to 16-bit wire size.
RECT_FROM_RECTL(rectTrg, (*prclBounds)); OEClipRect(&rectTrg);
// Punt the call back to GDI to do the drawing.
rc = EngLineTo(pso, pco, pbo, x1, y1, x2, y2, prclBounds, mix); if (rc) { if ((pso->hsurf == ppdev->hsurfFrameBuf) || (!(pdsurf->flags & DD_NO_OFFSCREEN))) { // Send a switch surface PDU if the destination surface is
// different from last drawing order. If we failed to send the
// PDU, we will just have to bail on this drawing order.
if (!OESendSwitchSurfacePDU(ppdev, pdsurf)) { TRC_ERR((TB, "failed to send the switch surface PDU")); DC_QUIT; } } else { // if noOffscreen flag is on, we will bail on sending
// the client any further offscreen rendering. And will send the final
// offscreen to screen blt as regular memblt.
TRC_NRM((TB, "Offscreen blt bail")); DC_QUIT; }
TRC_NRM((TB, "LINETO")); } else { TRC_ERR((TB, "EngLineTo failed")); DC_QUIT; } } else { if (pso->iType == STYPE_DEVBITMAP) { // Surface is non-NULL.
pso = OEGetSurfObjBitmap(pso, &pdsurf); // Punt the call back to GDI to do the drawing.
rc = EngLineTo(pso, pco, pbo, x1, y1, x2, y2, prclBounds, mix); } else { TRC_ERR((TB, "Called when disconnected")); } goto CalledOnDisconnect; }
// Check if we are allowed to send this order.
if (OE_SendAsOrder(TS_ENC_LINETO_ORDER)) { // Check for a solid brush required for the order.
if (pbo->iSolidColor != -1) { unsigned RetVal;
// Get the intersection between the dest rect and the
// clip rects. Check for overcomplicated or nonintersecting
// clipping.
RetVal = OEGetIntersectingClipRects(pco, &rectTrg, CD_ANY, &ClipRects); if (RetVal == CLIPRECTS_OK) { // Set up data for order.
startPoint.x = x1; startPoint.y = y1; endPoint.x = x2; endPoint.y = y2; } else if (RetVal == CLIPRECTS_TOO_COMPLEX) { TRC_NRM((TB, "Clipping is too complex")); INC_OUTCOUNTER(OUT_LINETO_SDA_COMPLEXCLIP); goto SendAsSDA; } else if (RetVal == CLIPRECTS_NO_INTERSECTIONS) { TRC_NRM((TB, "Clipping does not intersect destrect")); DC_QUIT; } } else { TRC_NRM((TB, "Bad brush for line")); INC_OUTCOUNTER(OUT_LINETO_SDA_BADBRUSH); goto SendAsSDA; } } else { TRC_NRM((TB, "LineTo order not allowed")); INC_OUTCOUNTER(OUT_LINETO_SDA_UNSUPPORTED); goto SendAsSDA; }
// Store the order.
if (OEEncodeLineToOrder(ppdev, &startPoint, &endPoint, mix & 0x1F, pbo->iSolidColor, &ClipRects)) { goto PostSDA; } else { TRC_DBG((TB, "Failed to add order - use SDA")); INC_OUTCOUNTER(OUT_LINETO_SDA_FAILEDADD); goto SendAsSDA; }
SendAsSDA: // If we got here we could not send as an order, send as screen data
// instead.
if (pso->hsurf == ppdev->hsurfFrameBuf) { INC_OUTCOUNTER(OUT_LINETO_SDA); ADD_INCOUNTER(IN_SDA_LINETO_AREA, COM_SIZEOF_RECT(rectTrg)); OEClipAndAddScreenDataArea(&rectTrg, pco); } else { // if we can't send orders for offscreen rendering, we will
// bail offscreen support for this bitmap
TRC_ALT((TB, "screen data call for offscreen rendering")); if (!(pdsurf->flags & DD_NO_OFFSCREEN)) CH_RemoveCacheEntry(sbcOffscreenBitmapCacheHandle, pdsurf->bitmapId);
DC_QUIT; }
PostSDA: // Have the scheduler consider flushing output.
SCH_DDOutputAvailable(ppdev, FALSE);
CalledOnDisconnect: DC_EXIT_POINT: DC_END_FN(); return rc; }
/****************************************************************************/ // OEEmitReplayOrders
//
// Direct-encodes a series of replay-last primary orders.
/****************************************************************************/ BOOL OEEmitReplayOrders( PDD_PDEV ppdev, unsigned NumFieldFlagBytes, OE_ENUMRECTS *pClipRects) { BOOL rc = TRUE; BYTE *pBuffer; unsigned i; unsigned NumRects; PINT_ORDER pOrder;
DC_BEGIN_FN("OEEmitReplayOrders");
// Since the first order took the first rect, emit replay orders for the
// remaining rects.
NumRects = pClipRects->rects.c; for (i = 1; i < NumRects; i++) { pOrder = OA_AllocOrderMem(ppdev, MAX_REPLAY_CLIPPED_ORDER_SIZE); if (pOrder != NULL) { pBuffer = pOrder->OrderData;
// Control flags are primary order plus all field flag bytes zero.
*pBuffer++ = TS_STANDARD | TS_BOUNDS | (NumFieldFlagBytes << TS_ZERO_FIELD_COUNT_SHIFT);
// Construct the new bounds rect just after this.
OE2_EncodeBounds(pBuffer - 1, &pBuffer, &pClipRects->rects.arcl[i]);
pOrder->OrderLength = (unsigned)(pBuffer - pOrder->OrderData);
INC_INCOUNTER(IN_REPLAY_ORDERS); ADD_INCOUNTER(IN_REPLAY_BYTES, pOrder->OrderLength); OA_AppendToOrderList(pOrder);
TRC_DBG((TB,"Emit replay order for cliprect (%d,%d,%d,%d)", pClipRects->rects.arcl[i].left, pClipRects->rects.arcl[i].top, pClipRects->rects.arcl[i].right, pClipRects->rects.arcl[i].bottom)); } else { TRC_ERR((TB,"Error allocating mem for replay order")); rc = FALSE; break; } }
DC_END_FN(); return rc; }
/****************************************************************************/ // DrvStrokePath - see NT DDK documentation.
/****************************************************************************/
// Worker function - encodes a delta from one point to another in a minimal
// form in the PolyLine 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 (fail 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.
BOOL OEEncodePolyLinePointDelta( BYTE **ppCurEncode, unsigned *pNumDeltas, unsigned *pDeltaSize, BYTE *ZeroFlags, POINTL *pFromPoint, POINTL *pToPoint, RECTL *pBoundRect) { int Delta; BYTE Zeros = 0; BYTE *pBuffer; unsigned EncodeLen;
DC_BEGIN_FN("OEEncodePolyLinePointDelta");
pBuffer = *ppCurEncode;
Delta = pToPoint->x - pFromPoint->x; if (Delta == 0) { EncodeLen = 0; Zeros |= ORD_POLYLINE_XDELTA_ZERO; } else if (Delta >= -64 && Delta <= 63) { *pBuffer++ = (BYTE)(Delta & 0x7F); EncodeLen = 1; } else { // This is ugly, but necessitated by some stress-type apps that
// will send us a large coordinate and expect us to clip it. In an
// ideal world we would actually clip the line coordinates to the
// clip rectangle given us in DrvStrokePath and recalc the deltas
// based on the new line endpoints. However, since no normal apps
// seem to send these bad lines, we simply clip the delta and hope
// the slope of the resulting line is similar to the slope from the
// original delta.
if (Delta >= -16384 && Delta <= 16384) { *pBuffer++ = (BYTE)((Delta >> 8) | ORD_POLYLINE_LONG_DELTA); *pBuffer++ = (BYTE)(Delta & 0xFF); EncodeLen = 2; } else { TRC_ALT((TB,"X delta %d too large/small to encode", Delta)); return FALSE; } }
Delta = pToPoint->y - pFromPoint->y; if (Delta == 0) { Zeros |= ORD_POLYLINE_YDELTA_ZERO; } else if (Delta >= -64 && Delta <= 63) { *pBuffer++ = (BYTE)(Delta & 0x7F); EncodeLen += 1; } else { // See comments for the similar code above.
if (Delta >= -16384 && Delta <= 16384) { *pBuffer++ = (BYTE)((Delta >> 8) | ORD_POLYLINE_LONG_DELTA); *pBuffer++ = (BYTE)(Delta & 0xFF); EncodeLen += 2; } else { TRC_ALT((TB,"Y delta %d too large/small to encode", Delta)); return FALSE; } }
// Set the zero flags by shifting the two bits we've accumulated.
ZeroFlags[(*pNumDeltas / 4)] |= (Zeros >> (2 * (*pNumDeltas & 0x03)));
*pNumDeltas += 1; *pDeltaSize += EncodeLen; *ppCurEncode = pBuffer;
// Update the bounding rect (exclusive coords).
if (pToPoint->x < pBoundRect->left) pBoundRect->left = pToPoint->x; else if ((pToPoint->x + 1) >= pBoundRect->right) pBoundRect->right = pToPoint->x + 1; if (pToPoint->y < pBoundRect->top) pBoundRect->top = pToPoint->y; else if ((pToPoint->y + 1) >= pBoundRect->bottom) pBoundRect->bottom = pToPoint->y + 1;
DC_END_FN(); return TRUE; }
// Worker function to allocate and direct-encode a PolyLine order.
// Note that the subpathing of a PolyLine makes it possible for
// the entire order to be clipped out by part of the clip rectangles.
// We cannot encode these clipped orders since they change the
// direct-encode state. To counter this we receive as a parameter the list of
// clip rects, and we don't allocate and create the order if it will be
// entirely clipped. Returns TRUE if there was no problem allocating space
// for the order (clipping the order completely is not an error).
BOOL OECreateAndFlushPolyLineOrder( PDD_PDEV ppdev, RECTL *pBoundRect, OE_ENUMRECTS *pClipRects, POINTL *pStartPoint, BRUSHOBJ *pbo, CLIPOBJ *pco, unsigned ROP2, unsigned NumDeltas, unsigned DeltaSize, BYTE *Deltas, BYTE *ZeroFlags) { BOOL rc = TRUE; unsigned i, NumRects; unsigned NumZeroFlagBytes; PINT_ORDER pOrder; OE_ENUMRECTS IntersectRects;
DC_BEGIN_FN("OECreateAndFlushPolyLineOrder");
// First check to see if the order is completely clipped by the
// rects returned by the clip object. pBoundRect is exclusive.
IntersectRects.rects.c = 0; if (pClipRects->rects.c == 0 || OEGetIntersectionsWithClipRects(pBoundRect, pClipRects, &IntersectRects) > 0) { // Round the number of zero flag bits actually used upward to the
// nearest byte. Each point encoded takes two bits.
NumZeroFlagBytes = (NumDeltas + 3) / 4; TRC_ASSERT((NumZeroFlagBytes <= ORD_MAX_POLYLINE_ZERO_FLAGS_BYTES), (TB,"Too many zero-flags bytes")); TRC_ASSERT((NumDeltas <= ORD_MAX_POLYLINE_ENCODED_POINTS), (TB,"Too many encoded orders")); TRC_ASSERT((DeltaSize <= ORD_MAX_POLYLINE_CODEDDELTAS_LEN), (TB,"Too many encoded delta bytes"));
// 1 field flag byte.
pOrder = OA_AllocOrderMem(ppdev, MAX_ORDER_SIZE(IntersectRects.rects.c, 1, MAX_POLYLINE_BASE_FIELDS_SIZE + 1 + NumZeroFlagBytes + DeltaSize)); if (pOrder != NULL) { BYTE *pControlFlags = pOrder->OrderData; BYTE *pBuffer = pControlFlags + 1; BYTE *pFieldFlags; short Delta, NormalCoordEncoding[2]; BOOLEAN bUseDeltaCoords; unsigned NumFields; DCCOLOR Color; unsigned TotalSize;
// Direct-encode the primary order fields. 1 field flag byte.
*pControlFlags = TS_STANDARD; OE2_EncodeOrderType(pControlFlags, &pBuffer, TS_ENC_POLYLINE_ORDER); pFieldFlags = pBuffer; *pFieldFlags = 0; pBuffer++; if (IntersectRects.rects.c != 0) OE2_EncodeBounds(pControlFlags, &pBuffer, &IntersectRects.rects.arcl[0]);
// Inline field encode directly to wire format.
// Simultaneously determine if each of the coordinate fields has
// changed, whether we can use delta coordinates, and save changed
// fields.
NumFields = 0; bUseDeltaCoords = TRUE;
// XStart
Delta = (short)(pStartPoint->x - PrevPolyLine.XStart); if (Delta) { PrevPolyLine.XStart = pStartPoint->x; if (Delta != (short)(char)Delta) bUseDeltaCoords = FALSE; pBuffer[NumFields] = (char)Delta; NormalCoordEncoding[NumFields] = (short)pStartPoint->x; NumFields++; *pFieldFlags |= 0x01; }
// YStart
Delta = (short)(pStartPoint->y - PrevPolyLine.YStart); if (Delta) { PrevPolyLine.YStart = pStartPoint->y; if (Delta != (short)(char)Delta) bUseDeltaCoords = FALSE; pBuffer[NumFields] = (char)Delta; NormalCoordEncoding[NumFields] = (short)pStartPoint->y; NumFields++; *pFieldFlags |= 0x02; }
// Copy the final coordinates to the order.
if (bUseDeltaCoords) { *pControlFlags |= TS_DELTA_COORDINATES; pBuffer += NumFields; } else { *((DWORD UNALIGNED *)pBuffer) = *((DWORD *)NormalCoordEncoding); pBuffer += NumFields * sizeof(short); }
// ROP2
if (ROP2 != PrevPolyLine.ROP2) { PrevPolyLine.ROP2 = ROP2; *pBuffer++ = (BYTE)ROP2; *pFieldFlags |= 0x04; } // BrushCacheEntry. This field is currently unused. We simply choose
// always to send zero, which means we can skip the field for
// the encoding. This is field encoding flag 0x08.
// PenColor is a 3-byte color field.
OEConvertColor(ppdev, &Color, pbo->iSolidColor, NULL); if (memcmp(&Color, &PrevPolyLine.PenColor, sizeof(Color))) { PrevPolyLine.PenColor = Color; *pBuffer++ = Color.u.rgb.red; *pBuffer++ = Color.u.rgb.green; *pBuffer++ = Color.u.rgb.blue; *pFieldFlags |= 0x10; }
// NumDeltaEntries
if (NumDeltas != PrevPolyLine.NumDeltaEntries) { PrevPolyLine.NumDeltaEntries = NumDeltas; *pBuffer++ = (BYTE)NumDeltas; *pFieldFlags |= 0x20; } // CodedDeltaList - a variable-length byte stream. First 1-byte
// value is the count of bytes, followed by the zero flags and
// then the deltas. This field is considered different from the
// previous if the length or the contents are different.
*pBuffer = (BYTE)(DeltaSize + NumZeroFlagBytes); memcpy(pBuffer + 1, ZeroFlags, NumZeroFlagBytes); memcpy(pBuffer + 1 + NumZeroFlagBytes, Deltas, DeltaSize); TotalSize = 1 + NumZeroFlagBytes + DeltaSize; if (memcmp(pBuffer, &PrevPolyLine.CodedDeltaList, TotalSize)) { memcpy(&PrevPolyLine.CodedDeltaList, pBuffer, TotalSize); pBuffer += TotalSize; *pFieldFlags |= 0x40; }
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_STROKEPATH_POLYLINE); ADD_INCOUNTER(IN_POLYLINE_BYTES, pOrder->OrderLength); OA_AppendToOrderList(pOrder);
// Flush the order.
if (IntersectRects.rects.c < 2) rc = TRUE; else rc = OEEmitReplayOrders(ppdev, 1, &IntersectRects); } else { TRC_ERR((TB,"Failed to alloc space for order")); rc = FALSE; } } else { TRC_DBG((TB,"Clipping PolyLine order - no intersecting clip rects")); }
DC_END_FN(); return rc; }
// Worker function - combines the chore of allocating EllipseSC order from
// the OA heap and contructing the order given the parameters. Then we give
// the order to OE to finish encoding. Returns TRUE on success (meaning no
// error allocating from the order heap).
BOOL OECreateAndFlushEllipseSCOrder( PDD_PDEV ppdev, RECT *pEllipseRect, BRUSHOBJ *pbo, OE_ENUMRECTS *pClipRects, unsigned ROP2, FLONG flOptions) { BOOL rc = TRUE; PINT_ORDER pOrder; PELLIPSE_SC_ORDER pEllipseSC; OE_ENUMRECTS IntersectRects; RECTL ExclusiveRect;
DC_BEGIN_FN("OECreateAndFlushEllipseSCOrder");
// EllipseRect is inclusive, we need exclusive for getting clip rects.
ExclusiveRect = *((RECTL *)pEllipseRect); ExclusiveRect.right++; ExclusiveRect.bottom++;
// First make sure the clip rects actually intersect with the ellipse
// after its target screen rect has been calculated. Note that
// *pEllipseRect is already in inclusive coords.
IntersectRects.rects.c = 0; if (pClipRects->rects.c == 0 || OEGetIntersectionsWithClipRects(&ExclusiveRect, pClipRects, &IntersectRects) > 0) { // 1 field flag byte.
pOrder = OA_AllocOrderMem(ppdev, MAX_ORDER_SIZE(IntersectRects.rects.c, 1, MAX_ELLIPSE_SC_FIELD_SIZE)); if (pOrder != NULL) { // Set up the order fields in the temp buffer.
pEllipseSC = (PELLIPSE_SC_ORDER)oeTempOrderBuffer; pEllipseSC->LeftRect = pEllipseRect->left; pEllipseSC->RightRect = pEllipseRect->right; pEllipseSC->TopRect = pEllipseRect->top; pEllipseSC->BottomRect = pEllipseRect->bottom; pEllipseSC->ROP2 = ROP2; pEllipseSC->FillMode = flOptions; OEConvertColor(ppdev, &pEllipseSC->Color, pbo->iSolidColor, NULL);
// Slow-field-encode the order with the first clip rect
// (if present).
pOrder->OrderLength = OE2_EncodeOrder(pOrder->OrderData, TS_ENC_ELLIPSE_SC_ORDER, NUM_ELLIPSE_SC_FIELDS, (BYTE *)pEllipseSC, (BYTE *)&PrevEllipseSC, etable_EC, (IntersectRects.rects.c == 0 ? NULL : &IntersectRects.rects.arcl[0]));
ADD_INCOUNTER(IN_ELLIPSE_SC_BYTES, pOrder->OrderLength); OA_AppendToOrderList(pOrder);
// Flush the order.
if (IntersectRects.rects.c < 2) rc = TRUE; else rc = OEEmitReplayOrders(ppdev, 1, &IntersectRects); } else { TRC_ERR((TB,"Failed to alloc order heap space for ellipse")); rc = FALSE; } } else { // We still return TRUE here since the order was handled OK, just not
// sent.
TRC_NRM((TB,"Ellipse does not intersect with cliprects")); }
DC_END_FN(); return rc; }
// The real function.
BOOL RDPCALL DrvStrokePath( SURFOBJ *pso, PATHOBJ *ppo, CLIPOBJ *pco, XFORMOBJ *pxo, BRUSHOBJ *pbo, POINTL *pptlBrushOrg, LINEATTRS *plineattrs, MIX mix) { PDD_PDEV ppdev = (PDD_PDEV)pso->dhpdev; PDD_DSURF pdsurf = NULL; BOOL rc = TRUE; RECTFX rectfxTrg; RECTL rectTrg; BOOL fMore = TRUE; PATHDATA pathData; POINTL originPoint; POINTL startPoint; POINTL nextPoint; POINTL endPoint; unsigned pathIndex; OE_ENUMRECTS ClipRects;
DC_BEGIN_FN("DrvStrokePath");
// Sometimes, we're called after being disconnected.
if (ddConnected && pddShm != NULL) { // Surface is non-NULL.
pso = OEGetSurfObjBitmap(pso, &pdsurf);
// Get bounding rectangle.
PATHOBJ_vGetBounds(ppo, &rectfxTrg); RECT_FROM_RECTFX(rectTrg, rectfxTrg);
// Punt the call back to GDI to do the drawing.
INC_OUTCOUNTER(OUT_STROKEPATH_ALL);
rc = EngStrokePath(pso, ppo, pco, pxo, pbo, pptlBrushOrg, plineattrs, mix); if (!rc) { TRC_ERR((TB, "EngStrokePath failed")); DC_QUIT; }
// if the path bound gives empty rect, we'll just ignore
if (rectTrg.left == 0 && rectTrg.right == 0 && rectTrg.top == 0 && rectTrg.bottom == 0) { TRC_ERR((TB, "Empty Path obj bounding rect, ignore")); DC_QUIT; } } else { if (pso->iType == STYPE_DEVBITMAP) { // Surface is non-NULL.
pso = OEGetSurfObjBitmap(pso, &pdsurf);
rc = EngStrokePath(pso, ppo, pco, pxo, pbo, pptlBrushOrg, plineattrs, mix); } else { TRC_ERR((TB, "Called when disconnected")); } goto CalledOnDisconnected; }
if ((pso->hsurf == ppdev->hsurfFrameBuf) || (!(pdsurf->flags & DD_NO_OFFSCREEN))) { // Send a switch surface PDU if the destination surface is different
// from last drawing order. If we failed to send the PDU, we will
// just have to bail on this drawing order.
if (!OESendSwitchSurfacePDU(ppdev, pdsurf)) { TRC_ERR((TB, "failed to send the switch surface PDU")); DC_QUIT; } } else { // if noOffscreen flag is on, we will bail on sending
// the client any further offscreen rendering. And will send the final
// offscreen to screen blt as regular memblt.
TRC_NRM((TB, "Offscreen blt bail")); DC_QUIT; }
// Check if we are allowed to send this order.
if (OE_SendAsOrder(TS_ENC_POLYLINE_ORDER)) { // Check for a valid brush for the test operation.
if (pbo->iSolidColor != -1) { unsigned RetVal;
// Get the intersection between the entire dest rect and
// the clip rects. Check for overcomplicated or
// nonintersecting clipping. Note this is a first cut,
// further interactions are calculated for each PolyLine
// subpath and ellipse created.
RetVal = OEGetIntersectingClipRects(pco, &rectTrg, CD_ANY, &ClipRects); if (RetVal == CLIPRECTS_TOO_COMPLEX) { TRC_NRM((TB, "Clipping is too complex")); INC_OUTCOUNTER(OUT_STROKEPATH_SDA_COMPLEXCLIP); goto SendAsSDA; } else if (RetVal == CLIPRECTS_NO_INTERSECTIONS) { TRC_NRM((TB, "Clipping does not intersect destrect")); DC_QUIT;
} } else { TRC_NRM((TB, "Bad brush for line")); INC_OUTCOUNTER(OUT_STROKEPATH_SDA_BADBRUSH); goto SendAsSDA; } } else { TRC_NRM((TB, "PolyLine order not allowed")); INC_OUTCOUNTER(OUT_STROKEPATH_SDA_NOLINETO); goto SendAsSDA; }
// See if we can optimize the path...
// We cannot send beziers, geometric lines, or nonstandard patterns.
if (ppo->fl & PO_ELLIPSE && OE_SendAsOrder(TS_ENC_ELLIPSE_SC_ORDER)) { RECT EllipseRect;
// Get the inclusive rect covering only the ellipse itself.
// Add 4/16 to left and top, subtract from right and bottom, to undo
// the GDI transformation already performed.
EllipseRect.left = FXTOLROUND(rectfxTrg.xLeft + 4); EllipseRect.top = FXTOLROUND(rectfxTrg.yTop + 4); EllipseRect.right = FXTOLROUND(rectfxTrg.xRight - 4); EllipseRect.bottom = FXTOLROUND(rectfxTrg.yBottom - 4);
// We use fillmode 0 to indidate this is a polyline ellipse.
if (OECreateAndFlushEllipseSCOrder(ppdev, &EllipseRect, pbo, &ClipRects, mix & 0x1F, 0)) { INC_OUTCOUNTER(OUT_STROKEPATH_ELLIPSE_SC); goto PostSDA; } else { // No order heap space, send all as SDAs.
INC_OUTCOUNTER(OUT_STROKEPATH_SDA_FAILEDADD); goto SendAsSDA; } }
else if (!(ppo->fl & PO_BEZIERS) && !(plineattrs->fl & LA_GEOMETRIC) && plineattrs->pstyle == NULL) { BYTE Deltas[ORD_MAX_POLYLINE_CODEDDELTAS_LEN]; BYTE ZeroFlags[ORD_MAX_POLYLINE_ZERO_FLAGS_BYTES]; BYTE *pCurEncode; RECTL BoundRect; POINTL HoldPoint; unsigned NumDeltas; unsigned DeltaSize;
// This is a set of solid cosmetic (i.e. no fancy end styles)
// width-1 lines. NT stores all paths as a set of independent
// sub-paths. Each sub-path can start at a new point that is NOT
// linked to the previous sub-path. Paths used for this function
// (as opposed to DrvFillPath or DrvStrokeAndFillPath) do not need
// to be closed. We assume that the first enumerated subpath will
// have the PD_BEGINSUBPATH flag set; this appears to match reality.
PATHOBJ_vEnumStart(ppo);
while (fMore) { // Get the next set of lines.
fMore = PATHOBJ_bEnum(ppo, &pathData);
TRC_DBG((TB, "PTS: %lu FLAG: %08lx", pathData.count, pathData.flags));
// If this is the start of a path, remember the origin point in
// case we need to close the path at the end. startPoint is the
// start point for the current PolyLine order in the rare case
// where we have more than MAX_POLYLINE_ENCODED_POINTS points.
if (pathData.flags & PD_BEGINSUBPATH) { POINT_FROM_POINTFIX(originPoint, pathData.pptfx[0]); nextPoint = originPoint; startPoint = originPoint;
// Set up encoding variables. Start the bound rect with
// a zero-size rect.
BoundRect.left = BoundRect.right = startPoint.x; BoundRect.top = BoundRect.bottom = startPoint.y; NumDeltas = DeltaSize = 0; pCurEncode = Deltas; memset(ZeroFlags, 0, sizeof(ZeroFlags)); pathIndex = 1; } else { // This is a continuation from a previous PATHDATA.
nextPoint = HoldPoint; pathIndex = 0; }
// Generate deltas for each point in the path.
for (; pathIndex < pathData.count; pathIndex++) { POINT_FROM_POINTFIX(endPoint, pathData.pptfx[pathIndex]);
// Don't try to encode points where both deltas are zero.
if ((nextPoint.x != endPoint.x) || (nextPoint.y != endPoint.y)) { if (OEEncodePolyLinePointDelta(&pCurEncode, &NumDeltas, &DeltaSize, ZeroFlags, &nextPoint, &endPoint, &BoundRect)) { // Check for full order and flush if need be.
if (NumDeltas == ORD_MAX_POLYLINE_ENCODED_POINTS) { if (OECreateAndFlushPolyLineOrder(ppdev, &BoundRect, &ClipRects, &startPoint, pbo, pco, mix & 0x1F, NumDeltas, DeltaSize, Deltas, ZeroFlags)) { // We have a new temporary start point in the
// middle of the path.
startPoint = endPoint;
// Reset encoding variables.
BoundRect.left = BoundRect.right = startPoint.x; BoundRect.top = BoundRect.bottom = startPoint.y; NumDeltas = DeltaSize = 0; pCurEncode = Deltas; memset(ZeroFlags, 0, sizeof(ZeroFlags)); } else { // No order heap space, send all as SDAs.
INC_OUTCOUNTER(OUT_STROKEPATH_SDA_FAILEDADD); goto SendAsSDA; } } } else { goto SendAsSDA; } }
nextPoint = endPoint; }
// Close the path if necessary.
if (pathData.flags & PD_CLOSEFIGURE) { // Don't try to encode points where both deltas are zero.
if ((nextPoint.x != originPoint.x) || (nextPoint.y != originPoint.y)) { if (!OEEncodePolyLinePointDelta(&pCurEncode, &NumDeltas, &DeltaSize, ZeroFlags, &nextPoint, &originPoint, &BoundRect)) { goto SendAsSDA; } }
// PD_CLOSEFIGURE is present only with PD_ENDSUBPATH but
// just in case...
TRC_ASSERT((pathData.flags & PD_ENDSUBPATH), (TB,"CLOSEFIGURE received without ENDSUBPATH")); }
if (pathData.flags & PD_ENDSUBPATH) { if (NumDeltas > 0) { // We are at the end of the subpath. Flush the data we
// have.
if (!OECreateAndFlushPolyLineOrder(ppdev, &BoundRect, &ClipRects, &startPoint, pbo, pco, mix & 0x1F, NumDeltas, DeltaSize, Deltas, ZeroFlags)) { // No order heap space, send all as SDAs.
INC_OUTCOUNTER(OUT_STROKEPATH_SDA_FAILEDADD); goto SendAsSDA; } } } else { HoldPoint = endPoint; } }
goto PostSDA; }
SendAsSDA: if (pso->hsurf == ppdev->hsurfFrameBuf) { INC_OUTCOUNTER(OUT_STROKEPATH_SDA); ADD_INCOUNTER(IN_SDA_STROKEPATH_AREA, COM_SIZEOF_RECT(rectTrg));
// Clip the bound rect to 16 bits and add to SDA.
OEClipRect(&rectTrg); TRC_DBG((TB, "SDA: (%d,%d)(%d,%d)", rectTrg.left, rectTrg.top, rectTrg.right, rectTrg.bottom)); OEClipAndAddScreenDataArea(&rectTrg, pco); } else { // If we can't send orders for offscreen rendering, we will
// bail offscreen support for this bitmap.
TRC_ALT((TB, "screen data call for offscreen rendering")); if (!(pdsurf->flags & DD_NO_OFFSCREEN)) CH_RemoveCacheEntry(sbcOffscreenBitmapCacheHandle, pdsurf->bitmapId);
DC_QUIT; }
PostSDA: // Have scheduler consider sending output.
SCH_DDOutputAvailable(ppdev, FALSE);
CalledOnDisconnected: DC_EXIT_POINT: DC_END_FN(); return rc; }
/****************************************************************************/ /* DrvFillPath - see NT DDK documentation. */ /****************************************************************************/
// Worker function - combines the chore of allocating PolyGonCB order from
// the OA heap and contructing the order given the parameters. Then we give
// the order to OE to finish encoding. Returns TRUE on success (meaning no
// error allocating from the order heap).
BOOL OECreateAndFlushPolygonCBOrder( PDD_PDEV ppdev, RECTL *pBoundRect, POINTL *pStartPoint, POE_BRUSH_DATA pCurrentBrush, POINTL *pptlBrushOrg, OE_ENUMRECTS *pClipRects, MIX mix, FLONG flOptions, unsigned NumDeltas, unsigned DeltaSize, BYTE *Deltas, BYTE *ZeroFlags) { BOOL rc = TRUE; unsigned NumZeroFlagBytes; PINT_ORDER pOrder; PPOLYGON_CB_ORDER pPolygonCB; OE_ENUMRECTS IntersectRects;
DC_BEGIN_FN("OECreateAndFlushPolygonCBOrder");
// First make sure the clip rects actually intersect with the polygon
// after its target screen rect has been calculated. Note that
// *pBoundRect is in exclusive coords.
IntersectRects.rects.c = 0; if (pClipRects->rects.c == 0 || OEGetIntersectionsWithClipRects(pBoundRect, pClipRects, &IntersectRects) > 0) { // Round the number of zero flag bits actually used upward to the
// nearest byte. Each point encoded takes two bits.
NumZeroFlagBytes = (NumDeltas + 3) / 4; TRC_ASSERT((NumZeroFlagBytes <= ORD_MAX_POLYGON_ZERO_FLAGS_BYTES), (TB,"Too many zero-flags bytes")); TRC_ASSERT((NumDeltas <= ORD_MAX_POLYGON_ENCODED_POINTS), (TB,"Too many encoded orders")); TRC_ASSERT((DeltaSize <= ORD_MAX_POLYGON_CODEDDELTAS_LEN), (TB,"Too many encoded delta bytes"));
// 2 field flag bytes.
pOrder = OA_AllocOrderMem(ppdev, MAX_ORDER_SIZE(IntersectRects.rects.c, 2, MAX_POLYGON_CB_BASE_FIELDS_SIZE + 1 + NumZeroFlagBytes + DeltaSize)); if (pOrder != NULL) { // Set up the order fields.
pPolygonCB = (PPOLYGON_CB_ORDER)oeTempOrderBuffer; pPolygonCB->XStart = pStartPoint->x; pPolygonCB->YStart = pStartPoint->y;
// If this is a hatched brush, the high bit of ROP2 indicates the
// background fill mode: 1 means transparent, 0 means opaque.
pPolygonCB->ROP2 = mix & 0x1F; if (pCurrentBrush->style == BS_HATCHED && ((mix & 0x1F00) >> 8) == R2_NOP) pPolygonCB->ROP2 |= 0x80;
pPolygonCB->FillMode = flOptions; pPolygonCB->NumDeltaEntries = NumDeltas; pPolygonCB->CodedDeltaList.len = DeltaSize + NumZeroFlagBytes;
// Pattern colors.
pPolygonCB->BackColor = pCurrentBrush->back; pPolygonCB->ForeColor = pCurrentBrush->fore;
// The protocol brush origin is the point on the screen where
// we want the brush to start being drawn from (tiling where
// necessary).
pPolygonCB->BrushOrgX = pptlBrushOrg->x; pPolygonCB->BrushOrgY = pptlBrushOrg->y; OEClipPoint((PPOINTL)&pPolygonCB->BrushOrgX);
// Extra brush data from the data when we realised the brush.
pPolygonCB->BrushStyle = pCurrentBrush->style; pPolygonCB->BrushHatch = pCurrentBrush->hatch;
memcpy(pPolygonCB->BrushExtra, pCurrentBrush->brushData, sizeof(pPolygonCB->BrushExtra));
// Copy the zero flags first.
memcpy(pPolygonCB->CodedDeltaList.Deltas, ZeroFlags, NumZeroFlagBytes);
// Next copy the encoded deltas.
memcpy(pPolygonCB->CodedDeltaList.Deltas + NumZeroFlagBytes, Deltas, DeltaSize);
// Slow-field-encode the order with the first clip rect
// (if present).
pOrder->OrderLength = OE2_EncodeOrder(pOrder->OrderData, TS_ENC_POLYGON_CB_ORDER, NUM_POLYGON_CB_FIELDS, (BYTE *)pPolygonCB, (BYTE *)&PrevPolygonCB, etable_BG, (IntersectRects.rects.c == 0 ? NULL : &IntersectRects.rects.arcl[0]));
ADD_INCOUNTER(IN_POLYGON_CB_BYTES, pOrder->OrderLength); OA_AppendToOrderList(pOrder);
// Flush the order.
if (IntersectRects.rects.c < 2) rc = TRUE; else rc = OEEmitReplayOrders(ppdev, 2, &IntersectRects); } else { TRC_ERR((TB,"Failed to alloc space for PolygonSC")); rc = FALSE; } } else { // We still return TRUE here since we handled the order by not
// sending it.
TRC_NRM((TB,"PolygonCB fully clipped out")); }
DC_END_FN(); return rc; }
// Worker function - combines the chore of allocating PolygonSC order from
// the OA heap and contructing the order given the parameters. Then we give
// the order to OE to finish encoding. Returns TRUE on success (meaning no
// error allocating from the order heap).
BOOL OECreateAndFlushPolygonSCOrder( PDD_PDEV ppdev, RECTL *pBoundRect, POINTL *pStartPoint, BRUSHOBJ *pbo, OE_ENUMRECTS *pClipRects, unsigned ROP2, FLONG flOptions, unsigned NumDeltas, unsigned DeltaSize, BYTE *Deltas, BYTE *ZeroFlags) { BOOL rc = TRUE; unsigned NumZeroFlagBytes; PINT_ORDER pOrder; PPOLYGON_SC_ORDER pPolygonSC; OE_ENUMRECTS IntersectRects;
DC_BEGIN_FN("OECreateAndFlushPolygonSCOrder");
// First make sure the clip rects actually intersect with the polygon
// after its target screen rect has been calculated. Note that
// *pBoundRect is in exclusive coords.
IntersectRects.rects.c = 0; if (pClipRects->rects.c == 0 || OEGetIntersectionsWithClipRects(pBoundRect, pClipRects, &IntersectRects) > 0) { // Round the number of zero flag bits actually used upward to the
// nearest byte. Each point encoded takes two bits.
NumZeroFlagBytes = (NumDeltas + 3) / 4; TRC_ASSERT((NumZeroFlagBytes <= ORD_MAX_POLYGON_ZERO_FLAGS_BYTES), (TB,"Too many zero-flags bytes")); TRC_ASSERT((NumDeltas <= ORD_MAX_POLYGON_ENCODED_POINTS), (TB,"Too many encoded orders")); TRC_ASSERT((DeltaSize <= ORD_MAX_POLYGON_CODEDDELTAS_LEN), (TB,"Too many encoded delta bytes"));
// 1 field flag byte.
pOrder = OA_AllocOrderMem(ppdev, MAX_ORDER_SIZE(IntersectRects.rects.c, 1, MAX_POLYGON_SC_BASE_FIELDS_SIZE + 1 + NumZeroFlagBytes + DeltaSize)); if (pOrder != NULL) { // Set up the order fields.
pPolygonSC = (PPOLYGON_SC_ORDER)oeTempOrderBuffer; pPolygonSC->XStart = pStartPoint->x; pPolygonSC->YStart = pStartPoint->y; pPolygonSC->ROP2 = ROP2; pPolygonSC->FillMode = flOptions; pPolygonSC->NumDeltaEntries = NumDeltas; pPolygonSC->CodedDeltaList.len = DeltaSize + NumZeroFlagBytes;
// Pattern colors.
OEConvertColor(ppdev, &pPolygonSC->BrushColor, pbo->iSolidColor, NULL);
// Copy the zero flags first.
memcpy(pPolygonSC->CodedDeltaList.Deltas, ZeroFlags, NumZeroFlagBytes);
// Next copy the encoded deltas.
memcpy(pPolygonSC->CodedDeltaList.Deltas + NumZeroFlagBytes, Deltas, DeltaSize);
// Slow-field-encode the order with the first clip rect
// (if present).
pOrder->OrderLength = OE2_EncodeOrder(pOrder->OrderData, TS_ENC_POLYGON_SC_ORDER, NUM_POLYGON_SC_FIELDS, (BYTE *)pPolygonSC, (BYTE *)&PrevPolygonSC, etable_CG, (IntersectRects.rects.c == 0 ? NULL : &IntersectRects.rects.arcl[0]));
ADD_INCOUNTER(IN_POLYGON_SC_BYTES, pOrder->OrderLength); OA_AppendToOrderList(pOrder);
// Flush the order.
if (IntersectRects.rects.c < 2) rc = TRUE; else rc = OEEmitReplayOrders(ppdev, 1, &IntersectRects); } else { TRC_ERR((TB,"Failed to alloc space for PolygonCB")); rc = FALSE; } } else { // We still return TRUE here since we handled the order by not
// sending it.
TRC_NRM((TB,"PolygonSC completely clipped")); }
DC_END_FN(); return rc; }
// Worker function - combines the chore of allocating EllipseCB order from
// the OA heap and contructing the order given the parameters. Then we give
// the order to OE to finish encoding. Returns TRUE on success (meaning no
// error allocating from the order heap).
BOOL OECreateAndFlushEllipseCBOrder( PDD_PDEV ppdev, RECT *pEllipseRect, POE_BRUSH_DATA pCurrentBrush, POINTL *pptlBrushOrg, OE_ENUMRECTS *pClipRects, MIX mix, FLONG flOptions) { BOOL rc = TRUE; PINT_ORDER pOrder; PELLIPSE_CB_ORDER pEllipseCB; OE_ENUMRECTS IntersectRects; RECTL ExclusiveRect;
DC_BEGIN_FN("OECreateAndFlushEllipseCBOrder");
// EllipseRect is inclusive, we need exclusive for getting clip rects.
ExclusiveRect = *((RECTL *)pEllipseRect); ExclusiveRect.right++; ExclusiveRect.bottom++;
// First make sure the clip rects actually intersect with the ellipse
// after its target screen rect has been calculated. Note that
// *pEllipseRect is already in inclusive coords.
IntersectRects.rects.c = 0; if (pClipRects->rects.c == 0 || OEGetIntersectionsWithClipRects(&ExclusiveRect, pClipRects, &IntersectRects) > 0) {
// 2 field flag bytes.
pOrder = OA_AllocOrderMem(ppdev, MAX_ORDER_SIZE(IntersectRects.rects.c, 2, MAX_ELLIPSE_CB_FIELD_SIZE)); if (pOrder != NULL) { // Set up the order fields.
pEllipseCB = (PELLIPSE_CB_ORDER)oeTempOrderBuffer; pEllipseCB->LeftRect = pEllipseRect->left; pEllipseCB->RightRect = pEllipseRect->right; pEllipseCB->TopRect = pEllipseRect->top; pEllipseCB->BottomRect = pEllipseRect->bottom; pEllipseCB->FillMode = flOptions;
// If this is a hatched brush, the high bit of ROP2 indicates the
// background fill mode: 1 means transparent, 0 means opaque.
pEllipseCB->ROP2 = mix & 0x1F; if (pCurrentBrush->style == BS_HATCHED && ((mix & 0x1F00) >> 8) == R2_NOP) pEllipseCB->ROP2 |= 0x80; // Pattern colors.
pEllipseCB->BackColor = pCurrentBrush->back; pEllipseCB->ForeColor = pCurrentBrush->fore;
// The protocol brush origin is the point on the screen where
// we want the brush to start being drawn from (tiling where
// necessary).
pEllipseCB->BrushOrgX = pptlBrushOrg->x; pEllipseCB->BrushOrgY = pptlBrushOrg->y; OEClipPoint((PPOINTL)&pEllipseCB->BrushOrgX);
// Extra brush data from the data when we realised the brush.
pEllipseCB->BrushStyle = pCurrentBrush->style; pEllipseCB->BrushHatch = pCurrentBrush->hatch;
memcpy(pEllipseCB->BrushExtra, pCurrentBrush->brushData, sizeof(pEllipseCB->BrushExtra));
// Slow-field-encode the order with the first clip rect
// (if present).
pOrder->OrderLength = OE2_EncodeOrder(pOrder->OrderData, TS_ENC_ELLIPSE_CB_ORDER, NUM_ELLIPSE_CB_FIELDS, (BYTE *)pEllipseCB, (BYTE *)&PrevEllipseCB, etable_EB, (IntersectRects.rects.c == 0 ? NULL : &IntersectRects.rects.arcl[0]));
ADD_INCOUNTER(IN_ELLIPSE_CB_BYTES, pOrder->OrderLength); OA_AppendToOrderList(pOrder);
// Flush the order.
if (IntersectRects.rects.c < 2) rc = TRUE; else rc = OEEmitReplayOrders(ppdev, 2, &IntersectRects); } else { TRC_ERR((TB,"Unable to alloc space for EllipseCB")); rc = FALSE; } } else { // We still return TRUE here since we handled the order by not
// sending it.
TRC_NRM((TB,"EllipseCB completely clipped")); }
DC_END_FN(); return rc; }
//
// DrvFillPath
//
BOOL RDPCALL DrvFillPath( SURFOBJ *pso, PATHOBJ *ppo, CLIPOBJ *pco, BRUSHOBJ *pbo, POINTL *pptlBrushOrg, MIX mix, FLONG flOptions) { PDD_PDEV ppdev = (PDD_PDEV)pso->dhpdev; PDD_DSURF pdsurf = NULL; BOOL rc = TRUE; RECTFX rectfxTrg; RECTL rectTrg; RECT EllipseRect; BOOL fMore = TRUE; PATHDATA pathData; POINTL startPoint; POINTL nextPoint; POINTL endPoint; unsigned pathIndex; POE_BRUSH_DATA pCurrentBrush; OE_ENUMRECTS ClipRects;
DC_BEGIN_FN("DrvFillPath");
// Sometimes, we're called after being disconnected.
if (ddConnected && pddShm != NULL) { // Surface is non-NULL.
pso = OEGetSurfObjBitmap(pso, &pdsurf);
// Get bounding rectangle.
PATHOBJ_vGetBounds(ppo, &rectfxTrg); RECT_FROM_RECTFX(rectTrg, rectfxTrg);
// Punt the call back to GDI to do the drawing.
INC_OUTCOUNTER(OUT_FILLPATH_ALL);
// Check if we are allowed to send this order (determined by the
// negotiated capabilities of all the machines in the conference).
// We shouldn't do Eng call if we return FALSE. Otherwise, the frame
// buffer will be already drawn, and it will cause rendering problems
// when GDI re-renders it to other drawings.
if (OE_SendAsOrder(TS_ENC_POLYGON_SC_ORDER) || OE_SendAsOrder(TS_ENC_POLYGON_CB_ORDER)) { rc = EngFillPath(pso, ppo, pco, pbo, pptlBrushOrg, mix, flOptions); } else { TRC_NRM((TB, "Polygon order not allowed")); INC_OUTCOUNTER(OUT_FILLPATH_SDA_NOPOLYGON);
// If the client doesn't support polygon, we just have
// to fail DrvFillPath, the GDI will rerender the drawing
// to other Drv calls
return FALSE; }
// if the path bound gives empty rect, we'll just ignore
if (rectTrg.left == 0 && rectTrg.right == 0 && rectTrg.top == 0 && rectTrg.bottom == 0) { TRC_ERR((TB, "Empty Path obj bounding rect, ignore")); DC_QUIT; }
if (rc) { if ((pso->hsurf == ppdev->hsurfFrameBuf) || (!(pdsurf->flags & DD_NO_OFFSCREEN))) { // Send a switch surface PDU if the destination surface is different
// from last drawing order. If we failed to send the PDU, we will
// just have to bail on this drawing order.
if (!OESendSwitchSurfacePDU(ppdev, pdsurf)) { TRC_ERR((TB, "failed to send the switch surface PDU")); DC_QUIT; } } else { // if noOffscreen flag is on, we will bail on sending
// the client any further offscreen rendering. And will send the final
// offscreen to screen blt as regular memblt.
TRC_NRM((TB, "Offscreen blt bail")); DC_QUIT; }
// Check for a valid brush for the test operation.
if (OECheckBrushIsSimple(ppdev, pbo, &pCurrentBrush)) { unsigned RetVal;
// Get the intersection between the dest rect and the
// clip rects. Check for overcomplicated or
// nonintersecting clipping. Note that this is an
// initial pass, we so another intersection with
// the (possibly smaller) individual order rect
// generated later.
RetVal = OEGetIntersectingClipRects(pco, &rectTrg, CD_ANY, &ClipRects); if (RetVal == CLIPRECTS_TOO_COMPLEX) { TRC_NRM((TB, "Clipping is too complex")); INC_OUTCOUNTER(OUT_FILLPATH_SDA_COMPLEXCLIP); goto SendAsSDA; } else if (RetVal == CLIPRECTS_NO_INTERSECTIONS) { TRC_NRM((TB, "Clipping does not intersect destrect")); DC_QUIT; } } else { TRC_NRM((TB, "Bad brush for polygon")); INC_OUTCOUNTER(OUT_FILLPATH_SDA_BADBRUSH); goto SendAsSDA; }
// See if we can optimize the path...
// We cannot send beziers, and ellipses are sent as a distinct order.
if (ppo->fl & PO_ELLIPSE && (OE_SendAsOrder(TS_ENC_ELLIPSE_SC_ORDER) || OE_SendAsOrder(TS_ENC_ELLIPSE_CB_ORDER))) { // Get the inclusive rect covering only the ellipse itself.
// Add 4/16 to left and top, subtract from right and bottom,
// to undo the GDI transformation.
EllipseRect.left = FXTOLROUND(rectfxTrg.xLeft + 4); EllipseRect.top = FXTOLROUND(rectfxTrg.yTop + 4); EllipseRect.right = FXTOLROUND(rectfxTrg.xRight - 4); EllipseRect.bottom = FXTOLROUND(rectfxTrg.yBottom - 4);
if (pbo->iSolidColor != -1) { // Solid color ellipse.
if (OECreateAndFlushEllipseSCOrder(ppdev, &EllipseRect, pbo, &ClipRects, mix & 0x1F, flOptions)) { INC_OUTCOUNTER(OUT_FILLPATH_ELLIPSE_SC); goto PostSDA; } else { // No order heap space, send all as SDAs.
INC_OUTCOUNTER(OUT_FILLPATH_SDA_FAILEDADD); goto SendAsSDA; } } else { // Color pattern brush ellipse.
if (OECreateAndFlushEllipseCBOrder(ppdev, &EllipseRect, pCurrentBrush, pptlBrushOrg, &ClipRects, mix, flOptions)) { INC_OUTCOUNTER(OUT_FILLPATH_ELLIPSE_CB); goto PostSDA; } else { // No order heap space, send all as SDAs.
INC_OUTCOUNTER(OUT_FILLPATH_SDA_FAILEDADD); goto SendAsSDA; } } } else if (!(ppo->fl & PO_BEZIERS)) { BYTE Deltas[ORD_MAX_POLYGON_CODEDDELTAS_LEN]; BYTE ZeroFlags[ORD_MAX_POLYGON_ZERO_FLAGS_BYTES]; POINTL SubPathBoundPts[ORD_MAX_POLYGON_ENCODED_POINTS]; BYTE *pCurEncode; RECTL BoundRect; POINTL HoldPoint; unsigned NumDeltas; unsigned DeltaSize; int PointIndex = 0; BOOL bPathStart = TRUE;
// This is a set of solid cosmetic (i.e. no fancy end styles)
// width-1 lines. NT stores all paths as a set of independent
// sub-paths. Each sub-path can start at a new point that is
// NOT linked to the previous sub-path. Paths used for this
// function need to be closed.
PATHOBJ_vEnumStart(ppo);
while (fMore) { // Get the next set of lines.
fMore = PATHOBJ_bEnum(ppo, &pathData);
TRC_DBG((TB, "PTS: %lu FLAG: %08lx", pathData.count, pathData.flags));
// If this is the start of a path, remember the start point as
// we need to close the path at the end. startPoint is the
// start point for the current PolyGon order.
if (bPathStart) { POINT_FROM_POINTFIX(startPoint, pathData.pptfx[0]); nextPoint = startPoint;
// Set up encoding variables.
BoundRect.left = BoundRect.right = startPoint.x; BoundRect.top = BoundRect.bottom = startPoint.y;
NumDeltas = DeltaSize = 0; pCurEncode = Deltas; memset(ZeroFlags, 0, sizeof(ZeroFlags)); pathIndex = 1; bPathStart = FALSE; } else { // This is a continuation from a previous PATHDATA.
nextPoint = HoldPoint; pathIndex = 0; }
// If NumDeltas is > max, we have to send as screen
// data unfortunately since we can't encode this.
if (NumDeltas + pathData.count + PointIndex > ORD_MAX_POLYGON_ENCODED_POINTS) { // No order heap space, send all as SDAs.
INC_OUTCOUNTER(OUT_FILLPATH_SDA_FAILEDADD); goto SendAsSDA; }
// record subpath's start point
if (pathData.flags & PD_BEGINSUBPATH) { POINT_FROM_POINTFIX(SubPathBoundPts[PointIndex] , pathData.pptfx[0]); PointIndex++; }
// Generate deltas for each point in the path.
for (; pathIndex < pathData.count; pathIndex++) { POINT_FROM_POINTFIX(endPoint, pathData.pptfx[pathIndex]);
// Don't try to encode points where both deltas are zero.
if ((nextPoint.x != endPoint.x) || (nextPoint.y != endPoint.y)) { if (!OEEncodePolyLinePointDelta(&pCurEncode, &NumDeltas, &DeltaSize, ZeroFlags, &nextPoint, &endPoint, &BoundRect)) { goto SendAsSDA; } }
nextPoint = endPoint; }
// Record subpath's end point
if (pathData.flags & PD_ENDSUBPATH) { SubPathBoundPts[PointIndex] = endPoint; PointIndex++; }
HoldPoint = endPoint; }
if (NumDeltas > 0) { // If NumDeltas is > max, we have to send as screen
// data unfortunately since we can't encode this.
if (NumDeltas + PointIndex - 2 > ORD_MAX_POLYGON_ENCODED_POINTS) { // No order heap space, send all as SDAs.
INC_OUTCOUNTER(OUT_FILLPATH_SDA_FAILEDADD); goto SendAsSDA; }
// For Polygon, we append all the subpath together
// and send as one polygon order. But we need to close
// each subpath respectively to make sure all the subpath
// are closed properly
for (pathIndex = PointIndex - 2; pathIndex > 0; pathIndex--) { endPoint = SubPathBoundPts[pathIndex];
// Don't try to encode points where both deltas are zero.
if ((nextPoint.x != endPoint.x) || (nextPoint.y != endPoint.y)) { if (!OEEncodePolyLinePointDelta(&pCurEncode, &NumDeltas, &DeltaSize, ZeroFlags, &nextPoint, &endPoint, &BoundRect)) goto SendAsSDA; }
nextPoint = endPoint; } // We are at the end of the path. Flush the data we have.
if (pbo->iSolidColor != -1) { // solid color polygon
if (OECreateAndFlushPolygonSCOrder(ppdev, &BoundRect, &startPoint, pbo, &ClipRects, mix & 0x1F, flOptions, NumDeltas, DeltaSize, Deltas, ZeroFlags)) { INC_OUTCOUNTER(OUT_FILLPATH_POLYGON_SC); } else { // No order heap space, send all as SDAs.
INC_OUTCOUNTER(OUT_FILLPATH_SDA_FAILEDADD); goto SendAsSDA; }
} else { // color pattern brush polygon
if (OECreateAndFlushPolygonCBOrder(ppdev, &BoundRect, &startPoint, pCurrentBrush, pptlBrushOrg, &ClipRects, mix, flOptions, NumDeltas, DeltaSize, Deltas, ZeroFlags)) { INC_OUTCOUNTER(OUT_FILLPATH_POLYGON_CB); } else { // No order heap space, send all as SDAs.
INC_OUTCOUNTER(OUT_FILLPATH_SDA_FAILEDADD); goto SendAsSDA; } } } goto PostSDA; } else { TRC_DBG((TB, "Got PO_BEZIERS fill")); goto SendAsSDA; } } else { TRC_ERR((TB, "EngFillPath failed")); DC_QUIT; } } else { if (pso->iType == STYPE_DEVBITMAP) { // Surface is non-NULL.
pso = OEGetSurfObjBitmap(pso, &pdsurf);
rc = EngFillPath(pso, ppo, pco, pbo, pptlBrushOrg, mix, flOptions);
} else { TRC_ERR((TB, "Called when disconnected")); } goto CalledOnDisconnected; }
SendAsSDA:
if (pso->hsurf == ppdev->hsurfFrameBuf) { // If we reached here we did not encode as an order, clip the bound
// rect to 16 bits and add to screen data.
INC_OUTCOUNTER(OUT_FILLPATH_SDA); ADD_INCOUNTER(IN_SDA_FILLPATH_AREA, COM_SIZEOF_RECT(rectTrg)); OEClipRect(&rectTrg); TRC_DBG((TB, "SDA: (%d,%d)(%d,%d)", rectTrg.left, rectTrg.top, rectTrg.right, rectTrg.bottom)); if((rectTrg.right != rectTrg.left) && (rectTrg.bottom != rectTrg.top)) { OEClipAndAddScreenDataArea(&rectTrg, pco); } else { TRC_ASSERT(FALSE,(TB,"Invalid Add Rect (%d,%d,%d,%d)", rectTrg.left, rectTrg.top, rectTrg.right, rectTrg.bottom)); DC_QUIT; } } else { // For now, if we can't send orders for offscreen rendering, we will
// bail offscreen support for this bitmap
TRC_ALT((TB, "screen data call for offscreen rendering")); if (!(pdsurf->flags & DD_NO_OFFSCREEN)) CH_RemoveCacheEntry(sbcOffscreenBitmapCacheHandle, pdsurf->bitmapId);
DC_QUIT; }
PostSDA: // Have scheduler consider sending output.
SCH_DDOutputAvailable(ppdev, FALSE);
CalledOnDisconnected: DC_EXIT_POINT: DC_END_FN(); return rc; }
/****************************************************************************/ // DrvPaint - see NT DDK documentation.
/****************************************************************************/ BOOL RDPCALL DrvPaint( SURFOBJ *pso, CLIPOBJ *pco, BRUSHOBJ *pbo, POINTL *pptlBrushOrg, MIX mix) { PDD_PDEV ppdev = (PDD_PDEV)pso->dhpdev; PDD_DSURF pdsurf = NULL; SURFOBJ *psoBitmap; BOOL rc = TRUE; RECTL rectTrg; BYTE rop3; OE_ENUMRECTS ClipRects;
DC_BEGIN_FN("DrvPaint");
// Sometimes, we're called after being disconnected.
if (ddConnected && pddShm != NULL) { // Surface is non-NULL.
psoBitmap = OEGetSurfObjBitmap(pso, &pdsurf); INC_OUTCOUNTER(OUT_PAINT_ALL);
// Throw the drawing to GDI first.
rc = EngPaint(psoBitmap, pco, pbo, pptlBrushOrg, mix); if (rc) { // If ppdev is NULL then this is a blt to GDI managed memory
// bitmap, so there is no need to send any orders to the client.
if (ppdev != NULL) { unsigned RetVal;
// Get bounding rectangle and clip to 16 bits.
RECT_FROM_RECTL(rectTrg, pco->rclBounds); OEClipRect(&rectTrg);
// If this function is changed, need to know that psoTrg
// points to the GDI DIB bitmap in offscreen bitmap case.
// Enumerate the clip rects into a usable form.
RetVal = OEGetIntersectingClipRects(pco, &rectTrg, CD_ANY, &ClipRects); if (RetVal == CLIPRECTS_TOO_COMPLEX) { TRC_NRM((TB, "Clipping is too complex")); INC_OUTCOUNTER(OUT_PAINT_SDA_COMPLEXCLIP); goto SendAsSDA; }
TRC_ASSERT((RetVal != CLIPRECTS_NO_INTERSECTIONS), (TB,"clipobj for DrvPaint is messed up")); } else { // if ppdev is NULL, we are blt to GDI managed bitmap,
// so, the dhurf of the target surface should be NULL
TRC_ASSERT((pdsurf == NULL), (TB, "NULL ppdev - psoTrg has non NULL dhsurf")); TRC_NRM((TB, "NULL ppdev - paint to GDI managed bitmap")); INC_OUTCOUNTER(OUT_PAINT_UNSENT); DC_QUIT; }
if ((psoBitmap->hsurf == ppdev->hsurfFrameBuf) || (!(pdsurf->flags & DD_NO_OFFSCREEN))) { // Send a switch surface PDU if the destination surface is
// different from last drawing order. If we failed to send
// the PDU, we will just have to bail on this drawing order.
if (!OESendSwitchSurfacePDU(ppdev, pdsurf)) { TRC_ERR((TB, "failed to send the switch surface PDU")); goto SendAsSDA; } } else { // If noOffscreen flag is on, we will bail on sending
// the client any further offscreen rendering.
// And will send the final offscreen to screen blt as
// regular memblt.
TRC_NRM((TB, "Offscreen blt bail")); goto SendAsSDA; } } else { TRC_ERR((TB,"Failed EngPaint call")); INC_OUTCOUNTER(OUT_PAINT_UNSENT); DC_QUIT; } } else { if (pso->iType == STYPE_DEVBITMAP) { // Surface is non-NULL.
psoBitmap = OEGetSurfObjBitmap(pso, &pdsurf); // Throw the drawing to GDI first.
rc = EngPaint(psoBitmap, pco, pbo, pptlBrushOrg, mix); } else { TRC_ERR((TB, "Called when disconnected")); } goto CalledOnDisconnected; }
// The low byte of the mix represents a ROP2. We need a ROP3 for
// the paint operation, so convert the mix as follows.
//
// Remember the definitions of 2, 3 & 4 way ROP codes.
//
// Msk Pat Src Dst
//
// 1 1 1 1 --------+------+ ROP2 uses P & D only
// 1 1 1 0 | |
// 1 1 0 1 -+ | | ROP3 uses P, S & D
// 1 1 0 0 |ROP2-1|ROP3 |ROP4
// 1 0 1 1 |(see | | ROP4 uses M, P, S & D
// 1 0 1 0 -+ note)| |
// 1 0 0 1 | |
// 1 0 0 0 --------+ |
// 0 1 1 1 |
// 0 1 1 0 | NOTE: Windows defines its
// 0 1 0 1 | ROP2 codes as the bitwise
// 0 1 0 0 | value calculated here
// 0 0 1 1 | plus one. All other ROP
// 0 0 1 0 | codes are the straight
// 0 0 0 1 | bitwise value.
// 0 0 0 0 ---------------+
//
// Or, algorithmically...
// ROP3 = (ROP2 & 0x3) | ((ROP2 & 0xC) << 4) | (ROP2 << 2)
// ROP4 = (ROP3 << 8) | ROP3
mix = (mix & 0x1F) - 1; rop3 = (BYTE)((mix & 0x3) | ((mix & 0xC) << 4) | (mix << 2));
// Check if we are allowed to send the ROP3.
if (OESendRop3AsOrder(rop3)) { // Check for a pattern or true destination BLT.
if (!ROP3_NO_PATTERN(rop3)) { // Check whether we can encode the PatBlt as an OpaqueRect.
// It must be solid with a PATCOPY rop.
if (pbo->iSolidColor != -1 && rop3 == OE_PATCOPY_ROP) { if (!OEEncodeOpaqueRect(&rectTrg, pbo, ppdev, &ClipRects)) { // Something went wrong with the encoding, so skip
// to the end to add this operation to the SDA.
goto SendAsSDA; } } else if (!OEEncodePatBlt(ppdev, pbo, &rectTrg, pptlBrushOrg, rop3, &ClipRects)) { // Something went wrong with the encoding, so skip to
// the end to add this operation to the SDA.
goto SendAsSDA; } } else { if (!OEEncodeDstBlt(&rectTrg, rop3, ppdev, &ClipRects)) goto SendAsSDA; } } else { TRC_NRM((TB, "Cannot send ROP3 %d", rop3)); INC_OUTCOUNTER(OUT_BITBLT_SDA_NOROP3); goto SendAsSDA; }
// Sent the order, skip sending SDA.
goto PostSDA;
SendAsSDA: // If we reached here we could not send via DrvBitBlt().
// Use EngPaint to paint the screen backdrop then add the area to the SDA.
if (psoBitmap->hsurf == ppdev->hsurfFrameBuf) { OEClipAndAddScreenDataArea(&rectTrg, pco);
// All done: consider sending the output.
SCH_DDOutputAvailable(ppdev, FALSE); INC_OUTCOUNTER(OUT_PAINT_SDA); } else { // If we can't send orders for offscreen rendering, we will
// bail offscreen support for the target bitmap.
TRC_ALT((TB, "screen data call for offscreen rendering")); if (!(pdsurf->flags & DD_NO_OFFSCREEN)) CH_RemoveCacheEntry(sbcOffscreenBitmapCacheHandle, pdsurf->bitmapId); }
PostSDA: CalledOnDisconnected: DC_EXIT_POINT: DC_END_FN(); return rc; }
/****************************************************************************/ // DrvRealizeBrush - see NT DDK documentation.
/****************************************************************************/ BOOL RDPCALL DrvRealizeBrush( BRUSHOBJ *pbo, SURFOBJ *psoTarget, SURFOBJ *psoPattern, SURFOBJ *psoMask, XLATEOBJ *pxlo, ULONG iHatch) { PDD_PDEV ppdev = (PDD_PDEV)psoTarget->dhpdev; BOOL rc = TRUE; PBYTE pData; ULONG iBitmapFormat; #ifdef DC_HICOLOR
BYTE brushBits[192]; BYTE brushIndices[64]; unsigned pelSizeFactor = 1; UINT32 osColor; unsigned iColor; #else
BYTE brushBits[64]; #endif
UINT32 color1; UINT32 color2; UINT32 currColor; INT i, j, currIndex; BOOL brushSupported = TRUE; BYTE palette[MAX_UNIQUE_COLORS]; UINT32 brushSupportLevel; UINT32 colors[MAX_BRUSH_ENCODE_COLORS] = {1, 0}; UINT32 colorCount = 2; ULONG iBytes = 0;
DC_BEGIN_FN("DrvRealizeBrush");
INC_OUTCOUNTER(OUT_BRUSH_ALL);
// A valid brush satisfies any of the following criteria.
// 1) It is a standard hatch brush (as passed by DrvEnablePDEV).
// 2) It is an 8x8 monochrome bitmap.
// 3) It is an 8x8 color bitmap and the client can support it.
// Check for a Windows standard hatch.
if (iHatch < HS_DDI_MAX) { TRC_DBG((TB, "Standard hatch %lu", iHatch)); rc = OEStoreBrush(ppdev, pbo, BS_HATCHED, 1, &psoPattern->sizlBitmap, 0, NULL, pxlo, (BYTE)iHatch, palette, colors, colorCount);
INC_OUTCOUNTER(OUT_BRUSH_STANDARD); //standard brushes count as mono, don't double count
DEC_OUTCOUNTER(OUT_BRUSH_MONO); DC_QUIT; }
// If the driver has been passed a dither color brush we can support
// this by sending a solid color brush definition.
if ((iHatch & RB_DITHERCOLOR) != 0) { TRC_DBG((TB, "Standard hatch %lu", iHatch)); colors[0] = iHatch & 0x00FFFFFF; rc = OEStoreBrush(ppdev, pbo, BS_SOLID, 1, &psoPattern->sizlBitmap, 0, NULL, NULL, (BYTE)iHatch, palette, colors, colorCount);
INC_OUTCOUNTER(OUT_BRUSH_STANDARD); //standard brushes count as mono, don't double count
DEC_OUTCOUNTER(OUT_BRUSH_MONO); DC_QUIT; }
if ((psoPattern->sizlBitmap.cx == 8) && (psoPattern->sizlBitmap.cy == 8)) { brushSupportLevel = pddShm->sbc.caps.brushSupportLevel;
// NOTE: There's a flag (BMF_TOPDOWN) in psoPattern->fjBitmap
// that's supposed to indicate whether the bitmap is top-down or
// bottom-up, but it is not always set up correctly. In fact, the
// bitmaps are always the wrong way up for our protocol, so we have
// to flip them regardless of the flag. Hence the row numbers are
// reversed ('i' loops) in all the conversions below.
pData = psoPattern->pvScan0; iBitmapFormat = psoPattern->iBitmapFormat;
#ifdef DC_HICOLOR
// mono brushes are easy, regardless of operating color depth
if (iBitmapFormat == BMF_1BPP) { // every 8 pixels take a byte @ 1bpp
iBytes = 8; for (i = 7; i >= 0; i--) { brushBits[i] = *pData; pData += psoPattern->lDelta; } } else if (ppdev->cClientBitsPerPel < 15) { // for 4 and 8 bpp sessions, colors end up as indices regardless
// of the color depth of the brush;
switch (iBitmapFormat) { case BMF_4BPP: { iBitmapFormat = BMF_8BPP;
// Copy over the brush bits at 1 byte / pixel and track
// how many unique colors we have. The vast vast majority
// of brushes are 4 colors or less.
iBytes = 64; memset(palette, 0, sizeof(palette)); colorCount = 0;
for (i = 7; i >= 0; i--) { currIndex = i * 8; for (j = 0; j < 4; j++) { color1 = XLATEOBJ_iXlate(pxlo, (pData[j] >> 4)); color2 = XLATEOBJ_iXlate(pxlo, (pData[j] & 0x0F));
brushBits[currIndex] = (BYTE) color1; brushBits[currIndex + 1] = (BYTE) color2; currIndex += 2;
if (palette[color1] && palette[color2]) continue;
// if possible assign each unique color a four bit index
if (!palette[color1]) { if (colorCount < MAX_BRUSH_ENCODE_COLORS) colors[colorCount] = color1; colorCount++; palette[color1] = (BYTE) colorCount; }
if (!palette[color2]) { if (colorCount < MAX_BRUSH_ENCODE_COLORS) colors[colorCount] = color2; colorCount++; palette[color2] = (BYTE) colorCount; } } pData += psoPattern->lDelta; }
// The encoding value was set one larger than it should
// have been so adjust it
if (colorCount <= MAX_BRUSH_ENCODE_COLORS) { for (currColor = 0; currColor < colorCount; currColor++) palette[colors[currColor]]--; }
brushSupported = (colorCount <= 2) || (brushSupportLevel > TS_BRUSH_DEFAULT); } break;
case BMF_24BPP: case BMF_16BPP: case BMF_8BPP: { // When running at 4/8bpp, the xlateobj will convert the
// Nbpp bitmap pel values to 8bpp palette indices, so we
// just have to
// - set up a multiplier to access the pels correctly
// - fix up the number of bytes in the bitmap
// - lie about the color depth of the bitmap
TRC_DBG((TB, "Examining brush format %d", iBitmapFormat)); if (iBitmapFormat == BMF_24BPP) pelSizeFactor = 3; else if (iBitmapFormat == BMF_16BPP) pelSizeFactor = 2; else pelSizeFactor = 1;
iBytes = 64; iBitmapFormat = BMF_8BPP;
// Copy over the brush bits and track how many unique
// colors we have. The vast vast majority of brushes are
// 4 colors or less.
memset(palette, 0, sizeof(palette)); colorCount = 0;
for (i = 7; i >= 0; i--) { currIndex = i * 8; for (j = 0; j < 8; j++) { osColor = 0; memcpy(&osColor, &pData[j * pelSizeFactor], pelSizeFactor); currColor = XLATEOBJ_iXlate(pxlo, osColor);
TRC_DBG((TB, "This pel: %02x %02x %02x", (UINT)pData[j * 3], (UINT)pData[j * 3 + 1], (UINT)pData[j * 3 + 2] )); TRC_DBG((TB, "Color %08lx", currColor));
brushBits[currIndex] = (BYTE) currColor; currIndex++;
// assign each unique color a two bit index
if (palette[currColor]) { continue; } else { if (colorCount < MAX_BRUSH_ENCODE_COLORS) colors[colorCount] = currColor; colorCount++; palette[currColor] = (BYTE) colorCount; } }
pData += psoPattern->lDelta; }
// The encoding value was set one larger than it should
// have been so adjust it
if (colorCount <= MAX_BRUSH_ENCODE_COLORS) { for (currColor = 0; currColor < colorCount; currColor++) palette[colors[currColor]]--; }
brushSupported = (colorCount <= 2) || (brushSupportLevel > TS_BRUSH_DEFAULT); } break;
default: { // Unsupported brush format.
TRC_ALT((TB, "Brush of unsupported format %d", (UINT32)psoPattern->iBitmapFormat)); iBytes = 0; brushSupported = FALSE; } break; } } else { // hicolor makes things a little more complex for us; we have
// to handle and 3 byte color values instead of color indices
switch (iBitmapFormat) { case BMF_4BPP: { // Copy over the brush bits at the correct color depth,
// tracking how many unique colors we have. The vast
// vast majority of brushes are 4 colors or less.
// First set up the correct formats
if (ppdev->cClientBitsPerPel == 24) { iBitmapFormat = BMF_24BPP; iBytes = 192; } else { iBitmapFormat = BMF_16BPP; iBytes = 128; } colorCount = 0;
for (i = 7; i >= 0; i--) { currIndex = i * 8; for (j = 0; j < 4; j++) { color1 = XLATEOBJ_iXlate(pxlo, (pData[j] >> 4)); color2 = XLATEOBJ_iXlate(pxlo, (pData[j] & 0x0F));
// we will store each pel twice - once 'as is' and
// once as an index to the in-use color table
if (ppdev->cClientBitsPerPel == 24) { brushBits[currIndex * 3] = (TSUINT8)( color1 & 0x000000FF); brushBits[currIndex * 3 + 1] = (TSUINT8)((color1 >> 8) & 0x000000FF); brushBits[currIndex * 3 + 2] = (TSUINT8)((color1 >> 16)& 0x000000FF);
brushBits[currIndex * 3 + 4] = (TSUINT8)( color2 & 0x000000FF); brushBits[currIndex * 3 + 5] = (TSUINT8)((color2 >> 8) & 0x000000FF); brushBits[currIndex * 3 + 6] = (TSUINT8)((color2 >> 16)& 0x000000FF); } else { brushBits[currIndex * 2] = (TSUINT8)( color1 & 0x00FF); brushBits[currIndex * 2 + 1] = (TSUINT8)((color1 >> 8) & 0x00FF); brushBits[currIndex * 2 + 3] = (TSUINT8)( color2 & 0x00FF); brushBits[currIndex * 2 + 4] = (TSUINT8)((color2 >> 8) & 0x00FF); }
if (colorCount <= MAX_BRUSH_ENCODE_COLORS) { // we try to assign each unique color a two bit
// index. We can't just look in the palette
// this time; we have to actually search the
// in-use color table
for (iColor = 0; iColor < colorCount; iColor++) { if (colors[iColor] == color1) break; }
// Did we find the color in the in-use table?
if (iColor < colorCount) { brushIndices[currIndex] = (BYTE)iColor; } else { // maybe record the new color
if (colorCount < MAX_BRUSH_ENCODE_COLORS) { colors[colorCount] = color1; brushIndices[currIndex] = (BYTE)colorCount; } TRC_DBG((TB, "New color %08lx", color1)); colorCount++; }
// update the index
currIndex ++;
for (iColor = 0; iColor < colorCount; iColor++) { if (colors[iColor] == color2) break; }
// Did we find the color in the in-use table?
if (iColor < colorCount) { brushIndices[currIndex] = (BYTE)iColor; } else { // maybe record the new color
if (colorCount < MAX_BRUSH_ENCODE_COLORS) { colors[colorCount] = color2; brushIndices[currIndex] = (BYTE)colorCount; } TRC_DBG((TB, "New color %08lx", color2)); colorCount++; } currIndex ++; } else { // update the index
currIndex += 2; } }
pData += psoPattern->lDelta; }
TRC_DBG((TB, "Final color count %d", colorCount)); brushSupported = (colorCount <= 2) || (brushSupportLevel > TS_BRUSH_DEFAULT);
// Should we use the index versions instead of full RGBs?
if (brushSupported && (colorCount <= MAX_BRUSH_ENCODE_COLORS)) { // yes - copy them over
memcpy(brushBits, brushIndices, 64); iBytes = 64; } } break;
case BMF_24BPP: case BMF_16BPP: case BMF_8BPP: { // When running in hicolor, just as for 8bpp, we have to
// set up a multiplier to access the bits correctly and
// fix up the number of bytes in the bitmap and color
// format of the bitmap
//
// The complication is that the xlate object can give us
// 2 or 3 byte color values depending on the session bpp.
// Its not practical to use these to build a color table
// as for the 8bpp case (the color array would have to
// have 16.4 million entries!), so instead we build a
// list of the colors used
TRC_DBG((TB, "Examining brush format %d", iBitmapFormat)); if (iBitmapFormat == BMF_24BPP) pelSizeFactor = 3; else if (iBitmapFormat == BMF_16BPP) pelSizeFactor = 2; else pelSizeFactor = 1;
// now set up the converted formats
if (ppdev->cClientBitsPerPel == 24) { iBitmapFormat = BMF_24BPP; iBytes = 192; } else { iBitmapFormat = BMF_16BPP; iBytes = 128; }
// Copy over the brush bits and track how many unique colors
// we have. The vast vast majority of brushes are 4 colors
// or less.
colorCount = 0;
for (i = 7; i >= 0; i--) { currIndex = i * 8; for (j = 0; j < 8; j++) { osColor = 0; memcpy(&osColor, &pData[j * pelSizeFactor], pelSizeFactor);
currColor = XLATEOBJ_iXlate(pxlo, osColor);
TRC_DBG((TB, "OS Color : %08lx", osColor)); TRC_DBG((TB, "Color : %08lx", currColor));
// we will store each pel twice - once 'as is' and
// once as an index to the in-use color table
if (ppdev->cClientBitsPerPel == 24) { brushBits[currIndex * 3] = (TSUINT8)( currColor & 0x000000FF); brushBits[currIndex * 3 + 1] = (TSUINT8)((currColor >> 8) & 0x000000FF); brushBits[currIndex * 3 + 2] = (TSUINT8)((currColor >> 16)& 0x000000FF);
TRC_DBG((TB, "This pel : %02x %02x %02x", (UINT)pData[j * pelSizeFactor], (UINT)pData[j * pelSizeFactor + 1], (UINT)pData[j * pelSizeFactor + 2] ));
TRC_DBG((TB, "Brush bits: %02x %02x %02x", (UINT)brushBits[currIndex * 3], (UINT)brushBits[currIndex * 3 + 1], (UINT)brushBits[currIndex * 3 + 2] )); } else { brushBits[currIndex * 2] = (TSUINT8)( currColor & 0x00FF); brushBits[currIndex * 2 + 1] = (TSUINT8)((currColor >> 8) & 0x00FF);
TRC_DBG((TB, "This pel : %02x %02x", (UINT)pData[j * pelSizeFactor], (UINT)pData[j * pelSizeFactor + 1] ));
TRC_DBG((TB, "Brush bits: %02x %02x", (UINT)brushBits[currIndex * 2], (UINT)brushBits[currIndex * 2 + 1] )); }
if (colorCount <= MAX_BRUSH_ENCODE_COLORS) { // we try to assign each unique color a two bit
// index. We can't just look in the palette
// this time; we have to actually search the
// in-use color table
for (iColor = 0; iColor < colorCount; iColor++) { if (colors[iColor] == currColor) break; // from for loop
}
// Did we find the color in the in-use table?
if (iColor < colorCount) { brushIndices[currIndex] = (BYTE)iColor;
// safe to update the index now
currIndex++; continue; // next j
}
// maybe record the new color
if (colorCount < MAX_BRUSH_ENCODE_COLORS) { colors[colorCount] = currColor; brushIndices[currIndex] = (BYTE)colorCount; } TRC_DBG((TB, "New color %08lx", currColor)); colorCount++; }
// safe to update the index now
currIndex++; } // next j
pData += psoPattern->lDelta; } // next i
TRC_DBG((TB, "Final color count %d", colorCount)); brushSupported = (colorCount <= 2) || (brushSupportLevel > TS_BRUSH_DEFAULT);
// Should we use the index versions instead of full RGBs?
if (brushSupported && (colorCount <= MAX_BRUSH_ENCODE_COLORS)) { // yes - copy them over
memcpy(brushBits, brushIndices, 64); iBytes = 64; } } break;
default: { // Unsupported brush format.
TRC_ALT((TB, "Brush of unsupported format %d", (UINT32)psoPattern->iBitmapFormat)); iBytes = 0; brushSupported = FALSE; } break; } } #else
switch (psoPattern->iBitmapFormat) { case BMF_1BPP: { // every 8 pixels take a byte @ 1bpp
iBytes = 8; for (i = 7; i >= 0; i--) { brushBits[i] = *pData; pData += psoPattern->lDelta; } } break;
case BMF_4BPP: { // Copy over the brush bits at 1 byte / pixel and track how many
// unique colors we have. The vast vast majority of brushes are
// 4 colors or less.
iBytes = 64; memset(palette, 0, sizeof(palette)); colorCount = 0;
for (i = 7; i >= 0; i--) { currIndex = i * 8; for (j = 0; j < 4; j++) { color1 = XLATEOBJ_iXlate(pxlo, (pData[j] >> 4)); color2 = XLATEOBJ_iXlate(pxlo, (pData[j] & 0x0F)); brushBits[currIndex] = (BYTE) color1; brushBits[currIndex + 1] = (BYTE) color2; currIndex += 2;
if (palette[color1] && palette[color2]) continue;
// If possible assign each unique color a two bit index
if (!palette[color1]) { if (colorCount < MAX_BRUSH_ENCODE_COLORS) colors[colorCount] = color1; colorCount++; palette[color1] = (BYTE) colorCount; }
if (!palette[color2]) { if (colorCount < MAX_BRUSH_ENCODE_COLORS) colors[colorCount] = color2; colorCount++; palette[color2] = (BYTE) colorCount; } } pData += psoPattern->lDelta; }
// The encoding value was set one larger than it should
// have been so adjust it
if (colorCount <= MAX_BRUSH_ENCODE_COLORS) { for (currColor = 0; currColor < colorCount; currColor++) { palette[colors[currColor]]--; } }
brushSupported = (colorCount <= 2) || (brushSupportLevel > TS_BRUSH_DEFAULT); } break;
case BMF_8BPP: { // Copy over the brush bits and track how many unique colors
// we have. The vast vast majority of brushes are 4 colors
// or less.
iBytes = 64; memset(palette, 0, sizeof(palette)); colorCount = 0;
for (i = 7; i >= 0; i--) { currIndex = i * 8; for (j = 0; j < 8; j++) { currColor = XLATEOBJ_iXlate(pxlo, pData[j]); brushBits[currIndex] = (BYTE) currColor; currIndex++;
// assign each unique color a two bit index
if (palette[currColor]) { continue; } else { if (colorCount < MAX_BRUSH_ENCODE_COLORS) colors[colorCount] = currColor; colorCount++; palette[currColor] = (BYTE) colorCount; } }
pData += psoPattern->lDelta; }
// The encoding value was set one larger than it should
// have been so adjust it
if (colorCount <= MAX_BRUSH_ENCODE_COLORS) { for (currColor = 0; currColor < colorCount; currColor++) palette[colors[currColor]]--; }
brushSupported = (colorCount <= 2) || (brushSupportLevel > TS_BRUSH_DEFAULT); } break;
default: { // Unsupported colour depth.
iBytes = 0; brushSupported = FALSE; } break; } #endif
} else { iBitmapFormat = psoPattern->iBitmapFormat;
// The brush is the wrong size or requires dithering and so cannot
// be sent over the wire.
#ifdef DC_HICOLOR
TRC_ALT((TB, "Non-8x8 or dithered brush")); #endif
brushSupported = FALSE; }
// Store the brush.
if (brushSupported) { if (colorCount <= 2) INC_OUTCOUNTER(OUT_BRUSH_MONO);
// Store the brush - note that if we have a monochrome brush the
// color bit is set up so that 0 = color2 and 1 = color1. This
// actually corresponds to 0 = fg and 1 = bg for the protocol
// colors.
TRC_DBG((TB, "Storing brush: type %d bg %x fg %x", psoPattern->iBitmapFormat, colors[0], colors[1]));
rc = OEStoreBrush(ppdev, pbo, BS_PATTERN, iBitmapFormat, &psoPattern->sizlBitmap, iBytes, brushBits, pxlo, 0, palette, colors, colorCount); } else { if (!iBytes) { TRC_ALT((TB, "Rejected brush h %08lx s (%ld, %ld) fmt %lu", iHatch, psoPattern != NULL ? psoPattern->sizlBitmap.cx : 0, psoPattern != NULL ? psoPattern->sizlBitmap.cy : 0, psoPattern != NULL ? psoPattern->iBitmapFormat : 0)); } rc = OEStoreBrush(ppdev, pbo, BS_NULL, iBitmapFormat, &psoPattern->sizlBitmap, iBytes, brushBits, pxlo, 0, NULL, NULL, 0); INC_OUTCOUNTER(OUT_BRUSH_REJECTED); }
DC_EXIT_POINT: DC_END_FN(); return rc; }
/****************************************************************************/ // OE_RectIntersectsSDA
//
// Returns nonzero if the supplied exclusive rect intersects any of the
// current screen data areas.
/****************************************************************************/ BOOL RDPCALL OE_RectIntersectsSDA(PRECTL pRect) { RECTL aBoundRects[BA_MAX_ACCUMULATED_RECTS]; unsigned cBounds; BOOL fIntersection = FALSE; unsigned i;
DC_BEGIN_FN("OE_RectIntersectsSDA");
// Fetch the current Screen Data Area (SDA). It is returned in
// virtual desktop (inclusive) coordinates.
BA_QueryBounds(aBoundRects, &cBounds);
// Loop through each of the bounding rectangles checking for
// an intersection with the supplied rectangle.
// Note we use '<' for pRect->right and pRect->bottom because *pRect is
// in exclusive coords.
for (i = 0; i < cBounds; i++) { if (aBoundRects[i].left < pRect->right && aBoundRects[i].top < pRect->bottom && aBoundRects[i].right > pRect->left && aBoundRects[i].bottom > pRect->top) { TRC_NRM((TB, "ExclRect(%d,%d)(%d,%d) intersects SDA(%d,%d)(%d,%d)", pRect->left, pRect->top, pRect->right, pRect->bottom, aBoundRects[i].left, aBoundRects[i].top, aBoundRects[i].right, aBoundRects[i].bottom)); fIntersection = TRUE; break; } }
DC_END_FN(); return fIntersection; }
|