/****************************************************************************/ // nsdgint.cpp // // RDP Screen Data Grabber internal functions // // Copyright (C) 1996-2000 Microsoft Corporation /****************************************************************************/ #include #pragma hdrstop #define TRC_FILE "nsdgint" #include #include /****************************************************************************/ /* Name: SDGSendSDARect */ /* */ /* Purpose: Attempts to send the given rectangle of Screen Data */ /* in one or more BitmapPDUs */ /* */ /* Returns: TRUE if whole rectangle sucessfully sent, FALSE otherwise. */ /* The supplied rectangle is always updated to remove the area */ /* that was successfully sent i.e. upon return the rectangle */ /* contains the area that was NOT sent. */ /* */ /* Params: IN: pFrameBuf - pointer to the Frame Buffer */ /* IN/OUT: pRect - pointer to rectangle to send. Updated to */ /* contain the rectangle that was not sent. */ /* IN: mustSend - set if the PDU must be sent (last rectangle) */ /* IN: pPkgInfo - PDU package to use */ /****************************************************************************/ BOOL RDPCALL SHCLASS SDGSendSDARect( BYTE *pFrameBuf, unsigned frameBufferWidth, PRECTL pRect, BOOL mustSend, PPDU_PACKAGE_INFO pPkgInfo, SDG_ENCODE_CONTEXT *pContext) { BOOL rc = FALSE; int rectWidth; int rectHeight; int paddedWidthInPels; // DC_HICOLOR int paddedWidthInBytes; unsigned padByte; unsigned maxRowsLeft; BYTE *pSrc; BYTE *pPrevSrc; unsigned uncompressedBitmapSize; unsigned compressedBitmapSize = 0; unsigned transferHeight; unsigned pduSize; unsigned compEst; unsigned cbAdded; unsigned compRowsLeft; unsigned dataLeft; unsigned buffSpaceLeft; BOOL compRc; // Macro to calculate remaining space in the BitmapPDU, allowing for // any current rectangles. #define SDG_SPACE_LEFT \ ((pContext->pPackageSpace + pContext->BitmapPDUSize) - \ ((BYTE *)pContext->pSDARect + \ FIELDOFFSET(TS_BITMAP_DATA, bitmapData[0]))) DC_BEGIN_FN("SDGSendSDARect"); TRC_NRM((TB, "SDA rect: (%d,%d)(%d,%d)", pRect->left, pRect->top, pRect->right, pRect->bottom)); #ifdef DC_HICOLOR // For simplicity on 4bpp, round left down to an even number. if (m_pTSWd->desktopBpp == 4) pRect->left &= (~0x1); // Get the rectangle width and height, remembering that the supplied // coords are inclusive. rectWidth = pRect->right - pRect->left; rectHeight = pRect->bottom - pRect->top; // Pad rect width so that each row is dword aligned. paddedWidthInPels = (rectWidth + 3) & (~0x03); // Set up a pointer to the first byte to fetch from the Frame Buffer, // alowing for the color depth. if (m_pTSWd->desktopBpp == 24) { pSrc = pFrameBuf + 3 * (pRect->top * frameBufferWidth + pRect->left); paddedWidthInBytes = paddedWidthInPels * 3; } else if ((m_pTSWd->desktopBpp == 16) || (m_pTSWd->desktopBpp == 15)) { pSrc = pFrameBuf + 2 * (pRect->top * frameBufferWidth + pRect->left); paddedWidthInBytes = paddedWidthInPels * 2; } else if (m_pTSWd->desktopBpp == 8) { pSrc = pFrameBuf + (pRect->top * frameBufferWidth) + pRect->left; paddedWidthInBytes = paddedWidthInPels; } else { pSrc = pFrameBuf + (((pRect->top * frameBufferWidth) + pRect->left) >> 1); paddedWidthInBytes = paddedWidthInPels; } #else // Set up a pointer to the first byte to fetch from the Frame Buffer. if (m_pTSWd->desktopBpp == 8) { // Get the rectangle width and height, remembering that the // supplied coords are exclusive. rectWidth = pRect->right - pRect->left; rectHeight = pRect->bottom - pRect->top; pSrc = pFrameBuf + (pRect->top * frameBufferWidth) + pRect->left; // DC_HICOLOR paddedWidthInBytes = (rectWidth + 3) & (~0x03); } else { // Get the rectangle width and height, remembering that the // supplied coords are exclusive. // For simplicity on 4bpp, round left down to an even number. pRect->left &= (~0x1); rectWidth = pRect->right - pRect->left; rectHeight = pRect->bottom - pRect->top; pSrc = pFrameBuf + (((pRect->top * frameBufferWidth) + pRect->left) >> 1); // DC_HICOLOR paddedWidthInBytes = (rectWidth + 3) & (~0x03); } #endif TRC_NRM((TB, "%dbpp: pSrc %p, bytes/row %d", m_pTSWd->desktopBpp, pSrc, paddedWidthInBytes)); #ifndef DC_HICOLOR // Pad rect width so that each row is dword aligned. paddedWidthInPels = (rectWidth + 3) & (~0x03); #endif while ((rectHeight > 0) || mustSend) { // Have we filled the current PDU? Yes, if: // - there is no room for more data; or // - the last rectangle has been completely added; or // - multiple rects per PDU are not supported. if ((pContext->pBitmapPDU != NULL) && ((SDG_SPACE_LEFT < paddedWidthInBytes) || (rectHeight == 0))) { pduSize = (unsigned)((BYTE *)pContext->pSDARect - pContext->pPackageSpace); TRC_NRM((TB, "Send PDU size %u, numrects=%u", pduSize, pContext->pBitmapPDU->numberRectangles)); // Add the PDU to the package. SC_AddToPackage(pPkgInfo, pduSize, TRUE); INC_INCOUNTER(IN_SND_SDA_PDUS); // Just used up an entire PDU. Quit if the entire rectangle // was consumed OR we are in the middle of accumulating a // shadow buffer. pContext->pPackageSpace = NULL; pContext->pBitmapPDU = NULL; pContext->pSDARect = NULL; TRC_DBG((TB, "Reset pSDARect and pBitmapPDU")); if ((rectHeight == 0) || m_pTSWd->shadowState) { // There is no new data. Break out of while loop. // mustSend must be set. TRC_NRM((TB, "Finished processing rectangle")); break; } } // Set up a new PDU if required. if (pContext->pBitmapPDU == NULL) { // Find space needed for headers plus one TS_BITMAP_DATA hdr plus // the size of the header, throttled by the large packing size. dataLeft = rectHeight * paddedWidthInBytes + scUpdatePDUHeaderSpace + (unsigned)FIELDOFFSET(TS_UPDATE_BITMAP_PDU_DATA, rectangle[0]) + (unsigned)FIELDOFFSET(TS_BITMAP_DATA, bitmapData[0]); dataLeft = min(dataLeft, m_pShm->sch.LargePackingSize); TRC_NRM((TB, "Getting PDU, min size %d", dataLeft)); pContext->pPackageSpace = SC_GetSpaceInPackage(pPkgInfo, dataLeft); if (pContext->pPackageSpace != NULL) { // Set up the current PDU size. Throttle to the large // packing size. pContext->BitmapPDUSize = min( (pPkgInfo->cbLen - pPkgInfo->cbInUse), m_pShm->sch.LargePackingSize); TRC_NRM((TB, "Got PDU size %d", pContext->BitmapPDUSize)); // Fill in PDU header. if (scUseFastPathOutput) { pContext->pPackageSpace[0] = TS_UPDATETYPE_BITMAP | scCompressionUsedValue; } else { ((TS_UPDATE_BITMAP_PDU UNALIGNED *) pContext->pPackageSpace)->shareDataHeader.pduType2 = TS_PDUTYPE2_UPDATE; } pContext->pBitmapPDU = (TS_UPDATE_BITMAP_PDU_DATA UNALIGNED *) (pContext->pPackageSpace + scUpdatePDUHeaderSpace); pContext->pBitmapPDU->updateType = TS_UPDATETYPE_BITMAP; pContext->pBitmapPDU->numberRectangles = 0; // Set up a pointer to the rectangles. pContext->pSDARect = &(pContext->pBitmapPDU->rectangle[0]); } else { TRC_ALT((TB, "Failed to allocate buffer")); DC_QUIT; } } // Now copy as many lines as possible into the PDU. Code above // means there must be room for a line. TRC_ASSERT((paddedWidthInBytes > 0), (TB, "zero paddedRectWidth")); maxRowsLeft = (unsigned)SDG_SPACE_LEFT / paddedWidthInBytes; TRC_ASSERT((maxRowsLeft > 0), (TB, "Internal error: no room for a row, space %u, width %u", SDG_SPACE_LEFT, paddedWidthInBytes)); // This figure does not allow for the compression we are about to // apply - lets revise it accordingly. compRowsLeft = maxRowsLeft * SCH_UNCOMP_BYTES / SCH_GetBACompressionEst(); transferHeight = min((unsigned)rectHeight, compRowsLeft); uncompressedBitmapSize = transferHeight * paddedWidthInBytes; // Check that this won't blow the compression algorithm. if (uncompressedBitmapSize > MAX_UNCOMPRESSED_DATA_SIZE) { TRC_NRM((TB, "Rect size %u too big to compress in one go", uncompressedBitmapSize)); transferHeight = MAX_UNCOMPRESSED_DATA_SIZE / paddedWidthInBytes; uncompressedBitmapSize = transferHeight * paddedWidthInBytes; TRC_NRM((TB, "Reduced size to %u (%u rows)", uncompressedBitmapSize, transferHeight)); } // Fill in the common header fields for this rectangle. // Convert the rect to inclusive for the wire protocol. pContext->pBitmapPDU->numberRectangles++; pContext->pSDARect->destLeft = (INT16)(pRect->left); pContext->pSDARect->destRight = (INT16)(pRect->right - 1); pContext->pSDARect->destTop = (INT16)(pRect->top); pContext->pSDARect->width = (UINT16)paddedWidthInPels; #ifdef DC_HICOLOR pContext->pSDARect->bitsPerPixel = (UINT16)m_pTSWd->desktopBpp; if (pContext->pSDARect->bitsPerPixel < 8) { pContext->pSDARect->bitsPerPixel = 8; } #else pContext->pSDARect->bitsPerPixel = 8; #endif // Set up the data in the transfer buffer. pPrevSrc = pSrc; SDGPrepareData(&pSrc, rectWidth, paddedWidthInBytes, transferHeight, frameBufferWidth); // The compression algorithm does not deal well with very small // buffers. if (transferHeight > 1 || paddedWidthInPels > 12) { // Try to compress the bitmap directly into the network packet. // First we try to compress with the data size based on how well // we expect it to compress. buffSpaceLeft = min((unsigned)SDG_SPACE_LEFT, uncompressedBitmapSize); #ifdef DC_HICOLOR compRc = BC_CompressBitmap( m_pShm->sdgTransferBuffer, pContext->pSDARect->bitmapData, NULL, buffSpaceLeft, &compressedBitmapSize, paddedWidthInPels, transferHeight, m_pTSWd->desktopBpp); #else compRc = BC_CompressBitmap(m_pShm->sdgTransferBuffer, pContext->pSDARect->bitmapData, buffSpaceLeft, &compressedBitmapSize, paddedWidthInPels, transferHeight); #endif if (compRc) { // We have successfully compressed the bitmap data. pContext->pSDARect->compressedFlag = TRUE; // Indicates if this SDA data includes BC header or not. pContext->pSDARect->compressedFlag |= m_pShm->bc.noBitmapCompressionHdr; TRC_NRM((TB, "1st pass compr of %u x %u, size %u -> %u", transferHeight, paddedWidthInPels, uncompressedBitmapSize, compressedBitmapSize)); goto COMPRESSION_DONE; } // Compression failed - may be because the data didn't compress // quite as well as we thought it would and hence didn't fit into // the buffer, so we'll try again. // Copy as many lines as possible into the PDU. Code above means // there must be room for a line. TRC_NRM((TB, "Failed compress bitmap size %u (%u rows) - " \ "try smaller chunk", uncompressedBitmapSize, transferHeight)); maxRowsLeft = (unsigned)SDG_SPACE_LEFT / paddedWidthInBytes; TRC_ASSERT((maxRowsLeft > 0), (TB, "No room for a row, space %u, width %u", SDG_SPACE_LEFT, paddedWidthInBytes)); transferHeight = min((unsigned)rectHeight, maxRowsLeft); uncompressedBitmapSize = transferHeight * paddedWidthInBytes; TRC_DBG((TB, "Retry with %u rows", transferHeight)); // Set up the new data in the transfer buffer. pSrc = pPrevSrc; SDGPrepareData(&pSrc, rectWidth, paddedWidthInBytes, transferHeight, frameBufferWidth); // Try to compress the bitmap directly into the network packet. buffSpaceLeft = min((unsigned)SDG_SPACE_LEFT, uncompressedBitmapSize); #ifdef DC_HICOLOR compRc = BC_CompressBitmap(m_pShm->sdgTransferBuffer, pContext->pSDARect->bitmapData, NULL, buffSpaceLeft, &compressedBitmapSize, paddedWidthInPels, transferHeight, m_pTSWd->desktopBpp); #else compRc = BC_CompressBitmap(m_pShm->sdgTransferBuffer, pContext->pSDARect->bitmapData, buffSpaceLeft, &compressedBitmapSize, paddedWidthInPels, transferHeight); #endif if (compRc) { TRC_NRM((TB, "2nd pass compr %u x %u, size %u -> %u", transferHeight, paddedWidthInPels, uncompressedBitmapSize, compressedBitmapSize)); pContext->pSDARect->compressedFlag = TRUE; // Indicates if this SDA data includes BC header or not. pContext->pSDARect->compressedFlag |= m_pShm->bc.noBitmapCompressionHdr; goto COMPRESSION_DONE; } // The compression really really failed so just copy the // uncompressed data from the sdgTransferBuffer to the packet and // send it uncompressed. TRC_NRM((TB, "Really failed to compress bitmap size(%u)", uncompressedBitmapSize)); TRC_NRM((TB, "Copy %u x %u, size %u", transferHeight, paddedWidthInPels, uncompressedBitmapSize)); goto NoCompression; } else { // Small buffer, no compression applied. // So we just copy the uncompressed data from the sdgTransferBuffer // to the packet and send it uncompressed. TRC_NRM((TB, "first time copy of %u rows by %u columns, size %u", transferHeight, paddedWidthInPels, uncompressedBitmapSize)); NoCompression: pContext->pSDARect->compressedFlag = FALSE; memcpy(pContext->pSDARect->bitmapData, m_pShm->sdgTransferBuffer, uncompressedBitmapSize); // Init the compressed data size to the uncompressed size. compressedBitmapSize = uncompressedBitmapSize; } COMPRESSION_DONE: // Write the size of the data into the header. Be sure compressedSize // has been set up, even if uncompressed. pContext->pSDARect->bitmapLength = (UINT16)compressedBitmapSize; // Now we know the height actually used, we can fill in the rest of // the SDA header. pContext->pSDARect->height = (UINT16)transferHeight; pContext->pSDARect->destBottom = (INT16)(pRect->top + (transferHeight - 1)); TRC_NRM((TB, "Add rect %d: (%d,%d)(%d,%d) %u(%u->%u bytes)", pContext->pBitmapPDU->numberRectangles, pContext->pSDARect->destLeft, pContext->pSDARect->destTop, pContext->pSDARect->destRight, pContext->pSDARect->destBottom, pContext->pSDARect->compressedFlag, uncompressedBitmapSize, compressedBitmapSize)); // Update the compression statistics. sdgUncompTotal += uncompressedBitmapSize; sdgCompTotal += compressedBitmapSize; if (sdgUncompTotal >= SDG_SAMPLE_SIZE) { // Compression estimate is average # of bytes that // SCH_UNCOMP_BYTES bytes of uncomp data compress to. compEst = SCH_UNCOMP_BYTES * sdgCompTotal / sdgUncompTotal; TRC_ASSERT((compEst <= 1024),(TB,"Screen data compression " "estimate out of range - %u", compEst)); sdgCompTotal = 0; sdgUncompTotal = 0; if (compEst < SCH_COMP_LIMIT) compEst = SCH_COMP_LIMIT; SCH_UpdateBACompressionEst(compEst); TRC_NRM((TB, "New BA compression estimate %lu", compEst)); } // Update variables to reflect the chunk of data we successfully sent. rectHeight -= transferHeight; pRect->top += transferHeight; // Update the SDA pointer. Add the TS_BITMAP_DATA header plus the // actual bitmap bits. cbAdded = (unsigned)FIELDOFFSET(TS_BITMAP_DATA, bitmapData[0]) + pContext->pSDARect->bitmapLength; pContext->pSDARect = (TS_BITMAP_DATA UNALIGNED *) ((BYTE *)pContext->pSDARect + cbAdded); TRC_DBG((TB, "pSDARect = %p", pContext->pSDARect)); } /* ... while (rectHeight > 0) */ // The entire rectangle was consumed if we managed to pack in all the rows. // For shadowing, we want to indicate if more work is required to finish rc = (rectHeight == 0); DC_EXIT_POINT: DC_END_FN(); return rc; } /****************************************************************************/ // SDGPrepareData // // Copies a bitmap rectangle from the screen buffer to sdgTransferBuffer. /****************************************************************************/ void RDPCALL SHCLASS SDGPrepareData( BYTE **ppSrc, int rectWidth, int paddedRectWidth, unsigned transferHeight, unsigned frameBufferWidth) { PBYTE pDst; PBYTE pEnd4, pDst4, pSrc4; unsigned row; unsigned numPadBytes; unsigned lineWidth; PBYTE pSrc = *ppSrc; DC_BEGIN_FN("SDGPrepareData"); // We need to copy the data from the Frame Buffer into the // sdgTransferBuffer so that each of the rectangle rows to be sent are // contiguous in memory. // However, we also need to flip the bitmap vertically to convert from // the top-down format of the frame buffer to the bottom-up format of // the PDU bitmap data. We therefore set pDst to point to the address // of the beginning of the last row (in memory) of the bitmap within // the Transfer Buffer and the code below moves it back through memory // as we copy each row of source data. #ifdef DC_HICOLOR pDst = m_pShm->sdgTransferBuffer + paddedRectWidth * (transferHeight - 1); if (m_pTSWd->desktopBpp == 8) lineWidth = frameBufferWidth; else if (m_pTSWd->desktopBpp == 24) lineWidth = frameBufferWidth * 3; else if ((m_pTSWd->desktopBpp == 16) || (m_pTSWd->desktopBpp == 15)) lineWidth = frameBufferWidth * 2; else if (m_pTSWd->desktopBpp == 4) lineWidth = frameBufferWidth / 2; #else pDst = m_pShm->sdgTransferBuffer + (paddedRectWidth * (transferHeight-1)); lineWidth = (m_pTSWd->desktopBpp == 4) ? (frameBufferWidth / 2) : frameBufferWidth; #endif TRC_NRM((TB, "FB width %d, line width %d, Bpp %d", frameBufferWidth, lineWidth, m_pTSWd->desktopBpp)); // Copy the data into the Transfer Buffer, reformatting it as we go. if (m_pTSWd->desktopBpp == 8) { for (row = 0; row < transferHeight; row++) { memcpy(pDst, pSrc, rectWidth); pSrc += lineWidth; pDst -= paddedRectWidth; } } #ifdef DC_HICOLOR else if (m_pTSWd->desktopBpp == 24) { TRC_NRM((TB, "Copy %d rows of %d pels, line w %d, prw %d", transferHeight, rectWidth, lineWidth, paddedRectWidth)); for (row = 0; row < transferHeight; row++) { memcpy(pDst, pSrc, 3 * rectWidth); pSrc += lineWidth; pDst -= paddedRectWidth; } } else if ((m_pTSWd->desktopBpp == 15) || (m_pTSWd->desktopBpp == 16)) { TRC_NRM((TB, "Copy %d rows of %d pels, line w %d, prw %d", transferHeight, rectWidth, lineWidth, paddedRectWidth)); for (row = 0; row < transferHeight; row++) { memcpy(pDst, pSrc, 2 * rectWidth); pSrc += lineWidth; pDst -= paddedRectWidth; } } #endif else { for (row = 0; row < transferHeight; row++) { pEnd4 = pDst + rectWidth; for (pDst4 = pDst, pSrc4 = pSrc; pDst4 < pEnd4; pDst4++, pSrc4++) { *pDst4 = (*pSrc4 >> 4) & 0xf; pDst4++; *pDst4 = *pSrc4 & 0xf; } pSrc += lineWidth; pDst -= paddedRectWidth; } } // Zero the pad bytes on the end of each row (this aids compression). // Split each case out separately to aid performance (rather than the // alternative of testing numPadBytes within every iteration of a for // loop). Set pDst to the first pad byte in the first row. #ifdef DC_HICOLOR pDst = m_pShm->sdgTransferBuffer + (rectWidth * ((m_pTSWd->desktopBpp + 7) / 8)); numPadBytes = (unsigned)(paddedRectWidth - (rectWidth * ((m_pTSWd->desktopBpp + 7) / 8))); #else pDst = m_pShm->sdgTransferBuffer + rectWidth; numPadBytes = (unsigned)(paddedRectWidth - rectWidth); #endif switch (numPadBytes) { case 0: // No padding required. break; case 1: // 1 byte padding per row required. for (row = 0; row < transferHeight; row++) { *pDst = 0; pDst += paddedRectWidth; } break; case 2: // 2 bytes padding per row required. for (row = 0; row < transferHeight; row++) { *((PUINT16_UA)pDst) = 0; pDst += paddedRectWidth; } break; case 3: // 3 bytes padding per row required. for (row = 0; row < transferHeight; row++) { *((PUINT16_UA)pDst) = 0; *(pDst + 2) = 0; pDst += paddedRectWidth; } break; #ifdef DC_HICOLOR case 4: // 4 bytes padding per row required. for (row = 0; row < transferHeight; row++) { *((PUINT32_UA)pDst) = 0; pDst += paddedRectWidth; } break; case 6: // 6 bytes padding per row required. for (row = 0; row < transferHeight; row++) { *((PUINT32_UA)pDst) = 0; *((PUINT16_UA)(pDst + 4)) = 0; pDst += paddedRectWidth; } break; case 9: // 9 bytes padding per row required. for (row = 0; row < transferHeight; row++) { *((PUINT32_UA)pDst) = 0; *((PUINT32_UA)(pDst+4)) = 0; *(pDst + 8) = 0; pDst += paddedRectWidth; } break; #endif default: #ifdef DC_HICOLOR TRC_ALT((TB, "Invalid numPadBytes %u, rect %u, p/rect %u", numPadBytes, rectWidth, paddedRectWidth)); #else TRC_ABORT((TB, "Invalid numPadBytes: %u", numPadBytes)); #endif break; } // All done - update the supplied source pointer. *ppSrc = pSrc; DC_END_FN(); }