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