Windows NT 4.0 source code leak
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.
 
 
 
 
 
 

586 lines
18 KiB

/*************************************************************************
* overlay.c
*
* Description -- emulates overlay functionality (dirty rect management).
*
* History --
* 4/07/95 Ported from act4d\sprite\sprite\idesktop.cxx andyco
* 5/01/95 Better integration with ddraw andyco
* 5/15/95 Modified to use DCI10 if available andyco
* 6/30/95 use GetProcessPrimary to get pointer to primary surface kylej
* 7/04/95 new direct draw object; changed overlays to use dest
* surface instead of primary craige
* 7/15/95 Overlaying onto the backbuffer andyco
*
* Copyright (c) Microsoft Corporation 1994-1995
*
**************************************************************************/
#ifdef USE_SOFTWARE_OVERLAYS
#include "ddhelos.h"
#ifdef WIN95
#include "dciddi.h"
#endif
#include "ddrawpr.h"
#include "ddrawi.h"
#include "ddhelpri.h"
#include "assert4d.h"
#include "bitblt.h"
#include "dibfx.h"
// get a surface from the ddraw doublenode (DBN)
#define DBNSURF(pdbn) ((LPDDRAWI_DDRAWSURFACE_LCL)pdbn->object)
int gcDirtyRects=0; // the current number of dirty rects
int gcMaxDirtyRects=0; // the max allocated space for dirty rects
// virtual screen size
extern int giScreenWidth;
extern int giScreenHeight;
extern int giPhysScreenWidth;
extern int giPhysScreenHeight;
// see ddhelpri.h for a description of the dirty rect structure
PDIRTYRECT gpDirtyRectArray; // pointer to the list of dirtyrects
// when we expand the dirty rect array, we resize it by DIRTYRECTINCREMENT
#define DIRTYRECTINCREMENT 5
// set when we create the comp buf. we use this to know if the user is trying
// to set a different comp buf
#if defined(WIN95) || defined(NT_FIX)
extern LPBITMAPINFO gpbmiSrc,gpbmiDest; // keep these around to pass to blitlib
BOOL gfCB=FALSE;
// Msg defined in ddhel.c
#ifdef DEBUG
#define MSG Msg
void __cdecl Msg( LPSTR szFormat, ... );
#else // DEBUG
#define MSG 1 ? (void)0 : (void)
#endif // DEBUG
#endif //NT_FIX or WIN95
/*******************************************************************
*
* SearchAndUnionDirtyRects
*
* the data structure of interest here is the dirty rect list
*
* an invarient for this list is that no rectangle in the list can overlap any other
* rectangle in the list.
*
* so, when we insert a rectangle into the list (via the adddirtyrect api) we go through the list
* and determine if the rectangle intersects any in the list. if it does, we intersect the given rect
* with the intersected rect, mark the rect that was in the list as deleted, and
* start checking again from the beginning.
*
* RETURNS the index of the first deleted dirty rect in the list
* Called only by AddDirtyRect
*
*********************************************************************/
int SearchAndUnionDirtyRects(RECT *pRect)
{
int idxMin=gcDirtyRects; // index of 1st deleted rect (return value)
int nFound=0; // number of rects checked (used to terminate loop)
PDIRTYRECT prcDirtyRect; // pointer into list of dirtyrects
RECT rcTmp; // temp rect to test for intersections
// keep going through the list until I get to the end. everytime i interesect a rect, union with it,
// delete it from the list, and start again.
while (nFound < gcDirtyRects) {
for (prcDirtyRect = gpDirtyRectArray; prcDirtyRect < gpDirtyRectArray+gcMaxDirtyRects; prcDirtyRect++)
{
if (!prcDirtyRect->bDelete)
{
nFound++; // we found a valid dirty rect
if (IntersectRect(&rcTmp,pRect, &prcDirtyRect->rcRect))
{
UnionRect(pRect,pRect,&prcDirtyRect->rcRect); // delete the intersecting rect, and start again
prcDirtyRect->bDelete = TRUE;
gcDirtyRects--;
nFound=0;
}
}
// check the same if, since the condition might have changed above...
if (prcDirtyRect->bDelete){
if ((prcDirtyRect-gpDirtyRectArray)< idxMin)
idxMin = (prcDirtyRect-gpDirtyRectArray);
}
}
}
return idxMin;
}
/*******************************************************************
*
* AddDirtyRect - Add a dirty rectangle to the dirty list
* (ported from act4d\sprite\idesktop.cxx)
*
* calls SearchAndUnionDirtyRects to make sure the added dirty rect
* does not overlap any rects in the list.
* adds the rect in the first empty position in the list.
*
*********************************************************************/
SCODE AddDirtyRect(LPRECT lpRect)
{
LPBYTE pNew=NULL; // tmp variable in case realloc fails, don't blow away current list.
HRESULT hr=NOERROR;
WORD idxFirstDeleted;
PDIRTYRECT prcDirtyRect;
RECT rcTmp;
RECT rcScreen;
SetRect(&rcScreen,0,0,giScreenWidth,giScreenHeight);
if (IsRectEmpty (lpRect)) {
*lpRect = rcScreen;
}
else
IntersectRect(lpRect, lpRect, &rcScreen);
// make sure we've alloc'ed the dirty rect array
if (0==gcMaxDirtyRects) {
gcMaxDirtyRects=DIRTYRECTINCREMENT;
gpDirtyRectArray = (PDIRTYRECT) osMemAlloc(sizeof(DIRTYRECT) * (gcMaxDirtyRects) );
if (!gpDirtyRectArray) {
assert(FALSE);
return(E_OUTOFMEMORY);
}
}
// store rect in rcTmp, since SearchAndUnion is destructive on the rect parameter
rcTmp = *lpRect;
idxFirstDeleted = SearchAndUnionDirtyRects(&rcTmp);
// Can we insert the the dirty rectangle in an deleted position?
if (idxFirstDeleted < gcMaxDirtyRects)
{
prcDirtyRect = gpDirtyRectArray + idxFirstDeleted;
}
else // increase the array size
{
// We we need to grow the array?
if (gcDirtyRects + 1 > gcMaxDirtyRects)
{
pNew = (unsigned char *) osMemReAlloc(gpDirtyRectArray, sizeof(DIRTYRECT) *
(gcMaxDirtyRects+DIRTYRECTINCREMENT));
if (!pNew)
{
assert (FALSE);
hr |= E_OUTOFMEMORY;
}
else {// use the new dirty rect array / location
gpDirtyRectArray = (PDIRTYRECT) pNew;
prcDirtyRect = gpDirtyRectArray + (gcMaxDirtyRects - 1);
gcMaxDirtyRects+=DIRTYRECTINCREMENT;
}
}
else {
assert(FALSE); // should never happen. logic error...
}
}
if (SUCCEEDED(hr))
{
prcDirtyRect->rcRect = rcTmp; // Store the rectangle
prcDirtyRect->bDelete = FALSE;
gcDirtyRects++;
}
return hr;
}
#if defined(WIN95) || defined(NT_FIX)
HRESULT DoSurfaceBltFast(LPDDRAWI_DDRAWSURFACE_LCL psurfDst,LPDDRAWI_DDRAWSURFACE_LCL psurfSrc,RECT *prc)
{
HRESULT hr;
while( 1 )
{
if( hr == DD_OK )
{
break;
}
if( hr == DDERR_WASSTILLDRAWING )
{
continue;
}
if( hr == DDERR_SURFACELOST )
{
psurfDst->lpVtbl->Restore((LPDIRECTDRAWSURFACE)psurfDst);
psurfSrc->lpVtbl->Restore((LPDIRECTDRAWSURFACE)psurfSrc);
continue;
}
break;
}
return(hr);
}
#ifdef DEBUG
void DebugDumpDib(PDIBINFO pDIB,PDIBBITS pdibbits)
{
HDC hdcScreen=NULL;
hdcScreen = GetDC(NULL);
SetDIBitsToDevice(hdcScreen,0,0,pDIB->bmiHeader.biWidth,pDIB->bmiHeader.biHeight,0,0,0,
pDIB->bmiHeader.biHeight,pdibbits,(LPBITMAPINFO)pDIB,DIB_RGB_COLORS);
ReleaseDC(NULL,hdcScreen);
}
#endif
HRESULT DoSurfaceBlt(LPDDRAWI_DDRAWSURFACE_LCL psurfDst,LPDDRAWI_DDRAWSURFACE_LCL psurfSrc,RECT *prcDest,RECT *prcSrc,
COLORREF crXparent,ALPHAREF arAlpha)
{
DDBLTFX ddbltfx;
DWORD dwFlags=0;
HRESULT hr;
memset(&ddbltfx,0,sizeof( ddbltfx ));
ddbltfx.dwSize = sizeof( ddbltfx );
dwFlags = DDBLT_ROP;
ddbltfx.dwROP = SRCCOPY;
if (crXparent != CLR_INVALID)
{
dwFlags |= DDBLT_KEYSRCOVERRIDE;
ddbltfx.ddckSrcColorkey.dwColorSpaceLowValue=ddbltfx.ddckSrcColorkey.dwColorSpaceHighValue=crXparent;
}
if (arAlpha != ALPHA_INVALID)
{
dwFlags |= DDBLT_ALPHASRCCONSTOVERRIDE;
ddbltfx.dwAlphaSrcConst=arAlpha;
}
while( 1)
{
hr = psurfDst->lpVtbl->Blt(
(LPDIRECTDRAWSURFACE)psurfDst, // dest surface
prcDest, // dest rect
(LPDIRECTDRAWSURFACE)psurfSrc, // src surface
prcSrc, // src rect
dwFlags,
&ddbltfx ) ;
if( hr == DD_OK )
{
break;
}
if( hr == DDERR_WASSTILLDRAWING )
{
continue;
}
if( hr == DDERR_SURFACELOST )
{
psurfDst->lpVtbl->Restore((LPDIRECTDRAWSURFACE)psurfDst);
psurfSrc->lpVtbl->Restore((LPDIRECTDRAWSURFACE)psurfSrc);
continue;
}
break;
}
return(hr);
}
/*
** UpdateRect
*
* PARAMETERS:
* lpDD - pointer to the direct draw object
* rcRect - the rect being updated
* lpdbnDispList - the display list. this is the list of overlay surfaces from back to front
* pdiCB,pdbCB - dibinfo and dibbits for the composition buffer. this is what we draw into.
*
* DESCRIPTION:
* for the given rect (rcRect) redraws all surfaces which intersect that rect into the compositon
* buffer (pdiCB,pdbCB).
*
* RETURNS:
* blitlib return code
*
* TODO: primary surface currently always backmost surface. need to make z order setable...(ddraw issue)
*/
HRESULT UpdateRect(LPDDHAL_UPDATEOVERLAYDATA puod,LPDDRAWI_DIRECTDRAW_LCL pdrvx,RECT rcRect,PDIBBITS pdbCB)
{
RECT rcUpdate;
RECT rcSrcUpdate; // the rect in the (unstretched) SRC space that rcUpdate maps back to
HRESULT hr;
PDIBBITS pdbSrc=NULL;
ALPHAREF arAlpha=ALPHA_INVALID;
COLORREF crXparent=CLR_INVALID;
LPDDRAWI_DDRAWSURFACE_LCL psurfDest;
RECT rcSrc,rcDest; // makes code a little cleaner - the current overlay rects
LPDBLNODE lpdbnDispList=NULL;
LPDDRAWI_DDRAWSURFACE_LCL psurfOverlay;
psurfDest = puod->lpDDDestSurface;
if (psurfDest)
{
// we really want to blt to the dest surface, we don't want to hit the buffer we tell people
// is the overlay dest surface...so we toggle the helcb bit
psurfDest->dwFlags &= ~DDRAWISURF_HELCB;
// first, update the background - surface we're overalying
hr = DoSurfaceBlt(psurfDest,pdrvx->lpCB,&rcRect,&rcRect,CLR_INVALID,ALPHA_INVALID);
}
else
{
MSG("Overlay - null destination specified!!");
return(E_UNEXPECTED);
}
lpdbnDispList=puod->lpDD->dbnOverlayRoot.next;
while (NULL != (psurfOverlay=DBNSURF(lpdbnDispList))) // update the rect, from back to front
{
if ( (ISVISIBLE(psurfOverlay)) &&
( IntersectRect(&rcUpdate,&rcRect,&(psurfOverlay->rcOverlayDest)) )) {
// crXparent and arAlpha are kept up to date with the overlay surface by the ddhel's UpdateOverlay API
crXparent= (COLORREF) psurfOverlay->dwClrXparent;
arAlpha = (ALPHAREF) psurfOverlay->dwAlpha;
// if we're stretching from src to dest, then we need the rect in the src that maps to the rect
// in the dest that we're updating
rcSrc = (psurfOverlay)->rcOverlaySrc;
rcDest= (psurfOverlay)->rcOverlayDest;
XformRect(&rcSrc,&rcDest,&rcUpdate,&rcSrcUpdate,SCALE_X(rcSrc,rcDest),SCALE_Y(rcSrc,rcDest));
hr = DoSurfaceBlt(psurfDest,psurfOverlay,&rcUpdate,&rcSrcUpdate,crXparent,arAlpha);
} // end if intersect rect
lpdbnDispList=lpdbnDispList->next;
} // end while
psurfDest->dwFlags |= DDRAWISURF_HELCB;
return(hr);
}
/*
** CreateCB
*
* PARAMETERS:
* puod - update overlay data
* pdrvx - the _lcl ddraw object
* psurfx - the _lcl surface that we're going to compose onto
*
* DESCRIPTION:
* psurfx is going to be the composition buffer. we create a new surface (lpcb) that we
* give to anyone who wants to use psurfx. that way, we can compose into psurfx in peace.
*
*/
HRESULT CreateCompBuf(LPDDHAL_UPDATEOVERLAYDATA puod,LPDDRAWI_DIRECTDRAW_LCL pdrvx,LPDDRAWI_DDRAWSURFACE_LCL psurfx)
{
DDSURFACEDESC ddsd;
HRESULT hr;
LPDDRAWI_DDRAWSURFACE_LCL psurfCB;
RECT rcScreen={0,0,giScreenWidth,giScreenHeight};
MSG("creating comp buff");
// init
memset(&ddsd,0,sizeof(DDSURFACEDESC));
ddsd.dwSize=sizeof(DDSURFACEDESC);
// we want to create a new surface that looks just like psurfx
psurfx->lpVtbl->GetSurfaceDesc((LPDIRECTDRAWSURFACE)psurfx,&ddsd);
ddsd.ddsCaps.dwCaps=DDSCAPS_OFFSCREENPLAIN;
ddsd.dwFlags= DDSD_CAPS|DDSD_HEIGHT|DDSD_WIDTH;
hr=pdrvx->lpVtbl->CreateSurface((LPDIRECTDRAW)pdrvx,&ddsd, (LPDIRECTDRAWSURFACE *)&psurfCB, NULL);
if (!SUCCEEDED(hr)) {
return(hr);
}
pdrvx->lpCB=psurfCB;
// copy the contents of psurfx to the newly created surface
DoSurfaceBltFast(psurfCB,psurfx,NULL);
AddDirtyRect(&rcScreen);
psurfx->dwFlags |= DDRAWISURF_HELCB;
MSG("comp buffer created");
return(S_OK);
}
/******************************************************************************
* UpdateDisplay
* cycle the dirty rect list, updating overlays that need fixin'
* for each dirty rect
* for each sprite that intersects the dirty rect (sprites from back to front)
* redraw the portion of the overlay intersecting the dirty list...
*
* the display list is stored in the ddraw object (puod->lpDD) which is a back to
* front list of sprites.
*
******************************************************************************/
SCODE UpdateDisplay(LPDDHAL_UPDATEOVERLAYDATA puod)
{
PDIRTYRECT pdrc;
PDIBBITS pdbCB; //,pdbScreen;
HRESULT hr=S_OK;
LPDDRAWI_DIRECTDRAW_LCL pdrvx;
LPDDRAWI_DDRAWSURFACE_LCL psurfx;
RECT rcBlt={0,0,giScreenWidth,giScreenHeight};
// overalys only on backbuffer for now?
if (!(puod->lpDDDestSurface->ddsCaps.dwCaps & DDSCAPS_BACKBUFFER))
{
MSG("ERROR - overlay destination must be backbuffer!!!");
return(DDERR_INVALIDPARAMS);
}
pdrvx = FindProcessDDObject( puod->lpDD );
psurfx = puod->lpDDDestSurface;
// create cb?
if (!(psurfx->dwFlags & DDRAWISURF_HELCB))
{
hr = CreateCompBuf(puod,pdrvx,psurfx);
} // create cb
// mirror the front buffer to the comp buffer, so we know what we're drawing onto
else if (puod->lpDDDestSurface->dwFlags & DDSCAPS_BACKBUFFER) {
// turn off the HELCB flag - we really do want to blt to the comp buffer
psurfx->dwFlags &= ~DDRAWISURF_HELCB;
DoSurfaceBltFast(psurfx,pdrvx->lpPrimary,NULL);
psurfx->dwFlags |= DDRAWISURF_HELCB;
}
SurfDibInfo(psurfx,gpbmiDest);
pdbCB = (PDIBBITS)psurfx->lpGbl->fpVidMem;
pdrc=gpDirtyRectArray;
// for each dirty rect
while (gcDirtyRects > 0)
{
if (!pdrc->bDelete) {
// we need to clean this rect
UpdateRect(puod,pdrvx,pdrc->rcRect,pdbCB);
#if 0 // to the screen?
pdbScreen=GetSurfPtr(puod->lpDDDestSurface,&pdrc->rcRect);
if (pdbScreen) {
// use pdicb as both the source and destination infoheader
hr = BlitLib_BitBlt(gpbmiDest,pdbScreen,&pdrc->rcRect,
gpbmiDest,pdbCB ,&pdrc->rcRect,
CLR_INVALID,ALPHA_INVALID,SRCCOPY);
ReleaseSurfPtr(puod->lpDDDestSurface);
}
else {
HDC hdc = GetPrimaryDC();
gpbmiDest->bmiHeader.biHeight *= -1;
StretchDIBits(hdc,
pdrc->rcRect.left,
pdrc->rcRect.top,
pdrc->rcRect.right - pdrc->rcRect.left,
pdrc->rcRect.bottom - pdrc->rcRect.top,
pdrc->rcRect.left,
pdrc->rcRect.top,
pdrc->rcRect.right - pdrc->rcRect.left,
pdrc->rcRect.bottom - pdrc->rcRect.top,
pdbCB,gpbmiDest,DIB_RGB_COLORS,SRCCOPY);
gpbmiDest->bmiHeader.biHeight *= -1;
ReleasePrimaryDC(hdc);
}
#endif
// this rectangle has been redrawn, we can now mark it as deleted
pdrc->bDelete=TRUE;
gcDirtyRects--;
}
pdrc++;
}
// fall through
//ERROR_EXIT:
return(hr);
}
/*
** OverlayPound
*
* forget about dirty rects
* forget about composition
* forget about clipping
* just draw the entire overlay list right on the dest surface
*
*/
HRESULT OverlayPound(LPDDHAL_UPDATEOVERLAYDATA puod)
{
LPDBLNODE lpdbnDisplayList;
PDIBBITS pdbSrc,pdbDest;
ALPHAREF arAlpha=ALPHA_INVALID;
COLORREF crXparent=CLR_INVALID;
HRESULT hr=S_OK;
RECT rcSrc,rcDest;
// hackhack where do we get the list from?
lpdbnDisplayList=puod->lpDD->dbnOverlayRoot.next;
SurfDibInfo(puod->lpDDDestSurface,gpbmiDest);
pdbDest = GetSurfPtr(puod->lpDDDestSurface,NULL);
if (pdbDest == NULL)
return hr;
while (NULL!=DBNSURF(lpdbnDisplayList)) // update the rect, from back to front
{
if ( ISVISIBLE(DBNSURF(lpdbnDisplayList)) ) {
SurfDibInfo(DBNSURF(lpdbnDisplayList),gpbmiSrc);
pdbSrc=GetSurfPtr(DBNSURF(lpdbnDisplayList),NULL);
// crXparent and arAlpha are kept up to date with the overlay surface by the ddhel's UpdateOverlay API
crXparent= (COLORREF) (DBNSURF(lpdbnDisplayList))->dwClrXparent;
arAlpha = (ALPHAREF) (DBNSURF(lpdbnDisplayList))->dwAlpha;
// if we're stretching from src to dest, then we need the rect in the src that maps to the rect
// in the dest that we're updating
rcSrc = (DBNSURF(lpdbnDisplayList))->rcOverlaySrc;
rcDest= (DBNSURF(lpdbnDisplayList))->rcOverlayDest;
// hack hack what about illegal rects???
#if 0 // we don't care about this ?
XformRect(&rcSrc,&rcDest,&rcUpdate,&rcSrcUpdate,SCALE_X(&rcSrc,&rcDest),SCALE_Y(&rcSrc,&rcDest));
#endif
hr |= BlitLib_BitBlt(gpbmiDest,pdbDest,&rcDest,gpbmiSrc,pdbSrc,
&rcSrc,crXparent,arAlpha,SRCCOPY);
ReleaseSurfPtr(DBNSURF(lpdbnDisplayList));
} // end if intersect rect
lpdbnDisplayList=lpdbnDisplayList->next;
} // end while
ReleaseSurfPtr(puod->lpDDDestSurface);
return(hr);
}
#endif
#endif // USE_SOFTWARE_OVERLAYS