-
Another weird error
OK, Here we go again... I am now learning how to load and display bitmaps (DirectX). The code I have runs fine. The problem I am having is when I hit the ESC key to exit. The program shuts down just fine. I don't get the error message until after I get back to my desktop. Windows says that there is a problem and the program must be shut down (which it already is?!?) I assume it is either a memory allocation problem or something with one of the pointers, but I have not been able to find it.
Code:
#define WIN32_LEAN_AND_MEAN
#define INITGUID // make sure directX guids are included
#include <windows.h>
#include <windowsx.h>
#include <cstdio>
#include <ddraw.h>
#define WINDOW_CLASS_NAME "WINCLASS1"
// default screen size
#define SCREEN_WIDTH 640 // size of screen
#define SCREEN_HEIGHT 480
#define SCREEN_BPP 8 // bits per pixel
#define MAX_COLORS 256 // maximum colors
#define BITMAP_ID 0x4D42 // universal id for a bitmap
// container structure for bitmaps .BMP file
typedef struct BITMAP_FILE_TAG {
BITMAPFILEHEADER bitmapfileheader; // this contains the bitmapfile header
BITMAPINFOHEADER bitmapinfoheader; // this is all the info including the palette
PALETTEENTRY palette[256]; // we will store the palette here
UCHAR *buffer; // this is a pointer to the data
} BITMAPFILE, *PTR_BITMAPFILE;
// MACROS //////////////////////////////////////////////////////////////////////
#define KEYDOWN(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0)
#define KEYUP(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 0 : 1)
// initializes a direct draw struct
#define DDRAW_INIT_STRUCT(ddstruct) { memset(&ddstruct,0,sizeof(ddstruct)); ddstruct.dwSize=sizeof(ddstruct); }
// GLOBALS /////////////////////////////////////////////////////////////////////
HWND main_window_handle = NULL; // globally track main window
HINSTANCE hinstance_app = NULL; // globally track hinstance
int window_closed = 0;
// directdraw stuff
LPDIRECTDRAW7 lpDD = NULL; // dd object
LPDIRECTDRAWSURFACE7 lpDDPrimarySurface = NULL; // dd primary surface
LPDIRECTDRAWSURFACE7 lpDDBackSurface = NULL; // dd back surface
LPDIRECTDRAWPALETTE lpDDPalette = NULL; // a pointer to the created dd palette
LPDIRECTDRAWCLIPPER lpDDClipper = NULL; // dd clipper
PALETTEENTRY palette[256]; // color palette
PALETTEENTRY save_palette[256]; // used to save palettes
DDSURFACEDESC2 DDSurfaceDesc; // a direct draw surface description struct
DDBLTFX DDbltfx; // used to fill
DDSCAPS2 DDSurfaceCaps; // a direct draw surface capabilities struct
HRESULT DDResultVal; // result back from dd calls
DWORD start_clock_count = 0; // used for timing
BITMAPFILE bitmap; // holds the bitmap
// FUNCTION PROTOTYPES /////////////////////////////////////////////////////////
LRESULT CALLBACK WindowProc(HWND, UINT, WPARAM, LPARAM);
int Game_Init(void *parms = NULL, int num_parms = 0);
int Game_Main(void *parms = NULL, int num_parms = 0);
int Game_Shutdown(void *parms = NULL, int num_parms = 0);
int Flip_Bitmap(UCHAR*, int, int);
int Load_Bitmap_File(PTR_BITMAPFILE, char*); //
int Unload_Bitmap_File(PTR_BITMAPFILE); //
int DDraw_Fill_Surface(LPDIRECTDRAWSURFACE7,int); //
// FUNCTION DEFINITIONS ////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// WINMAIN
int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE hprevinstance,
LPSTR lpcmdline, int ncmdshow) {
WNDCLASSEX winclass; // this will hold the class we create
HWND hwnd; // generic window handle
MSG msg; // generic message
HDC hdc; // graphics device context
// first fill in the window class stucture
winclass.cbSize = sizeof(WNDCLASSEX);
winclass.style = CS_DBLCLKS | CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
winclass.lpfnWndProc = WindowProc;
winclass.cbClsExtra = 0;
winclass.cbWndExtra = 0;
winclass.hInstance = hinstance;
winclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
winclass.hCursor = LoadCursor(NULL, IDC_ARROW);
winclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
winclass.lpszMenuName = NULL;
winclass.lpszClassName = WINDOW_CLASS_NAME;
winclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
// save hinstance in global
hinstance_app = hinstance;
// register the window class
if (!RegisterClassEx(&winclass))
return 0;
// create the window
if (!(hwnd = CreateWindowEx(0, // extended style
WINDOW_CLASS_NAME, // class
"DirectDraw Demo", // title
WS_POPUP,
0,0, // initial x,y
SCREEN_WIDTH, SCREEN_HEIGHT, // initial width, height
NULL, // handle to parent
NULL, // handle to menu
hinstance, // instance of this application
NULL) ) ) // extra creation parms
return 0;
// save main window handle
main_window_handle = hwnd;
// initialize game here
Game_Init();
// enter main event loop
while(TRUE) {
// test if there is a message in queue, if so get it
if (PeekMessage(&msg,NULL,0,0,PM_REMOVE)) {
// test if this is a quit
if (msg.message == WM_QUIT)
break;
// translate any accelerator keys
TranslateMessage(&msg);
// send the message to the window proc
DispatchMessage(&msg);
} // end if
// main game processing goes here
Game_Main();
} // end while
// closedown game here
Game_Shutdown();
// return to Windows like this
return(msg.wParam);
} // end WinMain
////////////////////////////////////////////////////////////////////////////////
// this is the main message handler of the system
LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
PAINTSTRUCT ps; // used in WM_PAINT
HDC hdc; // handle to a device context
char buffer[80]; // used to print strings
// what is the message
switch(msg) {
case WM_CREATE: {
// do initialization stuff here
// return success
return 0;
} break;
case WM_PAINT: {
// simply validate the window
hdc = BeginPaint(hwnd,&ps);
// end painting
EndPaint(hwnd,&ps);
// return success
return 0;
} break;
case WM_DESTROY: {
// kill the application, this sends a WM_QUIT message
PostQuitMessage(0);
// return success
return 0;
} break;
default: break;
} // end switch
// process any messages that we didn't take care of
return (DefWindowProc(hwnd, msg, wparam, lparam));
} // end WinProc
////////////////////////////////////////////////////////////////////////////////
// this function opens a bitmap file and loads the data into a bitmap
int Load_Bitmap_File(PTR_BITMAPFILE bitmap, char *filename) {
int FileHandle, // the file handle, duh...
index; // looping index
UCHAR *pTempBuffer = NULL; // used to convert 24 bit images to 16 bit
OFSTRUCT FileData; // the file data information
// open the file if it exists
if ( (FileHandle = OpenFile(filename, &FileData, OF_READ) ) == -1)
return 0;
// now load the bitmap file header
_lread(FileHandle, &bitmap->bitmapfileheader, sizeof(BITMAPFILEHEADER) );
// test if this is a bitmap file
if (bitmap->bitmapfileheader.bfType != BITMAP_ID) {
// close the file
_lclose(FileHandle);
// return error
return 0;
} // end if
// now we know this is a bitmap, so read in all the sections
// first the bitmap info header
_lread(FileHandle, &bitmap->bitmapinfoheader, sizeof(BITMAPINFOHEADER) );
// now load the color palette if there is one
if (bitmap->bitmapinfoheader.biBitCount == 8) {
_lread(FileHandle, &bitmap->palette, MAX_COLORS * sizeof(PALETTEENTRY) );
// now set all the flags in the palette correctly and fix
// the reversed BRG RGBQUAD data format
for (int index = 0; index < MAX_COLORS; index++) {
// reverse the red and green fields
int temp_color = bitmap->palette[index].peRed;
bitmap->palette[index].peRed = bitmap->palette[index].peBlue;
bitmap->palette[index].peBlue = temp_color;
// always set the flags word to this
bitmap->palette[index].peFlags =PC_NOCOLLAPSE;
} // end for index
} // end if
// finally, find the image data
_llseek(FileHandle, -(int)(bitmap->bitmapinfoheader.biSizeImage), SEEK_END);
// and read in the image
if (bitmap->bitmapinfoheader.biBitCount == 8 ||
bitmap->bitmapinfoheader.biBitCount == 16 ||
bitmap->bitmapinfoheader.biBitCount == 24) {
// delete the last image if there was one
if (bitmap->buffer)
free(bitmap->buffer);
// allocate the memory for the image
if (!(bitmap->buffer = (UCHAR*)malloc(bitmap->bitmapinfoheader.biSizeImage) ) ) {
// close the file
_lclose(FileHandle);
// return error
return 0;
} // end if
// now read it in
_lread(FileHandle, bitmap->buffer, bitmap->bitmapinfoheader.biSizeImage);
} else {
// serious problem
return 0;
} // end if / else
// close the file
_lclose(FileHandle);
// flip the bitmap
Flip_Bitmap(bitmap->buffer,
bitmap->bitmapinfoheader.biWidth * (bitmap->bitmapinfoheader.biBitCount / 8),
bitmap->bitmapinfoheader.biHeight);
// return success
return 1;
} // end Load_Bitmap_File
////////////////////////////////////////////////////////////////////////////////
// this function releases all memory associated with the bitmap
int Unload_Bitmap_File(PTR_BITMAPFILE bitmap) {
if (bitmap->buffer) {
// release the memory
free(bitmap->buffer);
// reset the pointer
bitmap->buffer = NULL;
} // end if
// return success
return 1;
} // end Unload_Bitmap_File
////////////////////////////////////////////////////////////////////////////////
// this function is used to flip bottom-up .BMP images
int Flip_Bitmap(UCHAR *image, int bytes_per_line, int height) {
UCHAR *buffer; // used to perform the image processing
int index; // looping index
// allocate the temporary buffer
if (!(buffer = (UCHAR*)malloc(bytes_per_line * height) ) )
return(0);
// copy image to work area
memcpy(buffer, image, bytes_per_line * height);
// flip vertically
for (index = 0; index < height; index++)
memcpy(&image[( (height-1) - index) * bytes_per_line],
&buffer[index * bytes_per_line], bytes_per_line);
// release the memory
free(buffer);
// return success
return 1;
} // end Flip_Bitmap
////////////////////////////////////////////////////////////////////////////////
//
int DDraw_Fill_Surface(LPDIRECTDRAWSURFACE7 lpDDSurface, int color) {
DDBLTFX DDBltFx; // this contains the DDBLTFX structure
// clear out the structure and set the size field
DDRAW_INIT_STRUCT(DDBltFx);
// set the dwfillcolor field to the desired color
DDBltFx.dwFillColor = color;
// ready to blt to surface
lpDDSurface->Blt(NULL, // ptr to dest rectangle
NULL, // ptr to source surface, NA
NULL, // ptr to source rectangle, NA
DDBLT_COLORFILL | DDBLT_WAIT,// fill and wait
&DDBltFx); // ptr to DDBLTFX structure
// return success
return 1;
} // end DDraw_Fill_Surface
////////////////////////////////////////////////////////////////////////////////
// this is called once after the initial window is created and
// before the main event loop is entered, do all your initialization here
int Game_Init(void *parms, int num_parms) {
// create IDirectDraw interface 7.0 object and test for error
if (FAILED(DirectDrawCreateEx(NULL, (void **)&lpDD, IID_IDirectDraw7, NULL) ) ) {
return 0;
}
// set cooperation to fullscreen
if (FAILED(lpDD->SetCooperativeLevel(main_window_handle, DDSCL_FULLSCREEN |
DDSCL_ALLOWMODEX |
DDSCL_EXCLUSIVE |
DDSCL_ALLOWREBOOT) ) ) {
return 0;
}
// set display mode to 640x480x8
if (FAILED(lpDD->SetDisplayMode(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP,
0, 0) ) ) {
return 0;
}
// clear ddsd and set size
DDRAW_INIT_STRUCT(DDSurfaceDesc);
// enable valid fields
DDSurfaceDesc.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
// set the backbuffer count field to 1, use 2 for triple buffering
DDSurfaceDesc.dwBackBufferCount = 1;
// request a complex, flippable
DDSurfaceDesc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_COMPLEX | DDSCAPS_FLIP;
// create the primary surface
if (FAILED(lpDD->CreateSurface(&DDSurfaceDesc, &lpDDPrimarySurface, NULL) ) ) {
return 0;
}
// now query for attached surface from the primary surface
// this line is needed by the call
DDSurfaceDesc.ddsCaps.dwCaps = DDSCAPS_BACKBUFFER;
// get the attached back buffer surface
if (FAILED(lpDDPrimarySurface->GetAttachedSurface(&DDSurfaceDesc.ddsCaps, &lpDDBackSurface) ) ) {
return 0;
}
// build up the palette data array
for (int color = 1; color < 255; color++) {
// fill with random RGB values
palette[color].peRed = rand()%256;
palette[color].peGreen = rand()%256;
palette[color].peBlue = rand()%256;
// set flags field to PC_NOCOLLAPSE
palette[color].peFlags = PC_NOCOLLAPSE;
} // end for color
// now fill in entry 0 and 255 with black and white
palette[0].peRed = 0;
palette[0].peGreen = 0;
palette[0].peBlue = 0;
palette[0].peFlags = PC_NOCOLLAPSE;
palette[255].peRed = 255;
palette[255].peGreen = 255;
palette[255].peBlue = 255;
palette[255].peFlags = PC_NOCOLLAPSE;
// create the palette object
if (FAILED(lpDD->CreatePalette(DDPCAPS_8BIT | DDPCAPS_ALLOW256 | DDPCAPS_INITIALIZE,
palette,&lpDDPalette, NULL) ) ) {
return 0;
}
// finally attach the palette to the primary surface
if (FAILED(lpDDPrimarySurface->SetPalette(lpDDPalette) ) ) {
return 0;
}
// load the 8-bit image
if (!Load_Bitmap_File(&bitmap, "MyBitMap.bmp") ) {
return 0;
}
// load it's palette into directdraw
if (FAILED(lpDDPalette->SetEntries(0, 0, MAX_COLORS, bitmap.palette) ) ) {
return 0;
}
// clean the surface
DDraw_Fill_Surface(lpDDPrimarySurface, 0);
// return success or failure or your own return code here
return 1;
} // end Game_Init
/////////////////////////////////////////////////////////////////////////////////
// this is called after the game is exited and the main event
// loop while is exited, do all you cleanup and shutdown here
int Game_Shutdown(void *parms, int num_parms) {
// this is called after the game is exited and the main event
// loop while is exited, do all you cleanup and shutdown here
// first the palette
if (lpDDPalette) {
lpDDPalette->Release();
lpDDPalette = NULL;
} // end if
// now the back buffer surface
if (lpDDBackSurface) {
lpDDBackSurface->Release();
lpDDBackSurface = NULL;
} // end if
// now the primary surface
if (lpDDPrimarySurface) {
lpDDPrimarySurface->Release();
lpDDPrimarySurface = NULL;
} // end if
// now blow away the IDirectDraw4 interface
if (lpDD) {
lpDD->Release();
lpDD = NULL;
} // end if
// return success or failure or your own return code here
return 1;
} // end Game_Shutdown
////////////////////////////////////////////////////////////////////////////////
// this is the main loop of the game, do all your processing here
int Game_Main(void *parms, int num_parms) {
// make sure this isn't executed again
if (window_closed)
return 0;
// for now test if user is hitting ESC and send WM_CLOSE
if (KEYDOWN(VK_ESCAPE) ) {
window_closed = 1;
SendMessage(main_window_handle, WM_CLOSE, 0, 0);
}
// copy the bitmap image to the primary buffer line by line
// note this is a good candidate operation to make into a function - hint!
// lock the primary surface
lpDDPrimarySurface->Lock(NULL, &DDSurfaceDesc, DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT, NULL);
// get video pointer to primary surfce
UCHAR *primary_buffer = (UCHAR*)DDSurfaceDesc.lpSurface;
// test if memory is linear
if (DDSurfaceDesc.lPitch == SCREEN_WIDTH) {
// copy memory from double buffer to primary buffer
memcpy( (void*)primary_buffer, (void*)bitmap.buffer, SCREEN_WIDTH * SCREEN_HEIGHT);
} else {
// non-linear
// make copy of source and destination addresses
UCHAR *dest_ptr = primary_buffer;
UCHAR *src_ptr = bitmap.buffer;
// memory is non-linear, copy line by line
for (int y = 0; y < SCREEN_HEIGHT; y++) {
// copy line
memcpy( (void*)dest_ptr, (void*)src_ptr, SCREEN_WIDTH);
// advance pointers to next line
dest_ptr += DDSurfaceDesc.lPitch;
src_ptr += SCREEN_WIDTH;
} // end for
} // end if / else
// now unlock the primary surface
if (FAILED(lpDDPrimarySurface->Unlock(NULL) ) ) {
return 0;
}
// do nothing -- look at pretty picture
// wait a sec so everything does not fly by so quick
//Sleep(30);
// return success or failure or your own return code here
return 1;
} // end Game_Main
-
Looks to me like an example from LaMothe's book. Are you sure you've copied it correctly? If you've modified it, would you mind pointing out the modified places? I'm pretty sure I've compiled the bitmap loading example in my time, and it worked.
-
I think I have traced the problem down to one of these two segments of code
from Function Game_Init:
Code:
// load the 8-bit image
if (!Load_Bitmap_File(&bitmap, "bitmap8.bmp") ) {
return 0;
}
// load it's palette into directdraw
if (FAILED(lpDDPalette->SetEntries(0, 0, MAX_COLORS, bitmap.palette) ) ) {
return 0;
}
// clean the surface
DDraw_Fill_Surface(lpDDPrimarySurface, 0);
and from function Game_Main:
Code:
// copy the bitmap image to the primary buffer line by line
// note this is a good candidate operation to make into a function - hint!
// lock the primary surface
lpDDPrimarySurface->Lock(NULL, &DDSurfaceDesc, DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT, NULL);
// get video pointer to primary surfce
UCHAR *primary_buffer = (UCHAR*)DDSurfaceDesc.lpSurface;
// test if memory is linear
if (DDSurfaceDesc.lPitch == SCREEN_WIDTH) {
// copy memory from double buffer to primary buffer
memcpy( (void*)primary_buffer, (void*)bitmap.buffer, SCREEN_WIDTH * SCREEN_HEIGHT);
} else {
// non-linear
// make copy of source and destination addresses
UCHAR *dest_ptr = primary_buffer;
UCHAR *src_ptr = bitmap.buffer;
// memory is non-linear, copy line by line
for (int y = 0; y < SCREEN_HEIGHT; y++) {
// copy line
memcpy( (void*)dest_ptr, (void*)src_ptr, SCREEN_WIDTH);
// advance pointers to next line
dest_ptr += DDSurfaceDesc.lPitch;
src_ptr += SCREEN_WIDTH;
} // end for
} // end if / else
// now unlock the primary surface
if (FAILED(lpDDPrimarySurface->Unlock(NULL) ) ) {
return 0;
}
still hav not figured it out yet, any ideas?
-
Yeah. Use a debugger to find the exact source line it crashes at.
-
Many of the reviews from amazon and other sources like gamedev state that the samples in the book rarely run as is on modern compilers. Some of the samples I've been told are completely broken and must be altered significantly in order to run.
In Lamothe's favor, however, this book is very old and DirectDraw 7 is definitely deprecated and unsupported. New drivers do not guarantee compatibility with DirectDraw interfaces.
Direct3D is now on top of DirectDraw or moreso merged with it. You can still gain access to DirectDraw surfaces but it is done through a completely different mechanism.
I highly recommend moving to Direct3D or OpenGL and doing 2D there. It will help you much more than dabbling in DirectDraw.