Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

10082 lines
315 KiB

/******************************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);
}