|
|
/******************************Module*Header*******************************\
* Module Name: sprite.cxx * * Contains all the drawing code for handling GDI sprites. * * Created: 16-Sep-1997 * Author: J. Andrew Goossen [andrewgo] * * Copyright (c) 1997-1999 Microsoft Corporation * \**************************************************************************/
#include "precomp.hxx"
// Notes
//
// - EngAlphaBlend always calls 32BitfieldsToBGRA with identity pxlo
// - pSprite->rclSrc no longer needed
// - Fix vProfile for smart heap management
// Global variable that defines a (0, 0) offset:
POINTL gptlZero; POINTL gptl00;
// Handy forward declarations:
VOID vSpComputeUnlockedRegion(SPRITESTATE*); VOID vSpCheckForWndobjOverlap(SPRITESTATE*, RECTL*, RECTL*);
/******************************Public*Routine******************************\
* VOID vSpDirectDriverAccess * * If 'bEnable' is TRUE, this routine undoes any sprite modifications to * the surface, in order to allow us to call the driver from within the * sprite code. * * If 'bEnable' is FALSE, this routine redoes any sprite modifications to * the surface that are needed for any drawing calls outside of this file, * in order to be redirected through the sprite code as appropriate. * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
VOID vSpDirectDriverAccess( SPRITESTATE* pState, BOOL bEnable) { PDEVOBJ po(pState->hdev);
po.vAssertDevLock();
if (bEnable) { SURFOBJ_TO_SURFACE_NOT_NULL(pState->psoScreen)-> flags(pState->flOriginalSurfFlags); SURFOBJ_TO_SURFACE_NOT_NULL(pState->psoScreen)-> iType(pState->iOriginalType);
pState->bInsideDriverCall = TRUE; } else { SURFOBJ_TO_SURFACE_NOT_NULL(pState->psoScreen)-> flags(pState->flSpriteSurfFlags); SURFOBJ_TO_SURFACE_NOT_NULL(pState->psoScreen)-> iType(pState->iSpriteType);
pState->bInsideDriverCall = FALSE; } }
#if DEBUG_SPRITES
/******************************Debug*Routine*******************************\
* VOID vSpValidateVisibleSprites * * Walk visible spritelist and validates cVisible count equals number of * sprites in pListVisible. * * 08-May-2001 -by- Jason Hartman [jasonha] * Wrote it. \**************************************************************************/
void vSpValidateVisibleSprites( SPRITESTATE *pState ) { ULONG cVisible; SPRITE *pSprite;
cVisible = pState->cVisible; for (pSprite = pState->pListVisible; pSprite != NULL; pSprite = pSprite->pNextVisible) { ASSERTGDI(cVisible != 0, "Invalid visible sprite list: list is longer than cVisible.\n"); cVisible--; ASSERTGDI(pSprite->fl & SPRITE_FLAG_VISIBLE, "Invalid sprite visible state: invisible sprite in visible list.\n"); } ASSERTGDI(cVisible == 0, "Invalid visible sprite list: list is missing a sprite or more.\n");
cVisible = pState->cVisible; for (pSprite = pState->pListZ; pSprite != NULL; pSprite = pSprite->pNextZ) { if (pSprite->fl & SPRITE_FLAG_VISIBLE) { ASSERTGDI(cVisible != 0, "Invalid visible sprite count: cVisible is too small.\n"); cVisible--; } } ASSERTGDI(cVisible == 0, "Invalid visible sprite count: cVisible is too big.\n"); } #endif
/*********************************Class************************************\
* class SPRITELOCK * * This class is responsible for reseting whatever sprite state is * necessary so that we can call the driver directly, bypassing any sprite * code. * * Must be called with the DEVLOCK already held, because we're * messing with the screen surface. * \***************************************************************************/
SPRITELOCK::SPRITELOCK(PDEVOBJ& po) { pState = po.pSpriteState();
po.vAssertDevLock();
bWasAlreadyInsideDriverCall = pState->bInsideDriverCall; if (!bWasAlreadyInsideDriverCall) { vSpDirectDriverAccess(pState, TRUE); }
#if DEBUG_SPRITES
vSpValidateVisibleSprites(pState); #endif
}
SPRITELOCK::~SPRITELOCK() { #if DEBUG_SPRITES
vSpValidateVisibleSprites(pState); #endif
if (!bWasAlreadyInsideDriverCall) { vSpDirectDriverAccess(pState, FALSE); } }
/******************************Public*Routine******************************\
* VOID psoSpCreateSurface * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
SURFOBJ* psoSpCreateSurface( SPRITESTATE* pState, ULONG iBitmapFormat, // If zero, use same format as display
LONG cx, LONG cy, BOOL bWantSystemMemory) // TRUE if must be system memory, FALSE
// if can be in video memory
{ HSURF hsurf; SIZEL sizl; SURFOBJ* psoScreen; SURFOBJ* psoRet; SURFACE* psurf;
PDEVOBJ po(pState->hdev);
psoRet = NULL;
sizl.cx = cx; sizl.cy = cy;
psoScreen = pState->psoScreen;
if (iBitmapFormat == 0) { iBitmapFormat = psoScreen->iBitmapFormat; }
hsurf = 0;
if ((!bWantSystemMemory) && (PPFNVALID(po, CreateDeviceBitmap)) && (iBitmapFormat == psoScreen->iBitmapFormat)) { hsurf = (HSURF) (*PPFNDRV(po, CreateDeviceBitmap)) (psoScreen->dhpdev, sizl, iBitmapFormat); } if (hsurf == 0) { hsurf = (HSURF) EngCreateBitmap(sizl, 0, iBitmapFormat, BMF_TOPDOWN, NULL); } if (hsurf != 0) { psoRet = EngLockSurface(hsurf); ASSERTGDI(psoRet != NULL, "How could lock possibly fail?");
// Mark the surface, so that it can be special-cased by
// the dynamic mode change code:
psurf = SURFOBJ_TO_SURFACE_NOT_NULL(psoRet); psurf->hdev(pState->hdev); }
return(psoRet); }
/******************************Public*Routine******************************\
* VOID vSpDeleteSurface * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
VOID vSpDeleteSurface( SURFOBJ* pso) { HSURF hsurf;
// Note that EngDeleteSurface handles calling DrvDeleteDeviceBitmap
// if it's a device format bitmap:
if (pso != NULL) { hsurf = pso->hsurf; EngUnlockSurface(pso); EngDeleteSurface(hsurf); } }
/**********************************Macros**********************************\
* OFFSET_POINTL, OFFSET_RECTL * * These little macros offset simple structures using a copy on the stack. * * 12-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
#define OFFSET_POINTL(pptl, xOff, yOff) \
POINTL ptlCopyOf##pptl; \ POINTL* pptlOriginal##pptl; \ if (pptl != NULL) \ { \ ptlCopyOf##pptl.x = xOff + pptl->x; \ ptlCopyOf##pptl.y = yOff + pptl->y; \ pptlOriginal##pptl = pptl; \ pptl = &ptlCopyOf##pptl; \ }
#define OFFSET_RECTL(prcl, xOff, yOff) \
RECTL rclCopyOf##prcl; \ if (prcl != NULL) \ { \ rclCopyOf##prcl.left = xOff + prcl->left; \ rclCopyOf##prcl.right = xOff + prcl->right; \ rclCopyOf##prcl.top = yOff + prcl->top; \ rclCopyOf##prcl.bottom = yOff + prcl->bottom; \ prcl = &rclCopyOf##prcl; \ }
#define OFFSET_POINTL_NOT_NULL(pptl, xOff, yOff) \
POINTL ptlCopyOf##pptl; \ ptlCopyOf##pptl.x = xOff + pptl->x; \ ptlCopyOf##pptl.y = yOff + pptl->y; \ pptl = &ptlCopyOf##pptl;
#define OFFSET_RECTL_NOT_NULL(prcl, xOff, yOff) \
RECTL rclCopyOf##prcl; \ rclCopyOf##prcl.left = xOff + prcl->left; \ rclCopyOf##prcl.right = xOff + prcl->right; \ rclCopyOf##prcl.top = yOff + prcl->top; \ rclCopyOf##prcl.bottom = yOff + prcl->bottom; \ prcl = &rclCopyOf##prcl;
// The following little macro is here to catch any Drv or Eng functions
// that illegaly modify the value of 'pptlBrush'. If you hit this assert,
// it means that the function we just called is a culprit.
#define ASSERT_BRUSH_ORIGIN(pptl, xOff, yOff) \
ASSERTGDI((pptl == NULL) || \ ((ptlCopyOf##pptl.x == xOff + pptlOriginal##pptl->x) && \ (ptlCopyOf##pptl.y == yOff + pptlOriginal##pptl->y)), \ "Called function modified pptlBrush");
/******************************Public*Routine******************************\
* VOID CLIPOBJ_vOffset * * These are little in-line routines to handle offseting of complex * structures that must remain in-place. * * 12-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
VOID FASTCALL CLIPOBJ_vOffset( CLIPOBJ* pco, LONG x, LONG y) { POINTL ptlOffset;
if (pco != NULL) { if ((x != 0) || (y != 0)) { pco->rclBounds.left += x; pco->rclBounds.right += x; pco->rclBounds.top += y; pco->rclBounds.bottom += y;
if (pco->iDComplexity != DC_TRIVIAL) { ptlOffset.x = x; ptlOffset.y = y;
((XCLIPOBJ*) pco)->bOffset(&ptlOffset); } } } }
/******************************Public*Routine******************************\
* VOID STROBJ_vOffset * * 12-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
VOID FASTCALL STROBJ_vOffset( STROBJ* pstro, LONG x, LONG y) { GLYPHPOS* pgp; ULONG cGlyphs;
if ((x != 0) || (y != 0)) { pstro->rclBkGround.left += x; pstro->rclBkGround.right += x; pstro->rclBkGround.top += y; pstro->rclBkGround.bottom += y;
// We are just offsetting positions, they are always computed,
// unless;
//
// a) this is a fixed pitch font (ulCharInc != 0), in which case only
// first position in the batch is set.
//
// b) we are dealing with linked fonts.
//
// bEnum is just putting bits in the cache, that is independent
// of computing positions.
if (((ESTROBJ*)pstro)->flTO & TO_HIGHRESTEXT) { x <<= 4; y <<= 4; }
pgp = ((ESTROBJ*)pstro)->pgpos;
if(((ESTROBJ*)pstro)->flTO & (TO_PARTITION_INIT|TO_SYS_PARTITION)) { LONG *plFont;
plFont = ((ESTROBJ*)pstro)->plPartition;
for (cGlyphs = pstro->cGlyphs ; cGlyphs != 0; pgp++, plFont++) { // only offset the glyphs that correspond to the current font:
if (*plFont == ((ESTROBJ*)pstro)->lCurrentFont) { cGlyphs--; pgp->ptl.x += x; pgp->ptl.y += y; } } } else { if (!pstro->ulCharInc) { cGlyphs = pstro->cGlyphs;
for (; cGlyphs != 0; cGlyphs--, pgp++) { pgp->ptl.x += x; pgp->ptl.y += y; } } else { // fixed pitch, only fix the first position, the rest will be
// computed during bEnum phase if necessary
pgp->ptl.x += x; pgp->ptl.y += y; } } } }
/******************************Public*Routine******************************\
* VOID PATHOBJ_vOffset * * 12-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
VOID FASTCALL PATHOBJ_vOffset( PATHOBJ* ppo, LONG x, LONG y) { BOOL bMore; PATHDATA pd; POINTFIX* pptfx; ULONG cptfx;
if ((x != 0) || (y != 0)) { EPOINTL eptl(x, y);
((EPATHOBJ*) ppo)->vOffset(eptl); } }
/*********************************Macro************************************\
* macro OFFBITBLT * * This handy little macro invokes OffBitBlt to call either EngBitBlt or * DrvBitBlt, depending on whether the destination surface is owned by * the device. \**************************************************************************/
#define OFFBITBLT(pOffDst_, psoDst_, pOffSrc_, psoSrc_, psoMsk_, pco_, \
pxlo_, prclDst_, pptlSrc_, pptlMsk_, pbo_, pptlBrush_,\ rop4_) \ OffBitBlt(PPFNDIRECT(psoDst_, BitBlt), \ pOffDst_, psoDst_, pOffSrc_, psoSrc_, psoMsk_, pco_, \ pxlo_, prclDst_, pptlSrc_, pptlMsk_, pbo_, pptlBrush_, rop4_)
/*********************************Macro************************************\
* macro OFFCOPYBITS * * This handy little macro invokes OffCopyBits to call either EngCopyBits or * DrvCopyBits, depending on whether either of the surfaces are owned by * the device. \**************************************************************************/
#define OFFCOPYBITS(pOffDst_, psoDst_, pOffSrc_, psoSrc_, pco_, pxlo_, \
prclDst_, pptlSrc_) \ OffCopyBits(((!(SURFOBJ_TO_SURFACE_NOT_NULL((psoDst_))->flags() & HOOK_CopyBits)\ && (psoSrc_->hdev)) \ ? PPFNDIRECT(psoSrc_, CopyBits) \ : PPFNDIRECT(psoDst_, CopyBits)), \ pOffDst_, psoDst_, pOffSrc_, psoSrc_, pco_, pxlo_, prclDst_, \ pptlSrc_)
/******************************Public*Routine******************************\
* BOOL Off* * * These routines handle the offseting of coordinates given to the driver, * for the purposes of multi-monitor and sprite support. Each of these * routines correspond to a DDI drawing call, and offsets all drawing * coordinates by the 'xOffset' and 'yOffset' values in the corresponding * SURFOBJ. * * Some complex DDI data-structures such as PATHOBJs and TEXTOBJs must be * modified in-place; they are always reset to their original values before * returning. * * 12-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
/******************************Public*Routine******************************\
* BOOL OffStrokePath * * 12-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
BOOL OffStrokePath( PFN_DrvStrokePath pfnStrokePath, POINTL* pOffset, SURFOBJ* pso, PATHOBJ* ppo, CLIPOBJ* pco, XFORMOBJ* pxo, BRUSHOBJ* pbo, POINTL* pptlBrush, LINEATTRS* pla, MIX mix) { LONG x; LONG y;
x = pOffset->x; y = pOffset->y;
PATHOBJ_vOffset(ppo, x, y); CLIPOBJ_vOffset(pco, x, y); OFFSET_POINTL(pptlBrush, x, y);
BOOL bRet = pfnStrokePath(pso, ppo, pco, pxo, pbo, pptlBrush, pla, mix); if ((bRet == FALSE) && ((pla->fl & LA_GEOMETRIC) || (ppo->fl & PO_BEZIERS))) { // When given a wideline, or given a line composed of bezier curves,
// the driver can return FALSE from DrvStrokePath to indicate that
// it would like the drawing broken up into simpler Drv calls.
// Unfortunately, this poses a problem for us because we might have
// just drawn and succeeded an XOR DrvStrokePath to a different area.
// If we returned FALSE, GDI would pop through the DrvStrokePath
// path and touch all the areas again, thus erasing the DrvStrokePath
// that is supposed to be drawn in the previous area!
//
// For this reason, we try not to return FALSE from optional calls.
bRet = EngStrokePath(pso, ppo, pco, pxo, pbo, pptlBrush, pla, mix); }
PATHOBJ_vOffset(ppo, -x, -y); CLIPOBJ_vOffset(pco, -x, -y); ASSERT_BRUSH_ORIGIN(pptlBrush, x, y);
return(bRet); }
/******************************Public*Routine******************************\
* BOOL OffFillPath * * 12-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
BOOL OffFillPath( PFN_DrvFillPath pfnFillPath, POINTL* pOffset, SURFOBJ* pso, PATHOBJ* ppo, CLIPOBJ* pco, BRUSHOBJ* pbo, POINTL* pptlBrush, MIX mix, FLONG flOptions) { LONG x; LONG y;
x = pOffset->x; y = pOffset->y;
PATHOBJ_vOffset(ppo, x, y); CLIPOBJ_vOffset(pco, x, y); OFFSET_POINTL(pptlBrush, x, y);
BOOL bRet = pfnFillPath(pso, ppo, pco, pbo, pptlBrush, mix, flOptions); if (bRet == FALSE) { // The driver can return FALSE from DrvFillPath to indicate that
// it would like the drawing broken up into simpler Drv calls.
// Unfortunately, this poses a problem for us because we might have
// just drawn and succeeded an XOR DrvFillPath to a different area.
// If we returned FALSE, GDI would pop through the DrvFillPath
// path and touch all the areas again, thus erasing the DrvFillPath
// that is supposed to be drawn in the previous area!
//
// For this reason, we try not to return FALSE from optional calls.
bRet = EngFillPath(pso, ppo, pco, pbo, pptlBrush, mix, flOptions); }
PATHOBJ_vOffset(ppo, -x, -y); CLIPOBJ_vOffset(pco, -x, -y); ASSERT_BRUSH_ORIGIN(pptlBrush, x, y);
return(bRet); }
/******************************Public*Routine******************************\
* BOOL OffStrokeAndFillPath * * 12-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
BOOL OffStrokeAndFillPath( PFN_DrvStrokeAndFillPath pfnStrokeAndFillPath, POINTL* pOffset, SURFOBJ* pso, PATHOBJ* ppo, CLIPOBJ* pco, XFORMOBJ* pxo, BRUSHOBJ* pboStroke, LINEATTRS* pla, BRUSHOBJ* pboFill, POINTL* pptlBrush, MIX mixFill, FLONG flOptions) { LONG x; LONG y;
x = pOffset->x; y = pOffset->y;
PATHOBJ_vOffset(ppo, x, y); CLIPOBJ_vOffset(pco, x, y); OFFSET_POINTL(pptlBrush, x, y);
BOOL bRet = pfnStrokeAndFillPath(pso, ppo, pco, pxo, pboStroke, pla, pboFill, pptlBrush, mixFill, flOptions); if (bRet == FALSE) { // The driver can return FALSE from DrvStrokeAndFillPath to indicate
// that it would like the drawing broken up into simpler Drv calls.
// Unfortunately, this poses a problem for us because we might have
// just drawn and succeeded an XOR DrvStrokeAndFillPath to a different
// area. If we returned FALSE, GDI would pop through the
// DrvStrokeAndFillPath path and touch all the areas again, thus
// erasing the DrvStrokeAndFillPath that is supposed to be drawn in
// the previous area!
//
// For this reason, we try not to return FALSE from optional calls.
bRet = EngStrokeAndFillPath(pso, ppo, pco, pxo, pboStroke, pla, pboFill, pptlBrush, mixFill, flOptions); }
PATHOBJ_vOffset(ppo, -x, -y); CLIPOBJ_vOffset(pco, -x, -y); ASSERT_BRUSH_ORIGIN(pptlBrush, x, y);
return(bRet); }
/******************************Public*Routine******************************\
* BOOL OffTextOut * * 12-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
BOOL OffTextOut( PFN_DrvTextOut pfnTextOut, POINTL* pOffset, SURFOBJ* pso, STROBJ* pstro, FONTOBJ* pfo, CLIPOBJ* pco, RECTL* prclExtra, // Not used by display drivers
RECTL* prclOpaque, BRUSHOBJ* pboFore, BRUSHOBJ* pboOpaque, POINTL* pptlOrg, // Not used by display drivers
MIX mix) { LONG x; LONG y;
x = pOffset->x; y = pOffset->y;
ASSERTGDI(prclExtra == NULL, "Unexpected non-NULL prclExtra");
// Must offset a copy of 'prclOpaque' first because GDI sometimes
// points 'prclOpaque' to '&pstro->rclBkGround', and so we would
// do the offset twice.
OFFSET_RECTL(prclOpaque, x, y); STROBJ_vOffset(pstro, x, y); CLIPOBJ_vOffset(pco, x, y);
BOOL bRet = pfnTextOut(pso, pstro, pfo, pco, prclExtra, prclOpaque, pboFore, pboOpaque, pptlOrg, mix);
STROBJ_vOffset(pstro, -x, -y); CLIPOBJ_vOffset(pco, -x, -y);
return(bRet); }
/******************************Public*Routine******************************\
* BOOL OffLineTo * * 12-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
BOOL OffLineTo( PFN_DrvLineTo pfnLineTo, POINTL* pOffset, SURFOBJ* pso, CLIPOBJ* pco, BRUSHOBJ* pbo, LONG x1, LONG y1, LONG x2, LONG y2, RECTL* prclBounds, MIX mix) { LONG x; LONG y;
x = pOffset->x; y = pOffset->y;
CLIPOBJ_vOffset(pco, x, y); x1 += x; x2 += x; y1 += y; y2 += y; OFFSET_RECTL(prclBounds, x, y);
BOOL bRet = pfnLineTo(pso, pco, pbo, x1, y1, x2, y2, prclBounds, mix); if (!bRet) { // The driver can return FALSE from DrvLineTo to indicate that
// it would like to instead by called via DrvStrokePath.
// Unfortunately, this poses a problem for us because we might have
// just drawn and succeeded an XOR DrvLineTo to a different area.
// If we returned FALSE, GDI would pop through the DrvStrokePath
// path and touch all the areas again, thus erasing the DrvLineTo
// that is supposed to be drawn in the previous area!
//
// For this reason, we try not to return FALSE from optional calls.
bRet = EngLineTo(pso, pco, pbo, x1, y1, x2, y2, prclBounds, mix); }
CLIPOBJ_vOffset(pco, -x, -y);
return(bRet); }
/******************************Public*Routine******************************\
* BOOL OffGradientFill * * 12-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
BOOL OffGradientFill( PFN_DrvGradientFill pfnGradientFill, POINTL* pOffset, SURFOBJ* pso, CLIPOBJ* pco, XLATEOBJ* pxlo, TRIVERTEX* pVertex, ULONG nVertex, PVOID pMesh, ULONG nMesh, RECTL* prclExtents, POINTL* pptlDitherOrg, ULONG ulMode) { LONG x; LONG y; ULONG i; TRIVERTEX* pTmp;
x = pOffset->x; y = pOffset->y;
CLIPOBJ_vOffset(pco, x, y); OFFSET_RECTL(prclExtents, x, y);
// The offseting of the dither origin is different than that of the other
// data structures. The dither origin is defined to be the negative of
// the offset of the window relative to the origin of the desktop surface.
// The right value to pass to xxxGradientFill would be the negative of the
// offset of the window relative to the origin of the sprite surface/
// PDEV surface (for multimon). This is computed by subtracting the
// pOffset from pptlDitherOrg.
//
// From definition of dither origin:
// pptlDitherOrg = (-xWin_surf, -yWin_surf)
//
// From surface to desktop coordinate conversion:
// (xWin_surf, yWin_surf) = (xWin_desk-xSurf_desk, yWin_desk-ySurf_desk)
// where (xSurf_Desk, ySurf_desk) is the surface origin relative to the
// desktop (corresponds to -pOffset in this function).
//
// Putting the two together, we get:
// pptlDitherOrg = (-(xWin_desk-xSurf_desk), -(yWin_desk-ySurf_desk))=
// = (-xWin_desk+xSurf_desk, -yWin_desk+ySurf_desk) =
// = (-xWin_desk, -yWin_desk) + (xSurf_desk, ySurf_desk) =
// = pptlDitherOrg_desk + (-pOffset)
// = pptlDitherOrg_desk - pOffset
//
OFFSET_POINTL(pptlDitherOrg, (-x), (-y));
for (pTmp = pVertex, i = 0; i < nVertex; i++, pTmp++) { pTmp->x += x; pTmp->y += y; }
BOOL bRet = pfnGradientFill(pso, pco, pxlo, pVertex, nVertex, pMesh, nMesh, prclExtents, pptlDitherOrg, ulMode);
CLIPOBJ_vOffset(pco, -x, -y); for (pTmp = pVertex, i = 0; i < nVertex; i++, pTmp++) { pTmp->x -= x; pTmp->y -= y; }
return(bRet); }
/******************************Public*Routine******************************\
* BOOL OffBitBlt * * 12-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
BOOL OffBitBlt( PFN_DrvBitBlt pfnBitBlt, POINTL* pOffDst, SURFOBJ* psoDst, POINTL* pOffSrc, SURFOBJ* psoSrc, SURFOBJ* psoMsk, CLIPOBJ* pco, XLATEOBJ* pxlo, RECTL* prclDst, POINTL* pptlSrc, POINTL* pptlMsk, BRUSHOBJ* pbo, POINTL* pptlBrush, ROP4 rop4) { LONG xDst; LONG yDst;
xDst = pOffDst->x; yDst = pOffDst->y;
CLIPOBJ_vOffset(pco, xDst, yDst); OFFSET_RECTL_NOT_NULL(prclDst, xDst, yDst); OFFSET_POINTL(pptlSrc, pOffSrc->x, pOffSrc->y); OFFSET_POINTL(pptlBrush, xDst, yDst); ASSERTGDI((!psoSrc) || (!(psoSrc->dhpdev) || !(psoDst->dhpdev)) || (psoSrc->dhpdev == psoDst->dhpdev), "OffBitBlt: ERROR blitting across devices.");
BOOL bRet = pfnBitBlt(psoDst, psoSrc, psoMsk, pco, pxlo, prclDst, pptlSrc, pptlMsk, pbo, pptlBrush, rop4);
CLIPOBJ_vOffset(pco, -xDst, -yDst); ASSERT_BRUSH_ORIGIN(pptlBrush, xDst, yDst);
return(bRet); }
/******************************Public*Routine******************************\
* BOOL OffCopyBits * * 12-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
BOOL OffCopyBits( PFN_DrvCopyBits pfnCopyBits, POINTL* pOffDst, SURFOBJ* psoDst, POINTL* pOffSrc, SURFOBJ* psoSrc, CLIPOBJ* pco, XLATEOBJ* pxlo, RECTL* prclDst, POINTL* pptlSrc) { LONG xDst; LONG yDst; LONG xSrc; LONG ySrc;
xSrc = pOffSrc->x; ySrc = pOffSrc->y; xDst = pOffDst->x; yDst = pOffDst->y;
if (pco != NULL) CLIPOBJ_vOffset(pco, xDst, yDst); OFFSET_RECTL_NOT_NULL(prclDst, xDst, yDst); OFFSET_POINTL_NOT_NULL(pptlSrc, xSrc, ySrc); ASSERTGDI((!psoSrc) || (!(psoSrc->dhpdev) || !(psoDst->dhpdev)) || (psoSrc->dhpdev == psoDst->dhpdev), "OffCopyBits: ERROR copying across devices.");
// A defacto DDI rule is that on a CopyBits call, the bounds of a complex
// clip object must intersect with the destination rectangle. This
// is due to that fact that some drivers, such as the VGA driver, do not
// bother checking for intersection with the destination rectangle when
// enumerating a complex clip object. Thus, if this assert is hit, the
// routine which constructed the clip object will have to be fixed. Note
// that this is unrelated to sprites; I simply put this assert here because
// it was a handy place for it, to check all CopyBits calls.
ASSERTGDI((pco == NULL) || (pco->iDComplexity == DC_TRIVIAL) || bIntersect(&pco->rclBounds, prclDst), "Clip object bounds doesn't intersect destination rectangle");
BOOL bRet = pfnCopyBits(psoDst, psoSrc, pco, pxlo, prclDst, pptlSrc);
if (pco != NULL) CLIPOBJ_vOffset(pco, -xDst, -yDst);
return(bRet); }
/******************************Public*Routine******************************\
* BOOL OffStretchBlt * * 12-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
BOOL OffStretchBlt( PFN_DrvStretchBlt pfnStretchBlt, POINTL* pOffDst, SURFOBJ* psoDst, POINTL* pOffSrc, SURFOBJ* psoSrc, SURFOBJ* psoMsk, CLIPOBJ* pco, XLATEOBJ* pxlo, COLORADJUSTMENT* pca, POINTL* pptlHTOrg, RECTL* prclDst, RECTL* prclSrc, POINTL* pptlMsk, ULONG iMode) { LONG xDst; LONG yDst; LONG xSrc; LONG ySrc;
xSrc = pOffSrc->x; ySrc = pOffSrc->y; xDst = pOffDst->x; yDst = pOffDst->y;
CLIPOBJ_vOffset(pco, xDst, yDst); OFFSET_RECTL_NOT_NULL(prclDst, xDst, yDst); OFFSET_RECTL_NOT_NULL(prclSrc, xSrc, ySrc); OFFSET_POINTL(pptlHTOrg, xDst, yDst);
BOOL bRet = pfnStretchBlt(psoDst, psoSrc, psoMsk, pco, pxlo, pca, pptlHTOrg, prclDst, prclSrc, pptlMsk, iMode);
CLIPOBJ_vOffset(pco, -xDst, -yDst);
return(bRet); }
/******************************Public*Routine******************************\
* BOOL OffStretchBltROP * * 12-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
BOOL OffStretchBltROP( PFN_DrvStretchBltROP pfnStretchBltROP, POINTL* pOffDst, SURFOBJ* psoDst, POINTL* pOffSrc, SURFOBJ* psoSrc, SURFOBJ* psoMsk, CLIPOBJ* pco, XLATEOBJ* pxlo, COLORADJUSTMENT* pca, POINTL* pptlHTOrg, RECTL* prclDst, RECTL* prclSrc, POINTL* pptlMsk, ULONG iMode, BRUSHOBJ* pbo, DWORD rop4) { LONG xDst; LONG yDst; LONG xSrc; LONG ySrc;
xSrc = pOffSrc->x; ySrc = pOffSrc->y; xDst = pOffDst->x; yDst = pOffDst->y;
CLIPOBJ_vOffset(pco, xDst, yDst); OFFSET_RECTL(prclDst, xDst, yDst); OFFSET_RECTL(prclSrc, xSrc, ySrc); OFFSET_POINTL(pptlHTOrg, xDst, yDst);
BOOL bRet = pfnStretchBltROP(psoDst, psoSrc, psoMsk, pco, pxlo, pca, pptlHTOrg, prclDst, prclSrc, pptlMsk, iMode, pbo, rop4);
CLIPOBJ_vOffset(pco, -xDst, -yDst);
return(bRet);
}
/******************************Public*Routine******************************\
* BOOL OffTransparentBlt * * 12-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
BOOL OffTransparentBlt( PFN_DrvTransparentBlt pfnTransparentBlt, POINTL* pOffDst, SURFOBJ* psoDst, POINTL* pOffSrc, SURFOBJ* psoSrc, CLIPOBJ* pco, XLATEOBJ* pxlo, RECTL* prclDst, RECTL* prclSrc, ULONG iTransColor, ULONG ulReserved) { LONG xDst; LONG yDst; LONG xSrc; LONG ySrc;
xSrc = pOffSrc->x; ySrc = pOffSrc->y; xDst = pOffDst->x; yDst = pOffDst->y;
CLIPOBJ_vOffset(pco, xDst, yDst); OFFSET_RECTL(prclDst, xDst, yDst); OFFSET_RECTL(prclSrc, xSrc, ySrc);
BOOL bRet = pfnTransparentBlt(psoDst, psoSrc, pco, pxlo, prclDst, prclSrc, iTransColor, ulReserved);
CLIPOBJ_vOffset(pco, -xDst, -yDst);
return(bRet); }
/******************************Public*Routine******************************\
* BOOL OffDrawStream * * 1-27-2001 bhouse * Wrote it. \**************************************************************************/
BOOL OffDrawStream( PFN_DrvDrawStream pfnDrawStream, POINTL* pOffDst, SURFOBJ* psoDst, SURFOBJ* psoSrc, CLIPOBJ* pco, XLATEOBJ* pxlo, RECTL* prclDstBounds, POINTL* pptlDstOffset, ULONG ulIn, PVOID pvIn, DSSTATE* pdss ) { LONG xDst; LONG yDst; LONG xSrc; LONG ySrc;
xDst = pOffDst->x; yDst = pOffDst->y;
CLIPOBJ_vOffset(pco, xDst, yDst); OFFSET_RECTL(prclDstBounds, xDst, yDst); OFFSET_POINTL(pptlDstOffset, xDst, yDst);
BOOL bRet = pfnDrawStream(psoDst, psoSrc, pco, pxlo, prclDstBounds, pptlDstOffset, ulIn, pvIn, pdss);
CLIPOBJ_vOffset(pco, -xDst, -yDst);
return(bRet); }
/******************************Public*Routine******************************\
* BOOL OffAlphaBlend * * 12-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
BOOL OffAlphaBlend( PFN_DrvAlphaBlend pfnAlphaBlend, POINTL* pOffDst, SURFOBJ* psoDst, POINTL* pOffSrc, SURFOBJ* psoSrc, CLIPOBJ* pco, XLATEOBJ* pxlo, RECTL* prclDst, RECTL* prclSrc, BLENDOBJ* pBlendObj) { LONG xDst; LONG yDst; LONG xSrc; LONG ySrc;
xSrc = pOffSrc->x; ySrc = pOffSrc->y; xDst = pOffDst->x; yDst = pOffDst->y;
CLIPOBJ_vOffset(pco, xDst, yDst); OFFSET_RECTL(prclDst, xDst, yDst); OFFSET_RECTL(prclSrc, xSrc, ySrc);
BOOL bRet = pfnAlphaBlend(psoDst, psoSrc, pco, pxlo, prclDst, prclSrc, pBlendObj);
CLIPOBJ_vOffset(pco, -xDst, -yDst);
return(bRet); }
/******************************Public*Routine******************************\
* BOOL OffPlgBlt * * 12-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
BOOL OffPlgBlt( PFN_DrvPlgBlt pfnPlgBlt, POINTL* pOffDst, SURFOBJ* psoDst, POINTL* pOffSrc, SURFOBJ* psoSrc, SURFOBJ* psoMsk, CLIPOBJ* pco, XLATEOBJ* pxlo, COLORADJUSTMENT* pca, POINTL* pptlBrush, POINTFIX* pptfx, RECTL* prcl, POINTL* pptlMsk, ULONG iMode) { LONG xDst; LONG yDst; LONG xSrc; LONG ySrc; POINTFIX aptfx[3];
xSrc = pOffSrc->x; ySrc = pOffSrc->y; xDst = pOffDst->x; yDst = pOffDst->y;
CLIPOBJ_vOffset(pco, xDst, yDst); OFFSET_POINTL(pptlBrush, xDst, yDst); OFFSET_RECTL(prcl, xSrc, ySrc);
aptfx[0].x = pptfx[0].x + (xDst << 4); aptfx[1].x = pptfx[1].x + (xDst << 4); aptfx[2].x = pptfx[2].x + (xDst << 4); aptfx[0].y = pptfx[0].y + (yDst << 4); aptfx[1].y = pptfx[1].y + (yDst << 4); aptfx[2].y = pptfx[2].y + (yDst << 4);
BOOL bRet = pfnPlgBlt(psoDst, psoSrc, psoMsk, pco, pxlo, pca, pptlBrush, aptfx, prcl, pptlMsk, iMode);
CLIPOBJ_vOffset(pco, -xDst, -yDst); ASSERT_BRUSH_ORIGIN(pptlBrush, xDst, yDst);
return(bRet); }
/******************************Public*Routine******************************\
* VOID vSpOrderInY * * Re-orders the sprite in the sorted-in-Y list. * * 'pSprite' must already be in the list, and all the other elements of the * list must be properly sorted. * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
VOID vSpOrderInY( SPRITE* pSprite) { LONG yThis; SPRITE* pPrevious; SPRITE* pNext;
ASSERTGDI(pSprite->pState->pListY != NULL, "If there's a sprite, there should be a Y-list!");
yThis = pSprite->rclSprite.top; if ((pSprite->pPreviousY != NULL) && (pSprite->pPreviousY->rclSprite.top > yThis)) { // The sprite has to move up in the list. First, remove
// the sprite's node from its current position:
pPrevious = pSprite->pPreviousY; pNext = pSprite->pNextY; pPrevious->pNextY = pNext; if (pNext) pNext->pPreviousY = pPrevious;
// Find the sprite's proper position in the list:
pNext = pPrevious; while (((pPrevious = pNext->pPreviousY) != NULL) && (pPrevious->rclSprite.top > yThis)) pNext = pPrevious;
// Insert the sprite in its new position:
pSprite->pPreviousY = pPrevious; pSprite->pNextY = pNext; pNext->pPreviousY = pSprite; if (pPrevious) pPrevious->pNextY = pSprite; else pSprite->pState->pListY = pSprite; } else if ((pSprite->pNextY != NULL) && (pSprite->pNextY->rclSprite.top < yThis)) { // The sprite has to move down in the list. First, remove
// the sprite's node from its current position:
pNext = pSprite->pNextY; pPrevious = pSprite->pPreviousY; pNext->pPreviousY = pPrevious; if (pPrevious) pPrevious->pNextY = pNext; else pSprite->pState->pListY = pNext;
// Find the sprite's proper position in the list:
pPrevious = pNext; while (((pNext = pPrevious->pNextY) != NULL) && (pNext->rclSprite.top < yThis)) pPrevious = pNext;
// Insert the sprite in its new position:
pSprite->pPreviousY = pPrevious; pSprite->pNextY = pNext; pPrevious->pNextY = pSprite; if (pNext) pNext->pPreviousY = pSprite; } }
/******************************Public*Routine******************************\
* VOID vSpComputeUncoveredRegion * * We construct the true RGNOBJ representing the uncovered portions of * the screen directly from the sprite-range list. * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
VOID vSpComputeUncoveredRegion( SPRITESTATE* pState) { REGION* prgnUncovered; SIZE_T sizt;
// As a simple guess as to the size of the necessary region, we simply
// use the sprite-range size:
ASSERTGDI(sizeof(SPRITERANGE) + sizeof(SPRITESCAN) >= NULL_SCAN_SIZE, "Our guess will be wrong!"); ASSERTGDI(pState->pRangeLimit > pState->pRange, "pRange/pRangeLimit mismatch");
sizt = (BYTE*) pState->pRangeLimit - (BYTE*) pState->pRange;
// Add in the size of a header, which our sprite ranges don't have:
sizt += NULL_REGION_SIZE;
prgnUncovered = pState->prgnUncovered; if (prgnUncovered->sizeObj < sizt) { // If the allocation fails, we'll simply stick with the old region
// and draw wrong (but we won't crash):
RGNMEMOBJ rmoNew((ULONGSIZE_T)sizt); if (!rmoNew.bValid()) return;
RGNOBJ roOld(prgnUncovered); roOld.vDeleteRGNOBJ();
prgnUncovered = rmoNew.prgnGet(); pState->prgnUncovered = prgnUncovered; }
RGNOBJ roUncovered(pState->prgnUncovered);
PDEVOBJ po(pState->hdev);
roUncovered.vComputeUncoveredSpriteRegion(po); roUncovered.vTighten();
ASSERTGDI(sizt >= roUncovered.sizeRgn(), "Uh oh, we overwrote the end of the region!");
// Any DirectDraw locked areas have to be added back to the uncovered
// area:
if (pState->prgnUnlocked != NULL) { RGNOBJ roUnlocked(pState->prgnUnlocked); RGNOBJ roTmp(pState->prgnTmp);
RGNMEMOBJTMP rmoLocked; if (rmoLocked.bValid()) { roTmp.vSet(&pState->rclScreen); if (!rmoLocked.bMerge(roTmp, roUnlocked, gafjRgnOp[RGN_DIFF])) { rmoLocked.vSet(); } if (!roTmp.bMerge(roUncovered, rmoLocked, gafjRgnOp[RGN_OR])) { roTmp.vSet(); }
pState->prgnUncovered = roTmp.prgnGet(); pState->prgnTmp = roUncovered.prgnGet(); } }
pState->prgnUncovered->vStamp(); }
/******************************Public*Routine******************************\
* VOID vSpSetNullRange * * Resets the sprite range to a null area. * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
VOID vSpSetNullRange( SPRITESTATE* pState, SPRITESCAN* pScan) // Must be pool allocated (it will be freed later)
// and at least sizeof(SPRITESCAN)
{ pScan->yTop = pState->rclScreen.top; pScan->yBottom = pState->rclScreen.bottom; pScan->siztScan = sizeof(SPRITESCAN); pScan->siztPrevious = 0;
pScan->aRange[0].xLeft = pState->rclScreen.left; pScan->aRange[0].xRight = pState->rclScreen.right; pScan->aRange[0].pSprite = NULL;
pState->pRange = pScan; pState->pRangeLimit = pScan + 1; }
#define BATCH_ALLOC_COUNT 20
inline SPRITERANGE* pSpRangeLimit( SPRITESTATE* pState) { // We adjust the range limit to leave room for that extra SPRITESCAN
// structure we allocated. It's also VERY important that we remove a
// SPRITERANGE structure to account for sizeof(SPRITERANGE) not
// dividing evenly into sizeof(SPRITESCAN):
return((SPRITERANGE*) ((BYTE*) pState->pRangeLimit - sizeof(SPRITESCAN) - sizeof(SPRITERANGE))); }
/******************************Public*Routine******************************\
* SPRITERANGE* pSpGrowRanges * * 12-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
SPRITERANGE* pSpGrowRanges( SPRITESTATE* pState, SPRITERANGE* pCurrentRange, SPRITESCAN** ppCurrentScan, SPRITERANGE** ppRangeLimit) { SPRITESCAN* pNewRegion; SIZE_T siztOldAlloc; SIZE_T siztNewAlloc; SIZE_T siztCurrentRange; SIZE_T siztCurrentScan;
#if DEBUG_SPRITES
KdPrint(("GDI: Growing sprite ranges\n")); #endif
// We always add in the size of a SPRITESCAN structure to allow
// 'bSpComputeScan' to initialize it without checking:
siztCurrentRange = (BYTE*) pCurrentRange - (BYTE*) pState->pRange; siztCurrentScan = (BYTE*) *ppCurrentScan - (BYTE*) pState->pRange; siztOldAlloc = (BYTE*) pState->pRangeLimit - (BYTE*) pState->pRange; siztNewAlloc = siztOldAlloc + sizeof(SPRITESCAN) + sizeof(SPRITERANGE) * BATCH_ALLOC_COUNT;
pNewRegion = (SPRITESCAN*) PALLOCNOZ((ULONGSIZE_T)siztNewAlloc, 'rpsG'); if (pNewRegion == NULL) { vSpSetNullRange(pState, pState->pRange);
// Should we mark every sprite as invisible?
return(NULL); }
RtlCopyMemory(pNewRegion, pState->pRange, siztCurrentRange);
VFREEMEM(pState->pRange);
pState->pRange = pNewRegion; pState->pRangeLimit = (BYTE*) pNewRegion + siztNewAlloc;
*ppCurrentScan = (SPRITESCAN*) ((BYTE*) pNewRegion + siztCurrentScan); *ppRangeLimit = pSpRangeLimit(pState);
return((SPRITERANGE*) ((BYTE*) pNewRegion + siztCurrentRange)); }
/******************************Public*Routine******************************\
* BOOL bSpComputeScan * * 12-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
BOOL bSpComputeScan( SPRITESTATE* pState, SPRITE* pActiveList, LONG yTop, LONG yBottom, SPRITESCAN** ppScan, SIZE_T* psiztPrevious) { SPRITESCAN* pScan; SPRITERANGE* pRange; SPRITERANGE* pRangeLimit; SPRITERANGE* pTmp; SPRITE* pSprite; LONG xLeft; LONG xRight; LONG xFinalRight; LONG cSprites;
ASSERTGDI(yTop < yBottom, "Invalid empty row");
xLeft = pState->rclScreen.left; xRight = pState->rclScreen.right; xFinalRight = pState->rclScreen.right;
pScan = *ppScan;
pScan->yTop = yTop; pScan->yBottom = yBottom; pScan->siztPrevious = *psiztPrevious;
pRangeLimit = pSpRangeLimit(pState); pRange = &pScan->aRange[0];
// Note that we always adjust 'pRangeLimit' to leave room for a
// SPRITESCAN structure:
ASSERTGDI((pScan >= (SPRITESCAN*) pState->pRange) && (pScan + 1 <= (SPRITESCAN*) pState->pRangeLimit), "pScan will overwrite buffer end!"); ASSERTGDI(pState->pRange < pState->pRangeLimit, "pRange/pRangeLimit mismatch");
do { cSprites = 0; for (pSprite = pActiveList; pSprite != NULL; pSprite = pSprite->pNextActive) { ASSERTGDI((pSprite != NULL) && (pSprite->rclSprite.top <= yTop) && (pSprite->rclSprite.bottom >= yBottom) && (pSprite->rclSprite.left < pSprite->rclSprite.right) && (pSprite->rclSprite.top < pSprite->rclSprite.bottom), "Invalid active list");
if (pSprite->rclSprite.left <= xLeft) { if (pSprite->rclSprite.right > xLeft) { cSprites++;
// Add this sprite:
if (pRange >= pRangeLimit) { pRange = pSpGrowRanges(pState, pRange, &pScan, &pRangeLimit); if (!pRange) return(FALSE); }
pRange->pSprite = pSprite; pRange++;
if (pSprite->rclSprite.right <= xRight) xRight = pSprite->rclSprite.right; } } else if (pSprite->rclSprite.left <= xRight) { xRight = pSprite->rclSprite.left; } }
if (cSprites == 0) { if (pRange >= pRangeLimit) { pRange = pSpGrowRanges(pState, pRange, &pScan, &pRangeLimit); if (!pRange) return(FALSE); }
pRange->pSprite = NULL; pRange->xLeft = xLeft; pRange->xRight = xRight; pRange++; } else { // Now, fill in the wall values for every range we just
// put down:
pTmp = pRange; do { pTmp--; pTmp->xLeft = xLeft; pTmp->xRight = xRight;
} while (--cSprites != 0); }
// Advance to the next rectangle in this scan:
xLeft = xRight; xRight = xFinalRight;
} while (xLeft < xRight);
pScan->siztScan = (BYTE*) pRange - (BYTE*) pScan; *psiztPrevious = pScan->siztScan; *ppScan = (SPRITESCAN*) pRange;
return(TRUE); }
/******************************Public*Routine******************************\
* BOOL vSpComputeSpriteRanges * * Recalculates the 'range' list that describes how the overlays overlap * the screen. * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
VOID vSpComputeSpriteRanges( SPRITESTATE* pState) { SPRITE* pPrevious; SPRITE* pNext; SPRITE* pThis; SPRITE* pSpriteY; BOOL bSprite; SPRITE ActiveHead; LONG yTop; LONG yBottom; LONG yFinalBottom; SPRITESCAN* pCurrentScan; SIZE_T siztPrevious;
ASSERTGDI(!(pState->bValidRange), "Should be called only when dirty.");
pCurrentScan = pState->pRange; siztPrevious = 0;
ActiveHead.pNextActive = NULL;
yTop = pState->rclScreen.top; yBottom = pState->rclScreen.bottom; yFinalBottom = pState->rclScreen.bottom;
// First, skip over any invisible sprites:
pSpriteY = pState->pListY; while ((pSpriteY != NULL) && (pSpriteY->rclSprite.bottom <= yTop)) pSpriteY = pSpriteY->pNextY;
do { // Prune the active list, throwing out anyone whose bottom
// matches the previous row's bottom, and at the same time
// compute the new bottom:
pPrevious = &ActiveHead; while ((pThis = pPrevious->pNextActive) != NULL) { if (pThis->rclSprite.bottom == yTop) { pPrevious->pNextActive = pThis->pNextActive; } else { if (pThis->rclSprite.bottom <= yBottom) yBottom = pThis->rclSprite.bottom;
// Advance to next node:
pPrevious = pThis; } }
// Add to the active list any sprites that have a 'top'
// value equal to the new top, watching out for a new
// 'bottom' value:
while (pSpriteY != NULL) { if (pSpriteY->rclSprite.top != yTop) { // Any not-yet-used sprites may affect the height of
// this row:
if (pSpriteY->rclSprite.top <= yBottom) yBottom = pSpriteY->rclSprite.top;
break; // =======>
}
// The active list is kept in back-to-front z-order:
pNext = &ActiveHead; do { pPrevious = pNext; pNext = pPrevious->pNextActive; } while ((pNext != NULL) && (pNext->z < pSpriteY->z));
pPrevious->pNextActive = pSpriteY; pSpriteY->pNextActive = pNext;
if (pSpriteY->rclSprite.bottom <= yBottom) yBottom = pSpriteY->rclSprite.bottom;
pSpriteY = pSpriteY->pNextY; }
// Now, use the active-list to compute the new row:
if (!bSpComputeScan(pState, ActiveHead.pNextActive, yTop, yBottom, &pCurrentScan, &siztPrevious)) { // In case of failure, 'bSpComputeScan' leaves the structure
// empty but consistent, so we can safely just leave:
return; }
// Advance to the next row:
yTop = yBottom; yBottom = yFinalBottom;
} while (yTop < yBottom);
// The range cache is now valid:
pState->bValidRange = TRUE;
// Finally, use the sprite range to compute a true region representing
// the uncovered parts:
vSpComputeUncoveredRegion(pState); }
/******************************Public*Routine******************************\
* ENUMAREAS::ENUMAREAS * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
ENUMAREAS::ENUMAREAS( SPRITESTATE* pState, RECTL* prclBounds, // Must be pre-clipped to screen
ULONG iDirection) // CD_RIGHTDOWN is the default
{ LONG yStart; SPRITESCAN* pTmpScan; SPRITERANGE* pTmpRange;
if (!pState->bValidRange) { vSpComputeSpriteRanges(pState); } bValidRange = pState->bValidRange; // valid only if the sprite range
// computation succeeded
// BUGFIX #22140 2-18-2000 bhouse
// We will just leave this assertion code commented out for now. If we
// ever have a bug causing us to fix the how we calculate the bounds
// in BLTRECORD__bRotate then perhaps we can un-comment out these
// lines.
// ASSERTGDI((prclBounds->left >= pState->rclScreen.left) &&
// (prclBounds->top >= pState->rclScreen.top)),
// "invalid rectangle");
ASSERTGDI((prclBounds->right <= pState->rclScreen.right) && (prclBounds->bottom <= pState->rclScreen.bottom) && (prclBounds->left < prclBounds->right) && (prclBounds->top < prclBounds->bottom), "Invalid rectangle specified");
iDir = iDirection; xBoundsLeft = prclBounds->left; xBoundsRight = prclBounds->right; yBoundsTop = prclBounds->top; yBoundsBottom = prclBounds->bottom;
// Find the first scan that contains the start 'y' value:
yStart = (iDirection & 2) ? (yBoundsBottom - 1) : (yBoundsTop); pTmpScan = pState->pRange; while (pTmpScan->yBottom <= yStart) pTmpScan = pSpNextScan(pTmpScan);
// If more than one sprite overlaps a region, there will be
// multiple sprite-range records for the same area. When
// going left-to-right, we should leave 'pTmpRange' pointing to
// the start of this sequence; when going right-to-left, we
// should leave 'pTmpRange' pointing to the end of this sequence.
if ((iDirection & 1) == 0) // Left-to-right
{ pTmpRange = &pTmpScan->aRange[0]; while (pTmpRange->xRight <= xBoundsLeft) pTmpRange++; } else // Right-to-left
{ pTmpRange = pSpLastRange(pTmpScan); while (pTmpRange->xLeft >= xBoundsRight) pTmpRange--; }
yTop = max(pTmpScan->yTop, yBoundsTop); yBottom = min(pTmpScan->yBottom, yBoundsBottom);
// We used temporary variables as opposed to the structure members
// directly so that the compiler would be more efficient:
pScan = pTmpScan; pRange = pTmpRange; }
/******************************Public*Routine******************************\
* BOOL ENUMAREAS::bEnum * * This routine enumerates the list of sprite and non-sprite ranges * comprising a rectangular area on the screen. If two sprites overlap, * for the overlapping rectangle this routine will always return the * bottom-most sprite. * * Returns TRUE if more ranges after this remain to be enumerated. * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
BOOL ENUMAREAS::bEnum( SPRITE** ppSprite, // Returns touched sprite. This will be NULL
// if the enumerated area is touched by
// no sprite
RECTL* prclSprite) // Returns area of sprite touched
{ SPRITESCAN* pTmpScan; SPRITERANGE* pTmpRange;
// Use local variables where possible to be more efficient in our
// inner loops:
pTmpRange = pRange;
// Remember some state for enumerating the layers:
pRangeLayer = pTmpRange; pScanLayer = pScan;
// Fill in details about the current range:
*ppSprite = pTmpRange->pSprite;
prclSprite->left = max(pTmpRange->xLeft, xBoundsLeft); prclSprite->right = min(pTmpRange->xRight, xBoundsRight); prclSprite->top = yTop; prclSprite->bottom = yBottom;
ASSERTGDI((prclSprite->left < prclSprite->right) && (prclSprite->top < prclSprite->bottom), "Invalid return rectangle"); ASSERTGDI((*ppSprite == NULL) || (((*ppSprite)->rclSprite.left <= prclSprite->left) && ((*ppSprite)->rclSprite.top <= prclSprite->top) && ((*ppSprite)->rclSprite.right >= prclSprite->right) && ((*ppSprite)->rclSprite.bottom >= prclSprite->bottom)), "Invalid return sprite");
if ((iDir & 1) == 0) // Left-to-right
{ if (pTmpRange->xRight < xBoundsRight) { do { pTmpRange++; } while ((pTmpRange - 1)->xLeft == pTmpRange->xLeft); } else { pTmpScan = pScan;
if (iDir == CD_RIGHTDOWN) { if (pTmpScan->yBottom >= yBoundsBottom) return(FALSE);
pTmpScan = pSpNextScan(pTmpScan); } else { ASSERTGDI(iDir == CD_RIGHTUP, "Unexpected iDir");
if (pTmpScan->yTop <= yBoundsTop) return(FALSE);
pTmpScan = pSpPreviousScan(pTmpScan); }
pScan = pTmpScan;
yTop = max(pTmpScan->yTop, yBoundsTop); yBottom = min(pTmpScan->yBottom, yBoundsBottom); pTmpRange = &pTmpScan->aRange[0];
while (pTmpRange->xRight <= xBoundsLeft) pTmpRange++; } } else // Right-to-left
{ if (pTmpRange->xLeft > xBoundsLeft) { do { pTmpRange--; } while ((pTmpRange + 1)->xLeft == pTmpRange->xLeft); } else { pTmpScan = pScan;
if (iDir == CD_LEFTDOWN) { if (pTmpScan->yBottom >= yBoundsBottom) return(FALSE);
pTmpScan = pSpNextScan(pTmpScan); } else { ASSERTGDI(iDir == CD_LEFTUP, "Unexpected iDir");
if (pTmpScan->yTop <= yBoundsTop) return(FALSE);
pTmpScan = pSpPreviousScan(pTmpScan); }
pScan = pTmpScan;
yTop = max(pTmpScan->yTop, yBoundsTop); yBottom = min(pTmpScan->yBottom, yBoundsBottom); pTmpRange = pSpLastRange(pTmpScan);
while (pTmpRange->xLeft >= xBoundsRight) pTmpRange--; } }
pRange = pTmpRange; return(TRUE); }
/******************************Public*Routine******************************\
* ENUMUNCOVERED::ENUMUNCOVERED * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
ENUMUNCOVERED::ENUMUNCOVERED( SPRITESTATE* pState) { if (!pState->bValidRange) { vSpComputeSpriteRanges(pState); }
yBoundsBottom = pState->rclScreen.bottom; pScan = pState->pRange; pRange = &pScan->aRange[0] - 1; pNextScan = pSpNextScan(pScan); }
/******************************Public*Routine******************************\
* BOOL ENUMUNCOVERED::bEnum * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
BOOL ENUMUNCOVERED::bEnum( RECTL* prclUncovered) { SPRITESCAN* pTmpScan = pScan; SPRITERANGE* pTmpRange = pRange;
do { pTmpRange++; if (pTmpRange >= (SPRITERANGE*) pNextScan) { if (pTmpScan->yBottom >= yBoundsBottom) return(FALSE);
pTmpScan = pNextScan; pNextScan = pSpNextScan(pNextScan); pTmpRange = &pTmpScan->aRange[0]; } } while (pTmpRange->pSprite != NULL);
prclUncovered->top = pTmpScan->yTop; prclUncovered->bottom = pTmpScan->yBottom; prclUncovered->left = pTmpRange->xLeft; prclUncovered->right = pTmpRange->xRight;
pScan = pTmpScan; pRange = pTmpRange;
return(TRUE); }
/******************************Public*Routine******************************\
* BOOL ENUMAREAS::bEnumLayers * * This routine may be called only after 'bEnum' is called, and lists * the sprites that overlay the 'bEnum' rectangle, bottom-most to top-most. * * Returns TRUE if the returned 'ppSprite' is a valid layer. * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
BOOL ENUMAREAS::bEnumLayers( SPRITE** ppSprite) { BOOL bRet = FALSE;
if ((iDir & 1) == 0) // Left-to-right
{ if ((pRangeLayer < pSpLastRange(pScanLayer)) && ((pRangeLayer + 1)->xLeft == pRangeLayer->xLeft)) { pRangeLayer++; bRet = TRUE; } } else // Right-to-left
{ if ((pRangeLayer > &pScanLayer->aRange[0]) && ((pRangeLayer - 1)->xLeft == pRangeLayer->xLeft)) { pRangeLayer--; bRet = TRUE; } }
*ppSprite = pRangeLayer->pSprite; return(bRet); }
/******************************Public*Routine******************************\
* BOOL ENUMAREAS::bAdvanceToTopMostOpaqueLayer * * This routine may be called only after 'bEnum' is called, and advances * to the top-most, unclipped opaque layer for this range. * * Returns TRUE if there was an opaque layer; FALSE if not. * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
BOOL ENUMAREAS::bAdvanceToTopMostOpaqueLayer( SPRITE** ppSprite) { SPRITERANGE* pThis; SPRITERANGE* pLast; SPRITERANGE* pOpaque; SPRITE* pSprite; REGION* prgnClip; BOOL bRet;
ASSERTGDI((iDir & 1) == 0, "Can use only when enumerating left-to-right");
pLast = pSpLastRange(pScanLayer); pThis = pRangeLayer; pOpaque = NULL;
// We can't do the opaque trick if any WNDOBJs are present on the screen,
// because we rely on the background repainting to refresh any sprites
// unveiled when a WNDOBJ moves.
if (gpto != NULL) { while (TRUE) { pSprite = pThis->pSprite; if (pSprite->fl & SPRITE_FLAG_EFFECTIVELY_OPAQUE) { // Do a quick check to make sure this opaque sprite isn't
// clipped for this range.
prgnClip = pSprite->prgnClip; if ((prgnClip == NULL) || ((prgnClip->sizeRgn <= SINGLE_REGION_SIZE) && (prgnClip->rcl.left <= pThis->xLeft) && (prgnClip->rcl.right >= pThis->xRight) && (prgnClip->rcl.top <= yTop) && (prgnClip->rcl.bottom >= yBottom))) { pOpaque = pThis; } }
if ((pThis >= pLast) || ((pThis + 1)->xLeft != pThis->xLeft)) break;
pThis++; } }
bRet = FALSE; if (pOpaque) { pRangeLayer = pOpaque; bRet = TRUE; }
*ppSprite = pRangeLayer->pSprite; return(bRet); }
/******************************Public*Routine******************************\
* VOID vSpReadFromScreen * * All reads from the screen should be routed through this call so that * we can: * * 1. Exclude any DirectDraw locked regions; * 2. Respect any WNDOBJ boundaries. * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
VOID vSpReadFromScreen( SPRITESTATE* pState, POINTL* pOffDst, SURFOBJ* psoDst, RECTL* prclDst) { ECLIPOBJ ecoUnlocked; ECLIPOBJ* pcoUnlocked;
pcoUnlocked = NULL; if (pState->prgnUnlocked != NULL) { ecoUnlocked.vSetup(pState->prgnUnlocked, *(ERECTL*)prclDst); if (ecoUnlocked.erclExclude().bEmpty()) return; // ======>
pcoUnlocked = &ecoUnlocked; }
XLATEOBJ* pxlo = NULL; SURFOBJ* psoSrc = pState->psoScreen; PSURFACE pSurfSrc = SURFOBJ_TO_SURFACE(psoSrc); EXLATEOBJ xloParent; POINTL* pOffSrc = &gptlZero;
PFN_DrvCopyBits pfnCopyBits;
PDEVOBJ pdoSrc(pSurfSrc->hdev());
// If source is mirror driver, redirect the call to DDML.
if (pSurfSrc->bMirrorSurface() && (pdoSrc.hdev() != pdoSrc.hdevParent())) { pOffSrc = pdoSrc.pptlOrigin(); PDEVOBJ pdoParent(pdoSrc.hdevParent()); SURFREF srParent((HSURF)pSurfSrc->hMirrorParent); if (!srParent.bValid()) return; if (xloParent.bInitXlateObj( NULL,DC_ICM_OFF, pdoParent.ppalSurf(), pdoSrc.ppalSurf(), ppalDefault, ppalDefault, 0L,0L,0L, XLATE_USE_SURFACE_PAL)) pxlo = xloParent.pxlo(); else return;
psoSrc = srParent.ps->pSurfobj(); pfnCopyBits = PPFNDRV(pdoParent, CopyBits); } else if (!(SURFOBJ_TO_SURFACE_NOT_NULL(psoDst)->flags() & HOOK_CopyBits) && (psoSrc->hdev)) { pfnCopyBits = PPFNDIRECT(psoSrc, CopyBits); } else { pfnCopyBits = PPFNDIRECT(psoDst, CopyBits); }
// WINBUG #440135 10-02-2001 kchiu
// Instead of always passing &gptlZero for pOffSrc. We now pass in pdoSrc->pptlOrigin() whenever
// the source is mirror driver. We do this for mirror driver only and not for secondary drivers
// with non-zero origin because the coords are already offset correctly in the case of a secondary
// driver, but not in the case of the mirror driver.
OffCopyBits(pfnCopyBits, pOffDst, psoDst, pOffSrc, psoSrc, pcoUnlocked, pxlo, prclDst, (POINTL*) prclDst); }
/******************************Public*Routine******************************\
* VOID vSpWriteToScreen * * All writes to the screen should be routed through this call so that * we can: * * 1. Exclude any DirectDraw locked regions; * 2. Respect any WNDOBJ boundaries. * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
VOID vSpWriteToScreen( SPRITESTATE* pState, POINTL* pOffSrc, SURFOBJ* psoSrc, RECTL* prclSrc) { GDIFunctionID(vSpWriteToScreen);
ECLIPOBJ ecoUnlocked; ECLIPOBJ* pcoUnlocked;
pcoUnlocked = NULL; if (pState->prgnUnlocked != NULL) { ecoUnlocked.vSetup(pState->prgnUnlocked, *(ERECTL*)prclSrc); if (ecoUnlocked.erclExclude().bEmpty()) return; // ======>
pcoUnlocked = &ecoUnlocked; }
// Mark the source surface to indicate that it shouldn't be cached,
// as it's pretty much guaranteed that we won't be blting the bitmap
// with exactly the same contents again.
//
// Note that this has to be in addition to any BMF_DONTCACHE flag
// that might have been set, as the TShare driver ignores the
// BMF_DONTCACHE flag.
psoSrc->iUniq = 0;
ASSERTGDI((pState->psoScreen->iType == pState->iOriginalType) && (SURFOBJ_TO_SURFACE_NOT_NULL(pState->psoScreen)->flags() == pState->flOriginalSurfFlags), "Writing to screen without restoring orignal surface settings.");
OFFCOPYBITS(&gptlZero, pState->psoScreen, pOffSrc, psoSrc, pcoUnlocked, NULL, prclSrc, (POINTL*) prclSrc); }
/******************************Public*Routine******************************\
* VOID vSpDrawCursor * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
VOID vSpDrawCursor( SPRITE* pSprite, POINTL* pOffComposite, SURFOBJ* psoComposite, RECTL* prclEnum, POINTL* pptlSrc) { SPRITESTATE* pState; POINTL ptlSrc; SURFOBJ* psoShape; POINTL* pOffShape; XLATEOBJ* pxlo;
// A NULL mask means that the shape couldn't be allocated:
if (pSprite->psoMask == NULL) return;
pState = pSprite->pState;
EXLATEOBJ xloMono; XEPALOBJ palMono(ppalMono); XEPALOBJ palScreen(SURFOBJ_TO_SURFACE_NOT_NULL(pState->psoScreen)->ppal()); XEPALOBJ palDefault(ppalDefault);
// The XLATEOBJ should always be in the cache, so this should be pretty
// cheap most of the time:
if (xloMono.bInitXlateObj(NULL, DC_ICM_OFF, palMono, palScreen, palDefault, palDefault, 0, 0xffffff, 0)) { OFFBITBLT(pOffComposite, psoComposite, &gptlZero, pSprite->psoMask, NULL, NULL, xloMono.pxlo(), prclEnum, pptlSrc, NULL, NULL, NULL, 0x8888); // SRCAND
if (pSprite->psoShape == NULL) { ptlSrc.x = pptlSrc->x; ptlSrc.y = pptlSrc->y + (pSprite->psoMask->sizlBitmap.cy >> 1); psoShape = pSprite->psoMask; pOffShape = &gptlZero; pxlo = xloMono.pxlo(); } else { ptlSrc.x = pptlSrc->x; ptlSrc.y = pptlSrc->y; psoShape = pSprite->psoShape; pOffShape = &pSprite->OffShape; pxlo = NULL; }
OFFBITBLT(pOffComposite, psoComposite, pOffShape, psoShape, NULL, NULL, pxlo, prclEnum, &ptlSrc, NULL, NULL, NULL, 0x6666); // SRCINVERT
} }
/******************************Public*Routine******************************\
* VOID vSpComposite * * Draws the specified sprite into the composition buffer. * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
VOID vSpComposite( SPRITE* pSprite, POINTL* pOffComposite, SURFOBJ* psoComposite, RECTL* prclEnum) { SPRITESTATE* pState; POINTL ptlSrc; CLIPOBJ* pco; DWORD dwShape; ECLIPOBJ ecoClip; CLIPOBJ* pcoClip; SURFOBJ* psoNull = NULL;
// First, calculate the clipping:
pcoClip = NULL; if (pSprite->prgnClip != NULL) { pcoClip = &ecoClip;
ecoClip.vSetup(pSprite->prgnClip, *((ERECTL*) prclEnum));
if ((ecoClip.rclBounds.left >= ecoClip.rclBounds.right) || (ecoClip.rclBounds.top >= ecoClip.rclBounds.bottom)) { // It's completely clipped, so we're outta here:
return; } }
pState = pSprite->pState;
ASSERTGDI(pState->bInsideDriverCall, "Must have sprite lock");
dwShape = pSprite->dwShape; if (pSprite->fl & SPRITE_FLAG_EFFECTIVELY_OPAQUE) dwShape = ULW_OPAQUE;
ptlSrc.x = pSprite->rclSrc.left + (prclEnum->left - pSprite->ptlDst.x); ptlSrc.y = pSprite->rclSrc.top + (prclEnum->top - pSprite->ptlDst.y);
XEPALOBJ palScreen(SURFOBJ_TO_SURFACE_NOT_NULL(pState->psoScreen)->ppal()); XEPALOBJ palSprite(pSprite->ppalShape); XEPALOBJ palDefault(ppalDefault); XEPALOBJ palRGB(gppalRGB); EXLATEOBJ xloSpriteToDst;
// If the current color depth is the same as the color depth in which
// the sprite was originally created, we don't need an XLATEOBJ.
// Otherwise, create one now:
if (((pSprite->iModeFormat == pState->iModeFormat) && (pSprite->flModeMasks == pState->flModeMasks)) || (xloSpriteToDst.bInitXlateObj(NULL, DC_ICM_OFF, palSprite, palScreen, palDefault, palDefault, 0, 0, 0))) { if (dwShape != ULW_ALPHA) { if (dwShape == ULW_OPAQUE) { if (pSprite->psoShape) { OFFCOPYBITS(pOffComposite, psoComposite, &pSprite->OffShape, pSprite->psoShape, pcoClip, xloSpriteToDst.pxlo(), prclEnum, &ptlSrc); } } else if (dwShape == ULW_COLORKEY) { if (pSprite->psoShape) { ERECTL rclSrc(ptlSrc.x, ptlSrc.y, ptlSrc.x + (prclEnum->right - prclEnum->left), ptlSrc.y + (prclEnum->bottom - prclEnum->top));
OffTransparentBlt(PPFNDIRECT(psoComposite, TransparentBlt), pOffComposite, psoComposite, &pSprite->OffShape, pSprite->psoShape, pcoClip, xloSpriteToDst.pxlo(), prclEnum, &rclSrc, pSprite->iTransparent, 0); } } else if (dwShape == ULW_CURSOR) { vSpDrawCursor(pSprite, pOffComposite, psoComposite, prclEnum, &ptlSrc); } else { ASSERTGDI(dwShape == ULW_DRAGRECT, "Unexpected shape");
PDEVOBJ po(pState->hdev);
OFFBITBLT(pOffComposite, psoComposite, NULL, psoNull, NULL, NULL, NULL, prclEnum, NULL, NULL, po.pbo(), &gptlZero, 0x5a5a); } } else { ASSERTGDI(dwShape == ULW_ALPHA, "Unexpected case");
if (pSprite->psoShape) { EBLENDOBJ eBlendObj; EXLATEOBJ xloSpriteTo32; EXLATEOBJ xloScreenTo32; EXLATEOBJ xlo32ToScreen;
ERECTL rclSrc(ptlSrc.x, ptlSrc.y, ptlSrc.x + (prclEnum->right - prclEnum->left), ptlSrc.y + (prclEnum->bottom - prclEnum->top));
if ((xloSpriteTo32.bInitXlateObj(NULL, DC_ICM_OFF, palSprite, palRGB, palDefault, palDefault, 0, 0, 0)) && (xloScreenTo32.bInitXlateObj(NULL, DC_ICM_OFF, palScreen, palRGB, palDefault, palDefault, 0, 0, 0)) && (xlo32ToScreen.bInitXlateObj(NULL, DC_ICM_OFF, palRGB, palScreen, palDefault, palDefault, 0, 0, 0))) { eBlendObj.BlendFunction = pSprite->BlendFunction; eBlendObj.pxloSrcTo32 = xloSpriteTo32.pxlo(); eBlendObj.pxloDstTo32 = xloScreenTo32.pxlo(); eBlendObj.pxlo32ToDst = xlo32ToScreen.pxlo();
OffAlphaBlend(PPFNDIRECT(psoComposite, AlphaBlend), pOffComposite, psoComposite, &pSprite->OffShape, pSprite->psoShape, pcoClip, xloSpriteToDst.pxlo(), prclEnum, &rclSrc, &eBlendObj); } } } } }
/******************************Public*Routine******************************\
* SURFOBJ* psoSpGetComposite * * Returns a surface to be used for compositing. * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
SURFOBJ* psoSpGetComposite( SPRITESTATE* pState, RECTL* prcl) { SURFOBJ* psoComposite; LONG cxMax; LONG cyMax; LONG cMaxArea; LONG cx; LONG cy; LONG cArea; BOOL bWantSystemMemory; SPRITE* pSprite; SPRITE* pBiggest;
psoComposite = pState->psoComposite; if ((psoComposite == NULL) || (psoComposite->sizlBitmap.cx < (prcl->right - prcl->left)) || (psoComposite->sizlBitmap.cy < (prcl->bottom - prcl->top))) { vSpDeleteSurface(psoComposite); // Handles NULL case
cMaxArea = 0; cxMax = 0; cyMax = 0;
// Find the minimum dimensions needed to accomodate every sprite:
for (pSprite = pState->pListZ; pSprite != NULL; pSprite = pSprite->pNextZ) { cx = pSprite->rclSprite.right - pSprite->rclSprite.left; if (cx > cxMax) cxMax = cx;
cy = pSprite->rclSprite.bottom - pSprite->rclSprite.top; if (cy > cyMax) cyMax = cy;
cArea = cx * cy; if (cArea > cMaxArea) { cMaxArea = cArea; pBiggest = pSprite; } }
ASSERTGDI((cxMax > 0) && (cyMax > 0), "Expected a non-zero sprite");
bWantSystemMemory = FALSE; if (pBiggest->dwShape == ULW_ALPHA) { PDEVOBJ po(pState->hdev);
if (pBiggest->BlendFunction.AlphaFormat & AC_SRC_ALPHA) { if (!(po.flAccelerated() & ACCELERATED_PIXEL_ALPHA)) { bWantSystemMemory = TRUE; } } else { if (!(po.flAccelerated() & ACCELERATED_CONSTANT_ALPHA)) { bWantSystemMemory = TRUE; } } }
// The compositing surface should always be in video-memory, unless
// there is a big alpha sprite somewhere, and the hardware doesn't
// accelerate alpha:
psoComposite = psoSpCreateSurface(pState, 0, cxMax, cyMax, bWantSystemMemory);
#if DEBUG_SPRITES
KdPrint(("psoComposite: %p\n", psoComposite)); #endif
pState->psoComposite = psoComposite;
// Mark the surface as uncacheable, so that we won't pollute the
// cache of the TShare driver, and the like:
if (psoComposite != NULL) { psoComposite->fjBitmap |= BMF_DONTCACHE;
// BMF_SPRITE appears to be unused. We are temporarily removing
// support to see if 3DLabs complains.
// psoComposite->fjBitmap |= BMF_SPRITE;
} }
return(psoComposite); }
/******************************Public*Routine******************************\
* VOID vSpRedrawArea * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
VOID vSpRedrawArea( SPRITESTATE* pState, RECTL* prclBounds, BOOL bMustRedraw = FALSE) { SPRITE* pSprite; RECTL rclEnum; SURFOBJ* psoComposite; POINTL OffComposite; BOOL bMore; BOOL hHasOpaqueLayer;
PDEVOBJ po(pState->hdev); if (po.bDisabled()) return;
ASSERTGDI(pState->bInsideDriverCall, "Must have sprite lock");
ENUMAREAS Enum(pState, prclBounds);
do { bMore = Enum.bEnum(&pSprite, &rclEnum);
ASSERTGDI((rclEnum.left >= pState->rclScreen.left) && (rclEnum.top >= pState->rclScreen.top) && (rclEnum.right <= pState->rclScreen.right) && (rclEnum.bottom <= pState->rclScreen.bottom), "Enumerated rectangle exceeds screen bounds");
if (pSprite != NULL) { // We always draw bottom-to-top, but we don't have to
// bother drawing any layers that are below the top-most
// opaque layer:
hHasOpaqueLayer = Enum.bAdvanceToTopMostOpaqueLayer(&pSprite);
// If something was drawn under this layer region, and
// there was any opaque layer, we can early-out because
// we know we won't have to update the screen:
if ((!hHasOpaqueLayer) || (bMustRedraw)) { // Create the compositing surface:
psoComposite = psoSpGetComposite(pState, &rclEnum); if (psoComposite == NULL) return; // ======>
// Set the composite buffer's origin for ease of use:
OffComposite.x = -rclEnum.left; OffComposite.y = -rclEnum.top;
// Okay, we have to do things the slow way. First, copy
// the underlay to the composite buffer:
OFFCOPYBITS(&OffComposite, psoComposite, &pSprite->OffUnderlay, pSprite->psoUnderlay, NULL, NULL, &rclEnum, (POINTL*) &rclEnum);
// Now composite each sprite:
do { vSpComposite(pSprite, &OffComposite, psoComposite, &rclEnum);
} while (Enum.bEnumLayers(&pSprite));
// Now copy the final composited result back to the screen:
vSpWriteToScreen(pState, &OffComposite, psoComposite, &rclEnum); } } } while (bMore); }
/******************************Public*Routine******************************\
* SPRITE* pSpFindInZ * * Finds the next sprite in the list that intersects with the specified * rectangle. * * This function's entire raison d'etre is for speed. * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
SPRITE* FASTCALL pSpFindInZ( SPRITE* pSprite, RECTL* prcl) { LONG xLeft = prcl->left; LONG yTop = prcl->top; LONG xRight = prcl->right; LONG yBottom = prcl->bottom;
while (pSprite != NULL) { if ((pSprite->rclSprite.left < xRight) && (pSprite->rclSprite.top < yBottom) && (pSprite->rclSprite.right > xLeft) && (pSprite->rclSprite.bottom > yTop)) { return(pSprite); }
pSprite = pSprite->pNextZ; }
return(pSprite); }
/******************************Public*Routine******************************\
* VOID vSpRedrawSprite * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
VOID vSpRedrawSprite( SPRITE* pSprite) { GDIFunctionID(vSpRedrawSprite);
SPRITESTATE* pState; SURFOBJ* psoComposite; POINTL OffComposite; SPRITE* pIntersect; RECTL rclIntersect;
pState = pSprite->pState;
// There's nothing to redraw if we're full-screen:
PDEVOBJ po(pState->hdev); if (po.bDisabled()) return;
ASSERTGDI(pState->bInsideDriverCall, "SPRITELOCK should be held");
if (pSprite->fl & SPRITE_FLAG_VISIBLE) { ASSERTGDI((pSprite->rclSprite.left < pSprite->rclSprite.right) && (pSprite->rclSprite.top < pSprite->rclSprite.bottom), "Badly ordered rclSprite");
psoComposite = psoSpGetComposite(pState, &pSprite->rclSprite); if (psoComposite == NULL) return;
// Set the composite buffer's origin for ease of use:
OffComposite.x = -pSprite->rclSprite.left; OffComposite.y = -pSprite->rclSprite.top;
// First, copy the underlay to the composite buffer:
OFFCOPYBITS(&OffComposite, psoComposite, &pSprite->OffUnderlay, pSprite->psoUnderlay, NULL, NULL, &pSprite->rclSprite, (POINTL*) &pSprite->rclSprite);
// Now composite each sprite:
pIntersect = pSpFindInZ(pState->pListZ, &pSprite->rclSprite); while (pIntersect != NULL) { if (bIntersect(&pIntersect->rclSprite, &pSprite->rclSprite, &rclIntersect)) { vSpComposite(pIntersect, &OffComposite, psoComposite, &rclIntersect); }
pIntersect = pSpFindInZ(pIntersect->pNextZ, &pSprite->rclSprite); }
// Now copy the final composited result back to the screen:
vSpWriteToScreen(pState, &OffComposite, psoComposite, &pSprite->rclSprite);
// set time stamp
pSprite->ulTimeStamp = NtGetTickCount();
} }
/******************************Public*Routine******************************\
* ULONG cSpSubtract * * Simple little routine that returns back the list of rectangles that * make up the area defined by 'prclA' minus 'prclB'. * * Returns the number and values of the resulting rectangles, which will * be no more than four. * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
ULONG FASTCALL cSpSubtract( CONST RECTL* prclA, // Computes A - B
CONST RECTL* prclB, RECTL* arcl) { RECTL rcl; RECTL* prcl;
prcl = arcl;
rcl.left = LONG_MIN; rcl.top = LONG_MIN; rcl.right = LONG_MAX; rcl.bottom = prclB->top;
if (bIntersect(&rcl, prclA, prcl)) prcl++;
rcl.top = prclB->top; rcl.right = prclB->left; rcl.bottom = prclB->bottom;
if (bIntersect(&rcl, prclA, prcl)) prcl++;
rcl.left = prclB->right; rcl.right = LONG_MAX;
if (bIntersect(&rcl, prclA, prcl)) prcl++;
rcl.left = LONG_MIN; rcl.top = prclB->bottom; rcl.bottom = LONG_MAX;
if (bIntersect(&rcl, prclA, prcl)) prcl++;
return((ULONG) (prcl - arcl)); }
/******************************Public*Routine******************************\
* VOID vSpRedrawUncoveredArea * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
VOID vSpRedrawUncoveredArea( SPRITE* pSprite, RECTL* prclNew) { SPRITESTATE* pState; RECTL arclUncovered[4]; RECTL rclTmp; ULONG crclUncovered; SURFOBJ* psoComposite; POINTL OffComposite; SPRITE* pTmp; BOOL bHit; ULONG i; ULONG j;
pState = pSprite->pState;
// There's nothing to redraw if we're full-screen:
PDEVOBJ po(pState->hdev); if (po.bDisabled()) return;
ASSERTGDI(pState->bInsideDriverCall, "SPRITELOCK should be held");
crclUncovered = cSpSubtract(&pSprite->rclSprite, prclNew, &arclUncovered[0]); if (crclUncovered) { bHit = FALSE;
psoComposite = psoSpGetComposite(pState, &pSprite->rclSprite); if (psoComposite == NULL) return;
// Set the composite buffer's origin for ease of use:
OffComposite.x = -pSprite->rclSprite.left; OffComposite.y = -pSprite->rclSprite.top;
pTmp = pSpFindInZ(pState->pListZ, &pSprite->rclSprite); while (pTmp != NULL) { if (pTmp != pSprite) { for (i = 0; i < crclUncovered; i++) { if (bIntersect(&arclUncovered[i], &pTmp->rclSprite, &rclTmp)) { if (!bHit) { for (j = 0; j < crclUncovered; j++) { OFFCOPYBITS(&OffComposite, psoComposite, &pSprite->OffUnderlay, pSprite->psoUnderlay, NULL, NULL, &arclUncovered[j], (POINTL*) &arclUncovered[j]); }
bHit = TRUE; }
vSpComposite(pTmp, &OffComposite, psoComposite, &rclTmp); } } }
pTmp = pSpFindInZ(pTmp->pNextZ, &pSprite->rclSprite); }
for (i = 0; i < crclUncovered; i++) { if (bHit) { vSpWriteToScreen(pState, &OffComposite, psoComposite, &arclUncovered[i]); } else { vSpWriteToScreen(pState, &pSprite->OffUnderlay, pSprite->psoUnderlay, &arclUncovered[i]); } } } }
/******************************Public*Routine******************************\
* VOID vSpSmallUnderlayCopy * * Updates the underlay bits of a sprite without forcing a re-compute * of the sprite-range data structure. * * Copies a rectangle from the screen to the specified underlay surface. * We update the underlay surface by first copying the entire rectangle * straight from the frame buffer (which may include bits of other sprites), * and then looping through the sprite list copying their underlays whereever * they intersect. * * Note that if multiple sprites overlap, we'll be overwriting portions of * the sprite many times. Overdrawing pixels like this is always inefficient, * but if the sprite size is small it won't be too bad. On the other hand, * if there are many sprites, the computation of the sprite range structure * will be *very* expensive. * * So for small sprites, it's often faster to over-draw pixels than to * re-compute the sprite range structure. * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
// The 'SMALL_SPRITE_DIMENSION' is the maximum pixel dimension for which
// a sprite move will be done without recomputing the sprite ranges.
#define SMALL_SPRITE_DIMENSION 128
VOID vSpSmallUnderlayCopy( SPRITE* pSprite, POINTL* pOffDst, SURFOBJ* psoDst, POINTL* pOffSrc, SURFOBJ* psoSrc, LONG dx, LONG dy, RECTL* prclNew, RECTL* prclOld) { SPRITESTATE* pState; RECTL rclSrc; RECTL rclDst; ULONG crcl; RECTL arcl[4]; ULONG i; SPRITE* pTmp; RECTL rclTmp;
pState = pSprite->pState;
// There's nothing to redraw if we're full-screen:
PDEVOBJ po(pState->hdev); if (po.bDisabled()) return;
ASSERTGDI(pState->bInsideDriverCall, "SPRITELOCK should be held");
if (bIntersect(prclOld, prclNew, &rclDst)) { rclSrc.left = dx + rclDst.left; rclSrc.right = dx + rclDst.right; rclSrc.top = dy + rclDst.top; rclSrc.bottom = dy + rclDst.bottom;
OFFCOPYBITS(pOffDst, psoDst, pOffSrc, psoSrc, NULL, NULL, &rclDst, (POINTL*) &rclSrc); }
crcl = cSpSubtract(prclNew, prclOld, arcl);
ASSERTGDI(crcl != 0, "Shouldn't have an empty source");
i = 0; do { vSpReadFromScreen(pState, pOffDst, psoDst, &arcl[i]);
} while (++i != crcl);
pTmp = pSpFindInZ(pState->pListZ, prclNew); while (pTmp != NULL) { if (pTmp != pSprite) { i = 0; do { if (bIntersect(&arcl[i], &pTmp->rclSprite, &rclTmp)) { OFFCOPYBITS(pOffDst, psoDst, &pTmp->OffUnderlay, pTmp->psoUnderlay, NULL, NULL, &rclTmp, (POINTL*) &rclTmp); } } while (++i != crcl); }
pTmp = pSpFindInZ(pTmp->pNextZ, prclNew); } }
/******************************Public*Routine******************************\
* VOID vSpBigUnderlayCopy * * Reads a (future underlay) buffer from the screen, automatically excluding * any sprites that may be in the way. * * This routine will be slow if the area is small, there are a large number * of sprites, and ENUMAREAS forces the sprite ranges to be re-calculated; * it will be fast if the buffer to be read is large. * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
VOID vSpBigUnderlayCopy( SPRITESTATE* pState, POINTL* pOffUnderlay, SURFOBJ* psoUnderlay, RECTL* prcl) { SPRITE* pSprite; RECTL rclEnum; BOOL bMore;
// There's nothing to redraw if we're full-screen:
PDEVOBJ po(pState->hdev); if (po.bDisabled()) return;
ASSERTGDI(pState->bInsideDriverCall, "SPRITELOCK should be held");
ENUMAREAS Enum(pState, prcl);
do { bMore = Enum.bEnum(&pSprite, &rclEnum);
if (pSprite == NULL) { vSpReadFromScreen(pState, pOffUnderlay, psoUnderlay, &rclEnum); } else { OFFCOPYBITS(pOffUnderlay, psoUnderlay, &pSprite->OffUnderlay, pSprite->psoUnderlay, NULL, NULL, &rclEnum, (POINTL*) &rclEnum); } } while (bMore); }
/******************************Public*Routine******************************\
* VOID vSpUpdateLockedScreenAreas * * When a screen-to-screen blt occurs, the screen-to-screen blt may occur * under an area of the screen that is both locked by DirectDraw and covered * by a sprite. Normally, we never update the locked area under the sprite, * but in this case we do, to reflect the moved contents. * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
VOID vSpUpdateLockedScreenAreas( SPRITESTATE* pState, POINTL* pOffDst, RECTL* prclDst, CLIPOBJ* pco, BOOL bReadFromScreen) { SPRITE* pSprite; BOOL bMore; RECTL rclEnum; RECTL rclArea; REGION* prgnIntersect; ECLIPOBJ ecoIntersect; RGNOBJ* proClip; RGNMEMOBJTMP rmoIntersect; RGNMEMOBJTMP rmoScreen;
ASSERTGDI(pState->prgnUnlocked != NULL, "Should really be called only when a DirectDraw lock is extant");
PDEVOBJ po(pState->hdev); SPRITELOCK slock(po);
prgnIntersect = NULL;
if (rmoIntersect.bValid() && rmoScreen.bValid() && bIntersect(prclDst, &pState->rclScreen, &rclArea)) { ENUMAREAS Enum(pState, &rclArea);
do { bMore = Enum.bEnum(&pSprite, &rclEnum);
if (pSprite != NULL) { if (prgnIntersect == NULL) { proClip = (ECLIPOBJ*) pco; if ((pco == NULL) || (pco->iDComplexity == DC_TRIVIAL)) { rmoScreen.vSet(&pState->rclScreen); proClip = &rmoScreen; }
// Intersect the clip object supplied by the drawing
// routine with the not of region representing the
// 'unlocked' portions of the screen.
RGNOBJ roUnlocked(pState->prgnUnlocked); if (!rmoIntersect.bMerge(*proClip, roUnlocked, gafjRgnOp[RGN_DIFF])) { rmoIntersect.vSet(); } prgnIntersect = rmoIntersect.prgnGet(); }
ecoIntersect.vSetup(prgnIntersect, *(ERECTL*) &rclEnum); if (!ecoIntersect.erclExclude().bEmpty()) { do { if (bReadFromScreen) { OFFCOPYBITS(&pSprite->OffUnderlay, pSprite->psoUnderlay, pOffDst, pState->psoScreen, &ecoIntersect, NULL, &rclEnum, (POINTL*) &rclEnum); } else { OFFCOPYBITS(pOffDst, pState->psoScreen, &pSprite->OffUnderlay, pSprite->psoUnderlay, &ecoIntersect, NULL, &rclEnum, (POINTL*) &rclEnum); } } while ((bReadFromScreen) && (Enum.bEnumLayers(&pSprite))); } } } while (bMore); } }
/******************************Public*Routine******************************\
* ENUMUNDERLAYS::ENUMUNDERLAYS * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
ENUMUNDERLAYS::ENUMUNDERLAYS( SURFOBJ* pso, CLIPOBJ* pco, RECTL* prclDraw) { PDEVOBJ po(pso->hdev); BOOL bRclNotEmpty = TRUE;
bDone = FALSE; bSpriteTouched = FALSE; bResetSurfFlag = FALSE; pCurrentSprite = NULL; pcoOriginal = pco; psoOriginal = pso;
// This extra 'if' was added for GetPixel:
if (po.bValid()) { pState = po.pSpriteState();
if ((pso == pState->psoScreen) && !(pState->bInsideDriverCall)) { ASSERTGDI(!po.bDisabled(), "Mustn't be called when PDEV disabled"); ASSERTGDI((pState->flOriginalSurfFlags & ~HOOK_FLAGS) == (SURFOBJ_TO_SURFACE(pso)->SurfFlags & ~HOOK_FLAGS), "SurfFlags was modified, but we're about to save over it!");
SURFOBJ_TO_SURFACE_NOT_NULL(pso)->flags(pState->flOriginalSurfFlags); SURFOBJ_TO_SURFACE_NOT_NULL(pso)->iType(pState->iOriginalType); pState->bInsideDriverCall = TRUE; bResetSurfFlag = TRUE;
if ((pco == NULL) || (pco->iDComplexity == DC_TRIVIAL)) { rclBounds = *prclDraw; pcoClip = &pState->coRectangular; } else { rclSaveBounds = pco->rclBounds; bRclNotEmpty = bIntersect(prclDraw, &pco->rclBounds, &rclBounds); pcoClip = pco; }
// Find intersecting sprite if we have non-empty bounds;
// otherwise, leave pCurrentSprite = NULL.
if (bRclNotEmpty) { pCurrentSprite = pSpFindInZ(pState->pListZ, &rclBounds); } } } }
/******************************Public*Routine******************************\
* ENUMUNDERLAYS::bEnum * * This routine is a helper function for drawing primitives that handles * the drawing of the primitive to various sprite underlay and screen * surfaces. It takes the form of an enumeration function in order to * return back the appropriate surface and clip objects that should be * drawn on by the caller. * * Returns: TRUE if the caller should draw using the returned values; * FALSE if no more drawing needs to be done. * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
BOOL ENUMUNDERLAYS::bEnum( SURFOBJ** ppso, POINTL* pOff, CLIPOBJ** ppco) { RECTL rclTmp;
// We first draw to the underlay surfaces of any sprites that
// intersect with our area of interest. See if there is
// another sprite that intersects, and if so return that:
while (pCurrentSprite != NULL) { if (bIntersect(&pCurrentSprite->rclSprite, &rclBounds, &rclTmp)) { ASSERTGDI(pcoClip->iDComplexity != DC_TRIVIAL, "Expected non-trivial clipping when drawing to sprite");
bSpriteTouched = TRUE; pcoClip->rclBounds = rclTmp;
*ppco = pcoClip; *ppso = pCurrentSprite->psoUnderlay; *pOff = pCurrentSprite->OffUnderlay;
pCurrentSprite = pCurrentSprite->pNextZ; return(TRUE); }
pCurrentSprite = pSpFindInZ(pCurrentSprite->pNextZ, &rclBounds); }
if (!bDone) { if (!bSpriteTouched) { // No sprites intersected with the region of drawing, so we
// can simply return back the original clipping and surface
// object:
bDone = TRUE; *ppco = pcoOriginal; *ppso = psoOriginal; pOff->x = 0; pOff->y = 0; return(TRUE); }
// Okay, at this point we have work to do. We have to 'cut out'
// of the original clip objects any areas that were covered
// by the sprites.
if (!pState->bValidRange) { vSpComputeSpriteRanges(pState); }
if (pcoClip->iDComplexity != DC_COMPLEX) { // Oh good, we can use our already-constructed complex region
// describing the uncovered parts of the screen.
//
// Use CLIP_FORCE to ensure that 'rclBounds' is always respected
// as the bounds, so that we don't revert to DC_TRIVIAL clipping
// when in fact the original call came in clipped:
pState->coTmp.vSetup(pState->prgnUncovered, *((ERECTL*)&rclBounds), CLIP_FORCE); } else { // Okay, we've got some work to do. We have to subtract each of
// the sprite rectangles from the drawing call's clip object.
RGNOBJ roUncovered(pState->prgnUncovered); RGNOBJ roTmp(pState->prgnTmp);
// Find the intersection of the two regions. If the bMerge fails,
// we'll draw wrong, but we won't crash.
if (!roTmp.bMerge(*((XCLIPOBJ*)pcoClip), roUncovered, gafjRgnOp[RGN_AND])) { roTmp.vSet(); }
// Compute the resulting clip object:
pState->prgnTmp = roTmp.prgnGet(); pState->coTmp.vSetup(pState->prgnTmp, *((ERECTL*)&rclBounds), CLIP_FORCE); }
// Note that this 'bIntersect' test handles the empty case for
// 'coTmp.rclBounds':
if (bIntersect(&pState->coTmp.rclBounds, &rclBounds)) { ASSERTGDI((pState->prgnUnlocked != NULL) || (pState->coTmp.iDComplexity != DC_TRIVIAL), "If sprite was touched, expect clipping on uncovered portion!");
bDone = TRUE; *ppco = &pState->coTmp; *ppso = psoOriginal; pOff->x = 0; pOff->y = 0; return(TRUE); } }
// We're all done, update the screen underneath the sprites if need be:
if (bResetSurfFlag) { if (bSpriteTouched) { // One problem that is visible with sprites, particularly when
// using a software cursor, is that when drawing a single
// primitive, there is sometimes a noticeable temporal gap between
// the time that the area around the sprite is drawn, and the area
// underneath the sprite is updated.
//
// I used to update the area underneath the sprite before
// doing the drawing that occurs around the sprite, reasoning that
// people tend to be looking exactly at the cursor sprite and would
// benefit most from that area being updated as soon as possible,
// before the surrounding area.
//
// But I've changed my mind, and think the opposite is better.
// This is partly because drawing operations with complex clipping
// often need longer setup time than it does to update the sprite,
// so the time difference between the two is lessened if the
// sprite is updated *after* the complex clipped drawing is done.
//
// (I'll add that I also tried updating in chunks from top to
// bottom, but the resulting tearing was far worse.)
vSpRedrawArea(pState, &rclBounds); }
ASSERTGDI((pState->flOriginalSurfFlags & ~HOOK_FLAGS) == (SURFOBJ_TO_SURFACE(psoOriginal)->SurfFlags & ~HOOK_FLAGS), "SurfFlags was modified, but we're about to restore over it!");
SURFOBJ_TO_SURFACE_NOT_NULL(psoOriginal)->flags(pState->flSpriteSurfFlags); SURFOBJ_TO_SURFACE_NOT_NULL(psoOriginal)->iType(pState->iSpriteType); pState->bInsideDriverCall = FALSE;
// This restores the original clip object's bounds if need be (or it
// overwrites 'coRectangular.rclBounds', which is also fine):
pcoClip->rclBounds = rclSaveBounds; }
return(FALSE); }
/******************************Public*Routine******************************\
* BOOL SpStrokePath * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
BOOL SpStrokePath( SURFOBJ* pso, PATHOBJ* ppo, CLIPOBJ* pco, XFORMOBJ* pxlo, BRUSHOBJ* pbo, POINTL* pptlBrush, LINEATTRS* pla, MIX mix) { BOOL bRet = TRUE; BOOL bThis; FLOAT_LONG elStyleState; POINTL Offset;
ASSERTGDI(pco != NULL, "Drivers always expect non-NULL pco's for this call");
elStyleState = pla->elStyleState;
ENUMUNDERLAYS Enum(pso, pco, &pco->rclBounds); while (Enum.bEnum(&pso, &Offset, &pco)) { // We have to reset the style state every time we call StrokePath,
// otherwise it incorrectly accumulates. Also reset the path enumeration.
pla->elStyleState = elStyleState; PATHOBJ_vEnumStart(ppo);
bThis = OffStrokePath(PPFNDIRECT(pso, StrokePath), &Offset, pso, ppo, pco, pxlo, pbo, pptlBrush, pla, mix);
// Drivers can return TRUE, FALSE, or DDI_ERROR from this call. We
// should have caught any FALSE cases lower down, so permute them
// to failures should they have actually occurred:
if (!bThis) bRet = DDI_ERROR; }
return(bRet); }
/******************************Public*Routine******************************\
* BOOL SpFillPath * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
BOOL SpFillPath( SURFOBJ* pso, PATHOBJ* ppo, CLIPOBJ* pco, BRUSHOBJ* pbo, POINTL* pptlBrush, MIX mix, FLONG flOptions) { BOOL bRet = TRUE; BOOL bThis; POINTL Offset;
ASSERTGDI(pco != NULL, "Drivers always expect non-NULL pco's for this call");
ENUMUNDERLAYS Enum(pso, pco, &pco->rclBounds); while (Enum.bEnum(&pso, &Offset, &pco)) { // Reset the path enumeration
PATHOBJ_vEnumStart(ppo);
bThis = OffFillPath(PPFNDIRECT(pso, FillPath), &Offset, pso, ppo, pco, pbo, pptlBrush, mix, flOptions);
// Drivers can return TRUE, FALSE, or DDI_ERROR from this call. We
// should have caught any FALSE cases lower down, so permute them
// to failures should they have actually occurred:
if (!bThis) bRet = DDI_ERROR; }
return(bRet); }
/******************************Public*Routine******************************\
* BOOL bSpBltScreenToScreen * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
BOOL bSpBltScreenToScreen( SURFOBJ* pso, SURFOBJ* psoIgnore, SURFOBJ* psoMsk, CLIPOBJ* pco, XLATEOBJ* pxlo, RECTL* prclDst, POINTL* pptlSrc, POINTL* pptlMsk, BRUSHOBJ* pbo, POINTL* pptlBrush, ROP4 rop4) { SPRITESTATE* pState; LONG dx; LONG dy; ULONG iDirection; POINTL Offset; RECTL rclIntersect; RECTL rclSrc; RECTL rclDst; RECTL rclSubSrc; RECTL rclSubDst; POINTL ptlSubMsk; POINTL ptlPreviousDst; SPRITE* pSpriteDst; SPRITE* pSpriteSrc; SPRITE* pSpriteTmp; SURFOBJ* psoSrc; SURFOBJ* psoDst; RECTL rclBounds; BOOL bMore; BOOL bSubMore; POINTL* pOffDst; POINTL* pOffSrc;
PDEVOBJ po(pso->hdev);
pState = po.pSpriteState();
// Restore the surface's original attributes.
SPRITELOCK slock(po);
dx = prclDst->left - pptlSrc->x; dy = prclDst->top - pptlSrc->y;
if (dx > 0) iDirection = (dy > 0) ? CD_LEFTUP : CD_LEFTDOWN; else iDirection = (dy > 0) ? CD_RIGHTUP : CD_RIGHTDOWN;
if (pco != NULL) { if (pco->iDComplexity == DC_TRIVIAL) pco = NULL; else rclBounds = pco->rclBounds; }
// If parts of the screen are locked by DirectDraw, update our underlay
// buffers for the portions covered by both the source rectangle and
// the destination rectangle and intersect with the locked portion of
// of the screen. (This case handles a screen-to-screen blt where
// a DirectDraw lock sits right in the middle of both.)
if (pState->prgnUnlocked != NULL) { rclSrc.left = prclDst->left - dx; rclSrc.right = prclDst->right - dx; rclSrc.top = prclDst->top - dy; rclSrc.bottom = prclDst->bottom - dy;
if (bIntersect(prclDst, &rclSrc, &rclIntersect)) { vSpUpdateLockedScreenAreas(pState, &gptlZero, &rclIntersect, pco, TRUE); } }
ENUMAREAS Enum(pState, prclDst, iDirection);
// Only check ENUMAREAS::bValid because of the nested ENUMAREAS call below
// (in the failure case, proceeding is bad because the nested call might
// succeed in recomputing the sprite ranges and leave us with bad
// pointers).
if (Enum.bValid()) { do { bMore = Enum.bEnum(&pSpriteDst, &rclDst);
rclSrc.left = rclDst.left - dx; rclSrc.right = rclDst.right - dx; rclSrc.top = rclDst.top - dy; rclSrc.bottom = rclDst.bottom - dy;
// For rectangles that intersect multiple sprites, I had thought
// about simply composing the one overlay for that rectangle, and
// then copying that to the others. The problem I saw with that
// was that if that 'master' underlay is in video memory, but the
// rest are in system memory, that will actually be slower.
do { if (pSpriteDst == NULL) { psoDst = pso; pOffDst = &gptlZero; } else { psoDst = pSpriteDst->psoUnderlay; pOffDst = &pSpriteDst->OffUnderlay; }
ENUMAREAS SubEnum(pState, &rclSrc, iDirection);
do { bSubMore = SubEnum.bEnum(&pSpriteSrc, &rclSubSrc);
// We have to be sure to use as the source the last layered
// underlay in the list, because we may be traversing
// through the same list to find the destination surfaces --
// and we don't want to modify our source before we've copied
// it to all the other surfaces! This bug took me an
// embarrassingly long time to fix, because it would only
// mis-draw when screen-to-screen blts occurred under
// actively moving sprites that overlayed each other.
while (SubEnum.bEnumLayers(&pSpriteTmp)) { pSpriteSrc = pSpriteTmp; }
if (pSpriteSrc == NULL) { psoSrc = pso; pOffSrc = &gptlZero; } else { psoSrc = pSpriteSrc->psoUnderlay; pOffSrc = &pSpriteSrc->OffUnderlay; }
rclSubDst.left = rclSubSrc.left + dx; rclSubDst.right = rclSubSrc.right + dx; rclSubDst.top = rclSubSrc.top + dy; rclSubDst.bottom = rclSubSrc.bottom + dy;
ASSERTGDI((rclSubDst.left >= pState->rclScreen.left) && (rclSubDst.top >= pState->rclScreen.top) && (rclSubDst.right <= pState->rclScreen.right) && (rclSubDst.bottom <= pState->rclScreen.bottom), "Enumerated area out of bounds");
// We must ensure that for DrvBitBlt, pco->rclBounds intersects
// with prclDst, otherwise drivers like the VGA will crash in
// their DC_COMPLEX code because they don't check for
// intersection:
if ((pco == NULL) || bIntersect(&rclSubDst, &rclBounds, &pco->rclBounds)) { if (rop4 == 0xcccc) { OFFCOPYBITS(pOffDst, psoDst, pOffSrc, psoSrc, pco, pxlo, &rclSubDst, (POINTL*) &rclSubSrc); } else { if (pptlMsk) { ptlSubMsk.x = pptlMsk->x + (rclSubDst.left - prclDst->left); ptlSubMsk.y = pptlMsk->y + (rclSubDst.top - prclDst->top); }
OFFBITBLT(pOffDst, psoDst, pOffSrc, psoSrc, psoMsk, pco, pxlo, &rclSubDst, (POINTL*) &rclSubSrc, &ptlSubMsk, pbo, pptlBrush, rop4); } } } while (bSubMore); } while (Enum.bEnumLayers(&pSpriteDst));
// We recomposite here as we do every sprite, instead of one big
// 'vSpRedrawArea(pState, prclDst)' at the end, to reduce the visible
// tearing:
if (pSpriteDst != NULL) vSpRedrawArea(pState, &rclDst);
} while (bMore); }
// Restore the clip object's original bounds, which we mucked with:
if (pco != NULL) pco->rclBounds = rclBounds;
// If parts of the screen are locked by DirectDraw, we may also have to
// update the area in the locked area, which we didn't do above:
if (pState->prgnUnlocked != NULL) vSpUpdateLockedScreenAreas(pState, &gptlZero, prclDst, pco, FALSE);
return(TRUE); }
/******************************Public*Routine******************************\
* BOOL bSpBltFromScreen * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
BOOL bSpBltFromScreen( SURFOBJ* psoDst, // Might be a sprite underlay surface, used
// by vSpUpdate for moving a sprite
SURFOBJ* psoSrc, // Always the primary screen
SURFOBJ* psoMsk, CLIPOBJ* pco, XLATEOBJ* pxlo, RECTL* prclDst, POINTL* pptlSrc, POINTL* pptlMsk, BRUSHOBJ* pbo, POINTL* pptlBrush, ROP4 rop4) { SPRITESTATE* pState; LONG dx; LONG dy; POINTL Offset; ULONG iDirection; RECTL rclSrc; RECTL rclDst; POINTL ptlMsk; SPRITE* pSpriteSrc; RECTL* prclBounds; BYTE iDComplexity; RECTL rclBounds; BOOL bMore; POINTL* pOffSrc;
PDEVOBJ po(psoSrc->hdev);
pState = po.pSpriteState();
// To allow software cursors to work, we have to respect the
// 'bInsideDriverCall' flag to allow the simulations to read
// directly from the screen. We also have to be sure that
// CopyBits calls get sent to CopyBits, otherwise the VGA
// driver (and possibly others) will crash.
// Also, if the 'IncludeSprites' flag is set in the destination surface,
// this is a signal GreStretchBltInternal that we should do a direct blt.
// (i.e. the ROP passed to StretchBlt/BitBlt had the CAPTUREBLT flag
// set.) [Bug #278291]
if ((pState->bInsideDriverCall) || (SURFOBJ_TO_SURFACE(psoDst)->bIncludeSprites())) { // Grab the sprite lock (needed for the CAPTUREBLT case).
SPRITELOCK slock(po);
if (rop4 == 0xcccc) { return(OFFCOPYBITS(&gptlZero, psoDst, &gptlZero, psoSrc, pco, pxlo, prclDst, pptlSrc)); } else { return(OFFBITBLT(&gptlZero, psoDst, &gptlZero, psoSrc, psoMsk, pco, pxlo, prclDst, pptlSrc, pptlMsk, pbo, pptlBrush, rop4)); } }
// Restore the surface's original attributes.
SPRITELOCK slock(po);
dx = prclDst->left - pptlSrc->x; dy = prclDst->top - pptlSrc->y;
rclSrc.left = pptlSrc->x; rclSrc.top = pptlSrc->y; rclSrc.right = prclDst->right - dx; rclSrc.bottom = prclDst->bottom - dy;
// If parts of the screen are locked by DirectDraw, update our underlay
// buffers for the portion read by the blt:
if (pState->prgnUnlocked != NULL) { Offset.x = -dx; Offset.y = -dy;
vSpUpdateLockedScreenAreas(pState, &Offset, prclDst, pco, TRUE); }
// We have to enumerate the rectangles according to the blt direction
// because vSpUpdate may call us with a sprite underlay surface as
// 'psoDst' in order to move a sprite.
if (dx > 0) iDirection = (dy > 0) ? CD_LEFTUP : CD_LEFTDOWN; else iDirection = (dy > 0) ? CD_RIGHTUP : CD_RIGHTDOWN;
iDComplexity = (pco == NULL) ? DC_TRIVIAL : pco->iDComplexity; if (iDComplexity != DC_TRIVIAL) rclBounds = pco->rclBounds;
ENUMAREAS Enum(pState, &rclSrc, iDirection);
do { bMore = Enum.bEnum(&pSpriteSrc, &rclSrc);
rclDst.left = rclSrc.left + dx; rclDst.right = rclSrc.right + dx; rclDst.top = rclSrc.top + dy; rclDst.bottom = rclSrc.bottom + dy;
// We must ensure that for DrvBitBlt, pco->rclBounds intersects
// with prclDst, otherwise drivers like the VGA will crash in
// their DC_COMPLEX code because they don't check for
// intersection:
if ((iDComplexity == DC_TRIVIAL) || bIntersect(&rclDst, &rclBounds, &pco->rclBounds)) { if (pSpriteSrc == NULL) { psoSrc = pState->psoScreen; pOffSrc = &gptlZero; } else { psoSrc = pSpriteSrc->psoUnderlay; pOffSrc = &pSpriteSrc->OffUnderlay; }
if (rop4 == 0xcccc) { OFFCOPYBITS(&gptlZero, psoDst, pOffSrc, psoSrc, pco, pxlo, &rclDst, (POINTL*) &rclSrc); } else { if (pptlMsk) { ptlMsk.x = pptlMsk->x + (rclDst.left - prclDst->left); ptlMsk.y = pptlMsk->y + (rclDst.top - prclDst->top); }
OFFBITBLT(&gptlZero, psoDst, pOffSrc, psoSrc, psoMsk, pco, pxlo, &rclDst, (POINTL*) &rclSrc, &ptlMsk, pbo, pptlBrush, rop4); } } } while (bMore);
// Restore the clip object's original bounds, which we mucked with:
if (iDComplexity != DC_TRIVIAL) pco->rclBounds = rclBounds;
return(TRUE); }
/******************************Public*Routine******************************\
* BOOL SpBitBlt * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
BOOL SpBitBlt( SURFOBJ* psoDst, SURFOBJ* psoSrc, SURFOBJ* psoMsk, CLIPOBJ* pco, XLATEOBJ* pxlo, RECTL* prclDst, POINTL* pptlSrc, POINTL* pptlMsk, BRUSHOBJ* pbo, POINTL* pptlBrush, ROP4 rop4) { SPRITESTATE* pState; PFN_DrvBitBlt pfnBitBlt; RECTL rclDstOriginal; POINTL OffDst; RECTL rclDst; POINTL ptlSrc; POINTL ptlMsk; LONG dx; LONG dy; BOOL bRet = TRUE;
PDEVOBJ poSrc((psoSrc != NULL) ? psoSrc->hdev : NULL);
// Handle the hard cases with specific routines:
if ((poSrc.bValid()) && (psoSrc == poSrc.pSpriteState()->psoScreen)) { pfnBitBlt = (psoDst == psoSrc) ? bSpBltScreenToScreen : bSpBltFromScreen;
bRet = pfnBitBlt(psoDst, psoSrc, psoMsk, pco, pxlo, prclDst, pptlSrc, pptlMsk, pbo, pptlBrush, rop4); } else { // Watch out for calls that point 'prclDst' to 'pco->rclBounds', which
// we'll be modifying:
rclDstOriginal = *prclDst;
ENUMUNDERLAYS Enum(psoDst, pco, prclDst); while (Enum.bEnum(&psoDst, &OffDst, &pco)) { if (rop4 == 0xcccc) { bRet &= OFFCOPYBITS(&OffDst, psoDst, &gptlZero, psoSrc, pco, pxlo, &rclDstOriginal, pptlSrc); } else if ((rop4 & 0xff) == (rop4 >> 8)) { bRet &= OFFBITBLT(&OffDst, psoDst, &gptlZero, psoSrc, psoMsk, pco, pxlo, &rclDstOriginal, pptlSrc, pptlMsk, pbo, pptlBrush, rop4); } else { // A lot of drivers don't properly handle adjusting 'pptlMsk'
// in their DrvBitBlt 'punt' code when *prclDst is larger than
// pco->rclBounds. So what we'll do here is muck with the
// parameters to ensure that *prclDst is not larger than
// pco->rclBounds.
//
// Note that this problem does not typically appear without
// sprites, as NtGdiMaskBlt automatically does this before
// calling DrvBitBlt. It's only our ENUMUNDERLAYS code above
// which generates this sort of clipping.
rclDst = rclDstOriginal; if ((!pco) || // If pco is NULL, don't call bIntersect
bIntersect(&pco->rclBounds, &rclDstOriginal, &rclDst)) { dx = rclDst.left - rclDstOriginal.left; dy = rclDst.top - rclDstOriginal.top;
POINTL* ptlSrcAdjusted = NULL;
if (pptlSrc) { ptlSrc.x = pptlSrc->x + dx; ptlSrc.y = pptlSrc->y + dy; ptlSrcAdjusted = &ptlSrc; }
POINTL* ptlMskAdjusted = NULL;
if (pptlMsk) { ptlMsk.x = pptlMsk->x + dx; ptlMsk.y = pptlMsk->y + dy; ptlMskAdjusted = &ptlMsk; }
bRet &= OFFBITBLT(&OffDst, psoDst, &gptlZero, psoSrc, psoMsk, pco, pxlo, &rclDst, ptlSrcAdjusted, ptlMskAdjusted, pbo, pptlBrush, rop4); } } } }
return(bRet); }
/******************************Public*Routine******************************\
* BOOL SpCopyBits * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
BOOL SpCopyBits( SURFOBJ* psoDst, SURFOBJ* psoSrc, CLIPOBJ* pco, XLATEOBJ* pxlo, RECTL* prclDst, POINTL* pptlSrc) { return(SpBitBlt(psoDst, psoSrc, NULL, pco, pxlo, prclDst, pptlSrc, NULL, NULL, NULL, 0xcccc)); }
/******************************Public*Routine******************************\
* BOOL SpStretchBlt * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
BOOL SpStretchBlt( // Is this call really needed?
SURFOBJ* psoDst, SURFOBJ* psoSrc, SURFOBJ* psoMsk, CLIPOBJ* pco, XLATEOBJ* pxlo, COLORADJUSTMENT* pca, POINTL* pptlHTOrg, RECTL* prclDst, RECTL* prclSrc, POINTL* pptlMsk, ULONG iMode) { BOOL bRet = TRUE; ERECTL erclDraw; POINTL Offset;
// The source rectangle should not exceed the bounds
// of the surface - that should have been handled by GDI.
// (bug 77102 - see EngStretchBlt)
ASSERTGDI((prclSrc->left >= 0) && (prclSrc->top >= 0) && (prclSrc->right <= psoSrc->sizlBitmap.cx) && (prclSrc->bottom <= psoSrc->sizlBitmap.cy), "Source rectangle exceeds source surface");
// I can't be bothered to handle the code that Stretchblt's when the
// source is the screen. To handle those cases, we would have to
// exclude all the sprites on the read, just as bSpBltFromScreen does.
// Fortunately, we've marked the screen surface as STYPE_DEVICE and
// so the Eng function will punt via SpCopyBits, which does do the
// right thing:
PDEVOBJ poSrc(psoSrc->hdev); if ((poSrc.bValid()) && (poSrc.pSpriteState()->psoScreen == psoSrc)) { return(EngStretchBlt(psoDst, psoSrc, psoMsk, pco, pxlo, pca, pptlHTOrg, prclDst, prclSrc, pptlMsk, iMode)); }
// The destination rectangle on a stretch will be poorly ordered when
// inverting or mirroring:
erclDraw = *prclDst; erclDraw.vOrder();
ENUMUNDERLAYS Enum(psoDst, pco, &erclDraw); while (Enum.bEnum(&psoDst, &Offset, &pco)) { bRet &= OffStretchBlt(PPFNDIRECT(psoDst, StretchBlt), &Offset, psoDst, &gptlZero, psoSrc, psoMsk, pco, pxlo, pca, pptlHTOrg, prclDst, prclSrc, pptlMsk, iMode); }
return(bRet); }
/******************************Public*Routine******************************\
* BOOL SpStretchBltROP * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
BOOL SpStretchBltROP( SURFOBJ* psoDst, SURFOBJ* psoSrc, SURFOBJ* psoMsk, CLIPOBJ* pco, XLATEOBJ* pxlo, COLORADJUSTMENT* pca, POINTL* pptlHTOrg, RECTL* prclDst, RECTL* prclSrc, POINTL* pptlMsk, ULONG iMode, BRUSHOBJ* pbo, DWORD rop4) { BOOL bRet = TRUE; ERECTL erclDraw; POINTL Offset;
// The source rectangle should not exceed the bounds
// of the surface - that should have been handled by GDI.
// (bug 77102 - see EngStretchBlt)
ASSERTGDI((prclSrc->left >= 0) && (prclSrc->top >= 0) && (prclSrc->right <= psoSrc->sizlBitmap.cx) && (prclSrc->bottom <= psoSrc->sizlBitmap.cy), "Source rectangle exceeds source surface");
// I can't be bothered to handle the code that Stretchblt's when the
// source is the screen. To handle those cases, we would have to
// exclude all the sprites on the read, just as bSpBltFromScreen does.
// Fortunately, we've marked the screen surface as STYPE_DEVICE and
// so the Eng function will punt via SpCopyBits, which does do the
// right thing:
PDEVOBJ poSrc(psoSrc->hdev); if ((poSrc.bValid()) && (poSrc.pSpriteState()->psoScreen == psoSrc)) { return(EngStretchBltROP(psoDst, psoSrc, psoMsk, pco, pxlo, pca, pptlHTOrg, prclDst, prclSrc, pptlMsk, iMode, pbo, rop4)); } // The destination rectangle on a stretch will be poorly ordered when
// inverting or mirroring:
erclDraw = *prclDst; erclDraw.vOrder();
ENUMUNDERLAYS Enum(psoDst, pco, &erclDraw); while (Enum.bEnum(&psoDst, &Offset, &pco)) { bRet &= OffStretchBltROP(PPFNDIRECT(psoDst, StretchBltROP), &Offset, psoDst, &gptlZero, psoSrc, psoMsk, pco, pxlo, pca, pptlHTOrg, prclDst, prclSrc, pptlMsk, iMode, pbo, rop4); }
return(bRet); }
/******************************Public*Routine******************************\
* BOOL SpTextOut * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
BOOL SpTextOut( SURFOBJ* pso, STROBJ* pstro, FONTOBJ* pfo, CLIPOBJ* pco, RECTL* prclExtra, RECTL* prclOpaque, BRUSHOBJ* pboFore, BRUSHOBJ* pboOpaque, POINTL* pptlOrg, MIX mix) { BOOL bRet = TRUE; RECTL* prclBounds; POINTL Offset; ULONG cgposCopied; BOOL bEngTextOutOnly; PFN_DrvTextOut pfnTextOut;
// Some drivers can't handle antialiased text, so in those cases don't
// try to call the driver. Note that we are calling 'SpTextOut' in the
// first place for these cases to allow 'EngTextOut' to draw directly on
// the bits if the surface is an STYPE_BITMAP surface, even if a sprite
// is on the screen.
bEngTextOutOnly = FALSE; if (pfo->flFontType & FO_GRAY16) { PDEVOBJ po(pso->hdev); if (!(po.flGraphicsCapsNotDynamic() & GCAPS_GRAY16) || (pfo->flFontType & FO_CLEARTYPE_X)) { bEngTextOutOnly = TRUE; } }
cgposCopied = ((ESTROBJ*)pstro)->cgposCopied;
prclBounds = (prclOpaque != NULL) ? prclOpaque : &pstro->rclBkGround;
ENUMUNDERLAYS Enum(pso, pco, prclBounds); while (Enum.bEnum(&pso, &Offset, &pco)) { ((ESTROBJ*)pstro)->cgposCopied = cgposCopied;
pfnTextOut = (bEngTextOutOnly) ? EngTextOut : PPFNDIRECT(pso, TextOut);
bRet &= OffTextOut(pfnTextOut, &Offset, pso, pstro, pfo, pco, prclExtra, prclOpaque, pboFore, pboOpaque, pptlOrg, mix); }
return(bRet); }
/******************************Public*Routine******************************\
* BOOL SpLineTo * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
BOOL SpLineTo( SURFOBJ *pso, CLIPOBJ *pco, BRUSHOBJ *pbo, LONG x1, LONG y1, LONG x2, LONG y2, RECTL *prclBounds, MIX mix) { BOOL bRet = TRUE; POINTL Offset;
ENUMUNDERLAYS Enum(pso, pco, prclBounds); while (Enum.bEnum(&pso, &Offset, &pco)) { bRet &= OffLineTo(PPFNDIRECT(pso, LineTo), &Offset, pso, pco, pbo, x1, y1, x2, y2, prclBounds, mix); }
return(bRet); }
/******************************Public*Routine******************************\
* BOOL SpDrawStream * * 1-27-2001 bhouse * Wrote it. \**************************************************************************/
BOOL SpDrawStream( SURFOBJ* psoDst, SURFOBJ* psoSrc, CLIPOBJ* pco, XLATEOBJ* pxlo, PRECTL prclDstBounds, PPOINTL pptlDstOffset, ULONG ulIn, PVOID pvIn, DSSTATE* pdss) { BOOL bRet = TRUE; POINTL Offset;
// DbgPrint("SpDrawStream entered\n");
// source can't be the screen
PDEVOBJ poSrc(psoSrc->hdev); if ((poSrc.bValid()) && (poSrc.pSpriteState()->psoScreen == psoSrc)) { DbgPrint("SpDrawStream: source is the screen, this should never happen\n"); return bRet; }
// The source is not the screen. Only need to worry about enumerating
// the sprites overlaying the destination.
ENUMUNDERLAYS Enum(psoDst, pco, prclDstBounds);
while (Enum.bEnum(&psoDst, &Offset, &pco)) { bRet &= OffDrawStream(PPFNDIRECTENG(psoDst, DrawStream), &Offset, psoDst, psoSrc, pco, pxlo, prclDstBounds, pptlDstOffset, ulIn, pvIn, pdss); }
// DbgPrint("SpDrawStream returning\n");
return(bRet); }
/******************************Public*Routine******************************\
* BOOL SpTransparentBlt * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
BOOL SpTransparentBlt( SURFOBJ *psoDst, SURFOBJ *psoSrc, CLIPOBJ *pco, XLATEOBJ *pxlo, RECTL *prclDst, RECTL *prclSrc, ULONG iTransColor, ULONG ulReserved) { BOOL bRet = TRUE; POINTL Offset;
// I can't be bothered to handle the code that TransparentBlt's when the
// source is the screen. To handle those cases, we would have to
// exclude all the sprites on the read, just as bSpBltFromScreen does.
// Fortunately, we've marked the screen surface as STYPE_DEVICE and
// so the Eng function will punt via SpCopyBits, which does do the
// right thing:
PDEVOBJ poSrc(psoSrc->hdev); if ((poSrc.bValid()) && (poSrc.pSpriteState()->psoScreen == psoSrc)) { return(EngTransparentBlt(psoDst, psoSrc, pco, pxlo, prclDst, prclSrc, iTransColor, ulReserved)); }
// The source is not the screen. Only need to worry about enumerating
// the sprites overlaying the destination.
ENUMUNDERLAYS Enum(psoDst, pco, prclDst); while (Enum.bEnum(&psoDst, &Offset, &pco)) { bRet &= OffTransparentBlt(PPFNDIRECT(psoDst, TransparentBlt), &Offset, psoDst, &gptlZero, psoSrc, pco, pxlo, prclDst, prclSrc, iTransColor, ulReserved); }
return(bRet); }
/******************************Public*Routine******************************\
* BOOL SpAlphaBlend * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
BOOL SpAlphaBlend( SURFOBJ *psoDst, SURFOBJ *psoSrc, CLIPOBJ *pco, XLATEOBJ *pxlo, RECTL *prclDst, RECTL *prclSrc, BLENDOBJ *pBlendObj) { BOOL bRet = TRUE; POINTL Offset;
// I can't be bothered to handle the code that AlphaBlend's when the
// source is the screen. To handle those cases, we would have to
// exclude all the sprites on the read, just as bSpBltFromScreen does.
// Fortunately, we've marked the screen surface as STYPE_DEVICE and
// so the Eng function will punt via SpCopyBits, which does do the
// right thing:
PDEVOBJ poSrc(psoSrc->hdev); if ((poSrc.bValid()) && (poSrc.pSpriteState()->psoScreen == psoSrc)) { return(EngAlphaBlend(psoDst, psoSrc, pco, pxlo, prclDst, prclSrc, pBlendObj)); }
// The source is not the screen. Only need to worry about enumerating
// the sprites overlaying the destination.
ENUMUNDERLAYS Enum(psoDst, pco, prclDst); while (Enum.bEnum(&psoDst, &Offset, &pco)) { bRet &= OffAlphaBlend(PPFNDIRECT(psoDst, AlphaBlend), &Offset, psoDst, &gptlZero, psoSrc, pco, pxlo, prclDst, prclSrc, pBlendObj); }
return(bRet); }
/******************************Public*Routine******************************\
* BOOL SpPlgBlt * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
BOOL SpPlgBlt( SURFOBJ* psoDst, SURFOBJ* psoSrc, SURFOBJ* psoMsk, CLIPOBJ* pco, XLATEOBJ* pxlo, COLORADJUSTMENT* pca, POINTL* pptlBrush, POINTFIX* pptfx, RECTL* prcl, POINTL* pptl, ULONG iMode) { RECTL rclDraw; BOOL bRet = TRUE; LONG iLeft = (pptfx[1].x > pptfx[0].x) == (pptfx[1].x > pptfx[3].x); LONG iTop = (pptfx[1].y > pptfx[0].y) == (pptfx[1].y > pptfx[3].y); POINTL Offset;
// I can't be bothered to handle the code that PlgBlt's when the
// source is the screen. To handle those cases, we would have to
// exclude all the sprites on the read, just as bSpBltFromScreen does.
// Fortunately, we've marked the screen surface as STYPE_DEVICE and
// so the Eng function will punt via SpCopyBits, which does do the
// right thing:
PDEVOBJ poSrc(psoSrc->hdev); if ((poSrc.bValid()) && (poSrc.pSpriteState()->psoScreen == psoSrc)) { return(EngPlgBlt(psoDst, psoSrc, psoMsk, pco, pxlo, pca, pptlBrush, pptfx, prcl, pptl, iMode)); }
if (pptfx[iLeft].x > pptfx[iLeft ^ 3].x) { iLeft ^= 3; }
if (pptfx[iTop].y > pptfx[iTop ^ 3].y) { iTop ^= 3; }
rclDraw.left = LONG_FLOOR_OF_FIX(pptfx[iLeft].x) - 1; rclDraw.top = LONG_FLOOR_OF_FIX(pptfx[iTop].y) - 1; rclDraw.right = LONG_CEIL_OF_FIX(pptfx[iLeft ^ 3].x) + 1; rclDraw.bottom = LONG_CEIL_OF_FIX(pptfx[iTop ^ 3].y) + 1;
ASSERTGDI((rclDraw.left < rclDraw.right) && (rclDraw.top < rclDraw.bottom), "Messed up bound calculation");
ENUMUNDERLAYS Enum(psoDst, pco, &rclDraw); while (Enum.bEnum(&psoDst, &Offset, &pco)) { bRet &= OffPlgBlt(PPFNDIRECT(psoDst, PlgBlt), &Offset, psoDst, &gptlZero, psoSrc, psoMsk, pco, pxlo, pca, pptlBrush, pptfx, prcl, pptl, iMode); }
return(bRet); }
/******************************Public*Routine******************************\
* BOOL SpGradientFill * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
BOOL SpGradientFill( SURFOBJ* pso, CLIPOBJ* pco, XLATEOBJ* pxlo, TRIVERTEX* pVertex, ULONG nVertex, PVOID pMesh, ULONG nMesh, RECTL* prclExtents, POINTL* pptlDitherOrg, ULONG ulMode) { BOOL bRet = TRUE; POINTL Offset;
ENUMUNDERLAYS Enum(pso, pco, prclExtents); while (Enum.bEnum(&pso, &Offset, &pco)) { bRet &= OffGradientFill(PPFNDIRECT(pso, GradientFill), &Offset, pso, pco, pxlo, pVertex, nVertex, pMesh, nMesh, prclExtents, pptlDitherOrg, ulMode); }
return(bRet); }
/******************************Public*Routine******************************\
* BOOL SpSaveScreenBits * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
ULONG_PTR SpSaveScreenBits( SURFOBJ* pso, ULONG iMode, ULONG_PTR ident, RECTL* prclSave) { SPRITESTATE* pState; SURFACE* pSurface; PTRACKOBJ pto; PEWNDOBJ pwo; BOOL bIntersectsFunkyFormat; BOOL bUnTearDown; DEVEXCLUDERECT dxo;
PDEVOBJ po(pso->hdev); po.vAssertDevLock();
pState = po.pSpriteState(); pSurface = SURFOBJ_TO_SURFACE_NOT_NULL(pState->psoScreen);
// For the most part, SaveScreenBits is completely redundant and
// handled perfectly fine by USER creating a compatible bitmap to
// hold the backing bits. However, there is one useful scenario
// for SaveScreenBits: when it occurs on top of a funky pixel-format
// window as used by some OpenGL hardware. In that case, the window
// format is not the same as the display, so information would be
// lost if the bits weren't saved in the original format, which is
// handled by the driver in DrvSaveScreenBits.
//
// So... We expect funky pixel format WNDOBJs to have the NOSPRITES
// attribute set, meaning we're not supposed to draw any sprites on
// them.
if ((pState->pfnSaveScreenBits == NULL) || (gpto == NULL)) return(0);
if (iMode == SS_SAVE) { // We must be holding the WNDOBJ semaphore before mucking
// with any WNDOBJs.
SEMOBJ so(ghsemWndobj);
// See if any funky format WNDOBJ intersects with the requested
// rectangle.
bIntersectsFunkyFormat = FALSE;
for (pto = gpto; (pto != NULL) && !bIntersectsFunkyFormat; pto = pto->ptoNext) { for (pwo = pto->pwo; pwo; pwo = pwo->pwoNext) { // The WNDOBJ coordinates must be device-relative so
// use UNDO:
UNDODESKTOPCOORD udc(pwo, pState);
if ((pwo->fl & WO_NOSPRITES) && bIntersect(&pwo->rclBounds, prclSave) && pwo->bInside(prclSave) == REGION_RECT_INTERSECT) { bIntersectsFunkyFormat = TRUE; break; } } }
// If the requested save rectangle doesn't intersect with any
// funky WNDOBJ, we can tell USER to revert to the sprite-friendly
// comaptible-bitmap save-bits method by returning 0.
if (!bIntersectsFunkyFormat) return(0); }
// Tear down any sprites that may overlap the area. This handles the
// case where the requested save rectangle isn't entirely contained
// within the WNDOBJ.
if (iMode != SS_FREE) dxo.vExclude(pso->hdev, prclSave);
return(pState->pfnSaveScreenBits(pso, iMode, ident, prclSave)); }
/******************************Public*Routine******************************\
* BOOL vSpHook * * Hooks into the DDI between GDI and the display driver to add an * additional layer. This only needs to be done when a sprite is * visible on the screen (and we take advantage of this to unhook * whenever possible, for better performance). * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
VOID vSpHook( SPRITESTATE* pState) { PDEVOBJ po(pState->hdev); PDEV* ppdev = po.ppdev; SURFACE* psurf = po.pSurface();
ASSERTGDI(!pState->bHooked, "Should hook only if already unhooked"); ASSERTGDI(pState->bInsideDriverCall, "SPRITELOCK should be held");
// Note that the surface's 'iType' and 'flSurfFlags' fields won't
// be modified to reflect the new state until the SPRITELOCK destructor.
pState->iSpriteType = STYPE_DEVICE;
// First, remove all HOOK_ flags for any drawing calls. We do this
// by removing all HOOK_FLAGS except HOOK_MOVEPANNING and HOOK_SYNCHRONIZE,
// which aren't really drawing calls:
pState->flSpriteSurfFlags = psurf->flags() & (~HOOK_FLAGS | HOOK_MOVEPANNING | HOOK_SYNCHRONIZE);
// Now add in all the flags for those drawing functions hooked by the
// sprite layer. Note that for performance reasons HOOK_STROKEANDFILLPATH
// is not one of these (since no drivers hook DrvStrokeAndFillPath,
// and EngStrokeAndFillPath isn't smart enough to call DrvStrokePath
// and DrvFillPath separately, and so we would get no accelerated drawing):
pState->flSpriteSurfFlags |= (HOOK_BITBLT | HOOK_STRETCHBLT | HOOK_PLGBLT | HOOK_TEXTOUT | HOOK_STROKEPATH | HOOK_FILLPATH | HOOK_LINETO | HOOK_COPYBITS | HOOK_STRETCHBLTROP | HOOK_TRANSPARENTBLT | HOOK_ALPHABLEND | HOOK_GRADIENTFILL);
ppdev->apfn[INDEX_DrvStrokePath] = (PFN) SpStrokePath; ppdev->apfn[INDEX_DrvFillPath] = (PFN) SpFillPath; ppdev->apfn[INDEX_DrvBitBlt] = (PFN) SpBitBlt; ppdev->apfn[INDEX_DrvCopyBits] = (PFN) SpCopyBits; ppdev->apfn[INDEX_DrvStretchBlt] = (PFN) SpStretchBlt; ppdev->apfn[INDEX_DrvTextOut] = (PFN) SpTextOut; ppdev->apfn[INDEX_DrvLineTo] = (PFN) SpLineTo; ppdev->apfn[INDEX_DrvTransparentBlt] = (PFN) SpTransparentBlt; ppdev->apfn[INDEX_DrvAlphaBlend] = (PFN) SpAlphaBlend; ppdev->apfn[INDEX_DrvPlgBlt] = (PFN) SpPlgBlt; ppdev->apfn[INDEX_DrvGradientFill] = (PFN) SpGradientFill; ppdev->apfn[INDEX_DrvDrawStream] = (PFN) SpDrawStream; ppdev->apfn[INDEX_DrvStretchBltROP] = (PFN) SpStretchBltROP; ppdev->apfn[INDEX_DrvSaveScreenBits] = (PFN) SpSaveScreenBits;
pState->bHooked = TRUE;
// Calculate the regions of the screen that shouldn't be drawn upon:
vSpComputeUnlockedRegion(pState); }
/******************************Public*Routine******************************\
* BOOL vSpUnhook * * If there are no visible sprites, unhook from the DDI for better * performance. * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
VOID vSpUnhook( SPRITESTATE* pState) { PDEVOBJ po(pState->hdev); PDEV* ppdev = po.ppdev; SURFACE* psurf = po.pSurface();
ASSERTGDI(pState->bHooked, "Should unhook only if already hooked"); ASSERTGDI(pState->bInsideDriverCall, "SPRITELOCK should be held");
// Note that the surface's 'iType' and 'flSurfFlags' fields won't
// be modified to reflect the new state until the SPRITELOCK destructor.
pState->iSpriteType = pState->iOriginalType; pState->flSpriteSurfFlags = pState->flOriginalSurfFlags;
ppdev->apfn[INDEX_DrvStrokePath] = (PFN) pState->pfnStrokePath; ppdev->apfn[INDEX_DrvFillPath] = (PFN) pState->pfnFillPath; ppdev->apfn[INDEX_DrvBitBlt] = (PFN) pState->pfnBitBlt; ppdev->apfn[INDEX_DrvCopyBits] = (PFN) pState->pfnCopyBits; ppdev->apfn[INDEX_DrvStretchBlt] = (PFN) pState->pfnStretchBlt; ppdev->apfn[INDEX_DrvTextOut] = (PFN) pState->pfnTextOut; ppdev->apfn[INDEX_DrvLineTo] = (PFN) pState->pfnLineTo; ppdev->apfn[INDEX_DrvTransparentBlt] = (PFN) pState->pfnTransparentBlt; ppdev->apfn[INDEX_DrvAlphaBlend] = (PFN) pState->pfnAlphaBlend; ppdev->apfn[INDEX_DrvPlgBlt] = (PFN) pState->pfnPlgBlt; ppdev->apfn[INDEX_DrvGradientFill] = (PFN) pState->pfnGradientFill; ppdev->apfn[INDEX_DrvStretchBltROP] = (PFN) pState->pfnStretchBltROP; ppdev->apfn[INDEX_DrvSaveScreenBits] = (PFN) pState->pfnSaveScreenBits; ppdev->apfn[INDEX_DrvDrawStream] = (PFN) pState->pfnDrawStream;
pState->bHooked = FALSE; }
/******************************Public*Routine******************************\
* BOOL bSpIsSystemMemory * * Simple little routine that returns TRUE if the surface is definitely * in system memory; FALSE if it's likely in video memory. (Video memory * is slow when we have to read the surface, because reads over the bus * are very painful.) * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
inline BOOL bSpIsSystemMemory( SURFOBJ* pso) { return((pso->iType == STYPE_BITMAP) && !(pso->fjBitmap & BMF_NOTSYSMEM)); }
/******************************Public*Routine******************************\
* VOID vSpCreateShape * * Allocates (if necessary) a new bitmap for the sprite, and copies it. * * Note: pSprite->psoShape will be NULL if this function fails! * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
VOID vSpCreateShape( SPRITE* pSprite, POINTL* pOffSrc, // Offset associated with 'psoSrc'
SURFOBJ* psoSrc, XLATEOBJ* pxlo, RECTL* prcl, PALETTE* ppalSrc, ULONG iFormat = 0,// May be '0' to select current screen format
ULONG bWantSystemMemory = TRUE, RECTL* prclDirty = NULL) // Requested type, STYPE_DEVBITMAP or STYPE_BITMAP.
{ SURFOBJ* psoShape; LONG cxSrc; LONG cySrc; POINTL ptlSrc; PFN_DrvCopyBits pfnCopyBits;
// First, handle the palette references:
XEPALOBJ palNew(ppalSrc); XEPALOBJ palOld(pSprite->ppalShape);
palNew.vRefPalette(); palOld.vUnrefPalette();
pSprite->ppalShape = ppalSrc;
// Now, handle the bitmap:
cxSrc = prcl->right - prcl->left; cySrc = prcl->bottom - prcl->top;
if (iFormat == 0) iFormat = pSprite->pState->psoScreen->iBitmapFormat;
// Note that we don't try to create a bitmap of type STYPE_DEVBITMAP
// if we already have a perfectly satisfactory STYPE_BITMAP sitting
// around. We do this for the case where we might be low on video
// memory, so that we don't keep re-allocating on every shape change.
psoShape = pSprite->psoShape; if ((psoShape == NULL) || (!bSpIsSystemMemory(psoShape) && (bWantSystemMemory)) || (psoShape->iBitmapFormat != iFormat) || (psoShape->sizlBitmap.cx < cxSrc) || (psoShape->sizlBitmap.cy < cySrc)) { // Max thing again? Or maybe hint?
vSpDeleteSurface(psoShape);
psoShape = psoSpCreateSurface(pSprite->pState, iFormat, cxSrc, cySrc, bWantSystemMemory);
pSprite->psoShape = psoShape; }
if (psoShape != NULL) { pSprite->OffShape.x = -prcl->left; pSprite->OffShape.y = -prcl->top; pSprite->iModeFormat = iFormat; pSprite->flModeMasks = palNew.flRed() | palNew.flBlu();
// If they passed us a dirty rectangle, only copy bits on that rectangle
ERECTL erclDirty = (ERECTL) *prcl; if (prclDirty) { erclDirty *= (*prclDirty); }
if (!erclDirty.bEmpty()) { // Calculate required source rectangle
ERECTL erclSrc(erclDirty); erclSrc += *pOffSrc; MULTISURF mSrc(psoSrc, &erclSrc); // Be sure to go through the DDI hooks and not directly to the Offset
// functions so that we can read the surface from the multi-mon screen
// and the like:
if (SURFOBJ_TO_SURFACE_NOT_NULL(psoShape)->flags() & HOOK_CopyBits) { PDEVOBJ poShape(psoShape->hdev);
// WINBUG #415010 06-12-2001 jasonha Properly fail shape update
// If bLoadSource fails, we can not guarentee a cross-device
// copy will succeed. Return failure by setting psoShape = NULL.
if (!mSrc.bLoadSource(poShape.hdev())) { vSpDeleteSurface(pSprite->psoShape); pSprite->psoShape = NULL; return; } else { pfnCopyBits = PPFNDRV(poShape,CopyBits); } } else { PDEVOBJ poSrc(psoSrc->hdev); pfnCopyBits = PPFNGET(poSrc, CopyBits, SURFOBJ_TO_SURFACE_NOT_NULL(psoSrc)->flags()); }
OffCopyBits(pfnCopyBits, &pSprite->OffShape, psoShape, &gptlZero, mSrc.pso, NULL, pxlo, (RECTL*) &erclDirty, mSrc.pptl()); } } }
/******************************Public*Routine******************************\
* VOID vSpDeleteShape * * Deletes any shape surfaces associated with the sprite. Note that this * does NOT delete the sprite itself. * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
VOID vSpDeleteShape( SPRITE* pSprite) { if (pSprite->ppalShape != NULL) { XEPALOBJ palShape(pSprite->ppalShape); palShape.vUnrefPalette(); pSprite->ppalShape = NULL; } if (pSprite->psoShape != NULL) { vSpDeleteSurface(pSprite->psoShape); pSprite->psoShape = NULL; } }
/******************************Public*Routine******************************\
* BOOL bSpUpdateAlpha * * Parses the BLENDFUNCTION parameter passed in, to ensure that it is * consistent with the type that the sprite originally was created as. * * Returns FALSE if the specified BLENDFUNCTION cannot be allowed, because * of an improper flag or because it's not consistent with the sprite type. * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
BOOL bSpUpdateAlpha( SPRITE* pSprite, BLENDFUNCTION* pblend, BOOL bUpdateOnlyAlpha) { BOOL bRet = FALSE;
// Note that we don't allow the AC_SRC_ALPHA status to be different
// from the previous AC_SRC_ALPHA status.
if ((pblend->BlendOp != AC_SRC_OVER) || (pblend->BlendFlags != 0) || ((pblend->AlphaFormat & ~AC_SRC_ALPHA) != 0)) { WARNING("bSpUpdateAlpha: Invalid alpha"); } else if (((pSprite->dwShape & ULW_ALPHA) == 0) && (pSprite->psoShape != NULL)) { WARNING("bSpUpdateAlpha: dwShape must be ULW_ALPHA"); } else { bRet = TRUE;
if (bUpdateOnlyAlpha) pSprite->BlendFunction.SourceConstantAlpha = pblend->SourceConstantAlpha; else pSprite->BlendFunction = *pblend;
pSprite->dwShape &= ~ULW_OPAQUE; pSprite->dwShape |= ULW_ALPHA;
// When trying to display a sprite at 8bpp, always render
// it opaque. It beats displaying a crappy image slowly.
if ((pSprite->pState->iModeFormat <= BMF_8BPP) || (!(pblend->AlphaFormat & AC_SRC_ALPHA) && (pblend->SourceConstantAlpha == 0xff))) { pSprite->fl |= SPRITE_FLAG_EFFECTIVELY_OPAQUE; } else { pSprite->fl &= ~SPRITE_FLAG_EFFECTIVELY_OPAQUE; } }
return(bRet); }
/******************************Public*Routine******************************\
* VOID vSpUpdatePerPixelAlphaFromColorKey * * Given a 32bpp surface, turns every pixel matching the transparent * color transparent, and turns every pixel not matching the transparent * color opaque. * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
VOID vSpUpdatePerPixelAlphaFromColorKey( SURFOBJ* pso, ULONG rgbTransparent, RECTL* prclDirty) { LONG i; LONG j; BYTE* pScan; ULONG* pul; ULONG ulTransparent; LONG lDelta; LONG cx, cy;
ASSERTGDI((pso->iBitmapFormat == BMF_32BPP) && (pso->iType == STYPE_BITMAP), "Expected readable 32bpp ARGB surface only");
ulTransparent = ((rgbTransparent & 0xff0000) >> 16) | ((rgbTransparent & 0x00ff00)) | ((rgbTransparent & 0x0000ff) << 16);
ERECTL erectlDirty(0, 0, pso->sizlBitmap.cx, pso->sizlBitmap.cy);
if (prclDirty) { // If the caller gives us a dirty rectangle, intersect it with the
// surface rectangle.
erectlDirty *= (*prclDirty); }
lDelta = pso->lDelta; cx = erectlDirty.right - erectlDirty.left; cy = erectlDirty.bottom - erectlDirty.top;
for (j = cy, pScan = ((BYTE*) pso->pvScan0) + erectlDirty.top * lDelta + + erectlDirty.left * sizeof(ULONG); j != 0; j--, pScan += lDelta) { for (i = cx, pul = (ULONG*) pScan; i != 0; i--, pul++) { if (*pul == ulTransparent) { // Write a pre-multiplied value of 0:
*pul = 0; } else { // Where the bitmap is not the transparent color, change
// the alpha value to opaque:
((RGBQUAD*) pul)->rgbReserved = 0xff; } } } }
/******************************Public*Routine******************************\
* BOOL bSpUpdateShape * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
BOOL bSpUpdateShape( SPRITE* pSprite, DWORD dwShape, HDC hdcDst, HDC hdcSrc, COLORREF crKey, BLENDFUNCTION* pblend, POINTL* pptlSrc, SIZEL* psizl, RECTL* prclDirty) { BOOL bStatus = FALSE; // Assume failure
SPRITESTATE* pState; SURFACE* psurfSrc; RECTL rclSrc; PALETTE* ppalDst; PALETTE* ppalDstDC; ULONG iFormat; ULONG crTextClr; ULONG crBackClr; LONG lIcmMode; BOOL bWantVideoMemory; BOOL bColorKeyAlpha; ULONG iSrcTransparent; BLENDFUNCTION blend;
pState = pSprite->pState; PDEVOBJ po(pState->hdev);
// Note that only kerne-mode callers can specify ULW_DRAGRECT, so we
// don't have to be as paranoid about checking parameters for this
// case.
if (dwShape == ULW_DRAGRECT) { // Note that there's not really a source surface, so this is a
// little faked.
pSprite->dwShape = dwShape; pSprite->rclSrc.left = 0; pSprite->rclSrc.right = psizl->cx; pSprite->rclSrc.top = 0; pSprite->rclSrc.bottom = psizl->cy; pSprite->iModeFormat = pState->iModeFormat; pSprite->flModeMasks = pState->flModeMasks;
return(TRUE); }
if (dwShape == 0) { dwShape = pSprite->dwShape; pblend = &pSprite->BlendFunction; }
if ((pptlSrc == NULL) || (psizl == NULL) || ((pblend == NULL) && (dwShape & ULW_ALPHA))) { WARNING("bSpUpdateShape: Invalid NULL parameter"); return(bStatus); }
// The supplied source DC has to either belong to the same PDEV as the
// sprite, or it has to belong to the multi-monitor meta-PDEV. This is
// because we do not support the S3 driver reading a device bitmap owned
// by the MGA driver, for example.
DCOBJ dcoSrc(hdcSrc); if (!dcoSrc.bValid() || dcoSrc.bFullScreen() || ((dcoSrc.hdev() != pState->hdev) && (dcoSrc.hdev() != po.hdevParent()))) { WARNING("bSpUpdateShape: Invalid source DC"); return(bStatus); }
if (hdcDst == 0) { // Supply some default information for the palette:
ppalDstDC = ppalDefault; crTextClr = 0x00ffffff; crBackClr = 0; lIcmMode = 0; } else { // Note that with multi-mon, the destination DC may be for a separate
// PDEV than our own PDEV. That is, the destination DC may be
// associated with the meta-PDEV while we're drawing to a specific
// PDEV. This is okay. All we'll be pulling out of the source DC
// is some palette information.
DCOBJ dcoDst(hdcDst); if (!dcoDst.bValid() || (dcoDst.hdev() != dcoSrc.hdev()) || ((dcoDst.hdev() != pState->hdev) && (dcoDst.hdev() != po.hdevParent()))) { WARNING("bSpUpdateShape: Invalid destination DC"); return(bStatus); }
ppalDstDC = dcoDst.ppal(); crTextClr = dcoDst.pdc->crTextClr(); crBackClr = dcoDst.pdc->crBackClr(); lIcmMode = dcoDst.pdc->lIcmMode(); }
rclSrc.left = pptlSrc->x; rclSrc.right = pptlSrc->x + psizl->cx; rclSrc.top = pptlSrc->y; rclSrc.bottom = pptlSrc->y + psizl->cy;
psurfSrc = dcoSrc.pSurface(); if ((psurfSrc != NULL) && (rclSrc.left >= 0) && (rclSrc.top >= 0) && (rclSrc.left < rclSrc.right) && (rclSrc.top < rclSrc.bottom) && (rclSrc.right <= psurfSrc->sizl().cx) && (rclSrc.bottom <= psurfSrc->sizl().cy)) { // Clip prclDirty to the source surface
if (prclDirty) { (*((ERECTL *) prclDirty)) *= ERECTL(0, 0, psurfSrc->sizl().cx, psurfSrc->sizl().cy); }
EXLATEOBJ xlo; XEPALOBJ palSrcDC(dcoSrc.ppal()); XEPALOBJ palSrc(psurfSrc->ppal()); XEPALOBJ palRGB(gppalRGB);
// If both ULW_ALPHA and ULW_COLORKEY are specified, then any
// pixels that match the color-key are completely transparent,
// and all other pixels have an alpha value of the global alpha
// specified.
//
// Since we don't have any low-level color-keyed-alpha-code, we
// simply convert this case to using per-pixel alpha.
bColorKeyAlpha = ((dwShape == (ULW_ALPHA | ULW_COLORKEY)) && (pblend->AlphaFormat == 0));
if (bColorKeyAlpha) { blend = *pblend; blend.AlphaFormat = AC_SRC_ALPHA; pblend = &blend; dwShape = ULW_ALPHA;
bColorKeyAlpha = TRUE;
iSrcTransparent = rgbFromColorref(palRGB, palSrcDC, crKey); }
// See whether we should make the sprite 32bpp, or convert it to
// being the same format as the screen:
if ((dwShape == ULW_ALPHA) && (pblend->AlphaFormat & AC_SRC_ALPHA)) { iFormat = BMF_32BPP; ppalDst = gppalRGB; ppalDstDC = ppalDefault; } else { iFormat = 0; ppalDst = po.ppalSurf(); // Don't use dcoDst.ppal() because
// with multimon, this may be the
} // wrong PDEV
XEPALOBJ palDstDC(ppalDstDC); XEPALOBJ palDst(ppalDst);
if (xlo.bInitXlateObj(NULL, lIcmMode, palSrc, palDst, palSrcDC, palDstDC, crTextClr, crBackClr, 0)) { bStatus = TRUE;
pSprite->dwShape = dwShape; pSprite->rclSrc = rclSrc;
if (dwShape == ULW_OPAQUE) { pSprite->fl |= SPRITE_FLAG_EFFECTIVELY_OPAQUE; bWantVideoMemory = TRUE; } else if (dwShape == ULW_COLORKEY) { // Transparency is of course specified using the
// source DC palette.
iSrcTransparent = ulGetNearestIndexFromColorref( palSrc, palSrcDC, crKey);
// ...but we're about to copy the source bitmap to a
// format that is the same as the screen. So we have
// to pipe the transparency color through the same
// conversion that the pixels on the blt go through.
//
// Note that this is NOT equivalent to doing:
//
// iTransparent = ulGetNearestIndexFromColorref(
// palDst,
// palDstDC,
// crKey)
//
// Converting to a compatible bitmap may cause us to
// increase the transparency color gamut (think of a
// 24bpp color wash being blt'd with a transparency
// key to 4bpp). But we do this for two reasons:
//
// 1. The shape bitmap is always stored in the same
// format as the screen, and so may be kept by the
// driver in off-screen memory and thus be accelerated;
// 2. On dynamic mode changes, we don't have to keep
// the source DC palette and source surface palette
// around to re-compute the proper translate.
pSprite->iTransparent = XLATEOBJ_iXlate(xlo.pxlo(), iSrcTransparent);
pSprite->fl &= ~SPRITE_FLAG_EFFECTIVELY_OPAQUE;
bWantVideoMemory = (po.flAccelerated() & ACCELERATED_TRANSPARENT_BLT); } else if (dwShape == ULW_ALPHA) { if (!bSpUpdateAlpha(pSprite, pblend, FALSE)) { bStatus = FALSE; } else if ((pblend->AlphaFormat & AC_SRC_ALPHA) && !bIsSourceBGRA(psurfSrc) && !bColorKeyAlpha) { bStatus = FALSE; } else if (bColorKeyAlpha) { // We need to be able to directly muck on the bits,
// so we don't want video memory:
bWantVideoMemory = FALSE; } else if (pblend->AlphaFormat & AC_SRC_ALPHA) { bWantVideoMemory = (po.flAccelerated() & ACCELERATED_PIXEL_ALPHA); } else { // There's no per-pixel alpha, so we should convert the
// bitmap to the video card's preferred format (namely,
// whatever the format of the primary is).
bWantVideoMemory = (po.flAccelerated() & ACCELERATED_CONSTANT_ALPHA); } } else { WARNING("bSpUpdateShape: Bad shape"); bStatus = FALSE; }
if (bStatus) { // For multi-mon, if the source is the meta-screen, we
// can't blt to a device surface because the two surfaces
// would belong to different PDEVs. Since I'm lazy, I'll
// simply enforce that one of the surfaces is not a device
// surface, when multi-mon:
#if 0 // WINBUG 315863/320834: We are leaving this old code till fixes for these bugs bake. We will remove these once the new code has baked,
if ((psurfSrc->iType() == STYPE_DEVICE) && (po.hdevParent() != po.hdev())) // Child PDEV
{ bWantVideoMemory = FALSE; } else if ((psurfSrc->iType() == STYPE_DEVBITMAP) && (po.hdevParent() != po.hdev()) && (po.flGraphicsCaps() & GCAPS_LAYERED)) { // We are given a meta dfb as source and want to update
// the shape for a mirrored device sprite. Use the meta
// dfb's corresponding mirror dev bitmap as source.
PSURFACE psurfSrcTmp; psurfSrcTmp = MulGetDevBitmapFromMasterDFB(psurfSrc,po.hdev()); if (psurfSrcTmp) psurfSrc = psurfSrcTmp; else { // We dont have a mirror dev bitmap. Use the master meta
// dfb after uncloaking it:
mSrc.vUnCloak(psurfSrc->pSurfobj()); } } #endif
// If the requested operation isn't accelerated, we convert
// the source bitmap to a true DIB. We do this on the
// assumption that the application is animating, and will
// soon call us again with the same bitmap:
if ((!bWantVideoMemory) && !bSpIsSystemMemory(psurfSrc->pSurfobj())) { // We don't care if the operation fails, as it was only a
// performance hint:
bConvertDfbDcToDib(&dcoSrc);
psurfSrc = dcoSrc.pSurface(); }
if (bStatus) { // We temporarily reset the 'bInsideDriverCall' flag for the
// duration of 'vSpCreateShape' so that the read from the source
// will be correctly handled if the source is actually the
// screen. We do this so that we don't capture the contents of
// other sprites when this happens.
ASSERTGDI(pState->bInsideDriverCall, "Expected sprite lock held");
vSpDirectDriverAccess(pState, FALSE);
vSpCreateShape(pSprite, &gptlZero, psurfSrc->pSurfobj(), xlo.pxlo(), &rclSrc, ppalDst, iFormat, !bWantVideoMemory, prclDirty);
// Restore the direct driver access state:
vSpDirectDriverAccess(pState, TRUE); } }
if ((bStatus) && (pSprite->psoShape != NULL)) { // Oh happy times, we succeeded.
if (bColorKeyAlpha) { // Anywhere the color-key is, convert it to transparent.
// Anywhere the color-key isn't, make it opaque:
vSpUpdatePerPixelAlphaFromColorKey(pSprite->psoShape, iSrcTransparent, prclDirty); } } else { // Uh oh, something failed. We have to delete the sprite
// now because we may have partially wacked some of our
// pSprite state.
vSpDeleteShape(pSprite); pSprite->dwShape = ULW_OPAQUE; // reset dwShape to
// something innocuous
bStatus = FALSE; } } } else { WARNING("bSpUpdateShape: Bad DCs or rectangles"); }
return(bStatus); }
/******************************Public*Routine******************************\
* VOID vSpTransferShape * * Creates a shape for a new sprite that is a copy of the old. * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
VOID vSpTransferShape( SPRITE* pSpriteNew, SPRITE* pSpriteOld) { ASSERTGDI(pSpriteNew->psoShape == NULL, "Expected new sprite to have no resources");
// Transfer HIDDEN state to the new sprite
pSpriteNew->fl |= pSpriteOld->fl & SPRITE_FLAG_HIDDEN;
if (pSpriteOld->psoShape != NULL) { // This will allocate a new sprite bitmap and copy it. Why not
// simply transfer 'psoShape' from the old sprite to the new?
// Because it may be a device bitmap!
vSpCreateShape(pSpriteNew, &pSpriteOld->OffShape, pSpriteOld->psoShape, NULL, &pSpriteOld->rclSrc, pSpriteOld->ppalShape, pSpriteOld->psoShape->iBitmapFormat, TRUE); // TRUE because by storing the sprite in
// system memory, we avoid the 'Both surfaces
// are unreadable and owned by different
// PDEVs" assert in vSpCreateShape
pSpriteNew->dwShape = pSpriteOld->dwShape; pSpriteNew->rclSrc = pSpriteOld->rclSrc; pSpriteNew->iTransparent = pSpriteOld->iTransparent; pSpriteNew->BlendFunction = pSpriteOld->BlendFunction; }
// Transfer the cached attributes to the new sprite
pSpriteNew->cachedAttributes = pSpriteOld->cachedAttributes; }
/******************************Public*Routine******************************\
* BOOL SpUpdatePosition * * NOTE: pSprite->psoShape may very well be NULL when entering this * function! * * NOTE: This function must never fail when hiding the sprite (because * that's used for cleanup and error handling) * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
BOOL bSpUpdatePosition( SPRITE* pSprite, POINTL* pptlDst, // May be NULL
BOOL bLeaveBits = FALSE) { GDIFunctionID(bSpUpdatePosition);
BOOL bStatus = TRUE; // Assume success
SPRITESTATE* pState; ERECTL rclSprite; RECTL* prcl; ULONG crcl; LONG cxSprite; LONG cySprite; SURFOBJ* psoUnderlay; SURFOBJ* psoShape; LONG dx; LONG dy; RECTL rclExclude; POINTL OffUnderlay; BOOL bWantSystemMemory; RECTL rclOldSprite; POINTL lastDst; FLONG flNewVisibility; pState = pSprite->pState;
lastDst = pSprite->ptlDst;
if (pptlDst == NULL) { rclSprite.bottom = LONG_MIN; } else { // Remember the original position for later.
pSprite->ptlDst.x = pptlDst->x; pSprite->ptlDst.y = pptlDst->y;
// Note that our handy 'bIntersect' routine will handle the
// cases where 'pptlDst' is too big, causing these adds to
// overflow.
rclSprite.left = pptlDst->x; rclSprite.top = pptlDst->y; rclSprite.right = pptlDst->x + (pSprite->rclSrc.right - pSprite->rclSrc.left); rclSprite.bottom = pptlDst->y + (pSprite->rclSrc.bottom - pSprite->rclSrc.top); }
// Non-visible sprites have a sprite rectangle of (LONG_MIN, LONG_MIN,
// LONG_MIN, LONG_MIN), which is depended upon by
// vSpComputeSpriteRanges:
if ((pSprite->fl & (SPRITE_FLAG_CLIPPING_OBSCURED | SPRITE_FLAG_HIDDEN)) || !bIntersect(&pState->rclScreen, &rclSprite, &rclSprite) ) { rclSprite.left = LONG_MIN; rclSprite.top = LONG_MIN; rclSprite.right = LONG_MIN; rclSprite.bottom = LONG_MIN; flNewVisibility = 0; } else { flNewVisibility = SPRITE_FLAG_VISIBLE; }
// WINBUG #368282 05-12-2001 jasonha Correctly maintain cVisible count
// If an invisible sprite was to be made visible, but the creation
// of the underlay failed, then it would be included in the cVisible
// count during the first bSpUpdatePosition call, but not removed
// during the recursive call to hide the sprite since
// pSprite->rclSprite would still be empty at all LONG_MIN's.
// SPRITE_FLAG_VISIBLE is used to represent inclusion in cVisible.
// If the uncovered region has changed, handle the underlay buffer:
if ((flNewVisibility != (pSprite->fl & SPRITE_FLAG_VISIBLE)) || (rclSprite.left != pSprite->rclSprite.left ) || (rclSprite.top != pSprite->rclSprite.top ) || (rclSprite.right != pSprite->rclSprite.right ) || (rclSprite.bottom != pSprite->rclSprite.bottom)) { // If the old sprite was visible, decrement the visible sprite
// count:
if (pSprite->fl & SPRITE_FLAG_VISIBLE) { ASSERTGDI(pState->cVisible != 0, "Messed up cVisible count"); pState->cVisible--; pSprite->fl &= ~SPRITE_FLAG_VISIBLE; #if DEBUG_SPRITES
{ ASSERTGDI(pState->pListVisible != NULL, "Invalid visible sprite list: pState->pListVisible == NULL when removing sprite.\n");
if (pState->cVisible == 0) { ASSERTGDI(pState->pListVisible == pSprite, "Invalid visible sprite list: pState->pListVisible != pSprite when removing last sprite.\n"); ASSERTGDI(pSprite->pNextVisible == NULL, "Invalid visible sprite list: pSprite->pNextVisible != NULL when removing last sprite.\n"); pState->pListVisible = pSprite->pNextVisible; } else { if (pState->pListVisible == pSprite) { pState->pListVisible = pSprite->pNextVisible; } else { SPRITE *pPrevSprite = pState->pListVisible;
while (pPrevSprite->pNextVisible != pSprite) { ASSERTGDI(pPrevSprite->pNextVisible != NULL, "Invalid visible sprite list: didn't find sprite in list.\n"); pPrevSprite = pPrevSprite->pNextVisible; }
pPrevSprite->pNextVisible = pSprite->pNextVisible; }
ASSERTGDI(pState->pListVisible != NULL, "Invalid visible sprite list: pState->pListVisible == NULL after removing non-last sprite.\n");
pSprite->pNextVisible = NULL; } } #endif
}
// If we're shrinking this sprite in any dimension, redraw the newly
// unobscured portions. Be sure to watch for the failure case where
// 'psoUnderlay' might not have been allocated:
if ((pSprite->psoUnderlay) && (!bLeaveBits)) { vSpRedrawUncoveredArea(pSprite, &rclSprite); } else if (bLeaveBits) { // Otherwise we should blit the sprite bits onto its position
// which will have the effect of updating the underlay bits
// for the overlapping sprites. See bug #252464.
CLIPOBJ* pco; ECLIPOBJ eco;
if (pSprite->prgnClip) { eco.vSetup(pSprite->prgnClip, *((ERECTL *) &pSprite->rclSprite)); pco = &eco; } else { pco = NULL; }
// Disable direct driver access for the duration of the
// SpCopyBits call so that we will be able to enumerate
// the sprites (see ENUMUNDERLAYS constructor).
ASSERTGDI(pState->bInsideDriverCall, "Expected sprite lock held"); vSpDirectDriverAccess(pState, FALSE);
if((!pco || (!eco.erclExclude().bEmpty())) && (pSprite->psoShape)) { // Let's not worry about setting up a color translation because
// the format of the sprite and the screen should be the same
// (only exception is for per pixel alpha sprites, but these
// shouldn't be using the ULW_NOREPAINT flag). However,
// it won't hurt to check here anyway and if not true no big deal
// if we don't update the underlay bits.
if(pState->psoScreen->iBitmapFormat == pSprite->psoShape->iBitmapFormat) { POINTL pt;
// WINFIX #96696 bhouse 4-14-2000
// Use the correct source point, rclSprite may have been
// clipped.
pt.x = pSprite->rclSprite.left - lastDst.x; pt.y = pSprite->rclSprite.top - lastDst.y;
SpCopyBits(pState->psoScreen, pSprite->psoShape, pco, NULL, &pSprite->rclSprite, &pt); } else { WARNING("bSpUpdatePosition: not updating underlay bits"); } }
// Restore direct driver access
vSpDirectDriverAccess(pState, TRUE); }
cxSprite = rclSprite.right - rclSprite.left; cySprite = rclSprite.bottom - rclSprite.top;
if (cxSprite == 0) { // The sprite is not visible, which may have been caused by
// disabling the sprite. Check to see if there are no other
// sprites on the screen, which would allow us to unhook the
// DDI:
if ((pState->cVisible == 0) && (pState->bHooked)) vSpUnhook(pState); } else { ASSERTGDI(cySprite != 0, "If cxSprite is 0, expected cySprite is 0");
// Since the new sprite is visible, increment the visible sprite
// count:
ASSERTGDI(!(pSprite->fl & SPRITE_FLAG_VISIBLE), "Invalid sprite visible state: making visible again.\n"); pSprite->fl |= SPRITE_FLAG_VISIBLE; pState->cVisible++; #if DEBUG_SPRITES
{ ASSERTGDI(pSprite->pNextVisible == NULL, "Invalid visible sprite list: pSprite->pNextVisible != NULL when adding to visible list.\n");
if (pState->cVisible == 1) { ASSERTGDI(pState->pListVisible == NULL, "Invalid visible sprite list: pState->pListVisible != NULL when adding first sprite.\n"); } else { ASSERTGDI(pState->pListVisible != NULL, "Invalid visible sprite list: pState->pListVisible == NULL when adding non-first sprite.\n"); }
pSprite->pNextVisible = pState->pListVisible; pState->pListVisible = pSprite; } #endif
// If the DDI isn't already hooked, do it now. This has to
// occur before we update the shape on the screen, because
// it recalculates prgnUnlocked, which must be respected
// whenever we write to the screen.
if (!pState->bHooked) vSpHook(pState);
// Check to see if the old underlay buffer will do.
psoUnderlay = pSprite->psoUnderlay; if ((psoUnderlay == NULL) || (cxSprite > psoUnderlay->sizlBitmap.cx) || (cySprite > psoUnderlay->sizlBitmap.cy)) { // There is no old underlay surface, or it's not large
// enough, so allocate a new underlay structure.
//
// Note that we don't free the old underlay surface yet,
// because we need it for the 'bSpBltFromScreen' we're
// about to do!
#if DEBUG_SPRITES
if (psoUnderlay != NULL) KdPrint(("Growing psoUnderlay: %p Old: (%li, %li) New: (%li, %li)\n", psoUnderlay, psoUnderlay->sizlBitmap.cx, psoUnderlay->sizlBitmap.cy, cxSprite, cySprite));
#endif
// Because alphablends require read-modify-write operations
// on the destination, it's a horrible performance penalty
// if we create the compositing surface in video memory and
// the device doesn't support accelerated alpha. Similarly,
// we don't want to have the underlay surface in video memory
// if the compositing surface is in system memory. So check
// here if alpha isn't accelerated, and simply create the
// underlay in system memory.
bWantSystemMemory = FALSE; if (pSprite->dwShape == ULW_ALPHA) { PDEVOBJ po(pState->hdev);
if (pSprite->BlendFunction.AlphaFormat & AC_SRC_ALPHA) { if (!(po.flAccelerated() & ACCELERATED_PIXEL_ALPHA)) { bWantSystemMemory = TRUE; } } else { if (!(po.flAccelerated() & ACCELERATED_CONSTANT_ALPHA)) { bWantSystemMemory = TRUE; } } }
{ PDEVOBJ po(pState->hdev); if (po.flGraphicsCaps() & GCAPS_LAYERED) bWantSystemMemory = TRUE; }
psoUnderlay = psoSpCreateSurface( pState, 0, max(cxSprite, pSprite->sizlHint.cx), max(cySprite, pSprite->sizlHint.cy), bWantSystemMemory); if (!psoUnderlay) { // Uh oh, we couldn't allocate the underlay buffer, so
// we won't be able to show the sprite. We'll handle this
// by simply marking the sprite as invisible later on:
bStatus = FALSE; } else { psoUnderlay->fjBitmap |= BMF_DONTCACHE;
// We have turned off BMF_SPRITE support (it appears to
// be unused)
// psoUnderlay->fjBitmap |= BMF_SPRITE;
OffUnderlay.x = -rclSprite.left; OffUnderlay.y = -rclSprite.top;
// Get the bits underneath where the sprite will appear:
if (((cxSprite <= SMALL_SPRITE_DIMENSION) && (cySprite <= SMALL_SPRITE_DIMENSION))) { vSpSmallUnderlayCopy(pSprite, &OffUnderlay, psoUnderlay, &pSprite->OffUnderlay, pSprite->psoUnderlay, 0, 0, &rclSprite, &pSprite->rclSprite); } else { vSpBigUnderlayCopy(pState, &OffUnderlay, psoUnderlay, &rclSprite); }
// Okay, we can now safely delete the old underlay:
vSpDeleteSurface(pSprite->psoUnderlay); pSprite->psoUnderlay = psoUnderlay; pSprite->OffUnderlay = OffUnderlay;
pSprite->rclUnderlay.left = rclSprite.left; pSprite->rclUnderlay.top = rclSprite.top; pSprite->rclUnderlay.right = rclSprite.left + psoUnderlay->sizlBitmap.cx; pSprite->rclUnderlay.bottom = rclSprite.top + psoUnderlay->sizlBitmap.cy; } } else { // Okay, we know the old underlay surface was already big
// enough. See if we have to read some bits from the screen
// to update the underlay, either because the sprite moved
// or because it got larger:
if ((rclSprite.left < pSprite->rclSprite.left ) || (rclSprite.top < pSprite->rclSprite.top ) || (rclSprite.right > pSprite->rclSprite.right ) || (rclSprite.bottom > pSprite->rclSprite.bottom)) { dx = 0; dy = 0;
if (rclSprite.left < pSprite->rclUnderlay.left) dx = rclSprite.left - pSprite->rclUnderlay.left; else if (rclSprite.right > pSprite->rclUnderlay.right) dx = rclSprite.right - pSprite->rclUnderlay.right;
if (rclSprite.top < pSprite->rclUnderlay.top) dy = rclSprite.top - pSprite->rclUnderlay.top; else if (rclSprite.bottom > pSprite->rclUnderlay.bottom) dy = rclSprite.bottom - pSprite->rclUnderlay.bottom;
// Note that 'dx' and 'dy' may still both be zero.
pSprite->rclUnderlay.left += dx; pSprite->rclUnderlay.right += dx; pSprite->rclUnderlay.top += dy; pSprite->rclUnderlay.bottom += dy;
ASSERTGDI( (pSprite->rclUnderlay.left <= rclSprite.left) && (pSprite->rclUnderlay.top <= rclSprite.top) && (pSprite->rclUnderlay.right >= rclSprite.right) && (pSprite->rclUnderlay.bottom >= rclSprite.bottom), "Improper rclUnderlay");
// I figure that on a move, the delta will typically be
// fairly small, so we'll always call 'vSpSmallUnderlayCopy'
// instead of 'bSpBltFromScreen' for this case.
pSprite->OffUnderlay.x = -pSprite->rclUnderlay.left; pSprite->OffUnderlay.y = -pSprite->rclUnderlay.top;
vSpSmallUnderlayCopy(pSprite, &pSprite->OffUnderlay, pSprite->psoUnderlay, &pSprite->OffUnderlay, pSprite->psoUnderlay, dx, dy, &rclSprite, &pSprite->rclSprite); } } }
if (bStatus) { rclOldSprite = pSprite->rclSprite;
// Finally, update the sprite rectangle and mark the range cache
// as being invalid. Note that we do this only in the case when we
// can display the sprite.
pSprite->rclSprite = rclSprite; pState->bValidRange = FALSE;
vSpOrderInY(pSprite);
// If either a DirectDraw Lock is active, or there are any active
// WNDOBJs, we have to do some more work to handle repercussions:
if (gpto != NULL) { vSpCheckForWndobjOverlap(pState, &rclSprite, &rclOldSprite); } } else { // If we couldn't display the sprite, then hide it. Note that
// there's no chance that this will infinitely recurse.
ASSERTGDI(pptlDst != NULL, "bSpUpdatePosition: Must never fail a hide"); bSpUpdatePosition(pSprite, NULL); } }
return(bStatus); }
/******************************Public*Routine******************************\
* VOID vSpFreeClipResources * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
VOID vSpFreeClipResources( SPRITE* pSprite) { RGNOBJ ro(pSprite->prgnClip); ro.vDeleteRGNOBJ(); pSprite->prgnClip = NULL; }
/******************************Public*Routine******************************\
* BOOL bSpUpdateSprite * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
BOOL bSpUpdateSprite( SPRITE* pSprite, HDC hdcDst, POINTL* pptlDst, SIZEL* psizl, HDC hdcSrc, POINTL* pptlSrc, COLORREF crKey, BLENDFUNCTION* pblend, DWORD dwShape, RECTL* prclDirty) { BOOL bStatus; SPRITESTATE* pState; BLENDFUNCTION blend;
if (!pSprite) { return FALSE; }
bStatus = FALSE; // Assume failure
pState = pSprite->pState;
PDEVOBJ po(pState->hdev);
DEVLOCKOBJ dlo(po); SPRITELOCK slock(po);
if (dwShape & ULW_NEW_ATTRIBUTES) { // Take out this instructions flag to avoid future confusion
dwShape &= ~ULW_NEW_ATTRIBUTES;
// Cache new attributes in the sprite
pSprite->cachedAttributes.dwShape = dwShape; pSprite->cachedAttributes.bf = *pblend; pSprite->cachedAttributes.crKey = (ULONG) crKey;
// If sprite hasn't been created yet, don't proceed so that a black
// sprite won't get painted on the screen. This is the case where
// we're called for the first time from _UpdateLayeredWindow, where
// user chooses to not pass us hdcSrc yet.
if (!hdcSrc) { return TRUE; } } else if (dwShape == ULW_DEFAULT_ATTRIBUTES) { // Retrieve arguments from cached values
dwShape = pSprite->cachedAttributes.dwShape; crKey = pSprite->cachedAttributes.crKey; blend = pSprite->cachedAttributes.bf; // Better to point to a local
// variable in case this gets
// changed later.
pblend = &blend; }
if ((hdcDst) || (psizl) || (hdcSrc) || (pptlSrc) || (crKey)) { bStatus = bSpUpdateShape(pSprite, dwShape, hdcDst, hdcSrc, crKey, pblend, pptlSrc, psizl, prclDirty); if (bStatus) { // Use the last position we were given if no position was specified on
// this call.
bStatus &= bSpUpdatePosition( pSprite, (pptlDst != NULL) ? pptlDst : &pSprite->ptlDst); } } else if (((dwShape == ULW_ALPHA) || (dwShape == (ULW_ALPHA | ULW_COLORKEY))) && (pblend != NULL) && (pptlDst == NULL)) { bStatus = bSpUpdateAlpha(pSprite, pblend, TRUE); } else if (((dwShape == 0) || (dwShape == ULW_NOREPAINT)) && (pblend == NULL)) { // Note that pptlDst may be either NULL or non-NULL.
bStatus = bSpUpdatePosition(pSprite, pptlDst, dwShape & ULW_NOREPAINT); } else { WARNING("bSpUpdateSprite: Unexpected argument"); }
// Finally, redraw the sprite:
if (prclDirty) { // Only redraw the dirty rectangle
ERECTL erclDirty(*prclDirty); erclDirty += pSprite->ptlDst; // Offset by origin of sprite window
// because the rectangle needs to
// be in screen coordinates
erclDirty *= pSprite->rclSprite; // To be safe, intersect with sprite
// rectangle
if (!erclDirty.bEmpty()) { // Only redraw if intersection is not empty
vSpRedrawArea(pSprite->pState, (RECTL*) &erclDirty); } } else { // Must redraw the whole sprite
vSpRedrawSprite(pSprite);
// synchrnize on output surface to ensure frame is rendered
if (!po.bDisabled()) { po.vSync(po.pSurface()->pSurfobj(), NULL, 0); } }
return(bStatus); }
/******************************Public*Routine******************************\
* VOID vSpRenumberZOrder * * Renumbers the sprites according to z-order, after the z-order is changed. * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
VOID vSpRenumberZOrder( SPRITESTATE* pState) { SPRITE* pSprite; ULONG z = 0;
// The sprite numbers are assigned starting at 0 with the back-most sprite:
for (pSprite = pState->pListZ; pSprite != NULL; pSprite = pSprite->pNextZ) { pSprite->z = z++; }
}
/******************************Public*Routine******************************\
* VOID vSpDeleteSprite * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
VOID vSpDeleteSprite( SPRITE* pSprite) { SPRITESTATE* pState; BOOL bRet; SPRITE* pTmp; SPRITE* pNext; SPRITE* pPrevious;
if (pSprite == NULL) return;
pState = pSprite->pState;
PDEVOBJ po(pState->hdev);
DEVLOCKOBJ dlo(po); SPRITELOCK slock(po);
// Hide the sprite:
bRet = bSpUpdatePosition(pSprite, NULL);
ASSERTGDI(bRet, "vSpDeleteSprite: hide failed");
#if DEBUG_SPRITES
vSpValidateVisibleSprites(pState); #endif
// Remove the sprite from the z-sorted linked list:
if (pState->pListZ == pSprite) { pState->pListZ = pSprite->pNextZ; } else { for (pTmp = pState->pListZ; pTmp != NULL; pTmp = pTmp->pNextZ) { if (pTmp->pNextZ == pSprite) { pTmp->pNextZ = pSprite->pNextZ; break; } } }
// Delete the sprite from the Y-sorted linked list:
pPrevious = pSprite->pPreviousY; pNext = pSprite->pNextY;
if (pNext) pNext->pPreviousY = pPrevious;
if (pPrevious) pPrevious->pNextY = pNext; else { ASSERTGDI(pState->pListY == pSprite, "Expected top of list"); pState->pListY = pNext; }
// Free all allocated data associated with this sprite:
vSpFreeClipResources(pSprite); vSpDeleteShape(pSprite); vSpDeleteSurface(pSprite->psoUnderlay);
if (pSprite->psoMask != NULL) { bDeleteSurface(pSprite->psoMask->hsurf); pSprite->psoMask = NULL; }
// Take this as an opportunity to free the compositing surface:
vSpDeleteSurface(pState->psoComposite); pState->psoComposite = NULL;
// We're all done with this object, so free the memory and
// leave:
VFREEMEM(pSprite); }
/******************************Public*Routine******************************\
* SPRITE* pSpCreateSprite * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
SPRITE* pSpCreateSprite( HDEV hdev, RECTL* prcl, HWND hwnd, POINTL* pptlDstInit=NULL) { SPRITE* pSprite; SPRITE* pPrevious; SPRITE* pNext; SPRITE* pTarget;
pSprite = NULL; // Assume failure
PDEVOBJ po(hdev);
if (po.bDisplayPDEV()) { DEVLOCKOBJ dlo(po); SPRITELOCK slock(po);
SPRITESTATE* pState = po.pSpriteState();
pSprite = (SPRITE*) PALLOCMEM(sizeof(SPRITE), ' psG'); if (pSprite != NULL) { if (prcl != NULL) { pSprite->sizlHint.cx = prcl->right - prcl->left; pSprite->sizlHint.cy = prcl->bottom - prcl->top; pSprite->ptlDst.x = pptlDstInit ? pptlDstInit->x : prcl->left; pSprite->ptlDst.y = pptlDstInit ? pptlDstInit->y : prcl->top; } else { pSprite->sizlHint.cx = 0; pSprite->sizlHint.cy = 0; pSprite->ptlDst.x = LONG_MIN; pSprite->ptlDst.y = LONG_MIN; }
pSprite->fl = 0; pSprite->pState = pState; pSprite->dwShape = ULW_OPAQUE; pSprite->rclSprite.top = LONG_MIN; pSprite->rclSprite.left = LONG_MIN; pSprite->rclSprite.bottom = LONG_MIN; pSprite->rclSprite.right = LONG_MIN; #if DEBUG_SPRITES
pSprite->pNextVisible = NULL; #endif
// Add this sprite to the end of the z-list, meaning that
// it will be top-most. Well, top-most except for the
// cursor sprites:
pTarget = pState->pBottomCursor; if (pState->pListZ == pTarget) { pSprite->pNextZ = pTarget; pState->pListZ = pSprite; } else { for (pPrevious = pState->pListZ; pPrevious->pNextZ != pTarget; pPrevious = pPrevious->pNextZ) ;
pSprite->pNextZ = pTarget; pPrevious->pNextZ = pSprite; }
vSpRenumberZOrder(pState);
// Add this sprite to the front of the y-list.
// pSprite->pPreviousY was already zero-initialized:
pNext = pState->pListY; pState->pListY = pSprite; pSprite->pNextY = pNext; if (pNext) pNext->pPreviousY = pSprite; pSprite->hwnd = hwnd;
vSpOrderInY(pSprite); } }
return(pSprite); }
/******************************Public*Routine******************************\
* BOOL bSpEnableSprites * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
BOOL bSpEnableSprites( HDEV hdev) { SPRITESTATE* pState; CLIPOBJ* pco; SPRITESCAN* pRange; SIZEL sizl; HSURF hsurf; SURFACE* psurfScreen; SURFOBJ* psoHitTest;
PDEVOBJ po(hdev); if (!po.bDisplayPDEV()) return(TRUE);
psurfScreen = SURFOBJ_TO_SURFACE_NOT_NULL(po.pSurface()->pSurfobj());
pState = po.pSpriteState();
// Remember some information about the current mode:
pState->hdev = hdev; pState->psoScreen = psurfScreen->pSurfobj(); pState->iModeFormat = psurfScreen->iFormat(); pState->iOriginalType = psurfScreen->iType(); pState->flOriginalSurfFlags = psurfScreen->flags();
pState->iSpriteType = pState->iOriginalType; pState->flSpriteSurfFlags = pState->flOriginalSurfFlags;
XEPALOBJ pal(psurfScreen->ppal()); pState->flModeMasks = pal.flRed() | pal.flBlu();
// Initialize the screen size:
pState->rclScreen.left = 0; pState->rclScreen.right = psurfScreen->sizl().cx; pState->rclScreen.top = 0; pState->rclScreen.bottom = psurfScreen->sizl().cy;
// Now allocate some regions that we'll use later:
RGNMEMOBJ rmoUncovered; RGNMEMOBJ rmoTmp; RGNMEMOBJ rmoRectangular;
if (rmoUncovered.bValid() && rmoTmp.bValid() && rmoRectangular.bValid()) { pRange = (SPRITESCAN*) PALLOCMEM(sizeof(SPRITESCAN), 'rpsG'); if (pRange) { // TRUE to denote that we want an STYPE_BITMAP surface, because
// we have to be able to access the bits directly for hit testing:
psoHitTest = psoSpCreateSurface(pState, 0, 1, 1, TRUE); if (psoHitTest) { // Mark the surface, so that it can be special-cased by
// the dynamic mode change code:
vSpSetNullRange(pState, pRange);
pState->psoHitTest = psoHitTest;
// We need a DC_RECT clip object:
rmoRectangular.vSet(&pState->rclScreen);
pState->prgnRectangular = rmoRectangular.prgnGet(); pState->coRectangular.vSetup(rmoRectangular.prgnGet(), *((ERECTL*) &pState->rclScreen), CLIP_FORCE);
pState->prgnUncovered = rmoUncovered.prgnGet(); pState->prgnUncovered->vStamp(); pState->prgnTmp = rmoTmp.prgnGet(); pState->hrgn = GreCreateRectRgn(0, 0, 0, 0);
// Save some hook state:
pState->pfnStrokePath = PPFNDRV(po, StrokePath); pState->pfnFillPath = PPFNDRV(po, FillPath); pState->pfnBitBlt = PPFNDRV(po, BitBlt); pState->pfnCopyBits = PPFNDRV(po, CopyBits); pState->pfnStretchBlt = PPFNDRV(po, StretchBlt); pState->pfnTextOut = PPFNDRV(po, TextOut); pState->pfnLineTo = PPFNDRV(po, LineTo); pState->pfnTransparentBlt = PPFNDRV(po, TransparentBlt); pState->pfnAlphaBlend = PPFNDRV(po, AlphaBlend); pState->pfnPlgBlt = PPFNDRV(po, PlgBlt); pState->pfnGradientFill = PPFNDRV(po, GradientFill); pState->pfnStretchBltROP = PPFNDRV(po, StretchBltROP); pState->pfnSaveScreenBits = PPFNDRV(po, SaveScreenBits); pState->pfnDrawStream = PPFNDRV(po, DrawStream);
return(TRUE); }
VFREEMEM(pRange); } }
WARNING("bSpEnableSprites: Failed!");
rmoUncovered.vDeleteRGNOBJ(); rmoTmp.vDeleteRGNOBJ(); rmoRectangular.vDeleteRGNOBJ();
return(FALSE); }
/******************************Public*Routine******************************\
* VOID vSpDisableSprites * * Called when the device's surface is about to be destroyed. * * Note: This function may be called without bDdEnableSprites having * first been called! * * Note that this may be called for printers and the like. * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
VOID vSpDisableSprites( HDEV hdev, CLEANUPTYPE cutype) { SPRITESTATE* pState;
PDEVOBJ po(hdev);
pState = po.pSpriteState();
SPRITE* pSprite = pState->pBottomCursor;
while(pSprite != NULL) { SPRITE* pNextSprite = pSprite->pNextZ; vSpDeleteSprite(pSprite); pSprite = pNextSprite; }
pState->pTopCursor = NULL; pState->pBottomCursor = NULL; pState->ulNumCursors = 0;
ASSERTGDI(pState->pListZ == NULL, "Expected to have 0 sprites"); ASSERTGDI(pState->psoComposite == NULL, "Expected no composite surface");
// During session cleanup (i.e., hydra shutdown), surfaces are
// deleted as part of the HMGR object cleanup. So we can skip
// this for CLEANUP_SESSION:
if (cutype != CLEANUP_SESSION) { vSpDeleteSurface(pState->psoHitTest); }
// These regions, on the other hand, are not in the HMGR so
// must be cleaned up always:
RGNOBJ roUncovered(pState->prgnUncovered); RGNOBJ roTmp(pState->prgnTmp); RGNOBJ roRectangular(pState->prgnRectangular);
roUncovered.vDeleteRGNOBJ(); roTmp.vDeleteRGNOBJ(); roRectangular.vDeleteRGNOBJ();
// Since we are referencing this region by handle, it is safe
// to do even during session cleanup:
GreDeleteObject(pState->hrgn);
if (pState->pRange) { VFREEMEM(pState->pRange); }
if (pState->ahdevMultiMon) { EngFreeMem(pState->ahdevMultiMon); }
if (pState->prgnUnlocked != NULL) { pState->prgnUnlocked->vDeleteREGION(); }
// Leave the sprite state in the PDEV in a newly initialized state:
RtlZeroMemory(pState, sizeof(*pState)); }
/******************************Public*Routine******************************\
* VOID vSpEnableMultiMon * * This routine is called by the multi-mon code to let us know the children * of a multi-mon meta PDEV. * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
VOID vSpEnableMultiMon( HDEV hdev, ULONG c, HDEV* ahdev) // Must be allocated by the caller and freed
{ // by calling vSpDisableMultiMon
SPRITESTATE* pState;
PDEVOBJ po(hdev); pState = po.pSpriteState();
pState->cMultiMon = c; pState->ahdevMultiMon = ahdev; }
/******************************Public*Routine******************************\
* VOID vSpDisableMultiMon * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
VOID vSpDisableMultiMon( HDEV hdev) { SPRITESTATE* pState;
PDEVOBJ po(hdev); pState = po.pSpriteState();
if (pState->ahdevMultiMon) { VFREEMEM(pState->ahdevMultiMon); }
pState->cMultiMon = 0; pState->ahdevMultiMon = NULL;
ASSERTGDI(pState->pListMeta == NULL, "Expected no meta-sprites"); }
/******************************Public*Routine******************************\
* VOID vSpHideSprites * * Hides or unhides all sprites on a PDEV and all of its child PDEVs. * * 1-Mar-2001 -by- Jason Hartman [jasonha] * Wrote it. \**************************************************************************/
VOID vSpHideSprites( HDEV hdev, BOOL bHide ) { GDIFunctionID(vSpHideSprites);
PDEVOBJ po(hdev); SPRITELOCK slock(po); SPRITESTATE* pState;
pState = po.pSpriteState();
if (pState->cMultiMon) { ULONG i;
for (i = 0; i < pState->cMultiMon; i++) { vSpHideSprites(pState->ahdevMultiMon[i], bHide); } } else { SPRITE* pSprite = pState->pListZ;
while (pSprite != NULL) { SPRITE* pNextSprite = pSprite->pNextZ;
if (bHide) { ASSERTGDI((pSprite->fl & SPRITE_FLAG_HIDDEN) == 0, "Sprite is already hidden.");
pSprite->fl |= SPRITE_FLAG_HIDDEN; } else { ASSERTGDI((pSprite->fl & SPRITE_FLAG_HIDDEN) != 0, "Sprite is not hidden.");
pSprite->fl &= ~SPRITE_FLAG_HIDDEN; }
bSpUpdatePosition(pSprite, &pSprite->ptlDst);
pSprite = pNextSprite; }
}
if (bHide) { ASSERTGDI(pState->cVisible == 0, "Sprites remain visible after we hid them all."); ASSERTGDI(!pState->bHooked, "Sprite layer remains hooked after we hid all sprites."); } }
/******************************Public*Routine******************************\
* SPRITE* pSpTransferSprite * * Transfers a sprite to a new PDEV by creating a new sprite that copies * the contents of the old, and then deleting the old sprite. * * NOTE: pSpriteOld is freed by this function, so it must not be accessed * after calling! * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
SPRITE* pSpTransferSprite( HDEV hdevNew, SPRITE* pSpriteOld) { SPRITE* pSpriteNew; POINTL ptlZero;
pSpriteNew = NULL;
if (pSpriteOld->hwnd == NULL) { // There shouldn't be any non-hwnd associated sprites at this point.
ASSERTGDI(FALSE, "pSpTransferSprite: pSpriteOld is not hwnd associated!"); } else { pSpriteNew = pSpCreateSprite(hdevNew, NULL, pSpriteOld->hwnd); if (pSpriteNew) { vSpTransferShape(pSpriteNew, pSpriteOld); if (!bSpUpdatePosition(pSpriteNew, &pSpriteOld->ptlDst)) { vSpDeleteSprite(pSpriteNew); pSpriteNew = NULL; } else { // If this sprite is a part of Meta Sprite,
// update Meta Sprite, too.
if (pSpriteOld->pMetaSprite != NULL) { METASPRITE *pMetaSprite = pSpriteOld->pMetaSprite; BOOL bSpriteInMeta = FALSE;
for (ULONG i = 0; i < pMetaSprite->chSprite; i++) { if (pMetaSprite->apSprite[i] == pSpriteOld) { pMetaSprite->apSprite[i] = pSpriteNew; pSpriteNew->pMetaSprite = pMetaSprite;
if (bSpriteInMeta) { WARNING("pSpTransferSprite: Sprite in meta multiple times!"); } bSpriteInMeta = TRUE; } }
if (!bSpriteInMeta) { WARNING("pSpTransferSprite: No sprite in meta!"); } } } } }
// If new sprite could not be created, the metasprite is still pointing
// to the old sprite which we are about to delete.
if (!pSpriteNew && (pSpriteOld->pMetaSprite != NULL)) { METASPRITE *pMetaSprite = pSpriteOld->pMetaSprite;
// Mark the meta sprite for deletion. This is because the call to
// UserRemoveRedirectionBitmap will unset the layered flag on the
// window and so user will not attempt to delete the sprite in the
// futue (i.e. if we don't do this we will leak memory). The actual
// deletion will happen in pSpTransferMetaSprite.
pMetaSprite->fl |= SPRITE_FLAG_NO_WINDOW;
for (ULONG i = 0; i < pMetaSprite->chSprite; i++) { if (pMetaSprite->apSprite[i] == pSpriteOld) { pMetaSprite->apSprite[i] = NULL; } } }
vSpDeleteSprite(pSpriteOld);
return(pSpriteNew); }
/******************************Public*Routine******************************\
* VOID vSpCorrectHdevReferences * * On a dynamic mode change, sprite state is transferred between the two * PDEVs along with the rest of the driver state. As such, we have to * go back through and correct any references to the old HDEV. * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
VOID vSpCorrectHdevReferences( SPRITESTATE* pState, HDEV hdev) { SPRITE* pSprite;
pState->hdev = hdev;
// Note that any surfaces created by psoSpCreateSurface are tagged
// as sprite surfaces, and left alone by bDynamicModeChange, so that
// we can handle them here.
if (pState->psoComposite != NULL) pState->psoComposite->hdev = hdev;
if (pState->psoHitTest != NULL) pState->psoHitTest->hdev = hdev;
for (pSprite = pState->pListZ; pSprite != NULL; pSprite = pSprite->pNextZ) { pSprite->pState = pState;
if (pSprite->psoShape != NULL) { ASSERTGDI(pSprite->psoShape->hdev != 0, "Didn't expect NULL shape hdev");
pSprite->psoShape->hdev = hdev; } if (pSprite->psoUnderlay != NULL) { ASSERTGDI(pSprite->psoUnderlay->hdev != 0, "Didn't expect NULL shape hdev");
pSprite->psoUnderlay->hdev = hdev; } } }
/******************************Public*Routine******************************\
* METASPRITE* pSpConvertSpriteToMeta * * On a dynamic mode change, sprite state is transferred between the meta-PDEV * and device PDEV along with the rest of the driver state. As such, we have * to convert "sprite" to "meta sprite" when 1 monitor to multi-monitors mode * change * * 2-Jul-1998 -by- Hideyuki Nagase [hideyukn] * Wrote it. \**************************************************************************/
METASPRITE* pSpConvertSpriteToMeta( HDEV hdevMetaNew, HDEV hdevDevOld, SPRITE* pSpriteOld) { METASPRITE *pMetaSpriteNew; SPRITE *pSpriteNew; SPRITESTATE *pState; BOOL bError = FALSE;
pMetaSpriteNew = NULL;
if (pSpriteOld->hwnd == NULL) { // There shouldn't be any non-hwnd associated sprites at this point.
ASSERTGDI(FALSE, "pSpConvertSpriteToMeta: pSpriteOld is not hwnd associated!"); } else { PDEVOBJ poMeta(hdevMetaNew);
// When running multi-mon, handle the creation of the meta sprite:
pState = poMeta.pSpriteState();
if (pState->cMultiMon) { // Create MetaSprites and 'real' sprites for children.
ULONG cjAlloc = sizeof(METASPRITE) + pState->cMultiMon * sizeof(pMetaSpriteNew->apSprite[0]);
pMetaSpriteNew = (METASPRITE*) PALLOCNOZ(cjAlloc, 'mpsG'); if (pMetaSpriteNew) { for (ULONG i = 0; i < pState->cMultiMon; i++) { PDEVOBJ poDevice(pState->ahdevMultiMon[i]); SPRITELOCK slock(poDevice);
pSpriteNew = pSpCreateSprite(poDevice.hdev(), NULL, pSpriteOld->hwnd); if (pSpriteNew) { POINTL ptlDst;
PDEVOBJ poDevOld(hdevDevOld);
// Transfer the information from old sprite
// to new sprite.
vSpTransferShape(pSpriteNew, pSpriteOld);
// The sprite position need to be adjusted based on
// poDevice origin, since we switch to multi-monitor
// system, and then poDevice origion might not be equal
// poDevOld.
// poDevOld coordinate to meta coordinate
ptlDst.x = pSpriteOld->ptlDst.x + poDevOld.pptlOrigin()->x; ptlDst.y = pSpriteOld->ptlDst.y + poDevOld.pptlOrigin()->y;
// meta coordinate to poDevice coordinate
ptlDst.x -= poDevice.pptlOrigin()->x; ptlDst.y -= poDevice.pptlOrigin()->y;
if (bSpUpdatePosition(pSpriteNew, &ptlDst)) { pMetaSpriteNew->apSprite[i] = pSpriteNew; pSpriteNew->pMetaSprite = pMetaSpriteNew; } else { vSpDeleteSprite(pSpriteNew); bError = TRUE; break; } } else { bError = TRUE; break; } }
if (bError) { for (; i > 0; i--) { vSpDeleteSprite(pMetaSpriteNew->apSprite[i - 1]); }
VFREEMEM(pMetaSpriteNew); // Set this to NULL so we don't write on it later.
// This fixes the logic below
pMetaSpriteNew = NULL; } else { pMetaSpriteNew->hwnd = pSpriteOld->hwnd; pMetaSpriteNew->chSprite = pState->cMultiMon; pMetaSpriteNew->fl = 0;
// Add this node to the head of the meta-sprite list:
pMetaSpriteNew->pNext = pState->pListMeta; pState->pListMeta = pMetaSpriteNew; } } } else { WARNING("pSpConvertSpriteToMeta(): cMultiMon is 0\n"); } }
// Delete old sprite
vSpDeleteSprite(pSpriteOld);
return(pMetaSpriteNew); }
/******************************Public*Routine******************************\
* SPRITE* pSpConvertSpriteFromMeta * * On a dynamic mode change, sprite state is transferred between the meta-PDEV * and device PDEV along with the rest of the driver state. As such, we have * to convert "meta sprite" to "sprite" when multi-monitors to 1 monitor mode * change * * 2-Jul-1998 -by- Hideyuki Nagase [hideyukn] * Wrote it. \**************************************************************************/
SPRITE* pSpConvertSpriteFromMeta( HDEV hdevDevNew, HDEV hdevMetaOld, METASPRITE* pMetaSpriteOld) { SPRITE* pSpriteNew; SPRITE* pSpriteOld; SPRITESTATE *pStateMeta; ULONG i;
PDEVOBJ poMeta(hdevMetaOld);
pSpriteNew = NULL;
pStateMeta = poMeta.pSpriteState();
if (pMetaSpriteOld->hwnd == NULL) { // There shouldn't be any non-hwnd associated sprites at this point.
ASSERTGDI(FALSE, "pSpConvertSpriteFromMeta: pSpriteOld is not hwnd associated!"); } else { pSpriteOld = NULL;
// Find a the sprite lives in PDEV which has highest color
// depth, remember this for transfering shape for new
// sprite.
ULONG iDitherHighest = 0;
for (i = 0; i < pMetaSpriteOld->chSprite; i++) { SPRITE* pSpriteTmp = pMetaSpriteOld->apSprite[i];
if (pSpriteTmp) { PDEVOBJ poTmp(pSpriteTmp->pState->hdev);
if (iDitherHighest < poTmp.iDitherFormat()) { pSpriteOld = pSpriteTmp; iDitherHighest = poTmp.iDitherFormat(); } } }
// Convert sprite on old hdev to new hdev.
if (pSpriteOld) { pSpriteNew = pSpCreateSprite(hdevDevNew, NULL, pMetaSpriteOld->hwnd); if (pSpriteNew) { vSpTransferShape(pSpriteNew, pSpriteOld);
// The sprite position needs to be adjusted based on
// origin of the old PDEV
PDEVOBJ poOld(pSpriteOld->pState->hdev); EPOINTL ptlSpritePos = pSpriteOld->ptlDst; ptlSpritePos += *(poOld.pptlOrigin());
if (!bSpUpdatePosition(pSpriteNew, &ptlSpritePos)) { vSpDeleteSprite(pSpriteNew); pSpriteNew = NULL; } } } }
// Delete old sprites on meta sprite.
for (i = 0; i < pMetaSpriteOld->chSprite; i++) { vSpDeleteSprite(pMetaSpriteOld->apSprite[i]); }
// Remove this meta sprite from the linked list :
if (pStateMeta->pListMeta == pMetaSpriteOld) { pStateMeta->pListMeta = pMetaSpriteOld->pNext; } else { for (METASPRITE *pTmp = pStateMeta->pListMeta; pTmp->pNext != pMetaSpriteOld; pTmp = pTmp->pNext) ;
pTmp->pNext = pMetaSpriteOld->pNext; }
// Delete meta sprite.
VFREEMEM(pMetaSpriteOld);
return(pSpriteNew); }
/******************************Public*Routine******************************\
* METASPRITE* pSpMoveSpriteFromMeta * * 1-Sep-1998 -by- Hideyuki Nagase [hideyukn] * Wrote it. \**************************************************************************/
SPRITE* pSpMoveSpriteFromMeta( HDEV hdevDevNew, HDEV hdevMetaOld, METASPRITE* pMetaSpriteOld, ULONG ulIndex) { ULONG i; SPRITE* pSpriteOrg; SPRITE* pSpriteNew = NULL; SPRITESTATE* pStateMeta;
PDEVOBJ poDev(hdevDevNew); PDEVOBJ poMeta(hdevMetaOld);
pStateMeta = poMeta.pSpriteState();
// Pick up the sprite we may reuse.
pSpriteOrg = pMetaSpriteOld->apSprite[ulIndex];
if (pSpriteOrg) { // Make sure the sprite belonging to device specific HDEV,
// actually under DDI. the sprite already lives this DHPDEV.
ASSERTGDI(pSpriteOrg->pState == poDev.pSpriteState(), "ERROR: pState does not points device PDEV");
// And this sprite no longer has meta sprite, the meta sprite
// will be deleted in below.
pSpriteOrg->pMetaSprite = NULL; }
if (pMetaSpriteOld->hwnd == NULL) { // There shouldn't be any non-hwnd associated sprites at this point.
ASSERTGDI(FALSE, "pSpMoveSpriteFromMeta: pSpriteOld is not hwnd associated!"); } else { pSpriteNew = pSpriteOrg; }
// Delete old sprites on meta sprite.
for (i = 0; i < pMetaSpriteOld->chSprite; i++) { if ((i != ulIndex) || (pSpriteNew == NULL)) { vSpDeleteSprite(pMetaSpriteOld->apSprite[i]); } }
// Remove this meta sprite from the linked list :
if (pStateMeta->pListMeta == pMetaSpriteOld) { pStateMeta->pListMeta = pMetaSpriteOld->pNext; } else { for (METASPRITE *pTmp = pStateMeta->pListMeta; pTmp->pNext != pMetaSpriteOld; pTmp = pTmp->pNext) ;
pTmp->pNext = pMetaSpriteOld->pNext; }
// Delete meta sprite.
VFREEMEM(pMetaSpriteOld);
return(pSpriteNew); }
/******************************Public*Routine******************************\
* METASPRITE* pSpTransferMetaSprite * * 30-Aug-1998 -by- Hideyuki Nagase [hideyukn] * Wrote it. \**************************************************************************/
METASPRITE* pSpTransferMetaSprite( HDEV hdevNew, HDEV hdevOld, METASPRITE* pMetaSpriteOld) { SPRITESTATE* pStateNew; SPRITESTATE* pStateOld; METASPRITE* pMetaSpriteNew; SPRITE* pSprite; ULONG i, j; BOOL bError = FALSE;
PDEVOBJ poNew(hdevNew); PDEVOBJ poOld(hdevOld);
pStateNew = poNew.pSpriteState(); pStateOld = poOld.pSpriteState();
if (pMetaSpriteOld->hwnd == NULL) { // There shouldn't be any non-hwnd associated sprites at this point.
ASSERTGDI(FALSE, "pSpTransferMetaSprite: pSpriteOld is not hwnd associated!"); } else { // Create METASPRITE on new hdev.
ULONG cjAlloc = sizeof(METASPRITE) + pStateNew->cMultiMon * sizeof(pMetaSpriteNew->apSprite[0]);
if (pMetaSpriteOld->fl & SPRITE_FLAG_NO_WINDOW) { // If the metasprite is marked for deletion, don't reallocate a new
// metasprite. The cleanup code below will make sure the old
// metasprite and its child sprites are deleted.
pMetaSpriteNew = NULL; } else { pMetaSpriteNew = (METASPRITE*) PALLOCMEM(cjAlloc, 'mpsG'); }
if (pMetaSpriteNew) { SPRITE *pSpriteBest = NULL; HDEV hdevBest = NULL; ULONG iFormatBest = 0;
// Transfer sprite from old meta to new as much as possible.
for (i = 0 ; i < pStateNew->cMultiMon ; i++) { for (j = 0; j < pMetaSpriteOld->chSprite ; j++) { pSprite = pMetaSpriteOld->apSprite[j];
if (pSprite) { PDEVOBJ poTmp(pSprite->pState->hdev);
// if pointer to pState is same, we can transfer
// to new pdev.
if (pStateNew == pSprite->pState) { // move this sprite to new meta sprite from old.
pMetaSpriteNew->apSprite[i] = pSprite; pMetaSpriteOld->apSprite[j] = NULL;
pSprite->pMetaSprite = pMetaSpriteNew; }
// if the sprite lives in PDEV which has higher color
// depth, remember this for transfering shape for new
// sprite
if (iFormatBest < poTmp.iDitherFormat()) { pSpriteBest = pSprite; hdevBest = poTmp.hdev(); iFormatBest = poTmp.iDitherFormat(); } } } }
// fill up other meta sprite fields
pMetaSpriteNew->hwnd = pMetaSpriteOld->hwnd; pMetaSpriteNew->chSprite = pStateNew->cMultiMon; pMetaSpriteNew->fl = 0;
// if there is any sprite which can not be trasnferred from old,
// create them
for (i = 0 ; i < pMetaSpriteNew->chSprite ; i++) { if (pMetaSpriteNew->apSprite[i] == NULL) { PDEVOBJ poDevice(pStateNew->ahdevMultiMon[i]); SPRITELOCK slockDevice(poDevice);
pSprite= pSpCreateSprite(poDevice.hdev(), NULL, pMetaSpriteOld->hwnd);
if (pSprite) { PDEVOBJ poBestHdev(hdevBest); SPRITELOCK slockBest(poBestHdev); POINTL ptlDst;
// copy sprite shape from best existing sprite.
vSpTransferShape(pSprite, pSpriteBest);
// The sprite position need to be adjusted based on
// poDevice origin. need to convert from hdevBest
// coordinate.
// poBestHdev coordinate to meta coordinate
ptlDst.x = pSpriteBest->ptlDst.x + poBestHdev.pptlOrigin()->x; ptlDst.y = pSpriteBest->ptlDst.y + poBestHdev.pptlOrigin()->y;
// meta coordinate to poDevice coordinate
ptlDst.x -= poDevice.pptlOrigin()->x; ptlDst.y -= poDevice.pptlOrigin()->y;
if (bSpUpdatePosition(pSprite, &ptlDst)) { // Put the sprite into meta sprite.
pMetaSpriteNew->apSprite[i] = pSprite; pSprite->pMetaSprite = pMetaSpriteNew; } else { vSpDeleteSprite(pSprite); bError = TRUE; } } else { bError = TRUE; } }
if (bError) { // if there is any error, stop looping, no more creation.
break; } }
if (!bError) { // Add this node to the head of the meta-sprite list:
pMetaSpriteNew->pNext = pStateNew->pListMeta; pStateNew->pListMeta = pMetaSpriteNew; } } }
// check any sprite left in old meta, if so delete them.
for (i = 0; i < pMetaSpriteOld->chSprite ; i++) { if (pMetaSpriteOld->apSprite[i] != NULL) { vSpDeleteSprite(pMetaSpriteOld->apSprite[i]); } }
// Remove old meta sprite from the linked list :
if (pStateOld->pListMeta == pMetaSpriteOld) { pStateOld->pListMeta = pMetaSpriteOld->pNext; } else { for (METASPRITE *pTmp = pStateOld->pListMeta; pTmp->pNext != pMetaSpriteOld; pTmp = pTmp->pNext) ;
pTmp->pNext = pMetaSpriteOld->pNext; }
if (bError) { // Delete new meta sprite.
for (i = 0; i < pMetaSpriteNew->chSprite ; i++) { if (pMetaSpriteNew->apSprite[i] != NULL) { vSpDeleteSprite(pMetaSpriteNew->apSprite[i]); } }
VFREEMEM(pMetaSpriteNew);
pMetaSpriteNew = NULL; }
// Delete meta sprite.
VFREEMEM(pMetaSpriteOld);
return(pMetaSpriteNew); }
/******************************Public*Routine******************************\
* VOID vSpDynamicModeChange * * This function transfers all layered-window sprites from one PDEV to the * other. * * Note that it transfers only layered-window sprites. Sprites that don't * have an associated hwnd are left with the old PDEV. * * 2-Jul-1998 -by- Hideyuki Nagase [hideyukn] * DDML (meta) mode change support. * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
VOID vSpDynamicModeChange( HDEV hdevA, HDEV hdevB) { SPRITESTATE StateTmp; SPRITESTATE* pStateA; SPRITESTATE* pStateB; ULONG ulTmp; SPRITE* pSprite; SPRITE* pSpriteNext; SPRITE* pSpriteNew; METASPRITE* pMetaSprite; METASPRITE* pMetaSpriteNew; METASPRITE* pMetaSpriteNext; UINT i, j;
PDEVOBJ poA(hdevA); PDEVOBJ poB(hdevB);
// Sprite state, being 'below the DDI level', is device specific.
// As such, it is tranferred along with the rest of the device
// specific state on the dynamic mode change.
//
// Do that now:
pStateA = poA.pSpriteState(); pStateB = poB.pSpriteState();
StateTmp = *pStateA; *pStateA = *pStateB; *pStateB = StateTmp;
// Swap back the drag-rect width:
ulTmp = pStateA->ulDragDimension; pStateA->ulDragDimension = pStateB->ulDragDimension; pStateB->ulDragDimension = ulTmp;
// Now find any 'hdev' references and correct them:
vSpCorrectHdevReferences(pStateA, hdevA); vSpCorrectHdevReferences(pStateB, hdevB);
// Now that we've transferred the state, grab the locks:
SPRITELOCK slockA(poA); SPRITELOCK slockB(poB);
pSprite = pStateA->pBottomCursor;
while(pSprite != NULL) { SPRITE* pNextSprite = pSprite->pNextZ; vSpDeleteSprite(pSprite); pSprite = pNextSprite; }
pStateA->pTopCursor = NULL; pStateA->pBottomCursor = NULL; pStateA->ulNumCursors = 0; pSprite = pStateB->pBottomCursor;
while(pSprite != NULL) { SPRITE* pNextSprite = pSprite->pNextZ; vSpDeleteSprite(pSprite); pSprite = pNextSprite; }
pStateB->pTopCursor = NULL; pStateB->pBottomCursor = NULL; pStateB->ulNumCursors = 0; // But the sprites themselves logically stay with the old PDEV
// and shouldn't be transferred in the first place. So
// now that we've transferred the broad state, we have to go
// back through and individually transfer every sprites back
// to their original PDEV, accounting for the new display
// format at the same time.
if (poA.bMetaDriver() && poB.bMetaDriver()) { // Exchange all Meta sprites between meta PDEVs.
pMetaSprite = pStateA->pListMeta; while (pMetaSprite != NULL) { // Grab the 'next' pointer while we can, because we're about
// to delete 'pMetaSprite'!
pMetaSpriteNext = pMetaSprite->pNext;
pMetaSpriteNew = pSpTransferMetaSprite(hdevB, hdevA, pMetaSprite); if (pMetaSpriteNew != NULL) { // Mark the new sprites in 'B's list as being just
// of transferring:
pMetaSpriteNew->fl |= SPRITE_FLAG_JUST_TRANSFERRED; }
pMetaSprite = pMetaSpriteNext; }
// Transfer all the sprites that now live in 'B' back to 'A',
// skipping the ones we just transferred:
pMetaSprite = pStateB->pListMeta; while (pMetaSprite != NULL) { // Grab the 'next' pointer while we can, because we're about
// to delete 'pMetaSprite'!
pMetaSpriteNext = pMetaSprite->pNext;
// The 'just-transferred' flag is so that we don't transfer
// back to A sprites we just transferred to B:
if (!(pMetaSprite->fl & SPRITE_FLAG_JUST_TRANSFERRED)) { pMetaSpriteNew = pSpTransferMetaSprite(hdevA, hdevB, pMetaSprite); } else { pMetaSprite->fl &= ~SPRITE_FLAG_JUST_TRANSFERRED; }
pMetaSprite = pMetaSpriteNext; } } else if (!poA.bMetaDriver() && !poB.bMetaDriver()) { pSprite = pStateA->pListZ; while (pSprite != NULL) { // Grab the 'next' pointer while we can, because we're about
// to delete 'pSprite'!
pSpriteNext = pSprite->pNextZ;
pSpriteNew = pSpTransferSprite(hdevB, pSprite); if (pSpriteNew != NULL) { // Mark the new sprites in 'B's list as being just
// of transferring:
pSpriteNew->fl |= SPRITE_FLAG_JUST_TRANSFERRED; }
pSprite = pSpriteNext; }
// Transfer all the sprites that now live in 'B' back to 'A',
// skipping the ones we just transferred:
pSprite = pStateB->pListZ; while (pSprite != NULL) { // Grab the 'next' pointer while we can, because we're about
// to delete 'pSprite'!
pSpriteNext = pSprite->pNextZ;
// The 'just-transferred' flag is so that we don't transfer
// back to A sprites we just transferred to B:
if (!(pSprite->fl & SPRITE_FLAG_JUST_TRANSFERRED)) { pSpriteNew = pSpTransferSprite(hdevA, pSprite); } else { pSprite->fl &= ~SPRITE_FLAG_JUST_TRANSFERRED; }
pSprite = pSpriteNext; } } else { PDEVOBJ poMeta(poA.bMetaDriver() ? hdevA : hdevB); PDEVOBJ poDev(poA.bMetaDriver() ? hdevB : hdevA);
SPRITESTATE* pStateMeta = poMeta.pSpriteState(); SPRITESTATE* pStateDev = poDev.pSpriteState();
BOOL bModeChgBtwnChildAndParent = FALSE;
for (ULONG iIndex = 0; iIndex < pStateMeta->cMultiMon; iIndex++) { // Find poMeta.hdev() (= originally this hdev *WAS* child device)
if (pStateMeta->ahdevMultiMon[iIndex] == poMeta.hdev()) { bModeChgBtwnChildAndParent = TRUE;
// Put a device PDEV.
pStateMeta->ahdevMultiMon[iIndex] = poDev.hdev(); break; } }
if (bModeChgBtwnChildAndParent) { // This must be only happened when 2 to 1 mode change occured.
ASSERTGDI(poA.hdev() == poDev.hdev(),"hdevA must be device PDEV"); ASSERTGDI(poB.hdev() == poMeta.hdev(),"hdevB must be meta PDEV");
// Only scan meta sprite to delete meta sprite and delete it's
// child sprites which we will not use anymore.
pMetaSprite = pStateMeta->pListMeta; while (pMetaSprite != NULL) { // Grab the 'next' pointer while we can, because we're about
// to delete 'pMetaSprite'!
pMetaSpriteNext = pMetaSprite->pNext;
// Get sprite from MetaSprite.
pSpMoveSpriteFromMeta(poDev.hdev(), poMeta.hdev(), pMetaSprite, iIndex);
pMetaSprite = pMetaSpriteNext; } } else { // First, convert sprites in poDev to meta sprites into poMeta
pSprite = pStateDev->pListZ; while (pSprite != NULL) { // Grab the 'next' pointer while we can, because we're about
// to delete 'pSprite'!
pSpriteNext = pSprite->pNextZ;
// Convert to meta sprite. "pSprite" in parameter, will be
// deleted inside pSpConvertSpriteToMeta().
pMetaSpriteNew = pSpConvertSpriteToMeta(poMeta.hdev(), poDev.hdev(), pSprite);
if (pMetaSpriteNew != NULL) { // Mark the new sprites in 'B's list as being just
// of transferring:
pMetaSpriteNew->fl |= SPRITE_FLAG_JUST_TRANSFERRED; }
pSprite = pSpriteNext; }
// Second, convert sprites in poMeta to regular sprites into poDev.
pMetaSprite = pStateMeta->pListMeta; while (pMetaSprite != NULL) { // Grab the 'next' pointer while we can, because we're about
// to delete 'pMetaSprite'!
pMetaSpriteNext = pMetaSprite->pNext;
// The 'just-transferred' flag is so that we don't convert
// back to regular sprites we just converted to meta.
if (!(pMetaSprite->fl & SPRITE_FLAG_JUST_TRANSFERRED)) { // Convert from meta sprite. "pMetaSprite" in parameter, will
// be deleted inside pSpConvertSpriteFromMeta().
pSpriteNew = pSpConvertSpriteFromMeta(poDev.hdev(), poMeta.hdev(), pMetaSprite); } else { pMetaSprite->fl &= ~SPRITE_FLAG_JUST_TRANSFERRED; }
pMetaSprite = pMetaSpriteNext; } } } }
/***************************************************************************\
* pSpGetSprite * * Returns the pointer to the associated sprite, given either a sprite * handle or a window handle. * * 8-Oct-1997 -by- Vadim Gorokhovsky [vadimg] * Wrote it. \***************************************************************************/
SPRITE* pSpGetSprite( SPRITESTATE* pState, HWND hwnd, HANDLE hSprite = NULL) { SPRITE* pSprite;
pSprite = (SPRITE*) hSprite; if ((pSprite == NULL) && (hwnd != NULL)) { for (pSprite = pState->pListZ; pSprite != NULL; pSprite = pSprite->pNextZ) { if (pSprite->hwnd == hwnd) break; } }
return(pSprite); }
/***************************************************************************\
* pSpGetMetaSprite * * Returns the pointer to the associated meta-sprite, given either a * meta-sprite handle or a window handle. * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \***************************************************************************/
METASPRITE* pSpGetMetaSprite( SPRITESTATE* pState, HWND hwnd, HANDLE hSprite = NULL) { METASPRITE* pMetaSprite;
ASSERTGDI((hwnd != NULL) || (hSprite != NULL), "Expected a non-NULL handle");
pMetaSprite = (METASPRITE*) hSprite; if ((pMetaSprite == NULL) && (hwnd != NULL)) { for (pMetaSprite = pState->pListMeta; pMetaSprite != NULL; pMetaSprite = pMetaSprite->pNext) { if (pMetaSprite->hwnd == hwnd) break; } }
return(pMetaSprite); }
/***************************************************************************\
* VOID vSpZorderSprite * * Re-arranges the sprite z-order, which goes from bottom-most to top-most. * * 8-Oct-1997 -by- Vadim Gorokhovsky [vadimg] * Wrote it. \***************************************************************************/
VOID vSpZorderSprite( HDEV hdev, SPRITE* pSprite, // May be NULL
SPRITE* pSpriteInsertAfter) // May be NULL to make sprite bottom-most
{ SPRITESTATE* pState; SPRITE* pTmp;
PDEVOBJ po(hdev);
pState = po.pSpriteState();
DEVLOCKOBJ dlo(po); SPRITELOCK slock(po);
pTmp = pState->pListZ; if ((pSprite == NULL) || (pTmp == NULL)) { return; }
// First, unlink pSprite from the list. Setting pSprite->pNextZ
// to NULL is mostly for debug purposes.
if (pTmp == pSprite) { pState->pListZ = pTmp->pNextZ; pTmp->pNextZ = NULL; } else { SPRITE* pSpritePrev;
do {
if (pTmp == pSprite) { pSpritePrev->pNextZ = pTmp->pNextZ; pTmp->pNextZ = NULL; break; }
pSpritePrev = pTmp; pTmp = pTmp->pNextZ;
} while (pTmp != NULL); }
// This would be bad, we probably didn't find it in the list:
if (pSprite->pNextZ != NULL) { WARNING("vSpZorderSprite: sprite not unlinked!"); return; }
if (pSpriteInsertAfter == NULL) { // Insert pSprite as the bottom-most one, i.e. first in the list:
pSprite->pNextZ = pState->pListZ; pState->pListZ = pSprite; } else { pSprite->pNextZ = pSpriteInsertAfter->pNextZ; pSpriteInsertAfter->pNextZ = pSprite; }
vSpRenumberZOrder(pState);
pState->bValidRange = FALSE;
vSpRedrawSprite(pSprite); }
/***************************************************************************\
* BOOL bSpPtInSprite * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \***************************************************************************/
BOOL bSpPtInSprite( SPRITE* pSprite, int x, int y) { BOOL bRet = FALSE; // Assume failure
SPRITESTATE* pState; SURFOBJ* psoHitTest; POINTL OffHitTest; RECTL rclPoint; ULONG* pulPixel; FLONG flModeMasks;
if (!pSprite) { return FALSE; }
pState = pSprite->pState;
PDEVOBJ po(pState->hdev);
DEVLOCKOBJ dlo(po); // Needed to access 'pState'
SPRITELOCK slock(po);
psoHitTest = pState->psoHitTest;
rclPoint.left = x; rclPoint.top = y; rclPoint.right = x + 1; rclPoint.bottom = y + 1;
// Compute the mode masks. Hit surface has same format as the screen.
XEPALOBJ palHitTest( SURFOBJ_TO_SURFACE_NOT_NULL(pSprite->pState->psoScreen)->ppal());
if (palHitTest.bIsBitfields()) { // Bug 280033: Make sure we only check to see if bits contained in
// one of bitfields below have been modified. If we don't do this,
// then we would get the wrong answer in some cases, such as 16bpp
// at 5-5-5 where the alphablend code would zero out the 16th bit,
// which doesn't mean that the sprite is visible because the value
// of that bit is undefined in this particular color resolution.
flModeMasks = palHitTest.flRed() | palHitTest.flGre() | palHitTest.flBlu(); } else { // Doesn't matter what we put here because bits that don't belong to
// the color won't be modified by the alphablend code.
flModeMasks = 0xffffffff; }
// First, see if the point intersects the sprite's bounds.
// If not, we're done.
if (bIntersect(&pSprite->rclSprite, &rclPoint)) { OffHitTest.x = -x; OffHitTest.y = -y;
pulPixel = (ULONG*) psoHitTest->pvScan0;
ASSERTGDI(psoHitTest->iType == STYPE_BITMAP, "Hit-test surface must be STYPE_BITMAP!");
// Okay, now let's narrow it down. To deal with
// alpha, transparency, and funky transforms, we
// accomplish the hit testing by setting a pixel to
// a specific value, asking the sprite to redraw that
// pixel, and then checking to see if the value
// changed.
//
// We actually don't have to do this for rectangular,
// non-rotated opaque sprites...
*pulPixel = 0L; vSpComposite(pSprite, &OffHitTest, psoHitTest, &rclPoint); if (((*pulPixel) & flModeMasks) != 0) { bRet = TRUE; } else { // Unfortunately, the sprite may have chosen to draw in
// the colour that we used to do the check. So to make
// sure, try again using a different colour:
*pulPixel = ~0L; vSpComposite(pSprite, &OffHitTest, psoHitTest, &rclPoint);
bRet = (((*pulPixel) & flModeMasks) != flModeMasks); } }
return(bRet); }
/***************************************************************************\
* VOID vSpUpdateSpriteVisRgn * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \***************************************************************************/
VOID vSpUpdateSpriteVisRgn( HDEV hdev) { SPRITESTATE* pState; SPRITE* pSprite; POINTL Offset; BOOL bMore; BOOL bEqual; REGION* prgnOld; REGION* prgnNew; CLIPENUMRECT ClipOld; CLIPENUMRECT ClipNew; BOOL bOldMore; BOOL bNewMore; ULONG i;
PDEVOBJ po(hdev); pState = po.pSpriteState();
pSprite = pState->pListZ; if (pSprite == NULL) return;
for (pSprite = pState->pListZ; pSprite != NULL; pSprite = pSprite->pNextZ) { if (pSprite->hwnd != NULL) { UserVisrgnFromHwnd(&pState->hrgn, pSprite->hwnd);
RGNMEMOBJ rmoNew; RGNOBJAPI roClip(pState->hrgn, FALSE);
if (!roClip.bValid() || !rmoNew.bValid() || !rmoNew.bCopy(roClip)) { rmoNew.vDeleteRGNOBJ(); } else { // Adjust for this device's multi-mon offset:
Offset.x = -po.pptlOrigin()->x; Offset.y = -po.pptlOrigin()->y;
rmoNew.bOffset(&Offset);
// Assume that the two regions will be equal:
bEqual = TRUE;
// Now check to see if the two clip regions really are equal:
prgnNew = rmoNew.prgnGet(); prgnOld = pSprite->prgnClip;
if (prgnOld == NULL) { // There was no old region. Let's assume the new one
// doesn't have trivial clipping:
bEqual = FALSE; } else { ECLIPOBJ ecoOld; ECLIPOBJ ecoNew; ERECTL erclUnclipped;
// If the sprite was not visible due to clipping,
// bSpUpdatePosition leaves 'rclSprite' as empty.
// Consequently, we can't use 'rclSprite' to determine
// the clip object complexity. Re-derive the
// unclipped sprite bounds:
erclUnclipped.left = pSprite->ptlDst.x; erclUnclipped.top = pSprite->ptlDst.y; erclUnclipped.right = erclUnclipped.left + (pSprite->rclSrc.right - pSprite->rclSrc.left); erclUnclipped.bottom = erclUnclipped.top + (pSprite->rclSrc.bottom - pSprite->rclSrc.top);
ecoOld.vSetup(prgnOld, erclUnclipped); ecoNew.vSetup(prgnNew, erclUnclipped);
if (ecoOld.erclExclude().bEmpty() ^ ecoNew.erclExclude().bEmpty()) { // One or the other (but not both) are empty, so are
// unequal:
bEqual = FALSE; } else if ((ecoOld.iDComplexity == DC_TRIVIAL) && (ecoNew.iDComplexity == DC_TRIVIAL)) { // Both are trivially clipped, so are equal.
} else if (ecoOld.iDComplexity != ecoNew.iDComplexity) { // The clipping complexity is different, so the regions are
// unequal:
bEqual = FALSE; } else { // Okay, we've got work to do. We want to see if the
// regions are any different where it intersects with the
// sprite. We do this by constructing and enumerating
// corresponding clip objects:
ecoOld.cEnumStart(FALSE, CT_RECTANGLES, CD_ANY, 100); ecoNew.cEnumStart(FALSE, CT_RECTANGLES, CD_ANY, 100);
bOldMore = TRUE; bNewMore = TRUE;
do { ClipOld.c = 0; ClipNew.c = 0;
if (bOldMore) bOldMore = ecoOld.bEnum(sizeof(ClipOld), &ClipOld); if (bNewMore) bNewMore = ecoNew.bEnum(sizeof(ClipNew), &ClipNew);
if (ClipOld.c != ClipNew.c) { bEqual = FALSE; break; }
for (i = 0; i < ClipOld.c; i++) { if ((ClipNew.arcl[i].left != ClipOld.arcl[i].left) || (ClipNew.arcl[i].top != ClipOld.arcl[i].top) || (ClipNew.arcl[i].right != ClipOld.arcl[i].right) || (ClipNew.arcl[i].bottom != ClipOld.arcl[i].bottom)) { bEqual = FALSE; bOldMore = FALSE; bNewMore = FALSE; break; } } } while (bOldMore || bNewMore); } }
// Free the old region (if any) and set the new one:
vSpFreeClipResources(pSprite); pSprite->prgnClip = prgnNew; pSprite->prgnClip->vStamp();
// Grab some locks we need for drawing.
PDEVOBJ po(pState->hdev);
DEVLOCKOBJ dlo(po); SPRITELOCK slock(po);
// We detect the case when the sprite has an empty VisRgn
// primarily for DirectDraw, so that we can unhook sprites
// and get out of its way when it goes full-screen.
//
// Note that we have to do this obscured check even if 'bEqual'
// is true because 'bEqual' applies only to the intersection
// with the current sprite rectangle, and the obscuring flag is
// independent of the sprite size or location.
pSprite->fl &= ~SPRITE_FLAG_CLIPPING_OBSCURED; if (rmoNew.bInside(&pState->rclScreen) != REGION_RECT_INTERSECT) pSprite->fl |= SPRITE_FLAG_CLIPPING_OBSCURED;
bSpUpdatePosition(pSprite, &pSprite->ptlDst);
if (gpto != NULL) { vSpCheckForWndobjOverlap(pState, &pSprite->rclSprite, &pSprite->rclSprite); }
// Only if the regions, intersected with the sprite, are unequal
// do we do the repaint. We do this because we get called
// constantly when any window is moving, even if none of the
// movement intersects with the sprite.
if (!bEqual) { // Note that we could go further and just redraw the
// difference in the regions. At this point, I can't
// be bothered. We'll redraw the whole thing.
vSpRedrawSprite(pSprite); } } } } }
/////////////////////////////////////////////////////////////////////////////
// Window Manager callable functions
/////////////////////////////////////////////////////////////////////////////
/***************************************************************************\
* BOOL GreCreateSprite * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \***************************************************************************/
HANDLE GreCreateSprite( HDEV hdev, HWND hwnd, RECT* prc) { SPRITESTATE* pState; METASPRITE* pMetaSprite; SPRITE* pSprite; ULONG cjAlloc; ULONG i; HANDLE pRet = NULL;
PDEVOBJ po(hdev);
pState = po.pSpriteState();
// When running multi-mon, handle the creation of the meta sprite:
if (pState->cMultiMon) { cjAlloc = sizeof(METASPRITE) + pState->cMultiMon * sizeof(pMetaSprite->apSprite[0]);
pMetaSprite = (METASPRITE*) PALLOCNOZ(cjAlloc, 'mpsG'); if (pMetaSprite) { for (i = 0; i < pState->cMultiMon; i++) { PDEVOBJ poMon(pState->ahdevMultiMon[i]);
POINTL *pptlDstInit = NULL; POINTL ptlDstInit;
if (prc) { ptlDstInit.x = prc->left - poMon.pptlOrigin()->x; ptlDstInit.y = prc->top - poMon.pptlOrigin()->y; pptlDstInit = &ptlDstInit; }
pSprite = pSpCreateSprite(pState->ahdevMultiMon[i], (RECTL*) prc, hwnd, pptlDstInit); if (pSprite == NULL) { for (; i > 0; i--) { vSpDeleteSprite(pMetaSprite->apSprite[i - 1]); }
VFREEMEM(pMetaSprite); return(pRet); }
pMetaSprite->apSprite[i] = pSprite; pSprite->pMetaSprite = pMetaSprite; }
pMetaSprite->hwnd = hwnd; pMetaSprite->chSprite = pState->cMultiMon; pMetaSprite->fl = 0;
// Add this node to the head of the meta-sprite list:
pMetaSprite->pNext = pState->pListMeta; pState->pListMeta = pMetaSprite;
pRet = pMetaSprite; } } else { // Note that USER doesn't actually need the pointer to the sprite
// since it uses pSpGetSprite to reference the sprite.
pRet = pSpCreateSprite(hdev, (RECTL*) prc, hwnd); }
return(pRet); }
/***************************************************************************\
* BOOL GreDeleteSprite * * 8-Oct-1997 -by- Vadim Gorokhovsky [vadimg] * Wrote it. \***************************************************************************/
BOOL GreDeleteSprite( HDEV hdev, HWND hwnd, HANDLE hSprite) { SPRITESTATE* pState; METASPRITE* pMetaSprite; METASPRITE* pTmp; SPRITE* pSprite; ULONG i; BOOL bRet = FALSE;
PDEVOBJ po(hdev);
pState = po.pSpriteState(); if (pState->cMultiMon) { pMetaSprite = pSpGetMetaSprite(pState, hwnd, hSprite); if (pMetaSprite) { for (i = 0; i < pState->cMultiMon; i++) { vSpDeleteSprite(pMetaSprite->apSprite[i]); }
// Remove this sprite from the linked list:
if (pState->pListMeta == pMetaSprite) { pState->pListMeta = pMetaSprite->pNext; } else { for (pTmp = pState->pListMeta; pTmp->pNext != pMetaSprite; pTmp = pTmp->pNext) ;
pTmp->pNext = pMetaSprite->pNext; }
VFREEMEM(pMetaSprite);
bRet = TRUE; } } else { pSprite = pSpGetSprite(pState, hwnd, hSprite); if (pSprite) { vSpDeleteSprite(pSprite); bRet = TRUE; } }
return(bRet); }
/***************************************************************************\
* BOOL GreGetSpriteAttributes * * 14-Mar-2000 -by- Jeff Stall [jstall] * Wrote it. \***************************************************************************/
BOOL GreGetSpriteAttributes( HDEV hdev, HWND hwnd, HANDLE hSprite, COLORREF* lpcrKey, BLENDFUNCTION* pblend, DWORD* pdwFlags) { SPRITESTATE* pState; SPRITE* pSprite = NULL; BOOL bRet = FALSE; PDEVOBJ po(hdev);
ASSERTGDI(lpcrKey != NULL, "Ensure valid pointer"); ASSERTGDI(pblend != NULL, "Ensure valid pointer"); ASSERTGDI(pdwFlags != NULL, "Ensure valid pointer");
//
// Get the sprite object.
//
pState = po.pSpriteState(); if (pState->cMultiMon) { //
// On a multimon system, query the values from the first sprite.
//
METASPRITE * pMetaSprite = pSpGetMetaSprite(pState, hwnd, hSprite); if (pMetaSprite != NULL) { pSprite = pMetaSprite->apSprite[0]; ASSERTGDI(pSprite != NULL, "Sprite should exist"); } } else { pSprite = pSpGetSprite(pState, hwnd, hSprite); }
//
// Query the data from the sprite.
//
if (pSprite != NULL) { bRet = TRUE;
*lpcrKey = pSprite->cachedAttributes.crKey; *pblend = pSprite->cachedAttributes.bf; *pdwFlags = pSprite->cachedAttributes.dwShape; }
return(bRet); }
/***************************************************************************\
* BOOL GreUpdateSprite * * 8-Oct-1997 -by- Vadim Gorokhovsky [vadimg] * Wrote it. \***************************************************************************/
BOOL GreUpdateSprite( HDEV hdev, HWND hwnd, HANDLE hSprite, HDC hdcDst, POINT* pptDst, SIZE* psize, HDC hdcSrc, POINT* pptSrc, COLORREF crKey, BLENDFUNCTION* pblend, DWORD dwShape, RECT* prcDirty) { SPRITESTATE* pState; METASPRITE* pMetaSprite; SPRITE* pSprite; ULONG i; POINTL* pptlDstTmp; POINTL ptlDstTmp; BOOL bRet = FALSE; ERECTL erclDirty;
if (prcDirty) { // Let's make sure we don't modify the caller memory pointed to by
// prcldirty
erclDirty = *((ERECTL *) prcDirty); prcDirty = (RECT *) &erclDirty; }
PDEVOBJ po(hdev);
if (po.bDisabled()) return bRet;
pState = po.pSpriteState(); if (pState->cMultiMon) { pMetaSprite = pSpGetMetaSprite(pState, hwnd, hSprite); if (pMetaSprite) { bRet = TRUE; // This allows us to record single monitor
// failures by ANDing bRet with the return
// value from bSpUpdateSprite
for (i = 0; i < pState->cMultiMon; i++) { PDEVOBJ po(pState->ahdevMultiMon[i]);
pptlDstTmp = NULL; if (pptDst != NULL) { ptlDstTmp.x = pptDst->x - po.pptlOrigin()->x; ptlDstTmp.y = pptDst->y - po.pptlOrigin()->y; pptlDstTmp = &ptlDstTmp; }
bRet &= bSpUpdateSprite(pMetaSprite->apSprite[i], hdcDst, pptlDstTmp, psize, hdcSrc, (POINTL*) pptSrc, crKey, pblend, dwShape, (RECTL*)prcDirty); } } } else { pSprite = pSpGetSprite(pState, hwnd, hSprite); if (pSprite) { bRet = bSpUpdateSprite(pSprite, hdcDst, (POINTL*) pptDst, psize, hdcSrc, (POINTL*) pptSrc, crKey, pblend, dwShape, (RECTL*) prcDirty); } }
return(bRet); }
/***************************************************************************\
* BOOL GrePtInSprite * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \***************************************************************************/
BOOL APIENTRY GrePtInSprite( HDEV hdev, HWND hwnd, int x, int y) { SPRITESTATE* pState; METASPRITE* pMetaSprite; SPRITE* pSprite; ULONG i; BOOL bRet = FALSE;
PDEVOBJ po(hdev);
pState = po.pSpriteState(); if (pState->cMultiMon) { pMetaSprite = pSpGetMetaSprite(pState, hwnd); if (pMetaSprite) { for (i = 0; i < pState->cMultiMon; i++) { PDEVOBJ po(pState->ahdevMultiMon[i]);
if (bSpPtInSprite(pMetaSprite->apSprite[i], x - po.pptlOrigin()->x, y - po.pptlOrigin()->y)) { bRet = TRUE; break; } } } } else { pSprite = pSpGetSprite(pState, hwnd); if (pSprite) { bRet = bSpPtInSprite(pSprite, x, y); } }
return(bRet); }
/***************************************************************************\
* BOOL GreUpdateSpriteVisRgn * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \***************************************************************************/
VOID APIENTRY GreUpdateSpriteVisRgn( HDEV hdev) { ULONG i; SPRITESTATE* pState; BOOL bRet = TRUE;
PDEVOBJ po(hdev); pState = po.pSpriteState();
if (pState->cMultiMon) { for (i = 0; i < pState->cMultiMon; i++) { vSpUpdateSpriteVisRgn(pState->ahdevMultiMon[i]); } } else { vSpUpdateSpriteVisRgn(pState->hdev); } }
/***************************************************************************\
* BOOL GreZorderSprite * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \***************************************************************************/
VOID APIENTRY GreZorderSprite( HDEV hdev, HWND hwnd, HWND hwndInsertAfter) { SPRITESTATE* pState; ULONG i;
PDEVOBJ po(hdev); pState = po.pSpriteState();
if (pState->cMultiMon) { for (i = 0; i < pState->cMultiMon; i++) { HDEV hdevSingle = pState->ahdevMultiMon[i]; PDEVOBJ poSingle(hdevSingle); SPRITESTATE *pSpriteStateSingle = poSingle.pSpriteState();
vSpZorderSprite(hdevSingle, pSpGetSprite(pSpriteStateSingle, hwnd), pSpGetSprite(pSpriteStateSingle, hwndInsertAfter)); } } else { vSpZorderSprite(pState->hdev, pSpGetSprite(pState, hwnd), pSpGetSprite(pState, hwndInsertAfter)); } }
/////////////////////////////////////////////////////////////////////////////
// Software Cursors
/////////////////////////////////////////////////////////////////////////////
/******************************Public*Routine******************************\
* BOOL SpUpdateCursor * * Updates the shape for a cursor. * * Returns FALSE if the function fails, and sets both 'pSprite->psoMask' * and 'pSprite->psoShape' to NULL. * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
BOOL bSpUpdateCursor( SPRITE* pSprite, SURFOBJ* psoMono, SURFOBJ* psoColor, XLATEOBJ* pxlo, RECTL* prclBounds) // Defines the parts of 'psoMono' and
// 'psoColor' to be used
{ BOOL bRet = FALSE; // Assume failure
SPRITESTATE* pState; RECTL rclSrc; SURFOBJ* psoMask; DEVBITMAPINFO dbmi; SURFMEM SurfDimo;
pState = pSprite->pState;
PDEVOBJ po(pState->hdev);
// Initialize some state for the sprite:
pSprite->rclSrc = *prclBounds;
if (psoMono == NULL) { // Handle the alpha cursor case:
ASSERTGDI((psoColor != NULL) && (psoColor->iBitmapFormat == BMF_32BPP), "Expected BGRA surface"); ASSERTGDI((prclBounds->right <= (psoColor->sizlBitmap.cx)) && (prclBounds->bottom <= (psoColor->sizlBitmap.cy)), "Bounds out of bounds");
// Mark this as an 'alpha' sprite:
pSprite->dwShape = ULW_ALPHA; pSprite->BlendFunction.AlphaFormat = AC_SRC_ALPHA; pSprite->BlendFunction.BlendOp = AC_SRC_OVER; pSprite->BlendFunction.BlendFlags = 0; pSprite->BlendFunction.SourceConstantAlpha = 0xff;
vSpCreateShape(pSprite, &gptlZero, psoColor, NULL, prclBounds, gppalRGB, BMF_32BPP, TRUE); // Allocate from system memory because the
// Alpha Blending will most likely be done by
// the CPU, and it's expensive to transfer the
// cursor bitmap across the bus every time
bRet = (pSprite->psoShape != NULL); } else { // Handle the non-alpha cursor case:
ASSERTGDI((prclBounds->right <= (psoMono->sizlBitmap.cx)) && (prclBounds->bottom <= (psoMono->sizlBitmap.cy >> 1)), "Bounds out of bounds");
psoMask = pSprite->psoMask; if (psoMask != NULL) { // If the new mask is different sized than the old cached mask,
// free up the old cached mask:
if ((psoMask->sizlBitmap.cx != psoMono->sizlBitmap.cx) || (psoMask->sizlBitmap.cy != psoMono->sizlBitmap.cy)) { bDeleteSurface(psoMask->hsurf); psoMask = NULL; } }
// Create a surface to hold a copy of the mask:
if (psoMask == NULL) { dbmi.iFormat = BMF_1BPP; dbmi.cxBitmap = psoMono->sizlBitmap.cx; dbmi.cyBitmap = psoMono->sizlBitmap.cy; dbmi.fl = BMF_TOPDOWN; dbmi.hpal = NULL;
if (SurfDimo.bCreateDIB(&dbmi, NULL)) { psoMask = SurfDimo.pSurfobj(); SurfDimo.vKeepIt(); SurfDimo.vSetPID(OBJECT_OWNER_PUBLIC); } }
// Copy the mask:
pSprite->psoMask = psoMask; if (psoMask != NULL) { // Use the given bounds for the copy, adjusting 'bottom' so that
// we can copy both the 'AND' parts and the 'OR' parts in one blt:
rclSrc = *prclBounds; rclSrc.bottom += (psoMask->sizlBitmap.cy >> 1);
EngCopyBits(psoMask, psoMono, NULL, NULL, &rclSrc, (POINTL*) &rclSrc); }
// Now account for the color surface, if there is one:
if (psoColor == NULL) { vSpDeleteShape(pSprite);
bRet = TRUE; } else { vSpCreateShape(pSprite, &gptlZero, psoColor, pxlo, prclBounds, po.ppalSurf(), 0, FALSE); // Allocate from video memory if
// possible, because the blit can be
// done entirely on the video card, and
// it's faster when the cursor bitmap is
// already there.
bRet = (pSprite->psoShape != NULL); }
// Finally, mark this as a non-alpha 'cursor' sprite, and update some of
// the sprite fields that would normally be updated by vSpCreateShape:
pSprite->dwShape = ULW_CURSOR; pSprite->flModeMasks = pState->flModeMasks; pSprite->iModeFormat = pState->iModeFormat; }
return(bRet); }
/***************************************************************************\
* ULONG EngMovePointer * * Move the engine managed pointer on the device. * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \***************************************************************************/
VOID EngMovePointer( SURFOBJ* pso, LONG x, LONG y, RECTL* prcl) // Ignored
{ GDIFunctionID(EngMovePointer);
SPRITESTATE* pState; SPRITE* pSprite; POINTL ptlDst;
PDEVOBJ po(pso->hdev);
pState = po.pSpriteState();
if (pState->pTopCursor != NULL) { SPRITELOCK slock(po);
if(pState->pBottomCursor != pState->pTopCursor) { // rotate cursors if enough time has past
ULONG tickCount = NtGetTickCount(); ULONG elapsedTicks = tickCount - pState->ulTrailTimeStamp;
if(elapsedTicks >= pState->ulTrailPeriod) { // trial maintenance time ... either spawn off a new trail cursor
// or hide the oldest
// find the first trail cursor (cursor before top)
pSprite = pState->pBottomCursor; while(pSprite->pNextZ != pState->pTopCursor) pSprite = pSprite->pNextZ;
// if it does not coincide with top cursor then spawn off another
if(pSprite->rclSprite.left != pState->pTopCursor->rclSprite.left || pSprite->rclSprite.top != pState->pTopCursor->rclSprite.top) { // hide the bottom most cursor
pSprite = pState->pBottomCursor; bSpUpdatePosition(pSprite, NULL); // rotate hidden cursor to front most
pState->pBottomCursor = pSprite->pNextZ; // Reorder sprites
vSpZorderSprite(pso->hdev, pSprite, pState->pTopCursor); pState->pTopCursor = pSprite; } else { // otherwise hide oldest visible trail
pSprite = pState->pBottomCursor; while(pSprite != pState->pTopCursor) { if (pSprite->fl & SPRITE_FLAG_VISIBLE) { ASSERTGDI(pSprite->rclSprite.left != pSprite->rclSprite.right, "Invalid rclSprite for visible sprite.\n"); bSpUpdatePosition(pSprite, NULL); break; } pSprite = pSprite->pNextZ; }
}
// update time stamp
pState->ulTrailTimeStamp = tickCount;
}
} if (x == -1) { // hide all cursors
ptlDst.x = LONG_MAX; ptlDst.y = LONG_MAX;
pSprite = pState->pBottomCursor; while(pSprite != NULL) { bSpUpdatePosition(pSprite, &ptlDst, FALSE);
pSprite = pSprite->pNextZ; } } else { ptlDst.x = x - pState->xHotCursor; ptlDst.y = y - pState->yHotCursor;
// update the position of the top most cursor
pSprite = pState->pTopCursor;
bSpUpdatePosition(pSprite, &ptlDst); vSpRedrawSprite(pSprite); }
// Nothing is more irritating than having a cursor that 'lags'. If
// the driver is a DDI batching driver (meaning that it updates the
// screen only occassionally unless we explicitly call DrvSynchronize),
// let's force it to update the screen now:
if (po.flGraphicsCaps2() & GCAPS2_SYNCTIMER) { po.vSync(po.pSurface()->pSurfobj(), NULL, DSS_TIMER_EVENT); } } }
/***************************************************************************\
* ULONG EngSetPointerShape * * Sets the pointer shape for the GDI pointer simulations. * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \***************************************************************************/
ULONG EngSetPointerShape( SURFOBJ* pso, SURFOBJ* psoMask, SURFOBJ* psoColor, XLATEOBJ* pxlo, LONG xHot, LONG yHot, LONG x, LONG y, RECTL* prclBounds, FLONG fl) { HDEV hdev; SPRITESTATE* pState; SPRITE* pSprite; ULONG ulRet = SPS_ACCEPT_NOEXCLUDE; ULONG numCursors = ((fl & SPS_LENGTHMASK) >> 8) + 1; ULONG ulFreq = (fl & SPS_FREQMASK) >> 12; ULONG ulTrailPeriod = (ulFreq == 0 ? 0 : 1000 / ulFreq);
hdev = pso->hdev;
PDEVOBJ po(hdev);
pState = po.pSpriteState();
// Handle the hide case. We take the opportunity to delete the sprite,
// so that we can start out fresh the next time.
if ((psoMask == NULL) && (psoColor == NULL)) {
pSprite = pState->pBottomCursor;
while(pSprite != NULL) { SPRITE* pNextSprite = pSprite->pNextZ; vSpDeleteSprite(pSprite); pSprite = pNextSprite; }
pState->pTopCursor = NULL; pState->pBottomCursor = NULL; pState->ulNumCursors = 0;
return(SPS_ACCEPT_NOEXCLUDE); }
// adjust the number of cursors
while (pState->ulNumCursors < numCursors) { pSprite = pSpCreateSprite(hdev, NULL, 0);
if(pSprite == NULL) break; if(pState->pTopCursor == NULL) { pState->pTopCursor = pSprite; }
pState->pBottomCursor = pSprite;
pState->ulNumCursors++; }
while(pState->ulNumCursors > numCursors) { pSprite = pState->pBottomCursor; pState->pBottomCursor = pSprite->pNextZ; vSpDeleteSprite(pSprite); pState->ulNumCursors--; }
pState->ulTrailPeriod = ulTrailPeriod;
// Handle the show case.
if (pState->pTopCursor != NULL) { // hide and update the shape of all cursors
pSprite = pState->pBottomCursor;
// hide cursors
{ SPRITELOCK slock(po); while(pSprite != NULL) { bSpUpdatePosition(pSprite, NULL, FALSE); vSpRedrawSprite(pSprite); pSprite = pSprite->pNextZ; } }
// update the shapes
pSprite = pState->pBottomCursor;
while(pSprite != NULL) {
if (!bSpUpdateCursor(pSprite, psoMask, psoColor, pxlo, prclBounds)) { ulRet = SPS_ERROR; break; }
pSprite = pSprite->pNextZ; }
// Remember the hot spot and force EngMovePointer to redraw:
pState->xHotCursor = xHot - prclBounds->left; pState->yHotCursor = yHot - prclBounds->top; }
// Draw the cursor. Note that it's okay if 'pSpriteCursor' is NULL:
EngMovePointer(pso, x, y, NULL);
return(ulRet); }
/******************************Public*Routine******************************\
* ULONG cIntersect * * This routine takes a list of rectangles from 'prclIn' and clips them * in-place to the rectangle 'prclClip'. The input rectangles don't * have to intersect 'prclClip'; the return value will reflect the * number of input rectangles that did intersect, and the intersecting * rectangles will be densely packed. * * History: * 3-Apr-1996 -by- J. Andrew Goossen andrewgo * Wrote it. \**************************************************************************/
ULONG cIntersect( RECTL* prclClip, RECTL* prclIn, // List of rectangles
LONG c) // Can be zero
{ ULONG cIntersections; RECTL* prclOut;
cIntersections = 0; prclOut = prclIn;
for (; c != 0; prclIn++, c--) { prclOut->left = max(prclIn->left, prclClip->left); prclOut->right = min(prclIn->right, prclClip->right);
if (prclOut->left < prclOut->right) { prclOut->top = max(prclIn->top, prclClip->top); prclOut->bottom = min(prclIn->bottom, prclClip->bottom);
if (prclOut->top < prclOut->bottom) { prclOut++; cIntersections++; } } }
return(cIntersections); }
/******************************Public*Routine******************************\
* BOOL bMoveDevDragRect * * Called by USER to move the drag rect on the screen. * * Note: Devlock must already have been acquired. * * History: * 3-Apr-1996 -by- J. Andrew Goossen andrewgo * Wrote it. \**************************************************************************/
BOOL bMoveDevDragRect( HDEV hdev, // Note that this may be a multi-mon meta-PDEV
RECTL *prclNew) { SPRITESTATE* pState; ULONG ulDimension; ULONG crclTemp; ULONG i; RECTL arclTemp[4]; SIZE siz;
PDEVOBJ po(hdev); pState = po.pSpriteState();
po.vAssertDevLock();
ulDimension = pState->ulDragDimension;
arclTemp[0].left = prclNew->left; arclTemp[0].right = prclNew->left + ulDimension; arclTemp[0].top = prclNew->top; arclTemp[0].bottom = prclNew->bottom;
arclTemp[1].left = prclNew->right - ulDimension; arclTemp[1].right = prclNew->right; arclTemp[1].top = prclNew->top; arclTemp[1].bottom = prclNew->bottom;
arclTemp[2].left = prclNew->left + ulDimension; arclTemp[2].right = prclNew->right - ulDimension; arclTemp[2].top = prclNew->top; arclTemp[2].bottom = prclNew->top + ulDimension;
arclTemp[3].left = prclNew->left + ulDimension; arclTemp[3].right = prclNew->right - ulDimension; arclTemp[3].top = prclNew->bottom - ulDimension; arclTemp[3].bottom = prclNew->bottom;
// We have to clip to the specified rectangle in order to handle
// chip-window drag-rects for MDI applications.
crclTemp = cIntersect(&pState->rclDragClip, &arclTemp[0], 4);
for (i = 0; i < crclTemp; i++) { siz.cx = arclTemp[i].right - arclTemp[i].left; siz.cy = arclTemp[i].bottom - arclTemp[i].top;
if (pState->ahDragSprite[i]) { GreUpdateSprite(hdev, NULL, pState->ahDragSprite[i], NULL, (POINT*) &arclTemp[i], &siz, NULL, NULL, NULL, NULL, ULW_DRAGRECT, NULL); } }
for (; i < 4; i++) { if (pState->ahDragSprite[i]) { GreUpdateSprite(hdev, NULL, pState->ahDragSprite[i], NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); } } return(TRUE); }
/******************************Public*Routine******************************\
* BOOL bSetDevDragRect * * Called by USER to slap the drag rect on the screen, or to tear it back * down. * * History: * 4-Apr-1998 -by- J. Andrew Goossen andrewgo * Converted drag rects to use sprites. \**************************************************************************/
BOOL bSetDevDragRect( HDEV hdev, // Note that this may be a multi-mon meta-PDEV
RECTL* prclDrag, RECTL* prclClip) { SPRITESTATE* pState; RECTL rclScreen; RECTL arclDrag[4]; ULONG crclDrag; HANDLE hSprite; ULONG i; BOOL bRet = TRUE; // Assume success
PDEVOBJ po(hdev); pState = po.pSpriteState();
DEVLOCKOBJ dlo(po);
pState->bHaveDragRect = FALSE;
// Create 4 sprites to handle each of the sides of the drag rectangle.
if (prclDrag != NULL) { ASSERTGDI(!pState->bHaveDragRect, "Expected not to have a drag rectangle"); ASSERTGDI(prclClip != NULL, "Expected to have a clip rectangle");
bRet = TRUE; for (i = 0; i < 4; i++) { hSprite = GreCreateSprite(hdev, NULL, NULL); pState->ahDragSprite[i] = hSprite;
if (!hSprite) bRet = FALSE; }
if (bRet) { pState->bHaveDragRect = TRUE; pState->rclDragClip = *prclClip;
bMoveDevDragRect(hdev, prclDrag); } }
// If we don't have a drag rectangle, delete any drag rectangle
// sprites we may have laying around.
if (!pState->bHaveDragRect) { for (i = 0; i < 4; i++) { if (pState->ahDragSprite[i] != NULL) { GreDeleteSprite(hdev, NULL, pState->ahDragSprite[i]); pState->ahDragSprite[i] = NULL; } } }
return(bRet); }
/******************************Public*Routine******************************\
* BOOL bSetDevDragWidth * * Called by USER to tell us how wide the drag rectangle should be. * * 24-Aug-1992 -by- Patrick Haluptzok patrickh * Wrote it. \**************************************************************************/
BOOL bSetDevDragWidth( HDEV hdev, ULONG ulWidth) { PDEVOBJ po(hdev);
DEVLOCKOBJ dlo(po);
SPRITESTATE* pState = po.pSpriteState();
pState->ulDragDimension = ulWidth;
return(TRUE); }
/******************************Public*Routine******************************\
* UNDODESKTOPCOORD * * Temporariliy convert the WNDOBJ from desktop coordinates to device- * relative coordinates. * * History: * 4-Apr-1998 -by- J. Andrew Goossen andrewgo \**************************************************************************/
UNDODESKTOPCOORD::UNDODESKTOPCOORD( EWNDOBJ* pwo, SPRITESTATE* pState) { pwoUndo = NULL; if ((pwo != NULL) && (pwo->fl & WO_RGN_DESKTOP_COORD)) { PDEVOBJ po(pState->hdev); po.vAssertDevLock();
pwoUndo = pwo; xUndo = po.pptlOrigin()->x; yUndo = po.pptlOrigin()->y;
pwo->vOffset(-xUndo, -yUndo); pwo->fl &= ~WO_RGN_DESKTOP_COORD; } }
/******************************Public*Routine******************************\
* VOID vSpDeviceControlSprites * * Undo our temporary conversion of a WNDOBJ to device-relative coordinates. * * History: * 4-Apr-1998 -by- J. Andrew Goossen andrewgo \**************************************************************************/
UNDODESKTOPCOORD::~UNDODESKTOPCOORD() { if (pwoUndo) { pwoUndo->vOffset(xUndo, yUndo); pwoUndo->fl |= WO_RGN_DESKTOP_COORD; } }
/******************************Public*Routine******************************\
* VOID vSpDeviceControlSprites * * Function callable from the driver to control the exclusion of sprites * from on top of a WNDOBJ window. * * History: * 4-Apr-1998 -by- J. Andrew Goossen andrewgo \**************************************************************************/
VOID vSpDeviceControlSprites( HDEV hdev, EWNDOBJ* pwo, FLONG fl) { SPRITESTATE* pState; BOOL bMore; SPRITE* pSprite; RECTL rclEnum; RECTL rclBounds;
PDEVOBJ po(hdev);
po.vAssertDevLock(); SPRITELOCK slock(po);
pState = po.pSpriteState();
// The WNDOBJ coordinates must be device-relative, so
// use UNDO:
UNDODESKTOPCOORD udc(pwo, pState);
if (fl == ECS_TEARDOWN) { pwo->fl |= WO_NOSPRITES;
// We only have to do some work if a sprite overlaps the
// window:
if ((pwo->fl & WO_SPRITE_OVERLAP) && bIntersect(&pwo->rclBounds, &pState->rclScreen, &rclBounds)) { // Tear down all sprites:
ENUMAREAS Enum(pState, &rclBounds);
do { bMore = Enum.bEnum(&pSprite, &rclEnum);
if (pSprite != NULL) { OFFCOPYBITS(&gptlZero, pState->psoScreen, &pSprite->OffUnderlay, pSprite->psoUnderlay, pwo, NULL, &rclEnum, (POINTL*) &rclEnum); } } while (bMore); }
// Now that we're done tearing down, re-compute the
// unlocked region, to account for the newly locked
// area.
vSpComputeUnlockedRegion(pState); } else { // Note that it's perfectly fine to call 'redraw' without having
// first done 'teardown' (this is useful for OpenGL windows when
// hardware double buffering, in order to prevent the cursor from
// flickering).
pwo->fl &= ~WO_NOSPRITES;
// Re-compute the unlocked region, to account for the changed
// state.
vSpComputeUnlockedRegion(pState);
if ((pwo->fl & WO_SPRITE_OVERLAP) && bIntersect(&pwo->rclBounds, &pState->rclScreen, &rclBounds)) { // First, get the new underlay bits:
ENUMAREAS Enum(pState, &rclBounds);
do { bMore = Enum.bEnum(&pSprite, &rclEnum);
if (pSprite != NULL) { // All underlays which overlap with this area must be
// updated, which explains the following 'bEnumLayers':
do { OFFCOPYBITS(&pSprite->OffUnderlay, pSprite->psoUnderlay, &gptlZero, pState->psoScreen, pwo, NULL, &rclEnum, (POINTL*) &rclEnum);
} while (Enum.bEnumLayers(&pSprite)); } } while (bMore);
// Now draw the sprites:
vSpRedrawArea(pState, &rclBounds, TRUE); } } }
/******************************Public*Routine******************************\
* BOOL EngControlSprites * * Multi-mon aware function that's callable from the driver to control the * exclusion of sprites from on top of a WNDOBJ window. * * History: * 4-Apr-1998 -by- J. Andrew Goossen andrewgo \**************************************************************************/
BOOL EngControlSprites( WNDOBJ* pwo, FLONG fl) { SPRITESTATE* pState; ULONG i;
if ((fl != ECS_TEARDOWN) && (fl != ECS_REDRAW)) return(FALSE);
PDEVOBJ po(((EWNDOBJ*) pwo)->pto->pSurface->hdev()); PDEVOBJ poParent(po.hdevParent());
DEVLOCKOBJ dlo(po);
pState = poParent.pSpriteState();
if (pState->cMultiMon) { for (i = 0; i < pState->cMultiMon; i++) { vSpDeviceControlSprites(pState->ahdevMultiMon[i], (EWNDOBJ*) pwo, fl); } } else { vSpDeviceControlSprites(po.hdev(), (EWNDOBJ*) pwo, fl); }
return(TRUE); }
/******************************Public*Routine******************************\
* VOID vSpComputeUnlockedRegion * * Compute the region that describes the area on which sprites are free * to draw (which does not include DirectDraw locked areas, or WNDOBJ * areas that have the WO_NOSPRITES flag set). * * History: * 4-Apr-1998 -by- J. Andrew Goossen andrewgo \**************************************************************************/
VOID vSpComputeUnlockedRegion( SPRITESTATE* pState) { RECTL rcl; SURFACE* pSurface; TRACKOBJ* pto; EWNDOBJ* pwo;
PDEVOBJ po(pState->hdev); po.vAssertDevLock();
// Get rid of the old region:
if (pState->prgnUnlocked != NULL) { pState->prgnUnlocked->vDeleteREGION(); pState->prgnUnlocked = NULL; }
pSurface = SURFOBJ_TO_SURFACE_NOT_NULL(pState->psoScreen);
// We have to to work if either a DirectDraw lock is active, or if a
// WNDOBJ is active.
if ((DxDdGetSurfaceLock(po.hdev()) || gpto != NULL)) { // Calculate the new region:
RGNMEMOBJ rmoUnlocked((BOOL) FALSE); if (rmoUnlocked.bValid()) { rcl.left = 0; rcl.top = 0; rcl.right = po.sizl().cx; rcl.bottom = po.sizl().cy;
rmoUnlocked.vSet(&rcl);
RGNMEMOBJTMP rmoRect((BOOL) FALSE); RGNMEMOBJTMP rmoTmp((BOOL) FALSE);
if (rmoRect.bValid() && rmoTmp.bValid()) { // Loop through the list of DirectDraw locked surfaces and
// remove their locked rectangles from the inclusion region:
RECTL rclDdLocked;
PVOID pvDdSurface = DxDdEnumLockedSurfaceRect(po.hdev(),NULL,&rclDdLocked);
while (pvDdSurface) { // We don't check the return code on 'bCopy' because it
// guarantees that it will maintain valid region constructs
// -- even if the contents are incorrect. And if we fail
// here because we're low on memory, it's guaranteed that
// there will already be plenty of incorrect drawing,
// so we don't care if our inclusion region is
// invalid:
rmoRect.vSet(&rclDdLocked); rmoTmp.bCopy(rmoUnlocked); if (!rmoUnlocked.bMerge(rmoTmp, rmoRect, gafjRgnOp[RGN_DIFF])) { rmoUnlocked.vSet(); }
// Move on to next surface.
pvDdSurface = DxDdEnumLockedSurfaceRect(po.hdev(),pvDdSurface,&rclDdLocked); }
// We must be holding the WNDOBJ semaphore before mucking
// with any WNDOBJs.
SEMOBJ so(ghsemWndobj);
// Now loop through the list of WNDOBJs, and subtract their
// regions.
for (pto = gpto; pto; pto = pto->ptoNext) { for (pwo = pto->pwo; pwo; pwo = pwo->pwoNext) { // The WNDOBJ coordinates must be device-relative, so
// use UNDO:
UNDODESKTOPCOORD udc(pwo, pState);
if (pwo->fl & WO_NOSPRITES) { rmoTmp.bCopy(rmoUnlocked); if (!rmoUnlocked.bMerge(rmoTmp, *pwo, gafjRgnOp[RGN_DIFF])) { rmoUnlocked.vSet(); } } } } }
rmoUnlocked.vStamp(); pState->prgnUnlocked = rmoUnlocked.prgnGet(); } }
// Finally, mark the range cache as invalid so that 'prgnUncovered'
// gets recomputed to incorporate 'prgnUnlocked':
pState->bValidRange = FALSE; }
/******************************Public*Routine******************************\
* VOID vSpUpdateWndobjOverlap * * Recalculate whether any sprites overlap a WNDOBJ area, and notify the * driver if that state has changed. * * History: * 4-Apr-1998 -by- J. Andrew Goossen andrewgo \**************************************************************************/
VOID vSpUpdateWndobjOverlap( SPRITESTATE* pState, EWNDOBJ* pwo) { BOOL bSpriteOverlap; SPRITE* pSprite;
PDEVOBJ po(pState->hdev);
po.vAssertDevLock();
ASSERTGDI(!(pwo->fl & WO_RGN_DESKTOP_COORD), "Use UNDODESKTOPCOORD");
// Ah ha. Recalculate total number of intersections for
// this WNDOBJ.
bSpriteOverlap = FALSE;
for (pSprite = pState->pListZ; pSprite != NULL; pSprite = pSprite->pNextZ) { if (bIntersect(&pwo->rclBounds, &pSprite->rclSprite)) { if (pwo->bInside(&pSprite->rclSprite) == REGION_RECT_INTERSECT) { RGNOBJ roClip(pSprite->prgnClip);
if (!roClip.bValid() || (roClip.bInside(&pwo->rclBounds) == REGION_RECT_INTERSECT)) { bSpriteOverlap = TRUE;
break; } } } }
// Inform the driver if the overlap state for this window
// has changed.
if ((bSpriteOverlap) && !(pwo->fl & WO_SPRITE_OVERLAP)) { pwo->fl |= WO_SPRITE_OVERLAP;
if (pwo->fl & WO_SPRITE_NOTIFY) pwo->pto->vUpdateDrv(pwo, WOC_SPRITE_OVERLAP); } else if ((!bSpriteOverlap) && (pwo->fl & WO_SPRITE_OVERLAP)) { pwo->fl &= ~WO_SPRITE_OVERLAP;
if (pwo->fl & WO_SPRITE_NOTIFY) pwo->pto->vUpdateDrv(pwo, WOC_SPRITE_NO_OVERLAP); } }
/******************************Public*Routine******************************\
* VOID vSpCheckForWndobjOverlap * * Go through all the WNDOBJs and see if either the old or the new sprite * position may have changed the sprite overlap state. * * History: * 4-Apr-1998 -by- J. Andrew Goossen andrewgo \**************************************************************************/
VOID vSpCheckForWndobjOverlap( SPRITESTATE* pState, RECTL* prclNew, RECTL* prclOld) { SURFACE* pSurface; TRACKOBJ* pto; EWNDOBJ* pwo;
PDEVOBJ po(pState->hdev);
po.vAssertDevLock();
pSurface = SURFOBJ_TO_SURFACE_NOT_NULL(pState->psoScreen);
// Hold the WNDOBJ semaphore before mucking with any WNDOBJs.
SEMOBJ so(ghsemWndobj);
for (pto = gpto; pto; pto = pto->ptoNext) { for (pwo = pto->pwo; pwo; pwo = pwo->pwoNext) { // The WNDOBJ coordinates must be device-relative, so use UNDO:
UNDODESKTOPCOORD udc(pwo, pState);
// Note that this cannot be an 'XOR' test, because we're
// only testing the bounds at this point.
if (bIntersect(&pwo->rclBounds, prclNew) || bIntersect(&pwo->rclBounds, prclOld)) { vSpUpdateWndobjOverlap(pState, pwo); } } } }
/******************************Public*Routine******************************\
* VOID vSpDeviceWndobjChange * * Routine to inform the sprite code when a WNDOBJ has changed. * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
VOID vSpDeviceWndobjChange( HDEV hdev, EWNDOBJ* pwo) { SPRITESTATE* pState;
PDEVOBJ po(hdev); pState = po.pSpriteState();
// The WNDOBJ coordinates must be device-relative, so use UNDO:
UNDODESKTOPCOORD udc(pwo, pState);
po.vAssertDevLock();
if (pwo != NULL) { vSpUpdateWndobjOverlap(pState, pwo); }
// Technically, we only have to recompute the regions when a WO_NOSPRITE
// WNDOBJ has been created, destroyed, or moved. But it won't hurt
// anything if we do it for any WNDOBJ.
vSpComputeUnlockedRegion(pState); }
/******************************Public*Routine******************************\
* VOID vSpWndobjChange * * Routine to inform the sprite code when a WNDOBJ has changed. This * routine is multi-mon aware. * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
VOID vSpWndobjChange( HDEV hdev, EWNDOBJ* pwo) { SPRITESTATE* pState; ULONG i;
PDEVOBJ po(hdev);
DEVLOCKOBJ dlo(po);
pState = po.pSpriteState();
if (pState->cMultiMon) { for (i = 0; i < pState->cMultiMon; i++) { vSpDeviceWndobjChange(pState->ahdevMultiMon[i], pwo); } } else { vSpDeviceWndobjChange(hdev, pwo); } }
/******************************Public*Routine******************************\
* BOOL bSpTearDownSprites * * This routine tears-down any sprites in the specified rectangle. * * Returns: TRUE if any sprites were torn down (and so need to be re-drawn * later), FALSE if no sprites were torn down. * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
BOOL bSpTearDownSprites( HDEV hdev, RECTL* prclExclude, BOOL bDirectDrawLock) // TRUE if we're being called by the DirectDraw
// Lock function. FALSE if we're being called
// from a routine which wants to just
// momentarily tear down the sprite
{ SPRITESTATE* pState; RECTL rclEnum; BOOL bTearDown; BOOL bMore; SPRITE* pSprite; RECTL rclExclude;
PDEVOBJ po(hdev);
// No sprites to tear down on printers
if (!po.bDisplayPDEV()) { return FALSE; }
po.vAssertDevLock(); pState = po.pSpriteState();
SPRITELOCK slock(po);
bTearDown = FALSE;
// We only need to do any actual work if any sprites are up on
// the screen:
if (pState->cVisible != 0) { if (bIntersect(prclExclude, &pState->rclScreen, &rclExclude)) { // Tear down all sprites:
ENUMAREAS Enum(pState, &rclExclude);
do { bMore = Enum.bEnum(&pSprite, &rclEnum);
if (pSprite != NULL) { bTearDown = TRUE;
vSpWriteToScreen(pState, &pSprite->OffUnderlay, pSprite->psoUnderlay, &rclEnum); } } while (bMore);
if (bDirectDrawLock) { // Now compute the new unlocked region:
vSpComputeUnlockedRegion(pState); } } }
return(bTearDown); }
/******************************Public*Routine******************************\
* VOID vSpUnTearDownSprites * * This routine redraws any sprites in the specified rectangle. * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
VOID vSpUnTearDownSprites( HDEV hdev, RECTL* prclExclude, BOOL bDirectDrawUnlock) { SPRITESTATE* pState; RECTL rclEnum; BOOL bMore; SPRITE* pSprite; RECTL rclExclude;
PDEVOBJ po(hdev);
ASSERTGDI(po.bDisplayPDEV(), "vSpUnTearDownSprites: not a display pdev");
po.vAssertDevLock(); pState = po.pSpriteState();
// We only need to do any actual work if any sprites are up on
// the screen:
if (pState->cVisible != 0) { if (bIntersect(prclExclude, &pState->rclScreen, &rclExclude)) { SPRITELOCK slock(po);
if (bDirectDrawUnlock) { // Now compute the new unlocked region:
vSpComputeUnlockedRegion(pState); }
// Reload the underlays:
ENUMAREAS Enum(pState, &rclExclude);
do { // We know that we already excluded any sprites from the
// 'prclExclude' area, so it's safe to update the underlays
// directly from the screen since we know we won't pick up
// any sprite images.
bMore = Enum.bEnum(&pSprite, &rclEnum);
if (pSprite != NULL) { do { vSpReadFromScreen(pState, &pSprite->OffUnderlay, pSprite->psoUnderlay, &rclEnum);
} while (Enum.bEnumLayers(&pSprite)); } } while (bMore);
// Redraw the affected area:
vSpRedrawArea(pState, &rclExclude, TRUE); } } }
/******************************Public*Routine******************************\
* BOOL bSpSpritesVisible * * Returns TRUE if any emulated sprites are currently visible. * * 16-Sep-1997 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
BOOL bSpSpritesVisible( HDEV hdev) { SPRITESTATE* pState;
PDEVOBJ po(hdev); po.vAssertDevLock(); pState = po.pSpriteState();
return(pState->cVisible != 0); }
|