/****************************************************************************/ // nssidisp.c // // SaveScreenBits Interceptor API functions. // // Copyright (C) 1997-2000 Microsoft Corporation /****************************************************************************/ #include #define TRC_FILE "nssidisp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define DC_INCLUDE_DATA #include #include #undef DC_INCLUDE_DATA #include #include #include #include /****************************************************************************/ // SSI_DDInit /****************************************************************************/ void SSI_DDInit() { DC_BEGIN_FN("SSI_DDInit"); memset(&ssiLocalSSBState, 0, sizeof(LOCAL_SSB_STATE)); SSIResetSaveScreenBitmap(); DC_END_FN(); } /****************************************************************************/ // SSI_InitShm // // Alloc-time SHM init. /****************************************************************************/ void SSI_InitShm() { DC_BEGIN_FN("SSI_InitShm"); pddShm->ssi.resetInterceptor = FALSE; pddShm->ssi.newSaveBitmapSize = FALSE; pddShm->ssi.sendSaveBitmapSize = 0; pddShm->ssi.newSaveBitmapSize = 0; DC_END_FN(); } /****************************************************************************/ // SSI_Update // // Called when there is an SSI update from the WD. bForce forces a reset; // it's used by shadowing. /****************************************************************************/ void SSI_Update(BOOL bForce) { DC_BEGIN_FN("SSI_Update"); if (pddShm->ssi.resetInterceptor || bForce) SSIResetSaveScreenBitmap(); if (pddShm->ssi.saveBitmapSizeChanged || bForce) { pddShm->ssi.sendSaveBitmapSize = pddShm->ssi.newSaveBitmapSize; pddShm->ssi.saveBitmapSizeChanged = FALSE; TRC_NRM((TB, "SSI caps: Size %ld", pddShm->ssi.sendSaveBitmapSize)); } DC_END_FN(); } /****************************************************************************/ // SSI_ClearOrderEncoding // // Called on share state change to reset the order state info for // orders stored in the DD data segment. /****************************************************************************/ void SSI_ClearOrderEncoding() { DC_BEGIN_FN("SSI_ClearOrderEncoding"); memset(&PrevSaveBitmap, 0, sizeof(PrevSaveBitmap)); DC_END_FN(); } /****************************************************************************/ // SSIResetSaveScreenBitmap // // Resets the SaveScreenBitmap state. /****************************************************************************/ void SSIResetSaveScreenBitmap() { int i; DC_BEGIN_FN("SSIResetSaveScreenBitmap"); TRC_DBG((TB, "Reset (%d)", ssiLocalSSBState.saveLevel)); // Discard all currently saved bits. ssiLocalSSBState.saveLevel = 0; // Reset the number of remote pels saved. ssiRemoteSSBState.pelsSaved = 0; // Note that we've seen the update. pddShm->ssi.resetInterceptor = FALSE; // Free off any memory we may have allocated. for (i = 0; i < SSB_MAX_SAVE_LEVEL; i++) { if (ssiLocalSSBState.saveState[i].pSaveData != NULL) { EngFreeMem(ssiLocalSSBState.saveState[i].pSaveData); ssiLocalSSBState.saveState[i].pSaveData = NULL; } } DC_END_FN(); } /****************************************************************************/ // DrvSaveScreenBits - see NT DDK documentation. /****************************************************************************/ ULONG_PTR DrvSaveScreenBits( SURFOBJ *pso, ULONG iMode, ULONG_PTR ident, RECTL *prcl) { ULONG_PTR rc; unsigned ourMode; RECTL rectTrg; PDD_PDEV ppdev = (PDD_PDEV)pso->dhpdev; PDD_DSURF pdsurf; DC_BEGIN_FN("DrvSaveScreenBits"); // Default is FALSE: let GRE handle SaveBits if we are not in a // position to do it ourselves - no reason for us to get in the // business of saving off memory etc. rc = FALSE; // Sometimes we're called after being disconnected. if (ddConnected && pddShm != NULL) { // Surface is non-NULL. pso = OEGetSurfObjBitmap(pso, &pdsurf); INC_OUTCOUNTER(OUT_SAVESCREEN_ALL); // Get the exclusive bounding rectangle for the operation. RECT_FROM_RECTL(rectTrg, (*prcl)); TRC_ASSERT((pso->hsurf == ppdev->hsurfFrameBuf), (TB, "DrvSaveScreenBits should be called for screen surface only")); if (pso->hsurf == ppdev->hsurfFrameBuf) { // 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")); // We always return TRUE on SS_SAVE operations, as we // don't want the engine saving away the bits in a bitmap // and doing a MemBlt to restore the data (not very // efficient). Instead we return FALSE (failure) on the // SS_RESTORE to force User to repaint the affected area, // which we then accumulate in the normal way. // // Return TRUE for SS_DISCARD too (although it shouldn't // matter what we return). rc = (iMode == SS_RESTORE) ? FALSE : TRUE; DC_QUIT; } } else { // We don't support DrvSaveScreenBits for offscreen // rendering. TRC_ERR((TB, "Offscreen blt bail")); // We always return TRUE on SS_SAVE operations, as we // don't want the engine saving away the bits in a bitmap // and doing a MemBlt to restore the data (not very // efficient). Instead we return FALSE (failure) on the // SS_RESTORE to force User to repaint the affected area, // which we then accumulate in the normal way. // // Return TRUE for SS_DISCARD too (although it shouldn't // matter what we return). rc = (iMode == SS_RESTORE) ? FALSE : TRUE; DC_QUIT; } // Make sure we can send the order. if (OE_SendAsOrder(TS_ENC_SAVEBITMAP_ORDER)) { switch (iMode) { case SS_SAVE: TRC_DBG((TB, "SaveBits=%u", ssiLocalSSBState.saveLevel)); // Save the bits. // Update the save level if the save was successful. // If it was not successful then RestoreBits will not // be called so we do not want to increment the save // level. rc = SSISaveBits(pso, &rectTrg); if (rc) { ssiLocalSSBState.saveLevel++; // Set the returned ident value to the index of // the save. Do this after the increment in // order to avoid returning index 0 (0=FAIL). rc = ssiLocalSSBState.saveLevel; } break; case SS_RESTORE: // Update the save level first. ssiLocalSSBState.saveLevel--; ident--; TRC_DBG((TB, "RestoreBits (%d), ident (%u)", ssiLocalSSBState.saveLevel, ident)); // Restore the bits. rc = SSIRestoreBits(pso, &rectTrg, ident); // Check for a negative save level. This will happen // if there are outstanding saves when the share is // started or when we get out-of-order restores/ // discards. if (ssiLocalSSBState.saveLevel < 0) { TRC_NRM((TB, "RestoreBits caused neg save level")); ssiLocalSSBState.saveLevel = 0; } break; case SS_FREE: // Update the save level first. ssiLocalSSBState.saveLevel--; ident--; TRC_DBG((TB, "Discard Bits (%d) ident(%d)", ssiLocalSSBState.saveLevel, ident)); // Discard the saved bits. rc = SSIDiscardSave(&rectTrg, ident); // Check for a negative save level. This will happen // if there are outstanding saves when the share is // started or when we get out-of-order restores/ // discards. if (ssiLocalSSBState.saveLevel < 0) { TRC_NRM((TB, "DiscardSave caused neg save level")); ssiLocalSSBState.saveLevel = 0; } break; } // Make an "insurance" check: If the local save level is zero // then there should be no pels saved remotely. if ((ssiLocalSSBState.saveLevel == 0) && (ssiRemoteSSBState.pelsSaved != 0)) { TRC_ALT((TB, "Outstanding remote pels %ld", ssiRemoteSSBState.pelsSaved )); ssiRemoteSSBState.pelsSaved = 0; } TRC_DBG((TB, "RemotePelsSaved (%ld) rc %u", ssiRemoteSSBState.pelsSaved, rc)); } else { // If the SaveBitmap order is not supported then return // 0 immediately. 0 is failure for SAVE (the return value // is a non-0 identifier for success) and FALSE is failure // for RESTORE. If we've failed SAVEs, we can never get a // FREE. TRC_DBG((TB, "SaveBmp not supported")); INC_OUTCOUNTER(OUT_SAVESCREEN_UNSUPP); // We always return TRUE on SS_SAVE operations, as we // don't want the engine saving away the bits in a bitmap // and doing a MemBlt to restore the data (not very // efficient). Instead we return FALSE (failure) on the // SS_RESTORE to force User to repaint the affected area, // which we then accumulate in the normal way. // // Return TRUE for SS_DISCARD too (although it shouldn't // matter what we return). rc = (iMode == SS_RESTORE) ? FALSE : TRUE; } } else { TRC_ERR((TB, "Called when disconnected")); rc = (iMode == SS_RESTORE) ? FALSE : TRUE; } DC_EXIT_POINT: DC_END_FN(); return rc; } /****************************************************************************/ // SSIRemotePelsRequired // // Returns the number of remote pels required to store the supplied // rectangle, taking account of the Save Bitmap granularity. /****************************************************************************/ __inline UINT32 SSIRemotePelsRequired(PRECTL pRect) { UINT32 rectWidth, rectHeight; UINT32 rc; DC_BEGIN_FN("SSIRemotePelsRequired"); // Calculate the supplied rectangle size (it is in EXCLUSIVE coords). rectWidth = pRect->right - pRect->left; rectHeight = pRect->bottom - pRect->top; // Required width and height are rounded up to next granularity level for // that dimension. rc = (UINT32)(((rectWidth + SAVE_BITMAP_X_GRANULARITY - 1) / SAVE_BITMAP_X_GRANULARITY * SAVE_BITMAP_X_GRANULARITY) * ((rectHeight + SAVE_BITMAP_Y_GRANULARITY - 1) / SAVE_BITMAP_Y_GRANULARITY * SAVE_BITMAP_Y_GRANULARITY)); DC_END_FN(); return rc; } /****************************************************************************/ // SSISendSaveBitmapOrder // // Sends a SaveBitmap order. Returns FALSE on failure. /****************************************************************************/ BOOL SSISendSaveBitmapOrder( PDD_PDEV ppdev, PRECTL pRect, unsigned SavePosition, unsigned Operation) { SAVEBITMAP_ORDER *pSaveBitmapOrder; PINT_ORDER pOrder; BOOL rc; DC_BEGIN_FN("SSISendSaveBitmapOrder"); TRC_NRM((TB, "Rect before conversion (%d,%d)(%d,%d)", pRect->left, pRect->bottom, pRect->right, pRect->top)); // 1 field flag byte. Note that SaveBitmap orders are not clipped, // so set zero for the number of clip rects. pOrder = OA_AllocOrderMem(ppdev, MAX_ORDER_SIZE(0, 1, MAX_SAVEBITMAP_FIELD_SIZE)); if (pOrder != NULL) { // Target rect is in exclusive coords, convert to inclusive // for the wire format. pSaveBitmapOrder = (SAVEBITMAP_ORDER *)oeTempOrderBuffer; pSaveBitmapOrder->SavedBitmapPosition = SavePosition; pSaveBitmapOrder->nLeftRect = pRect->left; pSaveBitmapOrder->nTopRect = pRect->top; pSaveBitmapOrder->nRightRect = pRect->right - 1; pSaveBitmapOrder->nBottomRect = pRect->bottom - 1; pSaveBitmapOrder->Operation = Operation; // Slow-field-encode the order. NULL for clip rect since we don't clip // SaveBitmaps. pOrder->OrderLength = OE2_EncodeOrder(pOrder->OrderData, TS_ENC_SAVEBITMAP_ORDER, NUM_SAVEBITMAP_FIELDS, (BYTE *)pSaveBitmapOrder, (BYTE *)&PrevSaveBitmap, etable_SV, NULL); INC_OUTCOUNTER(OUT_SAVEBITMAP_ORDERS); ADD_INCOUNTER(IN_SAVEBITMAP_BYTES, pOrder->OrderLength); OA_AppendToOrderList(pOrder); // All done: consider sending the output. SCH_DDOutputAvailable(ppdev, FALSE); rc = TRUE; TRC_NRM((TB, "SaveBitmap op %d pos %ld rect %d %d %d %d", Operation, SavePosition, pRect->left, pRect->top, pRect->right - 1, pRect->bottom - 1)); } else { TRC_ERR((TB, "Failed to alloc order mem")); rc= FALSE; } DC_END_FN(); return rc; } /****************************************************************************/ // SSISaveBits // // Saves the specified screen rectangle bits and sends a SaveBitmap order. // pRect is in exclusive coords. We return FALSE only on low-bounds errors; // this indicates to GDI that it must simulate the SaveBits using a // BitBlt without corresponding RestoreBits calls, which is not desirable. // Instead, if we cannot send a SaveBitmap order or other problems, we return // TRUE and note that we need to return FALSE on the Restore call, which // causes a less expensive repaint of the target area. /****************************************************************************/ BOOL SSISaveBits(SURFOBJ *pso, PRECTL pRect) { BOOL rc = TRUE; DC_BEGIN_FN("SSISaveBits"); TRC_DBG((TB, "SaveScreenBits (%d, %d, %d, %d) level %d", pRect->left, pRect->bottom, pRect->right, pRect->top, ssiLocalSSBState.saveLevel)); // The saveLevel should never be negative. if (ssiLocalSSBState.saveLevel >= 0) { // If the save level is greater than the number of levels that we // support we just return TRUE. The corresponding RestoreBits call // will return FALSE, causing Windows to repaint the area. // Our maximum save level is such that we should very rarely (if ever) // go through this path. if (ssiLocalSSBState.saveLevel < SSB_MAX_SAVE_LEVEL) { CURRENT_LOCAL_SSB_STATE.pSaveData = NULL; CURRENT_LOCAL_SSB_STATE.rect = *pRect; CURRENT_LOCAL_SSB_STATE.fSavedRemotely = FALSE; // If the rectangle to be saved intersects the current SDA then // we will have to force a repaint on the restore. This is // because orders are always sent before Screen Data, so if we // sent a SAVEBITS order at this point, we would not save the // intersecting Screen Data. if (!OE_RectIntersectsSDA(pRect)) { UINT32 cRemotePelsRequired; // Calculate the number of pels required in the remote Save // Bitmap to handle this rectangle. cRemotePelsRequired = SSIRemotePelsRequired(pRect); // If there aren't enough pels in the remote Save Bitmap to // handle this rectangle then return immediately. if ((ssiRemoteSSBState.pelsSaved + cRemotePelsRequired) <= pddShm->ssi.sendSaveBitmapSize) { // Try to send the SaveBits as an order. CURRENT_LOCAL_SSB_STATE.fSavedRemotely = SSISendSaveBitmapOrder((PDD_PDEV)(pso->dhpdev), pRect, ssiRemoteSSBState.pelsSaved, SV_SAVEBITS); if (CURRENT_LOCAL_SSB_STATE.fSavedRemotely) { // Store the relevant details in the current entry of // the local SSB structure. CURRENT_LOCAL_SSB_STATE.remoteSavedPosition = ssiRemoteSSBState.pelsSaved; CURRENT_LOCAL_SSB_STATE.remotePelsRequired = cRemotePelsRequired; // Update the count of remote pels saved. ssiRemoteSSBState.pelsSaved += cRemotePelsRequired; // Store the rectangle saved. Note that we still claim // success, even if the copy fails. The result of this // is that // - we send save and restore orders to the client // (gives bandwidth saving and client // responsiveness) // - we fail the restore, causing a less efficient // repaint at the server. // Other compromise options also available if this // proves inappropriate. SSICopyRect(pso, TRUE); } } else { TRC_NRM((TB, "no space for %lu pels", cRemotePelsRequired)); } } else { // Note we do not save the rect via SSICopyRect -- we only // restore when fSavedRemotely is TRUE. TRC_DBG((TB, "SSI intersects SDA, storing failed save")); CURRENT_LOCAL_SSB_STATE.fSavedRemotely = FALSE; } } else { // We return TRUE. On Restore, we'll get the same out-of-bounds // value and return FALSE to repaint. TRC_ALT((TB, "saveLevel(%d) exceeds maximum", ssiLocalSSBState.saveLevel)); } } else { // This is a real problem, so we tell GDI to to what it needs to. TRC_ERR((TB, "SSISaveBits called with negative saveLevel")); rc = FALSE; } DC_END_FN(); return rc; } /****************************************************************************/ /* FUNCTION: SSIFindSlotAndDiscardAbove */ /* */ /* Finds the top slot in the SSB stack which matches pRect and updates */ /* ssiLocalSSBState.saveLevel to index it. */ /* RETURNS: TRUE if a match was found, FALSE otherwise */ /****************************************************************************/ BOOL SSIFindSlotAndDiscardAbove(PRECTL pRect, ULONG_PTR ident) { int i; BOOL rc = FALSE; DC_BEGIN_FN("SSIFindSlotAndDiscardAbove"); // Find the bits we are trying to restore. for (i = ssiLocalSSBState.saveLevel; i >= 0; i--) { if (i == (int)ident) { // We're at the right level in the saveState. TRC_NRM((TB, "found match at level %d", i)); TRC_DBG((TB, "Rect matched (%d, %d, %d, %d)", ssiLocalSSBState.saveState[i].rect.left, ssiLocalSSBState.saveState[i].rect.bottom, ssiLocalSSBState.saveState[i].rect.right, ssiLocalSSBState.saveState[i].rect.top)); ssiLocalSSBState.saveLevel = i; rc = TRUE; DC_QUIT; } else { // Discard this entry on the stack. ssiRemoteSSBState.pelsSaved -= ssiLocalSSBState.saveState[i].remotePelsRequired; if (ssiLocalSSBState.saveState[i].pSaveData != NULL) { TRC_DBG((TB, "Freeing memory at %p", ssiLocalSSBState.saveState[i].pSaveData)); EngFreeMem(ssiLocalSSBState.saveState[i].pSaveData); ssiLocalSSBState.saveState[i].pSaveData = NULL; } } } // If we get here we failed to match on any of the entries. TRC_NRM((TB, "no match on stack")); ssiLocalSSBState.saveLevel = 0; DC_EXIT_POINT: DC_END_FN(); return rc; } /****************************************************************************/ // SSIRestoreBits // // Restores the specified screen rectangle. If the bits were saved remotely // we make sure to send a SaveBitmap order. We return TRUE if we restored // the bits at the client, else we return FALSE to have GDI repaint the // target rect. /****************************************************************************/ BOOL SSIRestoreBits(SURFOBJ *pso, PRECTL pRect, ULONG_PTR ident) { BOOL rc = FALSE; DC_BEGIN_FN("SSIRestoreBits"); TRC_DBG((TB, "RestoreScreenBits (%d, %d, %d, %d) level %d", pRect->left, pRect->bottom, pRect->right, pRect->top, ssiLocalSSBState.saveLevel)); pddCacheStats[SSI].CacheReads++; // If the save level is negative then either there was a save // outstanding when we hooked the SSB or we have received out of order // restores/discards so we discarded stuff from the SSB stack // ourselves. We can't distinguish between these two cases at this // point so we will always pass this on to the display driver and hope // that it is robust enough to cope with a discard which didn't have a // corresponding save. if (ssiLocalSSBState.saveLevel >= 0) { // If we don't have enough levels (rare problem), we return FALSE // which causes a repaint. if (ssiLocalSSBState.saveLevel < SSB_MAX_SAVE_LEVEL) { // Search for the corresponding save order on our stack. if (SSIFindSlotAndDiscardAbove(pRect, ident)) { if (CURRENT_LOCAL_SSB_STATE.fSavedRemotely) { // Make sure GDI is giving us back the same sized // block, otherwise the client can get messed up. //TRC_ASSERT((CURRENT_LOCAL_SSB_STATE.remotePelsRequired == // SSIRemotePelsRequired(pRect)), // (TB,"Rect (%d,%d,%d,%d) for restore level %u " // "size %u is too large (stored size=%u)", // pRect->left, pRect->top, pRect->right, // pRect->bottom, ssiLocalSSBState.saveLevel, // SSIRemotePelsRequired(pRect), // CURRENT_LOCAL_SSB_STATE.remotePelsRequired)); // Update the remote pel count first. Even if we fail // to send the order we want to free up the remote pels. ssiRemoteSSBState.pelsSaved -= CURRENT_LOCAL_SSB_STATE.remotePelsRequired; // The bits were saved remotely, send the restore order. TRC_DBG((TB, "Try sending the order")); rc = SSISendSaveBitmapOrder((PDD_PDEV)(pso->dhpdev), pRect, CURRENT_LOCAL_SSB_STATE.remoteSavedPosition, SV_RESTOREBITS); // Now restore the bits to the screen, so long as we sent // the order. No point repainting the screen if we could // not send the order as we'll want GRE+USER to redraw it // so that we can accumulate the output. if (rc) { pddCacheStats[SSI].CacheHits++; if (CURRENT_LOCAL_SSB_STATE.pSaveData != NULL) { TRC_DBG((TB, "Restore bits to local screen")); SSICopyRect(pso, FALSE); } else { TRC_DBG((TB, "No data to restore, repaint")); rc = FALSE; } } else { // We failed the send, but still need to discard any // locally saved data. if (CURRENT_LOCAL_SSB_STATE.pSaveData != NULL) { EngFreeMem(CURRENT_LOCAL_SSB_STATE.pSaveData); CURRENT_LOCAL_SSB_STATE.pSaveData = NULL; } } } else { // We failed to save the bitmap remotely originally so now // we need to return FALSE to force a repaint. // We should never have allocated any local memory. TRC_ASSERT((CURRENT_LOCAL_SSB_STATE.pSaveData == NULL), (TB,"We allocated memory without remote save!")); TRC_NRM((TB, "No remote save, force repaint")); } } else { // We failed to find a match. This is not an error - // it will happen when saves are not restored in LIFO fashion // and it will also happen when SSI gets a sync now whilst // something is saved. TRC_DBG((TB, "Cannot find save request")); } } else { TRC_ALT((TB, "saveLevel(%d) exceeds maximum", ssiLocalSSBState.saveLevel)); } } else { TRC_ALT((TB, "Restore without save")); } DC_END_FN(); return rc; } /****************************************************************************/ // SSIDiscardSave // // Discards the specified screen rectangle bits. Always returns TRUE // saying that the discard succeeded. /****************************************************************************/ BOOL SSIDiscardSave(PRECTL pRect, ULONG_PTR ident) { BOOL rc; DC_BEGIN_FN("SSIDiscardSave"); TRC_DBG((TB, "Discard for rect L%u R%u T%u B%u", pRect->left, pRect->right, pRect->top, pRect->bottom)); // If the save level is negative then either there was a save // outstanding when we hooked the SSB or we have received out of order // restores/discards so we discarded stuff from the SSB stack ourselves. if (ssiLocalSSBState.saveLevel >= 0) { // If the save level is greater than the number of levels that we // support we just return TRUE. We will have ignored the SaveBits // call - so we have effectively discarded the bits already. // Our maximum save level is such that we should very rarely (if ever) // go through this path. if (ssiLocalSSBState.saveLevel < SSB_MAX_SAVE_LEVEL) { pddCacheStats[SSI].CacheReads++; // Search for the corresponding save order on our stack. // Not finding a slot match is not unusual, it can happen // when saves are not restored in LIFO fashion or when SSI // gets a sync while something is saved. if (SSIFindSlotAndDiscardAbove(pRect, ident)) { // If the bits were saved remotely then update local counter // for removed bits. if (CURRENT_LOCAL_SSB_STATE.fSavedRemotely) { // We don't transmit FREE/DISCARDSAVE orders - there is // no need because each Save/Restore order contains all // the necessary information (e.g. position of the bits // in the Save Bitmap). Just update our counter to take // account of the remote freed bits. ssiRemoteSSBState.pelsSaved -= CURRENT_LOCAL_SSB_STATE.remotePelsRequired; } // If we actually copied the bits, then free the memory. if (CURRENT_LOCAL_SSB_STATE.pSaveData != NULL) { TRC_DBG((TB, "Free off stored memory at %p", CURRENT_LOCAL_SSB_STATE.pSaveData)); EngFreeMem(CURRENT_LOCAL_SSB_STATE.pSaveData); CURRENT_LOCAL_SSB_STATE.pSaveData = NULL; } } } else { TRC_ALT((TB, "saveLevel(%d) exceeds maximum", ssiLocalSSBState.saveLevel)); } } else { TRC_ALT((TB, "Restore without save")); } DC_END_FN(); return TRUE; } /****************************************************************************/ // SSICopyRect // // Copy a screen rectangle. /****************************************************************************/ void SSICopyRect(SURFOBJ *pso, BOOL save) { UINT32 size; PBYTE pSrc; PBYTE pDest; PBYTE pScreenLocation; unsigned rectWidth; unsigned rectHeight; unsigned paddedRectWidth; unsigned row; unsigned srcDelta; unsigned destDelta; PDD_PDEV pPDev; #ifdef DC_HICOLOR #ifdef DC_DEBUG unsigned bpp; #endif #endif DC_BEGIN_FN("SSICopyRect"); // Set up some key params which are independent of the direction of // copy. rectHeight = CURRENT_LOCAL_SSB_STATE.rect.bottom - CURRENT_LOCAL_SSB_STATE.rect.top; pPDev = (PDD_PDEV)pso->dhpdev; #ifdef DC_HICOLOR if (pso->iBitmapFormat == BMF_24BPP) { pScreenLocation = pPDev->pFrameBuf + (CURRENT_LOCAL_SSB_STATE.rect.top * pso->lDelta) + (CURRENT_LOCAL_SSB_STATE.rect.left * 3); // rectWidth is in bytes. At 24bpp, this is 3 * number of pels. rectWidth = (CURRENT_LOCAL_SSB_STATE.rect.right - CURRENT_LOCAL_SSB_STATE.rect.left) * 3; #ifdef DC_DEBUG bpp = 24; #endif } else if (pso->iBitmapFormat == BMF_16BPP) { pScreenLocation = pPDev->pFrameBuf + (CURRENT_LOCAL_SSB_STATE.rect.top * pso->lDelta) + (CURRENT_LOCAL_SSB_STATE.rect.left * 2); // rectWidth is in bytes. At 16bpp, this is 2 * number of pels. rectWidth = (CURRENT_LOCAL_SSB_STATE.rect.right - CURRENT_LOCAL_SSB_STATE.rect.left) * 2; #ifdef DC_DEBUG bpp = 16; #endif } else #endif if (pso->iBitmapFormat == BMF_8BPP) { pScreenLocation = pPDev->pFrameBuf + (CURRENT_LOCAL_SSB_STATE.rect.top * pso->lDelta) + CURRENT_LOCAL_SSB_STATE.rect.left; // rectWidth is in bytes. At 8bpp, this is the number of pels. rectWidth = CURRENT_LOCAL_SSB_STATE.rect.right - CURRENT_LOCAL_SSB_STATE.rect.left; #ifdef DC_HICOLOR #ifdef DC_DEBUG bpp = 8; #endif #endif } else { pScreenLocation = pPDev->pFrameBuf + (CURRENT_LOCAL_SSB_STATE.rect.top * pso->lDelta) + (CURRENT_LOCAL_SSB_STATE.rect.left / 2); // rectWidth is in bytes. At 4bpp, this is (number of pels)/2. // However, since we copy whole bytes, we need to round 'right' up // and 'left' down to the nearest multiple of 2. rectWidth = ((CURRENT_LOCAL_SSB_STATE.rect.right + 1) - (CURRENT_LOCAL_SSB_STATE.rect.left & ~1)) / 2; #ifdef DC_HICOLOR #ifdef DC_DEBUG bpp = 4; #endif #endif } paddedRectWidth = (unsigned)DC_ROUND_UP_4(rectWidth); TRC_DBG((TB, "CopyRect: L%u R%u B%u T%u H%u W%u, PW%u sc%p index %d", CURRENT_LOCAL_SSB_STATE.rect.left, CURRENT_LOCAL_SSB_STATE.rect.right, CURRENT_LOCAL_SSB_STATE.rect.bottom, CURRENT_LOCAL_SSB_STATE.rect.top, rectHeight, rectWidth, paddedRectWidth, pScreenLocation, ssiLocalSSBState.saveLevel)); #ifdef DC_HICOLOR TRC_ASSERT(((pso->iBitmapFormat == BMF_4BPP) || (pso->iBitmapFormat == BMF_8BPP) || (pso->iBitmapFormat == BMF_16BPP) || (pso->iBitmapFormat == BMF_24BPP)), (TB, "Bitmap format %d unsupported", pso->iBitmapFormat)); #else // Only coded for 4bpp and 8bpp thus far. TRC_ASSERT(((pso->iBitmapFormat == BMF_8BPP) || (pso->iBitmapFormat == BMF_4BPP)), (TB, "Bitmap format %d unsupported", pso->iBitmapFormat)); #endif // Allocate memory if required. The size can be calculated from the // rectl field: NB This is in exclusive coords. if (save) { size = rectHeight * paddedRectWidth; CURRENT_LOCAL_SSB_STATE.pSaveData = EngAllocMem(FL_ZERO_MEMORY, size, DD_ALLOC_TAG); #ifdef DC_HICOLOR TRC_DBG((TB, "Save: alloc %u bytes for %dBPP at %p", size, bpp, CURRENT_LOCAL_SSB_STATE.pSaveData)); #else TRC_DBG((TB, "Save: alloc %u bytes for %dBPP at %p", size, pso->iBitmapFormat == BMF_8BPP ? 8 : 4, CURRENT_LOCAL_SSB_STATE.pSaveData)); #endif if (CURRENT_LOCAL_SSB_STATE.pSaveData != NULL) { // Now copy the bits. No need to explicitly zero the pad bytes as // we've nulled the memory on allocation. pSrc = pScreenLocation; pDest = CURRENT_LOCAL_SSB_STATE.pSaveData; srcDelta = pso->lDelta; destDelta = paddedRectWidth; TRC_DBG((TB, "Save: Copying from %p to %p", pSrc, pDest)); for (row = 0; row < rectHeight; row++) { memcpy(pDest, pSrc, rectWidth); pDest += destDelta; pSrc += srcDelta; } } else { TRC_ALT((TB, "Failed alloc %ul bytes, SSI save aborted", size)); } } else { // Copy the bits to the screen bitmap. pSrc = CURRENT_LOCAL_SSB_STATE.pSaveData; pDest = pScreenLocation; srcDelta = paddedRectWidth; destDelta = pso->lDelta; TRC_DBG((TB, "Restore: Copying from %p to %p", pSrc, pDest)); TRC_ASSERT((pSrc != NULL), (TB,"Source for SSI restore is NULL!")); for (row = 0; row < rectHeight; row++) { memcpy(pDest, pSrc, rectWidth); pDest += destDelta; pSrc += srcDelta; } TRC_DBG((TB, "Freeing memory at %p")); EngFreeMem(CURRENT_LOCAL_SSB_STATE.pSaveData); CURRENT_LOCAL_SSB_STATE.pSaveData = NULL; } DC_END_FN(); } /* SSICopyRect */