GFX.C

/*========================================================================== 
*
* Copyright (C) 1995-1997 Microsoft Corporation. All Rights Reserved.
* Copyright (C) 1994-1995 ATI Technologies Inc. All Rights Reserved.
*
* File:gfx.c
* Content:graphics API
*
***************************************************************************/
#include "foxbear.h"

GFX_BITMAP *lpVRAM;

static BOOL fForceRestore = FALSE;

/*
* gfxBlt
*/
BOOL gfxBlt(RECT *dst, GFX_HBM bm, POINT *src)
{
GFX_BITMAP* pbm = (GFX_BITMAP*)bm;
HRESULT ddrval;
DWORD bltflags;
RECT rc;
int x,y,dx,dy;

if( GameSize.cy == C_SCREEN_H )
{
y = dst->top;
dy = dst->bottom - dst->top;
rc.top = src->y;
rc.bottom = rc.top + dy;
}
else
{
y = MapY(dst->top);
dy = MapY(dst->bottom) - y;
rc.top = MapDY(src->y);
rc.bottom = rc.top + dy;
}

if( GameSize.cx == C_SCREEN_W )
{
x = dst->left;
dx = dst->right - dst->left;
rc.left = src->x;
rc.right = rc.left + dx;
}
else
{
x = MapX(dst->left);
dx = MapX(dst->right) - x;
rc.left = MapDX(src->x);
rc.right = rc.left + dx;
}


if( dx == 0 || dy == 0 )
{
return TRUE;
}

if (pbm->lpSurface)
{
if( pbm->bTrans )
bltflags = bTransDest ? DDBLTFAST_DESTCOLORKEY : DDBLTFAST_SRCCOLORKEY;
else
bltflags = bTransDest ? DDBLTFAST_DESTCOLORKEY : DDBLTFAST_NOCOLORKEY;
ddrval = IDirectDrawSurface_BltFast(
lpBackBuffer, x, y,
pbm->lpSurface, &rc, bltflags | DDBLTFAST_WAIT);

if (ddrval != DD_OK)
{
Msg("BltFast failed err=%d", ddrval);
}
}
else
{
DDBLTFX ddbltfx;

rc.left = x;
rc.top = y;
rc.right = rc.left + dx;
rc.bottom = rc.top + dy;

ddbltfx.dwSize = sizeof( ddbltfx );
ddbltfx.dwFillColor = pbm->dwColor;

ddrval = IDirectDrawSurface_Blt(
lpBackBuffer, // dest surface
&rc, // dest rect
NULL, // src surface
NULL, // src rect
DDBLT_COLORFILL | DDBLT_WAIT,
&ddbltfx);
}

return TRUE;

} /* gfxBlt */

/*
* gfxCreateSolidColorBitmap
*/
GFX_HBM gfxCreateSolidColorBitmap(COLORREF rgb)
{
GFX_BITMAP *pvram;

pvram = MemAlloc( sizeof( *pvram ) );

if( pvram == NULL )
{
return NULL;
}

pvram->dwColor = DDColorMatch(lpBackBuffer, rgb);
pvram->lpSurface = NULL;
pvram->lpbi = NULL;
pvram->bTrans = FALSE;

pvram->link = lpVRAM;
lpVRAM = pvram;

return (GFX_HBM) pvram;

} /* gfxCreateSolidColorBitmap */

/*
* gfxCreateBitmap
*/
GFX_HBM gfxCreateVramBitmap(BITMAPINFOHEADER UNALIGNED *lpbi,BOOL bTrans)
{
GFX_BITMAP *pvram;

pvram = MemAlloc( sizeof( *pvram ) );

if( pvram == NULL )
{
return NULL;
}
pvram->lpSurface = DDCreateSurface(MapRX(lpbi->biWidth),
MapRY(lpbi->biHeight), FALSE, TRUE);
pvram->lpbi = lpbi;
pvram->dwColor = 0;
pvram->bTrans = bTrans;

if( pvram->lpSurface == NULL )
{
return NULL;
}

pvram->link = lpVRAM;
lpVRAM = pvram;
gfxRestore((GFX_HBM) pvram);

return (GFX_HBM) pvram;

} /* gfxCreateVramBitmap */

/*
* gfxDestroyBitmap
*/
BOOL gfxDestroyBitmap ( GFX_HBM hbm )
{
GFX_BITMAP *p = (GFX_BITMAP *)hbm;

if (hbm == NULL || hbm == GFX_TRUE)
{
return FALSE;
}

if (p->lpSurface)
{
IDirectDrawSurface_Release(p->lpSurface);
p->lpSurface = NULL;
}

if (p->lpbi)
{
p->lpbi = NULL;
}

MemFree((VOID *)p);

return TRUE;

} /* gfxDestroyBitmap */

/*
* gfxStretchBackBuffer()
*/
BOOL gfxStretchBackbuffer()
{
if (lpStretchBuffer)
{
IDirectDrawSurface_Blt(
lpStretchBuffer, // dest surface
NULL, // dest rect (all of it)
lpBackBuffer, // src surface
&GameRect, // src rect
DDBLT_WAIT,
NULL);

IDirectDrawSurface_Blt(
lpBackBuffer, // dest surface
NULL, // dest rect (all of it)
lpStretchBuffer, // src surface
NULL, // src rect
DDBLT_WAIT,
NULL);
}
else
{
IDirectDrawSurface_Blt(
lpBackBuffer, // dest surface
NULL, // dest rect (all of it)
lpBackBuffer, // src surface
&GameRect, // src rect
DDBLT_WAIT,
NULL);
}

return TRUE;

} /* gfxStretchBackbuffer */

/*
* gfxFlip
*/
BOOL gfxFlip( void )
{
HRESULT ddrval;

ddrval = IDirectDrawSurface_Flip( lpFrontBuffer, NULL, DDFLIP_WAIT );
if( ddrval != DD_OK )
{
Msg( "Flip FAILED, rc=%08lx", ddrval );
return FALSE;
}
return TRUE;

} /* gfxFlip */

/*
* gfxUpdateWindow
*/
BOOL gfxUpdateWindow()
{
HRESULT ddrval;

ddrval = IDirectDrawSurface_Blt(
lpFrontBuffer, // dest surface
&rcWindow, // dest rect
lpBackBuffer, // src surface
NULL, // src rect (all of it)
DDBLT_WAIT,
NULL);

return ddrval == DD_OK;

} /* gfxUpdateWindow */

/*
* gfxSwapBuffers
*
* this is called when the game loop has rendered a frame into
* the backbuffer, its goal is to display something for the user to see.
*
* there are four cases...
*
* Fullscreen:
* we just call IDirectDrawSurface::Flip(lpFrontBuffer)
* being careful to handle return code right.
*
* Fullscreen (stretched):
* the game loop has rendered a frame 1/2 the display
* size, we do a Blt to stretch the frame to the backbuffer
* the we just call IDirectDrawSurface::Flip(lpFrontBuffer)
*
* Window mode (foreground palette):
* in this case we call IDirectDrawSurface::Blt to copy
* the back buffer to the window.
*
* Window mode (background palette):
* in this case we are in a window, but we dont own the
* palette. all our art was loaded to a specific palette
* IDirectDrawSurface::Blt does not do color translation
* we have a few options in this case...
*
* reload or remap the art to the the current palette
* (we can do this easily with a GetDC, StetchDIBits)
* FoxBear has *alot* of art, so this would be too slow.
*
* use GDI to draw the backbuffer, GDI will handle
* the color conversion so things will look correct.
*
* pause the game (this is what we do so this function
* will never be called)
*
*/
BOOL gfxSwapBuffers( void )
{
if( bFullscreen )
{
if( bStretch )
{
gfxStretchBackbuffer();
}

if (nBufferCount > 1)
return gfxFlip();
else
return TRUE;
}
else
{
return gfxUpdateWindow();
}

} /* gfxSwapBuffers */

/*
* gfxBegin
*/
GFX_HBM gfxBegin( void )
{
if( !DDEnable() )
{
return NULL;
}

if( !DDCreateFlippingSurface() )
{
DDDisable(TRUE);
return NULL;
}
Splash();

return GFX_TRUE;

} /* gfxBegin */

/*
* gfxEnd
*/
BOOL gfxEnd ( GFX_HBM hbm )
{
GFX_BITMAP *curr;
GFX_BITMAP *next;

for( curr = lpVRAM; curr; curr=next )
{
next = curr->link;
gfxDestroyBitmap ((GFX_HBM)curr);
}

lpVRAM = NULL;

return DDDisable(FALSE);

return TRUE;

} /* gfxEnd */

/*
* gfxRestore
*
* restore the art when one or more surfaces are lost
*/
BOOL gfxRestore(GFX_HBM bm)
{
GFX_BITMAP *pbm = (GFX_BITMAP*)bm;
HRESULT ddrval;
HDC hdc;
LPVOID lpBits;
RGBQUAD *prgb;
int i,w,h;
RECT rc;

struct {
BITMAPINFOHEADER bi;
RGBQUAD ct[256];
} dib;

IDirectDrawSurface *pdds = pbm->lpSurface;
BITMAPINFOHEADER UNALIGNED *pbi = pbm->lpbi;

if (pdds == NULL)
return TRUE;

if (IDirectDrawSurface_Restore(pdds) != DD_OK)
return FALSE;

if (pbi == NULL)
return TRUE;

//
// in 8bbp mode if we get switched away from while loading
// (and palette mapping) our art, the colors will not be correct
// because some app may have changed the system palette.
//
// if we are in stress mode, just keep going. It is more important
// to make progress than to get the colors right.
//

if (!bFullscreen &&
GameBPP == 8 &&
GetForegroundWindow() != hWndMain &&
!bStress )
{
Msg("gfxRestore: **** foreground window changed while loading art!");
fForceRestore = TRUE;
PauseGame();
return FALSE;
}

dib.bi = *pbi;

prgb = (RGBQUAD *)((LPBYTE)pbi + pbi->biSize);
lpBits = (LPBYTE)(prgb + pbi->biClrUsed);

if( pbi->biClrUsed == 0 && pbi->biBitCount <= 8 )
{
lpBits = (LPBYTE)(prgb + (1<<pbi->biBitCount));
}

w = MapRX(pbi->biWidth);
h = MapRY(pbi->biHeight);
/*
* hack to make sure fox off-white doesn't become
* pure white (which is transparent)
*/
for( i=0; i<256; i++ )
{
dib.ct[i] = prgb[i];

if( dib.ct[i].rgbRed == 0xff &&
dib.ct[i].rgbGreen == 0xff &&
dib.ct[i].rgbBlue == 224 )
{
dib.ct[i].rgbBlue = 0x80;
}
else
if( dib.ct[i].rgbRed == 251 &&
dib.ct[i].rgbGreen == 243 &&
dib.ct[i].rgbBlue == 234 )
{
dib.ct[i].rgbBlue = 0x80;
}
}

/*
* if we are in 8bit mode we know the palette is 332 we can
* do the mapping our self.
*
* NOTE we can only do this in fullscreen mode
* in windowed mode, we have to share the palette with
* the window manager and we dont get all of the colors
* in the order we assume.
*
*/
if (bFullscreen && GameBPP == pbi->biBitCount && GameBPP == 8 )
{
BYTE xlat332[256];
DDSURFACEDESC ddsd;
int x,y,dib_pitch;
BYTE *src, *dst;
BOOL stretch;
IDirectDrawSurface *pdds1;
HDC hdc1;

stretch = w != pbi->biWidth || h != pbi->biHeight;

for( i=0;i<256;i++ )
{
xlat332[i] =
((dib.ct[i].rgbRed >> 0) & 0xE0 ) |
((dib.ct[i].rgbGreen >> 3) & 0x1C ) |
((dib.ct[i].rgbBlue >> 6) & 0x03 );
}

/*
* if we are stretching copy into the back buffer
* then use GDI to stretch later.
*/
if( stretch )
{
pdds1 = lpBackBuffer;
}
else
{
pdds1 = pdds;
}

ddsd.dwSize = sizeof(ddsd);
ddrval = IDirectDrawSurface_Lock(
pdds1, NULL, &ddsd, DDLOCK_WAIT, NULL);

if( ddrval == DD_OK )
{
dib_pitch = (pbi->biWidth+3)&~3;
src = (BYTE *)lpBits + dib_pitch * (pbi->biHeight-1);
dst = (BYTE *)ddsd.lpSurface;
for( y=0; y<(int)pbi->biHeight; y++ )
{
for( x=0; x<(int)pbi->biWidth; x++ )
{
dst[x] = xlat332[src[x]];
}
dst += ddsd.lPitch;
src -= dib_pitch;
}
IDirectDrawSurface_Unlock(pdds1, NULL);
}
else
{
Msg("Lock failed err=%d", ddrval);
return FALSE;
}

if( stretch )
{
if( IDirectDrawSurface_GetDC(pdds,&hdc) == DD_OK )
{
if( IDirectDrawSurface_GetDC(pdds1,&hdc1) == DD_OK )
{
SetStretchBltMode(hdc, COLORONCOLOR);
StretchBlt(hdc, 0, 0, w, h,
hdc1, 0, 0, pbi->biWidth, pbi->biHeight, SRCCOPY);
IDirectDrawSurface_ReleaseDC(pdds1,hdc1);
}
IDirectDrawSurface_ReleaseDC(pdds,hdc);
}
}
}
else if( IDirectDrawSurface_GetDC(pdds,&hdc) == DD_OK )
{
SetStretchBltMode(hdc, COLORONCOLOR);
StretchDIBits(hdc, 0, 0, w, h,
0, 0, pbi->biWidth, pbi->biHeight,
lpBits, (BITMAPINFO *)&dib.bi, DIB_RGB_COLORS, SRCCOPY);

IDirectDrawSurface_ReleaseDC(pdds,hdc);
}

/*
* show the art while loading...
*/
rc.left = rcWindow.left,
rc.top = rcWindow.top + 20;
rc.right = rc.left + w;
rc.bottom = rc.top + h;
IDirectDrawSurface_Blt(lpFrontBuffer, &rc, pdds, NULL, DDBLT_WAIT, NULL);

return TRUE;

} /* gfxRestore */

/*
* gfxRestoreAll
*
* restore the art when one or more surfaces are lost
*/
BOOL gfxRestoreAll()
{
GFX_BITMAP *curr;
HWND hwndF = GetForegroundWindow();

Splash();

for( curr = lpVRAM; curr != NULL; curr = curr->link)
{
if (curr->lpSurface &&
(fForceRestore || IDirectDrawSurface_IsLost(curr->lpSurface) == DDERR_SURFACELOST))
{
if( !gfxRestore(curr) )
{
Msg( "gfxRestoreAll: ************ Restore FAILED!" );
return FALSE;
}
}
}

DDClear();
fForceRestore = FALSE;
return TRUE;

} /* gfxRestoreAll */

/*
* gfxFillBack
*/
void gfxFillBack( DWORD dwColor )
{
DDBLTFXddbltfx;

ddbltfx.dwSize = sizeof( ddbltfx );
ddbltfx.dwFillColor = dwColor;

IDirectDrawSurface_Blt(
lpBackBuffer,// dest surface
NULL,// dest rect
NULL,// src surface
NULL,// src rect
DDBLT_COLORFILL | DDBLT_WAIT,
&ddbltfx);

} /* gfxFillBack */