home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Media Share 13
/
mediashare_13.zip
/
mediashare_13
/
ZIPPED
/
PROGRAM
/
WTJ9403.ZIP
/
BARNHART
/
SO_BASE.CPP
< prev
next >
Wrap
C/C++ Source or Header
|
1993-11-21
|
12KB
|
453 lines
// so_base.cpp - Implements ScreenObj and ScreenItem classes
// Provides much of the functionality, especially painting,
// for derived classes beneath
// Andy Barnhart
// This program is provided as a sample to accompany an article on graphics
// animation using C++
// This seems to work for me and I hope it works for you. No other warranty
// is expressed or implied.
#include <so_all.hpp>
// Set up our class statics
HDC ScreenObj::hDC = 0;
HWND ScreenObj::hWnd = 0;
HANDLE ScreenObj::hInst = 0;
ScreenObj *ScreenObj::Background = NULL;
// Some internal statics
static HDC hMemDC1 =0;
static HDC hMemDC2 =0;
static HBITMAP hbmBlank = 0;
static HBITMAP hbmPrev = 0;
// The screen size and location
int WinX=0, WinY=0, WinSX=0, WinSY=0;
// Initialize the screen and screen painting for this module
void InitScreen( HWND hw, HDC hd, HANDLE hi)
{
// We keep the DC open all the time and reuse it
ScreenObj::hDC = hd;
ScreenObj::hWnd = hw;
ScreenObj::hInst = hi;
// We set up compatible DCs for later BitBlts
// This is important - everything being set up in advance helps us
// paint fast enough during play
hMemDC1 = CreateCompatibleDC( hd);
hMemDC2 = CreateCompatibleDC( hMemDC1);
// Load a blank bitmap for the background
hbmBlank = LoadBitmap( hi, "blank");
hbmPrev = SelectObject(hMemDC1, hbmBlank);
}
void TermScreen()
{
// Free up our memory DCs
if(hMemDC1)
{
if(hbmBlank)
{
SelectObject( hMemDC1, hbmPrev);
DeleteObject( hbmBlank);
}
DeleteDC( hMemDC1);
hMemDC1 = 0;
}
if(hMemDC2)
{
DeleteDC( hMemDC2);
hMemDC2 = 0;
}
}
// If screen size changes, adjust. There is panning logic built in the
// game in so_play.cpp, but it is so slow on most systems that you will
// want to avoid using it. The code is left in as a sample, however.
void SizeScreen( int x, int y)
{
static recurse = 0;
WinX = x;
WinY = y;
if(!ScreenObj::Background) return;
if( recurse) return;
recurse = 1;
if( WinX > ScreenObj::Background->width)
WinX -= ( WinX - ScreenObj::Background->width);
if( WinY > ScreenObj::Background->height)
WinY -= ( WinY - ScreenObj::Background->height);
if( (WinY != y) || (WinX != x) )
{
RECT rc;
GetWindowRect( ScreenObj::hWnd, &rc);
SetWindowPos( ScreenObj::hWnd, NULL, rc.left, rc.top,
(rc.right - rc.left) - (x - WinX),
(rc.bottom - rc.top) - (y - WinY), SWP_NOZORDER);
}
recurse = 0;
}
// Create ScreenObj and chain it in with others
ScreenObj::ScreenObj( int ix, int iy)
{
if( Background == NULL)
Background = this;
else
{
ScreenObj * so;
so = Background;
while (so->next != NULL) so = so->next;
so->next = this;
}
x=ix;
y=iy;
width=height=0;
hmult = wmult = 1;
shown=FALSE;
next = NULL;
}
// Delete a ScreenObj and remove from the chain
ScreenObj::~ScreenObj( void)
{
if( Background == this)
Background->next = this->next;
else
{
ScreenObj * so;
so = Background;
while (so->next != this) so = so->next;
so->next = this->next;
}
}
// Determine the distance between two ScreenObjs
// This is a convenience function so that an object that detects it will hit
// more than one other object during a move can decide which one it will hit
// first
int SODist( ScreenObj * SO1, ScreenObj * SO2)
{
int dx = 0, dy = 0;
if( ((SO1->x + (SO1->width * SO1->wmult)) < SO2->x) ||
( SO1->x > (SO2->x + (SO2->width * SO2->wmult) )) )
{
if( abs((SO1->x + (SO1->width * SO1->wmult)) - SO2->x) <
abs(SO1->x - (SO2->x + (SO2->width * SO2->wmult) ) ) )
dx = abs((SO1->x + (SO1->width * SO1->wmult)) - SO2->x);
else
dx = abs( SO1->x - (SO2->x + (SO2->width * SO2->wmult) ));
}
if( ((SO1->y + (SO1->height * SO1->hmult)) < SO2->y) ||
( SO1->y > (SO2->y + (SO2->height * SO2->hmult)) ))
{
if( abs((SO1->y + (SO1->height * SO1->hmult)) - SO2->y) <
abs(SO1->y - (SO2->y + (SO2->height * SO2->hmult)) ) )
dy = abs((SO1->y + (SO1->height * SO1->hmult)) - SO2->y);
else
dy = abs( SO1->y - (SO2->y + (SO2->height * SO2->hmult)) );
}
return( dx + dy);
}
// ScreenBack - the screen background
ScreenBack::ScreenBack( int ix, int iy) : ScreenObj( 0, 0)
{
width = ix;
height = iy;
}
// Whenever we get called to Paint, we know we are first, so we just fill the
// bitmap with the black background color
void ScreenBack::Paint( RECT *rc, HDC hPaintDC, BOOL memdc)
{
if( ! memdc)
FillRect( hPaintDC, rc, GetStockObject( BLACK_BRUSH));
else
{
RECT rcm;
rcm.top = rcm.left = 0;
rcm.bottom = rc->bottom - rc->top;
rcm.right = rc->right - rc->left;
FillRect( hPaintDC, &rcm, GetStockObject( BLACK_BRUSH));
}
}
// "Generic" ScreenItem Update will move toward a target if one has been
// selected. If there is no where to move, it will return FALSE to
// indicate it doesn't need to move anymore.
ScreenItem::Update()
{
if( ((x != targetx) || (y != targety)) && ( --periodct <= 0) )
{
periodct = period;
if( x > targetx)
{
x-= xinc;
if (x < targetx) x = targetx;
}
if( x < targetx)
{
x+= xinc;
if (x > targetx) x = targetx;
}
if( y > targety)
{
y-= yinc;
if (y < targety) y = targety;
}
if( y < targety)
{
y+= yinc;
if (y > targety) y = targety;
}
if( shown)
Paint();
return( TRUE);
}
else
return( FALSE);
}
// Derived objects will usually use ScreenObj's Paint functions, so they are
// highly functional. This first Paint determines the rectangle affected by
// the object, including it's last and current (after painting) position.
// It then uses the memory DC and calls all objects on the screen to Paint
// in it if needed (using the second Paint function). After all objects
// which intersect the rectangle have painted into the memory bitmap, it is
// painted onto the screen. The Z order is maintained, and there is no
// flickering. It's also pretty damn quick, if I may say so myself...
void ScreenItem::Paint()
{
RECT rc;
ScreenObj * PntNxt;
if( x < oldx)
rc.left = x;
else
rc.left = oldx;
if( y < oldy)
rc.top = y;
else
rc.top = oldy;
if( (x +width) < (oldx + width))
rc.right = oldx+width;
else
rc.right = x + width;
if( (y + height) < (oldy + height))
rc.bottom = oldy+height;
else
rc.bottom = y+height;
for( PntNxt = Background; PntNxt != NULL; PntNxt = PntNxt->Next())
PntNxt->Paint( &rc, hMemDC1, TRUE);
BitBlt( hDC, // dest DC
rc.left,rc.top, // dest origin
rc.right - rc.left, rc.bottom - rc.top, // dest extents
hMemDC1, // src DC
0,0, // src origin
SRCCOPY ); // ROP code
oldx = x;
oldy = y;
}
// Paint into a memory DC (usually - sometimes it is the "real" main window DC
// if a WM_PAINT was received; then we are a little flashy. But we normally
// will not receive WM_PAINTs unless switching between apps.
void ScreenItem::Paint( RECT *rc, HDC hPaintDC, BOOL memdc)
{
int icx, icy;
HBITMAP hbmOld;
// If this object is hidden, or doesn't intersect the region, bail out!
if( !shown || !InRect( rc)) return;
// If it is a memory DC, it is only as large as the rect, and its origin
// (logically) is the location of the rect. It's faster to do the math
// than set and reset origins through the API (although we do that for
// panning, but that's a whole other story)
if( memdc)
{
icx = x - rc->left;
icy = y - rc->top;
}
else
{
icx = x;
icy = y;
}
hbmOld = SelectObject(hMemDC2, hbmImage);
if(hbmOld)
{
// Blit the image in the new position
BitBlt( hPaintDC,
icx, icy,
width,height,
hMemDC2,
0,0,SRCPAINT );
SelectObject(hMemDC2, hbmOld);
}
}
// Select a bitmap as the current bitmap for an object
// This function does more work that necessary. I thought about adding logic
// for "non rectangular" hit detection by checking the bitmap bits in a DBI
// bitmap. I never got around to adding such logic, but I decided to leave
// what I had in, since this is only a sample...
void ScreenItem::SelectBM( HBITMAP hb)
{
typedef struct xxxx
{
BITMAPINFO bminfo;
RGBQUAD r[15];
} xxx;
xxx x;
BITMAP bm;
hbmImage = hb;
// Get bitmap size
GetObject(hbmImage, sizeof(bm), (LPSTR)&bm);
if( (hbits == NULL) || (width != bm.bmWidth) || (height != bm.bmHeight) ||
(linewidth != bm.bmWidthBytes) || (planes != bm.bmPlanes) )
{
width = bm.bmWidth;
height = bm.bmHeight;
linewidth = bm.bmWidthBytes;
if( hbits != NULL)
{
GlobalUnlock( hbits);
GlobalFree( hbits);
bits = NULL;
}
bitslen = height * linewidth;
planes = bm.bmPlanes;
hbits = GlobalAlloc( GMEM_MOVEABLE, bitslen);
bits = (BYTE far *) GlobalLock( hbits);
}
if( bits == NULL) return;
// GetBitmapBits( hbmImage, bitslen, bits);
x.bminfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER) + (16 * sizeof(RGBQUAD));
x.bminfo.bmiHeader.biWidth = width;
x.bminfo.bmiHeader.biHeight = height;
x.bminfo.bmiHeader.biPlanes = 1;
x.bminfo.bmiHeader.biBitCount = 4;
x.bminfo.bmiHeader.biCompression = BI_RGB;
x.bminfo.bmiHeader.biSizeImage = bitslen;
x.bminfo.bmiHeader.biXPelsPerMeter = 3072;
x.bminfo.bmiHeader.biYPelsPerMeter = 2048;
x.bminfo.bmiHeader.biClrUsed = 0;
x.bminfo.bmiHeader.biClrImportant = 0;
GetDIBits(hDC, hbmImage, 0, height, bits, &x.bminfo, DIB_RGB_COLORS);
}
// ScreenItems are SreenObjs with only slightly higher functionality; they are
// aware of the resource to be loaded
ScreenItem::ScreenItem( char *res, int ix, int iy, int incx, int incy) : ScreenObj( ix, iy)
{
hbits = NULL;
bits = NULL;
if( res != NULL)
{
HBITMAP hb;
hb = LoadBitmap( hInst, res );
SelectBM( hb);
}
else
hbmImage = NULL;
oldx=targetx=x;
oldy=targety=y;
xinc = incx;
yinc = incy;
periodct = period = 1;
SOType = NULL;
}
ScreenItem::~ScreenItem( void)
{
if( hbmImage ) DeleteObject(hbmImage);
if( hbits != NULL)
{
GlobalUnlock( hbits);
GlobalFree( hbits);
bits = NULL;
}
}
void ScreenItem::SetTarget( int tx, int ty)
{
targetx=tx - width/2;
targety=ty - height/2;
if(targetx > Background->width) targetx = Background->width;
if(targety > Background->height) targety = Background->height;
if(targetx < 0) targetx = 0;
if(targety < 0) targety = 0;
}
StillMotion::StillMotion( char *res, int ix, int iy, int nframes, int per)
: ScreenItem(NULL, ix, iy)
{
int i;
char acIc[32];
int len;
SOType = TYPE_HAZARD;
if( nframes > 8)
numframes = 8;
else
numframes = nframes;
xinc=yinc=0;
strcpy( acIc, res);
len = strlen( acIc);
strcat( acIc, "0");
for(i = 0; i < numframes; i++)
{
BITMAP bm; // Bitmap info
acIc[len]++;
frames[i] = LoadBitmap( hInst, acIc);
}
curframe = 0;
periodct = period = per;
SelectBM( frames[0]);
}
StillMotion::~StillMotion( void)
{
int i;
for(i = 0; i < numframes; i++)
{
if (frames[i]) DeleteObject( frames[i]);
}
hbmImage = NULL;
}
BOOL StillMotion::Update( void)
{
if( --periodct <= 0)
{
if( (++curframe >= numframes) || (hbmImage == NULL) )
curframe = 0;
SelectBM( frames[curframe]);
periodct = period;
Paint();
}
return( TRUE);
}