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.
1205 lines
38 KiB
1205 lines
38 KiB
/******************************Module*Header*******************************\
|
|
* Module Name: panning.cxx
|
|
*
|
|
* Contains the code for a layered 'virtual' driver that emulates panning
|
|
* by switching the display device to the requested panning dimensions, and
|
|
* creating a shadow buffer the size of the desktop to which GDI does all
|
|
* the drawing. To update the physical display, we employ a 'dirty
|
|
* rectangles' technique where we simply blt from the shadow buffer to the
|
|
* screen for the affected areas.
|
|
*
|
|
* Created: 15-Sep-96
|
|
* Author: J. Andrew Goossen [andrewgo]
|
|
*
|
|
* Copyright (c) 1996-1999 Microsoft Corporation
|
|
*
|
|
\**************************************************************************/
|
|
|
|
#include "precomp.hxx"
|
|
|
|
extern BOOL G_fDoubleDpi;
|
|
|
|
// The panning layer's equivalent of a 'PDEV':
|
|
|
|
typedef struct _PANDEV
|
|
{
|
|
LONG cxScreen; // The smaller dimensions (the dimensions of
|
|
LONG cyScreen; // the physical screen)
|
|
LONG cxDesktop; // The larger dimensions (the dimensions of
|
|
LONG cyDesktop; // the virtual desktop)
|
|
RECTL rclVisible; // Position of panning window on virtual
|
|
// desktop
|
|
DHPDEV dhpdevDevice; // The device's 'dhpdev'
|
|
ULONG iBitmapFormat; // Bitmap format of surface
|
|
FLONG flOriginalCaps; // Driver's original flGraphicsCaps setting
|
|
HDEV hdev; // Handle to GDI's PDEV
|
|
HSURF hsurfScreen; // Handle to GDI's surface
|
|
SURFOBJ* psoShadow; // Pointer to our shadow surface
|
|
SURFOBJ* psoDevice; // Pointer to device's surface
|
|
SURFOBJ* psoShrink; // Pointer to shrink scratch buffer surface
|
|
REGION* prgnDirty; // Points to the current dirty region
|
|
REGION* prgnOld; // Points to the previous dirty region
|
|
REGION* prgnRect; // Points to a rectangular region we cache
|
|
BOOL bDirty; // If TRUE, we have some dirty rectangles
|
|
// that should be updated at the next tick
|
|
PFN apfn[INDEX_LAST]; // Dispatch table to device
|
|
} PANDEV;
|
|
|
|
/******************************Public*Routine******************************\
|
|
* VOID vPanningUpdate(ppan, prcl, pco)
|
|
*
|
|
* Updates the screen from the DIB surface for the given rectangle.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
VOID vPanningUpdate(PANDEV* ppan, RECTL* prcl, CLIPOBJ* pco)
|
|
{
|
|
RECTL rcl;
|
|
|
|
if ((pco == NULL) || (pco->iDComplexity == DC_TRIVIAL))
|
|
{
|
|
rcl = *prcl;
|
|
}
|
|
else
|
|
{
|
|
// We may as well save ourselves some blting by clipping to
|
|
// the clip object's maximum extent. The clip object's bounds
|
|
// are guaranteed to be contained within the dimensions of the
|
|
// desktop:
|
|
|
|
rcl.left = max(pco->rclBounds.left, prcl->left);
|
|
rcl.top = max(pco->rclBounds.top, prcl->top);
|
|
rcl.right = min(pco->rclBounds.right, prcl->right);
|
|
rcl.bottom = min(pco->rclBounds.bottom, prcl->bottom);
|
|
}
|
|
|
|
if (ppan->psoShrink)
|
|
{
|
|
rcl.left >>= 1;
|
|
rcl.top >>= 1;
|
|
rcl.right = (rcl.right + 1) >> 1;
|
|
rcl.bottom = (rcl.bottom + 1) >> 1;
|
|
}
|
|
|
|
if ((rcl.left < rcl.right) && (rcl.top < rcl.bottom))
|
|
{
|
|
// Merge this rectangle into the dirty region. We swap the 'old'
|
|
// and 'current' dirty regions in the process, so that we don't
|
|
// have to do an extra allocation:
|
|
|
|
RGNOBJ roRect(ppan->prgnRect);
|
|
RGNOBJ roDirty(ppan->prgnOld);
|
|
RGNOBJ roOld(ppan->prgnDirty);
|
|
|
|
roRect.vSet(&rcl);
|
|
if (!roDirty.bMerge(roOld, roRect, gafjRgnOp[RGN_OR]))
|
|
{
|
|
roDirty.vSet();
|
|
}
|
|
|
|
ppan->prgnOld = roOld.prgnGet();
|
|
ppan->prgnDirty = roDirty.prgnGet();
|
|
ppan->bDirty = TRUE;
|
|
}
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* vFiltered2xShrinkRectangle32bpp
|
|
*
|
|
* Quick and dirty single rectangle 0.5x shrink with simple color averaging.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
VOID vFilteredShrinkRectangle2x32bpp(
|
|
SURFOBJ* psoDst,
|
|
SURFOBJ* psoSrc,
|
|
RECTL* prclDst)
|
|
{
|
|
LONG cx;
|
|
LONG cy;
|
|
LONG lSrcDelta;
|
|
BYTE* pjSrc;
|
|
LONG lSrcSkip;
|
|
LONG lDstDelta;
|
|
BYTE* pjDst;
|
|
LONG lDstSkip;
|
|
LONG i;
|
|
ULONG ulDst;
|
|
|
|
ASSERTGDI((psoDst->iBitmapFormat == BMF_32BPP) &&
|
|
(psoSrc->iBitmapFormat == BMF_32BPP),
|
|
"Unexpected type");
|
|
|
|
cy = prclDst->bottom - prclDst->top;
|
|
cx = prclDst->right - prclDst->left;
|
|
|
|
lSrcDelta = psoSrc->lDelta;
|
|
pjSrc = (BYTE*) psoSrc->pvScan0 + (prclDst->top * 2) * lSrcDelta
|
|
+ (prclDst->left * 2) * sizeof(ULONG);
|
|
lSrcSkip = 2 * lSrcDelta - (cx * 2 * sizeof(ULONG));
|
|
|
|
lDstDelta = psoDst->lDelta;
|
|
pjDst = (BYTE*) psoDst->pvScan0 + prclDst->top * lDstDelta
|
|
+ prclDst->left * sizeof(ULONG);
|
|
lDstSkip = lDstDelta - (cx * sizeof(ULONG));
|
|
|
|
do {
|
|
i = cx;
|
|
do {
|
|
ulDst = *(pjSrc)
|
|
+ *(pjSrc + 4)
|
|
+ *(pjSrc + lSrcDelta)
|
|
+ *(pjSrc + 4 + lSrcDelta);
|
|
|
|
*(pjDst) = (BYTE) (ulDst >> 2);
|
|
|
|
ulDst = *(pjSrc + 1)
|
|
+ *(pjSrc + 5)
|
|
+ *(pjSrc + 1 + lSrcDelta)
|
|
+ *(pjSrc + 5 + lSrcDelta);
|
|
|
|
*(pjDst + 1) = (BYTE) (ulDst >> 2);
|
|
|
|
ulDst = *(pjSrc + 2)
|
|
+ *(pjSrc + 6)
|
|
+ *(pjSrc + 2 + lSrcDelta)
|
|
+ *(pjSrc + 6 + lSrcDelta);
|
|
|
|
*(pjDst + 2) = (BYTE) (ulDst >> 2);
|
|
|
|
pjDst += 4;
|
|
pjSrc += 8;
|
|
|
|
} while (--i != 0);
|
|
|
|
pjDst += lDstSkip;
|
|
pjSrc += lSrcSkip;
|
|
|
|
} while (--cy != 0);
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* vFiltered2xShrink32bpp
|
|
*
|
|
* Quick and dirty bilinear filtered 2x shrink routine.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
VOID vFilteredShrink2x32bpp(
|
|
SURFOBJ* psoDst,
|
|
SURFOBJ* psoSrc,
|
|
ECLIPOBJ* pco,
|
|
RECTL* prclDst)
|
|
{
|
|
BOOL bMore;
|
|
ULONG i;
|
|
CLIPENUMRECT clenr;
|
|
RECTL rclDst;
|
|
|
|
CLIPOBJ_cEnumStart(pco, FALSE, CT_RECTANGLES, CD_ANY, 0);
|
|
|
|
do {
|
|
bMore = pco->bEnum(sizeof(clenr), &clenr);
|
|
|
|
for (i = 0; i < clenr.c; i++)
|
|
{
|
|
if (bIntersect(&clenr.arcl[i], prclDst, &rclDst))
|
|
{
|
|
vFilteredShrinkRectangle2x32bpp(psoDst, psoSrc, &rclDst);
|
|
}
|
|
}
|
|
} while (bMore);
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* PanSynchronize
|
|
*
|
|
* Dumps the list of dirty rectangles to the screen. Because we set
|
|
* GCAPS2_SYNCTIMER, this routine is called by User 20 times a second.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
VOID PanSynchronize(
|
|
DHPDEV dhpdev,
|
|
RECTL* prcl)
|
|
{
|
|
PANDEV* ppan;
|
|
SURFACE* psurfDevice;
|
|
RECTL* prclVisible;
|
|
RECTL rclDevice;
|
|
RECTL rclSrc;
|
|
PFN_DrvCopyBits pfnCopyBits;
|
|
PFN_DrvStretchBlt pfnStretchBlt;
|
|
SURFOBJ* psoCopySrc;
|
|
SURFOBJ* psoDst;
|
|
SURFOBJ* psoDevice;
|
|
|
|
ppan = (PANDEV*) dhpdev;
|
|
if (ppan->bDirty)
|
|
{
|
|
ECLIPOBJ eco;
|
|
|
|
ASSERTGDI((ppan->rclVisible.right == ppan->rclVisible.left + ppan->cxScreen) &&
|
|
(ppan->rclVisible.bottom == ppan->rclVisible.top + ppan->cyScreen),
|
|
"Unexpected rclVisible dimensions");
|
|
|
|
prclVisible = &ppan->rclVisible;
|
|
|
|
eco.vSetup(ppan->prgnDirty, *(ERECTL*)prclVisible);
|
|
if (!eco.erclExclude().bEmpty())
|
|
{
|
|
// Now reset the device surface 'dhpdev' field that the any device's
|
|
// EngAssociateSurface call so rudely overwrote with the pointer
|
|
// to *our* 'ppan' structure!
|
|
|
|
ppan->psoDevice->dhpdev = ppan->dhpdevDevice;
|
|
|
|
psurfDevice = SURFOBJ_TO_SURFACE(ppan->psoDevice);
|
|
|
|
rclDevice.left = 0;
|
|
rclDevice.top = 0;
|
|
rclDevice.right = psurfDevice->sizl().cx;
|
|
rclDevice.bottom = psurfDevice->sizl().cy;
|
|
|
|
// Assume we'll be calling CopyBits straight from the shadow
|
|
// buffer:
|
|
|
|
psoCopySrc = ppan->psoShadow;
|
|
|
|
if (ppan->psoShrink)
|
|
{
|
|
ASSERTGDI(ppan->iBitmapFormat == BMF_32BPP,
|
|
"Expected psoShrink only when 32bpp");
|
|
|
|
// As a small optimization, we can draw directly to the
|
|
// frame buffer (and bypass the shrink buffer) if the primary
|
|
// surface was created as a GDI-managed surface:
|
|
|
|
if (psurfDevice->iType() == STYPE_BITMAP)
|
|
{
|
|
psoDst = psurfDevice->pSurfobj();
|
|
psoCopySrc = NULL;
|
|
}
|
|
else
|
|
{
|
|
psoDst = ppan->psoShrink;
|
|
psoCopySrc = ppan->psoShrink;
|
|
}
|
|
|
|
vFilteredShrink2x32bpp(psoDst,
|
|
ppan->psoShadow,
|
|
&eco,
|
|
&rclDevice);
|
|
}
|
|
|
|
if (psoCopySrc)
|
|
{
|
|
// Call the device's DrvCopyBits routine to do the copy (noting that
|
|
// if it's an engine managed surface, we may have to call EngCopyBits):
|
|
|
|
pfnCopyBits = (psurfDevice->flags() & HOOK_COPYBITS)
|
|
? PPFNTABLE(ppan->apfn, CopyBits)
|
|
: EngCopyBits;
|
|
|
|
(*pfnCopyBits)(psurfDevice->pSurfobj(),
|
|
psoCopySrc,
|
|
&eco,
|
|
NULL,
|
|
&rclDevice,
|
|
(POINTL*) prclVisible);
|
|
}
|
|
}
|
|
|
|
// Mark the fact that we have no dirty rectangles, and clear the
|
|
// dirty region:
|
|
|
|
ppan->bDirty = FALSE;
|
|
|
|
RGNOBJ ro(ppan->prgnDirty);
|
|
ro.vSet();
|
|
}
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* PanStrokePath
|
|
*
|
|
\**************************************************************************/
|
|
|
|
BOOL PanStrokePath(
|
|
SURFOBJ* pso,
|
|
PATHOBJ* ppo,
|
|
CLIPOBJ* pco,
|
|
XFORMOBJ* pxo,
|
|
BRUSHOBJ* pbo,
|
|
POINTL* pptlBrush,
|
|
LINEATTRS* pla,
|
|
MIX mix)
|
|
{
|
|
BOOL b;
|
|
PANDEV* ppan;
|
|
RECTFX rcfxBounds;
|
|
RECTL rclBounds;
|
|
|
|
ppan = (PANDEV*) pso->dhpdev;
|
|
|
|
b = EngStrokePath(ppan->psoShadow, ppo, pco, pxo, pbo, pptlBrush, pla, mix);
|
|
|
|
// Get the path bounds and make it lower-right exclusive:
|
|
|
|
PATHOBJ_vGetBounds(ppo, &rcfxBounds);
|
|
|
|
rclBounds.left = (rcfxBounds.xLeft >> 4);
|
|
rclBounds.top = (rcfxBounds.yTop >> 4);
|
|
rclBounds.right = (rcfxBounds.xRight >> 4) + 2;
|
|
rclBounds.bottom = (rcfxBounds.yBottom >> 4) + 2;
|
|
|
|
vPanningUpdate(ppan, &rclBounds, pco);
|
|
|
|
return(b);
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* PanTransparentBlt
|
|
*
|
|
\**************************************************************************/
|
|
|
|
BOOL PanTransparentBlt(
|
|
SURFOBJ* psoDst,
|
|
SURFOBJ* psoSrc,
|
|
CLIPOBJ* pco,
|
|
XLATEOBJ* pxlo,
|
|
RECTL* prclDst,
|
|
RECTL* prclSrc,
|
|
ULONG iTransColor,
|
|
ULONG ulReserved)
|
|
{
|
|
BOOL b;
|
|
PANDEV* ppan;
|
|
|
|
ppan = (PANDEV*) psoDst->dhpdev;
|
|
|
|
b = EngTransparentBlt(ppan->psoShadow, psoSrc, pco, pxlo, prclDst,
|
|
prclSrc, iTransColor, ulReserved);
|
|
|
|
vPanningUpdate(ppan, prclDst, pco);
|
|
|
|
return(b);
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* PanAlphaBlend
|
|
*
|
|
\**************************************************************************/
|
|
|
|
BOOL PanAlphaBlend(
|
|
SURFOBJ* psoDst,
|
|
SURFOBJ* psoSrc,
|
|
CLIPOBJ* pco,
|
|
XLATEOBJ* pxlo,
|
|
RECTL* prclDst,
|
|
RECTL* prclSrc,
|
|
BLENDOBJ* pBlendObj)
|
|
{
|
|
BOOL b;
|
|
PANDEV* ppan;
|
|
|
|
ppan = (PANDEV*) psoDst->dhpdev;
|
|
|
|
b = EngAlphaBlend(ppan->psoShadow, psoSrc, pco, pxlo, prclDst,
|
|
prclSrc, pBlendObj);
|
|
|
|
vPanningUpdate(ppan, prclDst, pco);
|
|
|
|
return(b);
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* PanGradientFill
|
|
*
|
|
\**************************************************************************/
|
|
|
|
BOOL PanGradientFill(
|
|
SURFOBJ* pso,
|
|
CLIPOBJ* pco,
|
|
XLATEOBJ* pxlo,
|
|
TRIVERTEX* pVertex,
|
|
ULONG nVertex,
|
|
PVOID pMesh,
|
|
ULONG nMesh,
|
|
RECTL* prclExtents,
|
|
POINTL* pptlDitherOrg,
|
|
ULONG ulMode)
|
|
{
|
|
BOOL b;
|
|
PANDEV* ppan;
|
|
|
|
ppan = (PANDEV*) pso->dhpdev;
|
|
|
|
b = EngGradientFill(ppan->psoShadow, pco, pxlo, pVertex, nVertex, pMesh,
|
|
nMesh, prclExtents, pptlDitherOrg, ulMode);
|
|
|
|
vPanningUpdate(ppan, prclExtents, pco);
|
|
|
|
return(b);
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* PanStretchBlt
|
|
*
|
|
\**************************************************************************/
|
|
|
|
BOOL PanStretchBlt(
|
|
SURFOBJ* psoDst,
|
|
SURFOBJ* psoSrc,
|
|
SURFOBJ* psoMsk,
|
|
CLIPOBJ* pco,
|
|
XLATEOBJ* pxlo,
|
|
COLORADJUSTMENT* pca,
|
|
POINTL* pptlHTOrg,
|
|
RECTL* prclDst,
|
|
RECTL* prclSrc,
|
|
POINTL* pptlMsk,
|
|
ULONG iMode)
|
|
{
|
|
BOOL b;
|
|
PANDEV* ppan;
|
|
|
|
ppan = (PANDEV*) psoDst->dhpdev;
|
|
|
|
b = EngStretchBlt(ppan->psoShadow, psoSrc, psoMsk, pco, pxlo, pca, pptlHTOrg,
|
|
prclDst, prclSrc, pptlMsk, iMode);
|
|
|
|
vPanningUpdate(ppan, prclDst, pco);
|
|
|
|
return(b);
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* PanBitBlt
|
|
*
|
|
\**************************************************************************/
|
|
|
|
BOOL PanBitBlt(
|
|
SURFOBJ* psoDst,
|
|
SURFOBJ* psoSrc,
|
|
SURFOBJ* psoMask,
|
|
CLIPOBJ* pco,
|
|
XLATEOBJ* pxlo,
|
|
RECTL* prclDst,
|
|
POINTL* pptlSrc,
|
|
POINTL* pptlMask,
|
|
BRUSHOBJ* pbo,
|
|
POINTL* pptlBrush,
|
|
ROP4 rop4)
|
|
{
|
|
BOOL bUpdate;
|
|
BOOL b;
|
|
PANDEV* ppan;
|
|
|
|
bUpdate = FALSE;
|
|
if (psoDst->iType == STYPE_DEVICE)
|
|
{
|
|
bUpdate = TRUE;
|
|
ppan = (PANDEV*) psoDst->dhpdev;
|
|
psoDst = ppan->psoShadow;
|
|
}
|
|
if ((psoSrc != NULL) && (psoSrc->iType == STYPE_DEVICE))
|
|
{
|
|
ppan = (PANDEV*) psoSrc->dhpdev;
|
|
psoSrc = ppan->psoShadow;
|
|
}
|
|
|
|
b = EngBitBlt(psoDst, psoSrc, psoMask, pco, pxlo, prclDst, pptlSrc,
|
|
pptlMask, pbo, pptlBrush, rop4);
|
|
|
|
if (bUpdate)
|
|
{
|
|
vPanningUpdate(ppan, prclDst, pco);
|
|
}
|
|
|
|
return(b);
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* PanCopyBits
|
|
*
|
|
\**************************************************************************/
|
|
|
|
BOOL PanCopyBits(
|
|
SURFOBJ* psoDst,
|
|
SURFOBJ* psoSrc,
|
|
CLIPOBJ* pco,
|
|
XLATEOBJ* pxlo,
|
|
RECTL* prclDst,
|
|
POINTL* pptlSrc)
|
|
{
|
|
BOOL bUpdate;
|
|
BOOL b;
|
|
PANDEV* ppan;
|
|
|
|
return(PanBitBlt(psoDst, psoSrc, NULL, pco, pxlo, prclDst, pptlSrc,
|
|
NULL, NULL, NULL, 0xcccc));
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* PanTextOut
|
|
*
|
|
\**************************************************************************/
|
|
|
|
BOOL PanTextOut(
|
|
SURFOBJ* pso,
|
|
STROBJ* pstro,
|
|
FONTOBJ* pfo,
|
|
CLIPOBJ* pco,
|
|
RECTL* prclExtra,
|
|
RECTL* prclOpaque,
|
|
BRUSHOBJ* pboFore,
|
|
BRUSHOBJ* pboOpaque,
|
|
POINTL* pptlOrg,
|
|
MIX mix)
|
|
{
|
|
BOOL b;
|
|
PANDEV* ppan;
|
|
|
|
ppan = (PANDEV*) pso->dhpdev;
|
|
|
|
b = EngTextOut(ppan->psoShadow, pstro, pfo, pco, prclExtra, prclOpaque,
|
|
pboFore, pboOpaque, pptlOrg, mix);
|
|
|
|
vPanningUpdate(ppan,
|
|
(prclOpaque != NULL) ? prclOpaque : &pstro->rclBkGround,
|
|
pco);
|
|
|
|
return(b);
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* VOID PanMovePointer
|
|
*
|
|
\**************************************************************************/
|
|
|
|
VOID PanMovePointer(
|
|
SURFOBJ* pso,
|
|
LONG x,
|
|
LONG y,
|
|
RECTL* prcl)
|
|
{
|
|
PANDEV* ppan;
|
|
BOOL bUpdate;
|
|
PFN_DrvMovePointer pfnMovePointer;
|
|
|
|
ppan = (PANDEV*) pso->dhpdev;
|
|
|
|
// A negative 'y' coordinate indicates a positional notification.
|
|
// Use this to move the panning coordinates if need be:
|
|
|
|
ASSERTGDI((y < 0) && (x >= 0), "Unexpected coordinates");
|
|
|
|
// It's weird, but the display driver may actually be panning
|
|
// underneath our virtual panning display driver! (This will
|
|
// only happen when all the video driver's accelerations are
|
|
// disabled.)
|
|
|
|
pfnMovePointer = PPFNTABLE(ppan->apfn, MovePointer);
|
|
if ((pfnMovePointer != NULL) &&
|
|
(ppan->flOriginalCaps & GCAPS_PANNING))
|
|
{
|
|
(*pfnMovePointer)(ppan->psoDevice,
|
|
x,
|
|
y,
|
|
prcl);
|
|
}
|
|
|
|
// Adjust to positive space:
|
|
|
|
y += pso->sizlBitmap.cy;
|
|
|
|
bUpdate = FALSE;
|
|
|
|
if (x < ppan->rclVisible.left)
|
|
{
|
|
ppan->rclVisible.left = x;
|
|
ppan->rclVisible.right = x + ppan->cxScreen;
|
|
bUpdate = TRUE;
|
|
}
|
|
if (x > ppan->rclVisible.right)
|
|
{
|
|
ppan->rclVisible.right = x;
|
|
ppan->rclVisible.left = x - ppan->cxScreen;
|
|
bUpdate = TRUE;
|
|
}
|
|
if (y < ppan->rclVisible.top)
|
|
{
|
|
ppan->rclVisible.top = y;
|
|
ppan->rclVisible.bottom = y + ppan->cyScreen;
|
|
bUpdate = TRUE;
|
|
}
|
|
if (y > ppan->rclVisible.bottom)
|
|
{
|
|
ppan->rclVisible.bottom = y;
|
|
ppan->rclVisible.top = y - ppan->cyScreen;
|
|
bUpdate = TRUE;
|
|
}
|
|
|
|
ASSERTGDI((ppan->rclVisible.left >= 0) &&
|
|
(ppan->rclVisible.top >= 0) &&
|
|
(ppan->rclVisible.right <= ppan->cxDesktop) &&
|
|
(ppan->rclVisible.bottom <= ppan->cyDesktop),
|
|
"unexpected pointer coordinates");
|
|
|
|
if (bUpdate)
|
|
{
|
|
// The panning coordinates changed, so update the display.
|
|
//
|
|
// NOTE: A screen-to-screen blt could be done as an optimization.
|
|
|
|
vPanningUpdate(ppan,
|
|
&ppan->rclVisible,
|
|
NULL);
|
|
}
|
|
|
|
// Flush anything pending. We do this for a couple of reasons:
|
|
//
|
|
// 1. To flush the vPanningUpdate we may have just done;
|
|
// 2. For smoother mouse movement.
|
|
|
|
PanSynchronize((DHPDEV) ppan, NULL);
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* PanDitherColor
|
|
*
|
|
* Dithers an RGB color to an 8X8 approximation using the reserved VGA
|
|
* colors.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
ULONG APIENTRY PanDitherColor(
|
|
DHPDEV dhpdev,
|
|
ULONG iMode,
|
|
ULONG rgb,
|
|
ULONG* pul)
|
|
{
|
|
PANDEV* ppan;
|
|
ULONG iRet;
|
|
|
|
ppan = (PANDEV*) dhpdev;
|
|
|
|
// EngDitherColor supports dithers only for 8bpp and higher, so at
|
|
// anything lower we have to call the driver.
|
|
|
|
if (ppan->iBitmapFormat >= BMF_8BPP)
|
|
{
|
|
iRet = EngDitherColor(ppan->hdev, iMode, rgb, pul);
|
|
}
|
|
else
|
|
{
|
|
iRet = PPFNTABLE(ppan->apfn, DitherColor)(ppan->dhpdevDevice,
|
|
iMode,
|
|
rgb,
|
|
pul);
|
|
}
|
|
|
|
return(iRet);
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* BOOL PanSetPalette
|
|
*
|
|
* DDI entry point for manipulating the palette.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
BOOL PanSetPalette(
|
|
DHPDEV dhpdev,
|
|
PALOBJ* ppal,
|
|
FLONG fl,
|
|
ULONG iStart,
|
|
ULONG cColors)
|
|
{
|
|
PANDEV* ppan;
|
|
|
|
ppan = (PANDEV*) dhpdev;
|
|
|
|
return((*PPFNTABLE(ppan->apfn, SetPalette))(ppan->dhpdevDevice,
|
|
ppal,
|
|
fl,
|
|
iStart,
|
|
cColors));
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* VOID PanAssertMode
|
|
*
|
|
* This asks the device to reset itself to the mode of the pdev passed in.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
BOOL PanAssertMode(
|
|
DHPDEV dhpdev,
|
|
BOOL bEnable)
|
|
{
|
|
PANDEV* ppan;
|
|
BOOL b;
|
|
|
|
ppan = (PANDEV*) dhpdev;
|
|
|
|
b = (*PPFNTABLE(ppan->apfn, AssertMode))(ppan->dhpdevDevice,
|
|
bEnable);
|
|
|
|
return(b);
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* DHPDEV PanEnablePDEV
|
|
*
|
|
* Initializes a bunch of fields for GDI, based on the mode we've been asked
|
|
* to do. This is the first thing called after PanEnableDriver, when GDI
|
|
* wants to get some information about us.
|
|
*
|
|
* (This function mostly returns back information; PanEnableSurface is used
|
|
* for initializing the hardware and driver components.)
|
|
*
|
|
\**************************************************************************/
|
|
|
|
DHPDEV PanEnablePDEV(
|
|
DEVMODEW* pdm, // Contains data pertaining to requested mode
|
|
PWSTR pwszLogAddr, // Logical address
|
|
ULONG cPat, // Count of standard patterns
|
|
HSURF* phsurfPatterns, // Buffer for standard patterns
|
|
ULONG cjCaps, // Size of buffer for device caps 'pdevcaps'
|
|
ULONG* pdevcaps, // Buffer for device caps, also known as 'gdiinfo'
|
|
ULONG cjDevInfo, // Number of bytes in device info 'pdi'
|
|
DEVINFO* pdi, // Device information
|
|
HDEV hdev, // HDEV, used for callbacks
|
|
PWSTR pwszDeviceName, // Device name
|
|
HANDLE hDriver) // Kernel driver handle
|
|
{
|
|
PANDEV* ppan;
|
|
PFN* ppfnDevice;
|
|
DHPDEV dhpdevDevice;
|
|
GDIINFO* pGdiInfo;
|
|
DEVMODEW dmTmp;
|
|
|
|
ASSERTGDI((pdm->dmPanningWidth <= pdm->dmPelsWidth) &&
|
|
(pdm->dmPanningHeight <= pdm->dmPelsHeight),
|
|
"Bad devmode sizes");
|
|
|
|
ppan = (PANDEV*) PALLOCMEM(sizeof(PANDEV), 'napG');
|
|
if (ppan != NULL)
|
|
{
|
|
PPDEV ppdev = (PPDEV)hdev;
|
|
|
|
// Snag a copy of the device's dispatch table, from
|
|
// the private PDEV structure, which is the hdev.
|
|
|
|
ppfnDevice = &ppdev->pldev->apfn[0];
|
|
|
|
RtlCopyMemory(ppan->apfn, ppfnDevice, sizeof(PFN) * INDEX_LAST);
|
|
|
|
// Remember our dimensions:
|
|
|
|
ppan->cxDesktop = pdm->dmPelsWidth;
|
|
ppan->cyDesktop = pdm->dmPelsHeight;
|
|
|
|
if (pdm->dmPanningWidth != 0)
|
|
{
|
|
ppan->cxScreen = pdm->dmPanningWidth;
|
|
ppan->cyScreen = pdm->dmPanningHeight;
|
|
}
|
|
else
|
|
{
|
|
ppan->cxScreen = pdm->dmPelsWidth;
|
|
ppan->cyScreen = pdm->dmPelsHeight;
|
|
}
|
|
|
|
// Ask the driver to set its physical mode to the panning
|
|
// dimensions:
|
|
|
|
dmTmp = *pdm;
|
|
dmTmp.dmPelsWidth = ppan->cxScreen;
|
|
dmTmp.dmPelsHeight = ppan->cyScreen;
|
|
|
|
// But wait, if we're being asked to double the DPI and the
|
|
// mode is 32bpp, drvsup.cxx already set up the Devmode so
|
|
// that it's double the actual resolution. That means that we
|
|
// then have to halve the resolution that we request of the
|
|
// driver:
|
|
|
|
if ((G_fDoubleDpi) && (dmTmp.dmBitsPerPel == 32))
|
|
{
|
|
dmTmp.dmPelsWidth >>= 1;
|
|
dmTmp.dmPelsHeight >>= 1;
|
|
}
|
|
|
|
dhpdevDevice = (*PPFNTABLE(ppan->apfn, EnablePDEV))
|
|
(&dmTmp,
|
|
pwszLogAddr,
|
|
cPat,
|
|
phsurfPatterns,
|
|
cjCaps,
|
|
(GDIINFO*) pdevcaps,
|
|
cjDevInfo,
|
|
pdi,
|
|
hdev,
|
|
pwszDeviceName,
|
|
hDriver);
|
|
|
|
if (dhpdevDevice != NULL)
|
|
{
|
|
ppan->iBitmapFormat = pdi->iDitherFormat;
|
|
ppan->dhpdevDevice = dhpdevDevice;
|
|
ppan->hdev = hdev;
|
|
ppan->flOriginalCaps = pdi->flGraphicsCaps;
|
|
|
|
// The display driver will have set the desktop coordinates
|
|
// to the smaller screen coordinates, so we'll have to
|
|
// overwrite that:
|
|
|
|
pGdiInfo = (GDIINFO*) pdevcaps;
|
|
|
|
pGdiInfo->ulHorzRes = ppan->cxDesktop;
|
|
pGdiInfo->ulVertRes = ppan->cyDesktop;
|
|
|
|
// Overwrite some of the driver's capabilities:
|
|
|
|
pdi->flGraphicsCaps &= (GCAPS_PALMANAGED |
|
|
GCAPS_MONO_DITHER |
|
|
GCAPS_COLOR_DITHER);
|
|
pdi->flGraphicsCaps |= GCAPS_PANNING;
|
|
|
|
// Enable synchronization calls:
|
|
|
|
pdi->flGraphicsCaps2 = (GCAPS2_SYNCFLUSH |
|
|
GCAPS2_SYNCTIMER);
|
|
|
|
return((DHPDEV) ppan);
|
|
}
|
|
|
|
VFREEMEM(ppan);
|
|
}
|
|
|
|
return(0);
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* PanDisablePDEV
|
|
*
|
|
* Release the resources allocated in PanEnablePDEV. If a surface has been
|
|
* enabled PanDisableSurface will have already been called.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
VOID PanDisablePDEV(
|
|
DHPDEV dhpdev)
|
|
{
|
|
PANDEV* ppan;
|
|
|
|
ppan = (PANDEV*) dhpdev;
|
|
|
|
(*PPFNTABLE(ppan->apfn, DisablePDEV))(ppan->dhpdevDevice);
|
|
|
|
VFREEMEM(ppan);
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* VOID PanCompletePDEV
|
|
*
|
|
\**************************************************************************/
|
|
|
|
VOID PanCompletePDEV(
|
|
DHPDEV dhpdev,
|
|
HDEV hdev)
|
|
{
|
|
PANDEV* ppan;
|
|
|
|
ppan = (PANDEV*) dhpdev;
|
|
|
|
ppan->hdev = hdev;
|
|
|
|
(*PPFNTABLE(ppan->apfn, CompletePDEV))(ppan->dhpdevDevice,
|
|
hdev);
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* HSURF PanEnableSurface
|
|
*
|
|
* Creates the drawing surface, initializes the hardware, and initializes
|
|
* driver components. This function is called after PanEnablePDEV, and
|
|
* performs the final device initialization.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
HSURF PanEnableSurface(
|
|
DHPDEV dhpdev)
|
|
{
|
|
PANDEV* ppan;
|
|
HSURF hsurfDevice;
|
|
HSURF hsurfScreen;
|
|
HSURF hsurfShadow;
|
|
HSURF hsurfShrink;
|
|
SIZEL sizl;
|
|
SIZEL sizlShrink;
|
|
SURFOBJ* psoShadow;
|
|
SURFOBJ* psoDevice;
|
|
SURFOBJ* psoShrink;
|
|
BOOL bShrink;
|
|
|
|
ppan = (PANDEV*) dhpdev;
|
|
|
|
// Initialize the paning position:
|
|
|
|
ppan->rclVisible.left = (ppan->cxDesktop - ppan->cxScreen) >> 1;
|
|
ppan->rclVisible.top = (ppan->cyDesktop - ppan->cyScreen) >> 1;
|
|
ppan->rclVisible.right = (ppan->rclVisible.left + ppan->cxScreen);
|
|
ppan->rclVisible.bottom = (ppan->rclVisible.top + ppan->cyScreen);
|
|
|
|
// Call the device to enable its surface:
|
|
|
|
hsurfDevice = (*PPFNTABLE(ppan->apfn, EnableSurface))(ppan->dhpdevDevice);
|
|
if (hsurfDevice)
|
|
{
|
|
psoDevice = EngLockSurface(hsurfDevice);
|
|
if (psoDevice)
|
|
{
|
|
SURFACE* pDevSurface = SURFOBJ_TO_SURFACE(psoDevice);
|
|
|
|
pDevSurface->flags(pDevSurface->flags() & ~(HOOK_SYNCHRONIZE));
|
|
|
|
ppan->psoDevice = psoDevice;
|
|
|
|
// Now reset the device's 'dhpdev' field that the device's
|
|
// EngAssociateSurface call so rudely overwrote with the pointer
|
|
// to *our* 'ppan' structure!
|
|
|
|
psoDevice->dhpdev = ppan->dhpdevDevice;
|
|
|
|
// Have GDI create the actual SURFOBJ.
|
|
//
|
|
// Our drawing surface is going to be 'device-managed', meaning
|
|
// that GDI cannot draw on the framebuffer bits directly, and as
|
|
// such we create the surface via EngCreateSurface. By doing
|
|
// this, we ensure that GDI will only ever access the bitmaps
|
|
// bits via the Pan calls that we've HOOKed.
|
|
|
|
sizl.cx = ppan->cxDesktop;
|
|
sizl.cy = ppan->cyDesktop;
|
|
|
|
hsurfScreen = EngCreateDeviceSurface(NULL,
|
|
sizl,
|
|
ppan->iBitmapFormat);
|
|
if (hsurfScreen != 0)
|
|
{
|
|
ppan->hsurfScreen = hsurfScreen;
|
|
|
|
// Now associate the surface and the PANDEV.
|
|
//
|
|
// We have to associate the surface we just created with our
|
|
// physical device so that GDI can get information related to
|
|
// the PANDEV when it's drawing to the surface (such as, for
|
|
// example, the length of styles on the device when simulating
|
|
// styled lines).
|
|
|
|
if (EngAssociateSurface(hsurfScreen,
|
|
ppan->hdev,
|
|
(HOOK_BITBLT |
|
|
HOOK_TEXTOUT |
|
|
HOOK_COPYBITS |
|
|
HOOK_STRETCHBLT |
|
|
HOOK_ALPHABLEND |
|
|
HOOK_TRANSPARENTBLT |
|
|
HOOK_GRADIENTFILL |
|
|
HOOK_STROKEPATH |
|
|
HOOK_SYNCHRONIZE)))
|
|
{
|
|
// Create the shadow DIB on which we'll have GDI do all
|
|
// the drawing. We'll merely occasionally blt portions
|
|
// to the screen to update.
|
|
|
|
hsurfShadow = (HSURF) EngCreateBitmap(sizl,
|
|
sizl.cx,
|
|
ppan->iBitmapFormat,
|
|
BMF_TOPDOWN,
|
|
NULL);
|
|
|
|
psoShadow = EngLockSurface(hsurfShadow);
|
|
if (psoShadow != NULL)
|
|
{
|
|
ppan->psoShadow = psoShadow;
|
|
|
|
// If we're doing the double-dpi thing, we need to
|
|
// create a scratch buffer for the shrink:
|
|
|
|
hsurfShrink = NULL;
|
|
psoShrink = NULL;
|
|
bShrink = ((G_fDoubleDpi) &&
|
|
(ppan->iBitmapFormat == BMF_32BPP));
|
|
|
|
if (bShrink)
|
|
{
|
|
// Create a temporary surface used as the scratch
|
|
// buffer destination for our 'shrink' blt:
|
|
|
|
sizlShrink.cx = sizl.cx >> 1;
|
|
sizlShrink.cy = sizl.cy >> 1;
|
|
|
|
hsurfShrink = (HSURF) EngCreateBitmap(sizlShrink,
|
|
sizlShrink.cx,
|
|
ppan->iBitmapFormat,
|
|
BMF_TOPDOWN,
|
|
NULL);
|
|
psoShrink = EngLockSurface(hsurfShrink);
|
|
}
|
|
|
|
// If shrinking and couldn't allocate 'psoShrink',
|
|
// then fail:
|
|
|
|
if ((!bShrink) || (psoShrink != NULL))
|
|
{
|
|
ppan->psoShrink = psoShrink;
|
|
|
|
if (EngAssociateSurface(hsurfShadow,
|
|
ppan->hdev,
|
|
0))
|
|
{
|
|
RGNMEMOBJ rmoDirty;
|
|
RGNMEMOBJ rmoOld;
|
|
RGNMEMOBJ rmoRect;
|
|
|
|
if ((rmoOld.bValid()) &&
|
|
(rmoDirty.bValid()) &&
|
|
(rmoRect.bValid()))
|
|
{
|
|
rmoDirty.vSet();
|
|
rmoOld.vSet();
|
|
|
|
ppan->prgnDirty = rmoDirty.prgnGet();
|
|
ppan->prgnOld = rmoOld.prgnGet();
|
|
ppan->prgnRect = rmoRect.prgnGet();
|
|
|
|
// Blank the screen:
|
|
|
|
PanSynchronize(dhpdev, NULL);
|
|
|
|
// Success!
|
|
|
|
return(hsurfScreen);
|
|
}
|
|
|
|
rmoOld.vDeleteRGNOBJ();
|
|
rmoDirty.vDeleteRGNOBJ();
|
|
rmoRect.vDeleteRGNOBJ();
|
|
}
|
|
}
|
|
|
|
EngUnlockSurface(psoShrink);
|
|
EngDeleteSurface(hsurfShrink);
|
|
}
|
|
|
|
EngUnlockSurface(psoShadow);
|
|
EngDeleteSurface(hsurfShadow);
|
|
}
|
|
|
|
EngDeleteSurface(hsurfScreen);
|
|
}
|
|
|
|
EngUnlockSurface(psoDevice);
|
|
}
|
|
|
|
(*PPFNTABLE(ppan->apfn, DisableSurface))(ppan->dhpdevDevice);
|
|
}
|
|
|
|
return(0);
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* VOID PanDisableSurface
|
|
*
|
|
* Free resources allocated by PanEnableSurface. Release the surface.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
VOID PanDisableSurface(
|
|
DHPDEV dhpdev)
|
|
{
|
|
PANDEV* ppan;
|
|
HSURF hsurfShadow;
|
|
HSURF hsurfShrink;
|
|
|
|
ppan = (PANDEV*) dhpdev;
|
|
|
|
RGNOBJ roDirty(ppan->prgnDirty);
|
|
RGNOBJ roOld(ppan->prgnOld);
|
|
RGNOBJ roRect(ppan->prgnRect);
|
|
|
|
roOld.vDeleteRGNOBJ();
|
|
roDirty.vDeleteRGNOBJ();
|
|
roRect.vDeleteRGNOBJ();
|
|
|
|
hsurfShadow = ppan->psoShadow->hsurf;
|
|
EngUnlockSurface(ppan->psoShadow);
|
|
EngDeleteSurface(hsurfShadow);
|
|
|
|
if (ppan->psoShrink)
|
|
{
|
|
hsurfShrink = ppan->psoShrink->hsurf;
|
|
EngUnlockSurface(ppan->psoShrink);
|
|
EngDeleteSurface(hsurfShrink);
|
|
}
|
|
|
|
EngDeleteSurface(ppan->hsurfScreen);
|
|
EngUnlockSurface(ppan->psoDevice);
|
|
|
|
(*PPFNTABLE(ppan->apfn, DisableSurface))(ppan->dhpdevDevice);
|
|
}
|
|
|
|
/******************************Public*Structure****************************\
|
|
* DFVFN gadrvfnPanning[]
|
|
*
|
|
* Build the driver function table gadrvfn with function index/address
|
|
* pairs. This table tells GDI which DDI calls we support, and their
|
|
* location (GDI does an indirect call through this table to call us).
|
|
*
|
|
\**************************************************************************/
|
|
|
|
DRVFN gadrvfnPanning[] = {
|
|
{ INDEX_DrvEnablePDEV, (PFN) PanEnablePDEV },
|
|
{ INDEX_DrvCompletePDEV, (PFN) PanCompletePDEV },
|
|
{ INDEX_DrvDisablePDEV, (PFN) PanDisablePDEV },
|
|
{ INDEX_DrvEnableSurface, (PFN) PanEnableSurface },
|
|
{ INDEX_DrvDisableSurface, (PFN) PanDisableSurface },
|
|
{ INDEX_DrvDitherColor, (PFN) PanDitherColor },
|
|
{ INDEX_DrvAssertMode, (PFN) PanAssertMode },
|
|
{ INDEX_DrvBitBlt, (PFN) PanBitBlt },
|
|
{ INDEX_DrvTextOut, (PFN) PanTextOut },
|
|
{ INDEX_DrvStrokePath, (PFN) PanStrokePath },
|
|
{ INDEX_DrvCopyBits, (PFN) PanCopyBits },
|
|
{ INDEX_DrvTransparentBlt, (PFN) PanTransparentBlt },
|
|
{ INDEX_DrvAlphaBlend, (PFN) PanAlphaBlend },
|
|
{ INDEX_DrvGradientFill, (PFN) PanGradientFill },
|
|
{ INDEX_DrvStretchBlt, (PFN) PanStretchBlt },
|
|
{ INDEX_DrvSetPalette, (PFN) PanSetPalette },
|
|
{ INDEX_DrvMovePointer, (PFN) PanMovePointer },
|
|
{ INDEX_DrvSynchronize, (PFN) PanSynchronize },
|
|
};
|
|
|
|
ULONG gcdrvfnPanning = sizeof(gadrvfnPanning) / sizeof(DRVFN);
|
|
|
|
/******************************Public*Routine******************************\
|
|
* BOOL PanEnableDriver
|
|
*
|
|
* Standard driver DrvEnableDriver function
|
|
*
|
|
\**************************************************************************/
|
|
|
|
BOOL PanEnableDriver(
|
|
ULONG iEngineVersion,
|
|
ULONG cj,
|
|
DRVENABLEDATA* pded)
|
|
{
|
|
pded->pdrvfn = gadrvfnPanning;
|
|
pded->c = gcdrvfnPanning;
|
|
pded->iDriverVersion = DDI_DRIVER_VERSION_NT5;
|
|
|
|
return(TRUE);
|
|
}
|