BITMAP.C

/*********************************************************************** 
File: BitMap.c
Date: 5-23-92


Abstract:

This file contains functions pertaining to drawing a bitmap on the screen.

Contents:

FadePic() -- Slowly fade a picture on the screen to the background
DrawClippedPic() -- Draw a picture, clipped to a specified rectangle
NewFullPic() -- Create a record of type FullPicType
DeleteFullPic() -- Delete a record of type FullPicType
AddPic() -- Load a new picture bitmap set
DelPic() -- Remove a picture bitmap set from memory.


Revision History:


************************************************************************/
#include "winmaze.h"
#include "mazproto.h"

#define MASKROP(fore,back) (DWORD)(((back << 8)&0xFF000000) | fore)
#define MASKROP2(black,white) (DWORD)(((black << 8)&0xFF000000) | white) & 0xFFFF0000




//
// PicSpec goes:
// Base BM filename, iPicNum for the picture, offsets for each facing:
// {width, depth, left offset, top offset}
//
PicSpecType PicSpec[] = {
{"FISH", PIC_FISH,
{
{35,41,30,42},
{62,35,15,49},
{21,31,36,51},
{62,31,21,53}
}},
{"ROBO", PIC_ROBOT,
{
{40,107,19,43},
{50,107,13,43},
{43,106,23,44},
{48,107,30,43}
}},
{"SMIL", PIC_SMILEY,
{
{62,62,17,40},
{68,62,17,40},
{62,62,17,40},
{70,62,9,40}
}},
{"REAP", PIC_REAPER,
{
{90,114,5,26},
{85,114,8,26},
{90,114,5,26},
{85,114,8,26}
}}
};

int iNumPicSpecs = sizeof(PicSpec) / sizeof(PicSpecType);





/*=====================================================================
Function: FadePic()

Inputs: Picture #, facing, and clipping rectangle to fade.

Outputs:none

Abstract:
Fade will fade out a given bitmap by XORing its bitmap with a 50% grey tone.
======================================================================*/

void FadePic(
int iPicNum,
BYTE bFacing,
HDC hDC,
LPRECT lprFrom,
LPRECT lprDRect
)
{
FullPicType FAR *fptTrav;
BOOL bFound;
HDC hFadeDC,hMaskDC,hbmFadeDC;
HBITMAP hbmFade;
int DestWidth,DestHeight,SrcWidth,SrcHeight;
int iRelDirIndex,i,j;


fptTrav = &fptPic;
bFound = FALSE;

iRelDirIndex=2;
while(bFacing != ptSelf.Pos.Facing) {
iRelDirIndex = (iRelDirIndex+1)%4;
bFacing = LEFT_TO_ABS(bFacing);
}

while(fptTrav->next != NULL) {
fptTrav = fptTrav->next;
if (fptTrav->iPicNum == iPicNum) {
bFound = TRUE;
break;
}
}

if (bFound) {
hMaskDC = CreateCompatibleDC(hDC);
hFadeDC = CreateCompatibleDC(hDC);
hbmFadeDC = CreateCompatibleDC(hFadeDC);
SrcWidth = lprFrom->right-lprFrom->left+1;
SrcHeight = lprFrom->bottom-lprFrom->top+1;
DestWidth = lprDRect->right-lprDRect->left+1;
DestHeight = lprDRect->bottom-lprDRect->top+1;
hbmFade = CreateCompatibleBitmap(hFadeDC,DestWidth,DestHeight);
SelectObject(hbmFadeDC,hbmFade);
SelectObject(hFadeDC,hFadeBM[0]);
SelectObject(hMaskDC,fptTrav->M[iRelDirIndex].hBitmap);

//
// Copy the Fade pattern into the Fade Bitmap
//
for(i=0;i<DestWidth;i+=PIC_X) {
for(j=0;j<DestHeight;j+=PIC_Y) {
BitBlt(hbmFadeDC,i,j,
((i+PIC_X)<=DestWidth) ? PIC_X : DestWidth,
((j+PIC_Y)<=DestHeight) ? PIC_Y : DestHeight,
hFadeDC,0,0,
SRCCOPY);
}
}

//
// StretchBlt the mask onto the fade
//
StretchBlt(hbmFadeDC,0,0,DestWidth,DestHeight,
hMaskDC,0,0,SrcWidth,SrcHeight,
NOTSRCERASE);

//
// BitBlt the result onto the screen.
//
BitBlt(hDC,lprDRect->left,lprDRect->top,DestWidth,DestHeight,
hbmFadeDC,0,0,
SRCPAINT);

DeleteDC(hFadeDC);
DeleteDC(hMaskDC);
DeleteDC(hbmFadeDC);
DeleteObject(hbmFade);
DeleteObject(hFadeBM[0]);
hFadeBM[0] = LoadBitmap(hInst,(LPCTSTR)((0)?"FADE2":"FADE1"));

}

return;

}




/*=====================================================================
Function: DrawClippedPic()

Inputs: Picture #, Relative facing, clip/dest rectangles,

Outputs:none

Abstract:
DrawClippedPic will draw the portions of picture # iPicNum visible through
the clipping window. Player bitmaps smaller than a full panel in size
Will need to be scaled.
======================================================================*/

void DrawClippedPic(
int iPicNum,
BYTE bFacingIndex,
HDC hDC,
LPRECT lprDRect,
LPRECT lprClip,
LPRECT lprFrom,
int iRelx,
int iRely
)

{
RECT rSrc;
FullPicType FAR *fptTrav;
BOOL bFound;
HDC hPicDC,hMaskDC;
int DestWidth,DestHeight;
POINT pBlt[3];
//
// the floats are offset ratio's, ie percentage of the total
// width/depth to add to each side of the source rect.
//
float sWidth,sDepth,dWidth,dDepth;
float fRelScaling;

if (lprClip->right < lprClip->left+2) {
return;
}

fptTrav = &fptPic;
bFound = FALSE;

//
// Check to see if the Picture we're supposed to draw has been loaded.
//
AddPic(iPicNum);

while(fptTrav->next != NULL) {
fptTrav = fptTrav->next;
if (fptTrav->iPicNum == iPicNum) {
bFound = TRUE;
break;
}
}

if (bFound) {

//
// bFound will now indicates whether the picture has been pre-stretched
//

bFound = (PreStretch[iPicNum][iRely].P[bFacingIndex].hBitmap != (HANDLE) NULL);
bFound = bFound && (PreStretch[iPicNum][iRely].M[bFacingIndex].hBitmap != (HANDLE) NULL);

//
// We'll need the source dimensions whether it's prestretched or not.
//
if (bFound) {
sWidth = (float) PreStretch[iPicNum][iRely].P[bFacingIndex].xSize;
sDepth = (float) PreStretch[iPicNum][iRely].P[bFacingIndex].ySize;
}
else {
sWidth = (float) fptTrav->P[bFacingIndex].xSize;
sDepth = (float) fptTrav->P[bFacingIndex].ySize;
}

//
// Assume that we want the ENTIRE source bitmap.
//
rSrc.left = 0;
rSrc.top = 0;
rSrc.right = (INT) sWidth-1;
rSrc.bottom = (INT) sDepth-1;

//
// fRelScaling*an offset gives # of pixels of REAL offset.
//
fRelScaling = ((float) (lprDRect->bottom - lprDRect->top))/(float)PIC_Y;

if (bFound) {
lprDRect->left += PreStretch[iPicNum][iRely].P[bFacingIndex].xOrg;
lprDRect->top += PreStretch[iPicNum][iRely].P[bFacingIndex].yOrg;
lprDRect->right = lprDRect->left + (int) sWidth;
lprDRect->bottom = lprDRect->top + (int) sDepth;
}
else {
lprDRect->left += (int) (fRelScaling*fptTrav->P[bFacingIndex].xOrg);
lprDRect->top += (int) (fRelScaling*fptTrav->P[bFacingIndex].yOrg);
lprDRect->right = lprDRect->left + (int) (fRelScaling*sWidth);
lprDRect->bottom = lprDRect->top + (int) (fRelScaling*sDepth);
}

dWidth = (float) lprDRect->right - lprDRect->left;
dDepth = (float) lprDRect->bottom - lprDRect->top;

//
// Rather than doing a clipped bitblt, we just modify the source
// rectangle to be the parts which are visible.
//
if (lprDRect->left < lprClip->left) {
rSrc.left += (LONG) (sWidth*((float) (lprClip->left - lprDRect->left))/dWidth);
lprDRect->left = lprClip->left;
}
if (lprDRect->top < lprClip->top) {
rSrc.top += (LONG) (sDepth*((float) lprClip->top - lprDRect->top)/dDepth);
lprDRect->top = lprClip->top;
}
if (lprDRect->right > lprClip->right) {
rSrc.right += (LONG) (sWidth*((float) lprClip->right - lprDRect->right)/dWidth);
lprDRect->right = lprClip->right;
}
if (lprDRect->bottom > lprClip->bottom) {
rSrc.bottom += (LONG) (sDepth*((float) lprClip->bottom - lprDRect->bottom)/dDepth);
lprDRect->bottom = lprClip->bottom;
}
//
// Set the Dest variables to the TRUE physical dest width/depth
//
DestWidth = lprDRect->right - lprDRect->left;
DestHeight = lprDRect->bottom - lprDRect->top+1;

if ((DestWidth < 1)||(DestHeight < 1)) {
return;
}

hPicDC = CreateCompatibleDC(hDC);
hMaskDC = CreateCompatibleDC(hPicDC);

if (bFound) {
//
// If the bitmaps are pre-stretched...
//

SelectObject(hPicDC,PreStretch[iPicNum][iRely].P[bFacingIndex].hBitmap);
SelectObject(hMaskDC,PreStretch[iPicNum][iRely].M[bFacingIndex].hBitmap);

BitBlt(hDC,lprDRect->left,lprDRect->top,DestWidth,DestHeight,
hMaskDC,rSrc.left,rSrc.top,
SRCAND);
BitBlt(hDC,lprDRect->left,lprDRect->top,DestWidth,DestHeight,
hPicDC,rSrc.left,rSrc.top,
SRCPAINT);
}
else {
//
// If the bitmaps are NOT pre-stretched.
//
SelectObject(hPicDC,fptTrav->P[bFacingIndex].hBitmap);
SelectObject(hMaskDC,fptTrav->M[bFacingIndex].hBitmap);
//
// stretch the mask onto the screen
//
pBlt[0].x = lprDRect->left;
pBlt[0].y = lprDRect->top;
pBlt[1].x = lprDRect->right;
pBlt[1].y = lprDRect->top;
pBlt[2].x = lprDRect->left;
pBlt[2].y = lprDRect->bottom;

StretchBlt(hDC,lprDRect->left,lprDRect->top,DestWidth,DestHeight,
hMaskDC,rSrc.left,rSrc.top,rSrc.right-rSrc.left+1,rSrc.bottom-rSrc.top+1,
SRCAND);
//
// then the picture as well
//
StretchBlt(hDC,lprDRect->left,lprDRect->top,DestWidth,DestHeight,
hPicDC,rSrc.left,rSrc.top,rSrc.right-rSrc.left+1,rSrc.bottom-rSrc.top+1,
SRCPAINT);
}

DeleteDC(hPicDC);
DeleteDC(hMaskDC);
}
else {
MessageBox(NULL,"Unknown Picture Draw Requested","DrawClippedPic",
MB_ICONEXCLAMATION|MB_APPLMODAL);
}

*lprFrom = rSrc;

return;
}




/*=====================================================================
Function: NewFullPic()

Inputs: Picture # to load, pointer to next

Outputs:Returns a pointer to an initialized FullPicType data structure

Abstract:
======================================================================*/

FullPicType FAR *NewFullPic(
int iPicNum,
FullPicType FAR *next
)
{
FullPicType FAR *fptTemp;
int i;
HANDLE hMem;

hMem = GlobalAlloc(GHND,sizeof(FullPicType));
fptTemp = (FullPicType FAR *) GlobalLock(hMem);
if (fptTemp == NULL) {
MessageBox((HANDLE)NULL,"Out of memory: unable to create NewFullPic.","NewFullPic",
MB_ICONEXCLAMATION|MB_APPLMODAL);
}
else {
for(i=0;i<4;i++) {
fptTemp->P[i].hBitmap = (HANDLE) NULL;
fptTemp->P[i].xSize = PicSpec[iPicNum].PicPos[i].xSize;
fptTemp->P[i].ySize = PicSpec[iPicNum].PicPos[i].ySize;
fptTemp->P[i].xOrg = PicSpec[iPicNum].PicPos[i].xOrg;
fptTemp->P[i].yOrg = PicSpec[iPicNum].PicPos[i].yOrg;
fptTemp->M[i].hBitmap = (HANDLE) NULL;
fptTemp->M[i].xSize = PicSpec[iPicNum].PicPos[i].xSize;
fptTemp->M[i].ySize = PicSpec[iPicNum].PicPos[i].ySize;
fptTemp->M[i].xOrg = PicSpec[iPicNum].PicPos[i].xOrg;
fptTemp->M[i].yOrg = PicSpec[iPicNum].PicPos[i].yOrg;
}
fptTemp->iPicNum = iPicNum;
fptTemp->next = next;
}

return(fptTemp);

}



/*=====================================================================
Function: DeleteFullPic()

Inputs: pointer to FullPicType to free

Outputs:Returns pointer to ->next element of deleted pic.

Abstract:
======================================================================*/

FullPicType FAR *DeleteFullPic(
FullPicType FAR *fptTrav
)
{
int i;
FullPicType FAR *fptTemp;
HANDLE hMem;

if (fptTrav == NULL) {
fptTemp = fptTrav;
}
else {
fptTemp = fptTrav->next;

for (i=0;i<4;i++) {
if (fptTrav->P[i].hBitmap != (HANDLE) NULL) {
DeleteObject(fptTrav->P[i].hBitmap);
}
if (fptTrav->M[i].hBitmap != (HANDLE) NULL) {
DeleteObject(fptTrav->M[i].hBitmap);
}
}

hMem = (HGLOBAL) GlobalHandle(SELECTOROF(fptTrav));
GlobalUnlock(hMem);
GlobalFree(hMem);
}

return(fptTemp);
}




/*=====================================================================
Function: AddPic()

Inputs: Picture # to load

Outputs:Returns success/failure.

Abstract:
AddPic will take care of loading in a given picture's bitmap, if it isn't
already in the fptPic list. If the specified type of picture can't be found,
FALSE is returned. Note that when we load a picture, we also make sure
that it is restricted to the confines of its mask by turning everything
outside the mask BLACK. This is necessary for drawing as well.
======================================================================*/

BOOL AddPic(
int iPicNum
)
{
char cDirs[4] = {'F','L','B','R'};
FullPicType FAR *fptTrav;
BOOL bFound;
int i,j;
char cBitmapName[32];
HDC hDC,hPicDC,hMaskDC;

fptTrav = &fptPic;
bFound = FALSE;

//
// Look to see if the picture's already been loaded into memory. If
// it has, we won't have to load it.
//
while (fptTrav->next != NULL) {
fptTrav = fptTrav->next;

if (fptTrav->iPicNum == iPicNum) {
bFound = TRUE;
break;
}
}

//
// If we didn't find the bitmap, we really DO need to load it.
//
if (!bFound) {

//
// We can only load the requested picture if it's in our list of
// known pics. Check to make sure it is.
//
for(i=0;i<iNumPicSpecs;i++) {
if (PicSpec[i].iPicNum == iPicNum) {
bFound = TRUE;
break;
}
}

if (bFound) {
hDC = GetDC(hWndMaze);
hPicDC = CreateCompatibleDC(hDC);
hMaskDC = CreateCompatibleDC(hPicDC);
ReleaseDC(hWndMaze,hDC);

fptTrav->next = NewFullPic(iPicNum,fptTrav->next);
fptTrav = fptTrav->next;
strcpy(cBitmapName,PicSpec[i].cBase);
cBitmapName[5]='\0';

//
// Load in the bitmaps
//
for(j=0;j<4;j++) {
cBitmapName[4]=cDirs[j];
if (fptTrav->P[j].hBitmap != NULL) {
DeleteObject(fptTrav->P[j].hBitmap);
}
fptTrav->P[j].hBitmap = LoadBitmap(hInst,(LPCTSTR)cBitmapName);
if (fptTrav->P[j].hBitmap == (HANDLE) NULL) {
MessageBox((HANDLE) NULL,"Unable to load bitmap","AddPic",
MB_ICONEXCLAMATION|MB_APPLMODAL);
bFound = FALSE;
}
//
// Copy stats for the Bitmap's size/origin into the struct
//
fptTrav->P[j].xSize = PicSpec[i].PicPos[j].xSize;
fptTrav->P[j].ySize = PicSpec[i].PicPos[j].ySize;
fptTrav->P[j].xOrg = PicSpec[i].PicPos[j].xOrg;
fptTrav->P[j].yOrg = PicSpec[i].PicPos[j].yOrg;
}

cBitmapName[5]='M';
cBitmapName[6]='\0';

//
// Load in the masks for the bitmaps
//
for(j=0;j<4;j++) {
cBitmapName[4]=cDirs[j];
if (fptTrav->M[j].hBitmap != NULL) {
DeleteObject(fptTrav->M[j].hBitmap);
}
fptTrav->M[j].hBitmap = LoadBitmap(hInst,(LPCTSTR)cBitmapName);
if (fptTrav->M[j].hBitmap == (HANDLE) NULL) {
MessageBox((HANDLE) NULL,"Unable to load bitmap mask","AddPic",
MB_ICONEXCLAMATION|MB_APPLMODAL);
bFound = FALSE;
}
//
// Copy stats for the Mask's size/origin into the struct
//
fptTrav->M[j].xSize = PicSpec[i].PicPos[j].xSize;
fptTrav->M[j].ySize = PicSpec[i].PicPos[j].ySize;
fptTrav->M[j].xOrg = PicSpec[i].PicPos[j].xOrg;
fptTrav->M[j].yOrg = PicSpec[i].PicPos[j].yOrg;
}
/***********BUGBUG
//
// Invert the background of the bitmaps from white to black
//
for(j=0;j<4;j++) {
SelectObject(hPicDC,fptTrav->P[j].hBitmap);
SelectObject(hMaskDC,fptTrav->M[j].hBitmap);
BitBlt(hPicDC,0,0,
PicSpec[iPicNum].PicPos[i].xSize,
PicSpec[iPicNum].PicPos[i].ySize,
hMaskDC,0,0,SRCINVERT);
}
*************/

DeleteDC(hPicDC);
DeleteDC(hMaskDC);
}
}

return(bFound);
}




/*=====================================================================
Function: DelPic()

Inputs: Picture # to Delete from pic list

Outputs:Returns success/failure.

Abstract:
DelPic will remove a picture, assuming that it is not being used by anyone
in ptDrones and ptPlayers, and it isn't of type PIC_DEFAULT.
======================================================================*/

BOOL DelPic(
int iPicNum
)
{
FullPicType FAR *fptTrav;
BOOL bFound,bStillUsed;
PlayerType FAR *ptTrav;


if (iPicNum == PIC_DEFAULT) {
return(TRUE);
}

fptTrav = &fptPic;
bFound = FALSE;

while (fptTrav->next != NULL) {
if (fptTrav->next->iPicNum == iPicNum) {
bFound = TRUE;
bStillUsed = FALSE;

ptTrav = &ptPlayers;
while (ptTrav->next != NULL) {
ptTrav = ptTrav->next;
if (ptTrav->iPicNum == iPicNum) {
bStillUsed = TRUE;
break;
}
}

ptTrav = &ptDrones;
while (ptTrav->next != NULL) {
ptTrav = ptTrav->next;
if (ptTrav->iPicNum == iPicNum) {
bStillUsed = TRUE;
break;
}
}

if (!bStillUsed) {
fptTrav->next = DeleteFullPic(fptTrav->next);
}

break;
}

fptTrav = fptTrav->next;
}

return(bFound);
}