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