TGSdemo: C++ Version
TGSdemo uses a C++ class named VB to hold the code and data required to manage a virtual buffer. It includes a private data item for the virtual buffer handle, a constructor, a destructor, and one member function:
class VB {
private:
int hVB; // virtual buffer handle
public:
VB(int, int, int init=FALSE);// constructor function
~VB(); // destructor function
int handle(); // member function (get VB handle)
};
The constructor function creates a virtual buffer with the specified dimensions, makes it the active virtual buffer, and assigns the logical palette colors to the virtual buffer. Its optional third parameter provides a way to initialize Fastgraph's virtual buffer environment, which we must do the first time we create a VB object:
VB::VB(int Width, int Height, int Init)
{
if (Init) fg_vbinit();
hVB = fg_vballoc(Width,Height);
fg_vbopen(hVB);
fg_vbcolors();
}
The destructor function closes the active virtual buffer and releases the virtual buffer memory. If all virtual buffers (that is, all VB objects) have been freed, fg_vbfin() releases any remaining virtual buffer resources:
VB::~VB()
{
fg_vbclose();
fg_vbfree(hVB);
fg_vbfin();
}
Finally, the VB class includes a member function to return the handle by which Fastgraph references the virtual buffer:
int VB::handle(void)
{
return(hVB);
}
The TGSdemo WinMain() function creates two virtual buffer objects from the VB class:
VB Workspace(320,200,TRUE); // 320x200 workspace virtual buffer object
VB Background(1600,200); // 1600x200 background virtual buffer object
WorkVB = &Workspace; // global pointer to workspace VB object
BackVB = &Background; // global pointer to background VB object
We use the optional third parameter to make the constructor call fg_vbinit() when we create the workspace object. Because the VB objects retain their scope only within WinMain(), we set up two global pointers to the objects:
VB *WorkVB; // global pointer to workspace VB
VB *BackVB; // global pointer to background VB
These pointers are declared outside WinMain() immediately after the VB class declarations.
In the examples we've presented so far, the WM_CREATE and WM_DESTROY message handlers respectively perform the virtual buffer initialization and shutdown tasks. In TGSdemo, the VB constructor and destructor functions handle these details. We can't create the VB objects in the WM_CREATE handler because they would lose scope when it returns, thus destroying any VB objects we just created. As we've seen, the solution is to let WinMain() create the VB objects once CreateWindow() returns and then reference them through the global pointers. Note also that the WM_DESTROY handler doesn't call fg_vbclose(), fg_vbfree(), and fg_vbfin(), as we've moved these calls into the destructor.
/****************************************************************************\
* *
* TGSdemo.cpp *
* *
* This program displays a scaled image (a logo) against a background of *
* scrolling clouds. The background and logo images are stored in PCX files *
* with identical palettes that do not use the Windows system colors. *
* *
* TGSdemo is based on an original program by Michael Miller. *
* *
\****************************************************************************/
#include <fgwin.h>
LRESULT CALLBACK WindowProc(HWND,UINT,WPARAM,LPARAM);
void MoveBackground();
char DataPath[256];
/****************************************************************************\
* *
* Virtual Buffer class and function declarations *
* *
\****************************************************************************/
class VB {
private:
int hVB; // virtual buffer handle
public:
VB(int, int, int init=FALSE); // constructor function
~VB(); // destructor function
int handle(); // member function (get VB handle)
};
VB::VB(int Width, int Height, int Init) // constructor
{
if (Init) fg_vbinit();
hVB = fg_vballoc(Width,Height);
fg_vbopen(hVB);
fg_vbcolors();
}
VB::~VB() // destructor
{
fg_vbclose();
fg_vbfree(hVB);
fg_vbfin();
}
int VB::handle(void) // member function to get the VB handle
{
return(hVB);
}
VB *WorkVB; // global pointer to workspace VB
VB *BackVB; // global pointer to background VB
/****************************************************************************\
* *
* Image Buffer class and function declarations *
* *
\****************************************************************************/
class IMAGE
{
private:
int ImageWidth, ImageHeight;
int xCenter, yLoc;
BYTE *ImageData;
BYTE *ScaleData;
public:
IMAGE(char *PCXfile, int Width, int Height, int yPos);
~IMAGE();
void Show(int);
};
// constructor
IMAGE::IMAGE(char *PCXfile, int Width, int Height, int yPos)
{
char FileName[256];
lstrcpy(FileName,DataPath);
lstrcat(FileName,PCXfile);
// assign ImageWidth and ImageHeight
ImageWidth = Width;
ImageHeight = Height;
yLoc = yPos;
// find page center coordinates
xCenter = (320 - ImageWidth) / 2;
// dynamically allocate memory for ImageData and scaling
ImageData = new BYTE [fg_imagesiz(ImageWidth,ImageHeight)];
ScaleData = new BYTE [fg_imagesiz(ImageWidth,ImageHeight)];
// load the actual image from the PCX file
fg_vbopen(WorkVB->handle());
fg_showpcx(FileName,FG_KEEPCOLORS);
// retrieve the image as a 256-color bitmap
fg_move(0,ImageHeight-1);
fg_getimage(ImageData,ImageWidth,ImageHeight);
};
// destructor
IMAGE::~IMAGE()
{
delete[] ImageData;
delete[] ScaleData;
}
// member function to display the scaled object
void IMAGE::Show(int Scale)
{
if (Scale <= 0)
return;
// image is scaled down
else if (Scale < 10)
{
int dWidth = (ImageWidth * Scale) / 10;
int dHeight = (ImageHeight * Scale) / 10;
int xLoc = (320 - dWidth) / 2;
fg_scale(ImageData,ScaleData,ImageWidth,ImageHeight,dWidth,dHeight);
fg_move(xLoc,yLoc);
fg_drwimage(ScaleData,dWidth,dHeight);
}
// image is displayed at full size
else
{
fg_move(xCenter,yLoc);
fg_drwimage(ImageData,ImageWidth,ImageHeight);
}
}
/****************************************************************************\
* *
* WinMain *
* *
\****************************************************************************/
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdParam, int iCmdShow)
{
static char szAppName[] = "FGtgsdemo";
HWND hWnd;
MSG msg;
WNDCLASSEX wndclass;
char FileName[256];
register int i, j;
wndclass.cbSize = sizeof(wndclass);
wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
wndclass.lpfnWndProc = WindowProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(NULL,IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL,IDC_ARROW);
wndclass.hbrBackground = NULL;
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName;
wndclass.hIconSm = LoadIcon(NULL,IDI_APPLICATION);
RegisterClassEx(&wndclass);
hWnd = CreateWindow(
szAppName, // window class name
"Scaling and Scrolling", // window caption
WS_OVERLAPPEDWINDOW, // window style
CW_USEDEFAULT, // initial x position
CW_USEDEFAULT, // initial y position
CW_USEDEFAULT, // initial x size
CW_USEDEFAULT, // initial y size
NULL, // parent window handle
NULL, // window menu handle
hInstance, // program instance handle
NULL); // creation parameters
VB Workspace(320,200,TRUE); // 320x200 workspace virtual buffer object
VB Background(1600,200); // 1600x200 background virtual buffer object
WorkVB = &Workspace; // global pointer to workspace VB object
BackVB = &Background; // global pointer to background VB object
ShowWindow(hWnd,iCmdShow);
UpdateWindow(hWnd);
GetModuleFileName(hInstance,DataPath,255);
for (i = lstrlen(DataPath); i >= 0; i--)
{
if (DataPath[i] == '\\')
{
DataPath[i+1] = 0;
break;
}
}
lstrcpy(FileName,DataPath);
lstrcat(FileName,"Sky.pcx");
fg_vbopen(BackVB->handle());
fg_showpcx(FileName,FG_KEEPCOLORS);
// copy the first 640 columns onto the next 640 columns, in
// reverse order (make a mirror image)
for (i = 639; i >= 0; i--)
{
j = 1279 - i;
fg_vbcopy(i,i,0,199,j,199,BackVB->handle(),BackVB->handle());
}
// make another copy of first 320 columns
fg_vbcopy(0,319,0,199,1280,199,BackVB->handle(),BackVB->handle());
// work buffer is currently open for pasting
fg_vbopen(WorkVB->handle());
// handle messags while scrolling background
while (TRUE)
{
if (PeekMessage(&msg,NULL,0,0,PM_REMOVE))
{
if (msg.message == WM_QUIT)
break;
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
else
MoveBackground();
}
return msg.wParam;
}
/****************************************************************************\
* *
* WindowProc() *
* *
\****************************************************************************/
HDC hDC;
HPALETTE hPal;
UINT cxClient, cyClient;
LRESULT CALLBACK WindowProc(HWND hWnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps;
switch (iMsg)
{
case WM_CREATE:
hDC = GetDC(hWnd);
fg_setdc(hDC);
hPal = fg_defpal();
fg_realize(hPal);
return 0;
case WM_PAINT:
BeginPaint(hWnd,&ps);
fg_vbscale(0,319,0,199,0,cxClient-1,0,cyClient-1);
EndPaint(hWnd,&ps);
return 0;
case WM_SETFOCUS:
fg_realize(hPal);
InvalidateRect(hWnd,NULL,TRUE);
return 0;
case WM_SIZE:
cxClient = LOWORD(lParam);
cyClient = HIWORD(lParam);
return 0;
case WM_DESTROY:
DeleteObject(hPal);
ReleaseDC(hWnd,hDC);
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd,iMsg,wParam,lParam);
}
/****************************************************************************\
* *
* MoveBackground() *
* *
* Build the next frame and display it in the client area. *
* *
\****************************************************************************/
void MoveBackground()
{
static IMAGE Logo1 = IMAGE("Logo1.pcx",264,40,160);
static IMAGE Logo2 = IMAGE("Logo2.pcx",264,40,80);
static int hBack = BackVB->handle();
static int hWork = WorkVB->handle();
static int x = 0;
// increment storage virtual buffer starting x coordinate
x += 8;
if (x >= 1280) x = 0;
// copy part of background image to workspace buffer
fg_vbcopy(x,x+319,0,199,0,199,hBack,hWork);
// add the logo
if (x <= 240)
Logo2.Show(x/10);
else
{
Logo1.Show((x-240)/10);
Logo2.Show(10);
}
// display the frame in the client area
fg_vbscale(0,319,0,199,0,cxClient-1,0,cyClient-1);
}
|