Original Post
I've been working the last few days on trying to get bitmaps showing in directx. I have the surfaces and all setup, and have managed to plot pxiles etc.. But can get any damn images to show! This is the code I'm using: And it crashes when it trys to blit... Any help is appreciated, even if its just to tell me something I'm doing slightly wrong ;) Cheers ^_^ [Edited by - Andrew Russell on December 15, 2005 5:00:40 PM]
/////////////////////////////////////////////////////////////////////////////////////////////
// Includes //
/////////////////////////////////////////////////////////////////////////////////////////////
#define WIN32_LEAN_AND_MEAN // Just say no to MFC
#define INITGUID // Make sure directX guids are included
#include <windows.h>
#include <windowsx.h>
#include <mmsystem.h>
#include <iostream.h>
#include <conio.h>
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
#include <string.h>
#include <stdarg.h>
#include <stdio.h>
#include <math.h>
#include <io.h>
#include <fcntl.h>
#include <sys/timeb.h>
#include <time.h>
#include <ddraw.h> // Include directdraw
/////////////////////////////////////////////////////////////////////////////////////////////
// Defines //
/////////////////////////////////////////////////////////////////////////////////////////////
#define WINDOW_CLASS_NAME "WINCLASS"
#define SCREEN_WIDTH 800
#define SCREEN_HEIGHT 600
#define SCREEN_BPP 32
// Universal id for a bitmap
#define BITMAP_ID 0x4D42
// Pixel formats
#define DD_PIXEL_FORMAT8 8
#define DD_PIXEL_FORMAT555 15
#define DD_PIXEL_FORMAT565 16
#define DD_PIXEL_FORMAT888 24
#define DD_PIXEL_FORMATALPHA888 32
// Game states
#define GAME_LOGO 1
/////////////////////////////////////////////////////////////////////////////////////////////
// Types //
/////////////////////////////////////////////////////////////////////////////////////////////
typedef unsigned short USHORT;
typedef unsigned short WORD;
typedef unsigned char UCHAR;
typedef unsigned char BYTE;
// 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
} BITMAP_FILE, *BITMAP_FILE_PTR;
// the simple bitmap image
typedef struct BITMAP_IMAGE_TYP
{
int state; // state of bitmap
int attr; // attributes of bitmap
int x,y; // position of bitmap
int width, height; // size of bitmap
int num_bytes; // total bytes of bitmap
int bpp; // bits per pixel
UCHAR *buffer; // pixels of bitmap
} BITMAP_IMAGE, *BITMAP_IMAGE_PTR;
/////////////////////////////////////////////////////////////////////////////////////////////
// Prototypes //
/////////////////////////////////////////////////////////////////////////////////////////////
int GameInit();
int GameMain();
int GameShutdown();
int CreateSurface(LPDIRECTDRAWSURFACE7 *lpSource, int xs, int ys);
void DrawBitmap(IDirectDrawSurface7 *lpSurface, HBITMAP hBitmap,
int x, int y, int width, int height);
void CreateBitmapSurface(LPDIRECTDRAWSURFACE7 lpSurface, char *fname, int xs, int ys);
int OpenErrorFile(char *filename, FILE *fp_override=NULL);
int CloseErrorFile(void);
int WriteError(char *string, ...);
/////////////////////////////////////////////////////////////////////////////////////////////
// Macros //
/////////////////////////////////////////////////////////////////////////////////////////////
// Tests if a key is up or down
#define KEYDOWN(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0)
#define KEYUP(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 0 : 1)
// Our colour marco, creates a 32-bit colour WORD
#define _RGB32BIT(a,r,g,b) ((b) + ((g) << 8) + ((r) << 16) + ((a) << 24))
// this builds a 16 bit color value in 5.5.5 format (1-bit alpha mode)
#define _RGB16BIT555(r,g,b) ((b & 31) + ((g & 31) << 5) + ((r & 31) << 10))
// this builds a 16 bit color value in 5.6.5 format (green dominate mode)
#define _RGB16BIT565(r,g,b) ((b & 31) + ((g & 63) << 5) + ((r & 31) << 11))
// this builds a 24 bit color value in 8.8.8 format
#define _RGB24BIT(a,r,g,b) ((b) + ((g) << 8) + ((r) << 16) )
// initializes a direct draw struct, basically zeros it and sets the dwSize field
#define DDRAW_INIT_STRUCT(ddstruct) { memset(&ddstruct,0,sizeof(ddstruct)); ddstruct.dwSize=sizeof(ddstruct); }
//////////////////////////////////////////////////////////////////////////////////////////////
// Globals //
//////////////////////////////////////////////////////////////////////////////////////////////
HWND main_window_handle = NULL; // Save the window handle
HINSTANCE main_instance = NULL; // Save the instance
LPDIRECTDRAW7 lpdd; // Holds our interface
LPDIRECTDRAWSURFACE7 lpddsprimary = NULL; // Primary Surface interface
LPDIRECTDRAWSURFACE7 lpddsback = NULL; // Back Buffer Surface interface
LPDIRECTDRAWCLIPPER lpddclipper = NULL; // Holds clipper
DDSURFACEDESC2 ddsd; // Surface Description
DDSCAPS2 ddscaps; // Surface capabilities struct
int game_state; // state tracking
char buffer[80]; // General printing buffer
DWORD start_time = 0; // Timing
int dd_pixel_format; // Default pixel format
BITMAP_FILE bitmap;
FILE *fp_error; // general error file
// Surfaces
LPDIRECTDRAWSURFACE7 lpddsLogo;
//////////////////////////////////////////////////////////////////////////////////////////////
// Functions //
//////////////////////////////////////////////////////////////////////////////////////////////
//-------------------------------------------------------//
// GameInit
// - Kicks everything off, getting rarred to go....
//-------------------------------------------------------//
int GameInit()
{
//
// Open Error File
//
OpenErrorFile("error.txt");
//
// Create DirectDraw object interface (fill lpdd)
//
if(FAILED(DirectDrawCreateEx(NULL, (void **)&lpdd, IID_IDirectDraw7, NULL)))
{
WriteError("\nFailed to create Direct Draw Object");
return(0); // ERROR!
}
//
// Set Full Screen
//
if(FAILED(lpdd->SetCooperativeLevel(main_window_handle, DDSCL_FULLSCREEN |
DDSCL_EXCLUSIVE | DDSCL_ALLOWREBOOT | DDSCL_ALLOWMODEX)))
{
WriteError("\nFailed to set co-operative level");
return(0); // ERROR!
}
//
// Set the display mode
//
if(FAILED(lpdd->SetDisplayMode(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP, 0, 0)))
{
WriteError("\nFailed to set display mode");
return(0); // ERROR!
}
// Clearout surface structure and set size
DDRAW_INIT_STRUCT(ddsd);
// Enable DD surface capabilites, and buffering
ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
// Request a back buffer
ddsd.dwBackBufferCount = 1;
// Set surface capabilites (primary, and buffering)
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_COMPLEX | DDSCAPS_FLIP;
//
// Create the Primary surface!
//
if(FAILED(lpdd->CreateSurface(&ddsd, &lpddsprimary, NULL)))
{
WriteError("\nFailed to create primary surface");
return(0); // ERROR!
}
//
// Get pixel format of the primary surface
//
DDPIXELFORMAT ddpf;
DDRAW_INIT_STRUCT(ddpf);
// query the format from primary surface
lpddsprimary->GetPixelFormat(&ddpf);
dd_pixel_format = ddpf.dwRGBBitCount;
//
// Now get the attached (buffer) surface
//
ddsd.ddsCaps.dwCaps = DDSCAPS_BACKBUFFER;
if(FAILED(lpddsprimary->GetAttachedSurface(&ddsd.ddsCaps, &lpddsback)))
{
WriteError("\nFailed to get secondary buffer surface");
return(0); // ERROR!
}
//
// Create the clipper
//
if(FAILED(lpdd->CreateClipper(0, &lpddclipper, NULL)))
{
WriteError("\nFailed to create clipper");
return(0); // ERROR!
}
//
// Create the clip list
//
RECT clip_list[1] = {{0, 0, SCREEN_WIDTH-1, SCREEN_HEIGHT-1}};
// Allocate memory
LPRGNDATA region_data = (LPRGNDATA) malloc(sizeof(RGNDATAHEADER) + sizeof(RECT));
// Copy rects into region data
memcpy(region_data->Buffer, clip_list, sizeof(RECT));
// Setup header fields
region_data->rdh.dwSize = sizeof(RGNDATAHEADER);
region_data->rdh.iType = RDH_RECTANGLES;
region_data->rdh.nCount = 1;
region_data->rdh.nRgnSize = sizeof(RECT);
region_data->rdh.rcBound.bottom = SCREEN_HEIGHT-1;
region_data->rdh.rcBound.right = SCREEN_WIDTH-1;
region_data->rdh.rcBound.left = 0;
region_data->rdh.rcBound.top = 0;
//
// Set Clip list
//
if(FAILED(lpddclipper->SetClipList(region_data, 0)))
{
WriteError("\nFailed to set clip list");
free(region_data);
return(0); // ERROR!
}
//
// Attach Clipper
//
if(FAILED(lpddsback->SetClipper(lpddclipper)))
{
WriteError("\nFailed to attach clipper");
free(region_data);
return(0); // ERROR!
}
// Release memory
free(region_data);
//
// We are ready to roll!
//
game_state = GAME_LOGO;
/*/ Load in logo
if(!(LoadBitmap(&bitmap, "logo.bmp")))
{
WriteError("\nLogo Bitmap Load Failed!");
return(0); // ERROR!
}*/
//
// Load Surfaces
//
CreateBitmapSurface(lpddsLogo, "logo.bmp", 640, 480);
return(1);
}
//-------------------------------------------------------//
//-------------------------------------------------------//
// GameShutdown
// - Kills everything, and mops up....
//-------------------------------------------------------//
int GameShutdown()
{
//
// Get rid of surfaces
//
if(lpddsLogo)
{
lpddsLogo->Release();
lpddsLogo = NULL;
}
// Close error file
CloseErrorFile();
// Get rid of the back buffer surface
if(lpddsback)
{
lpddsback->Release();
lpddsback = NULL;
}
// Get rid of the primary surface
if(lpddsprimary)
{
lpddsprimary->Release();
lpddsprimary = NULL;
}
// Get rid of the DirectDraw Interface
if(lpdd)
{
lpdd->Release();
lpdd = NULL;
}
return(1);
}
//-------------------------------------------------------//
//-------------------------------------------------------//
// GameMain
// -
//-------------------------------------------------------//
int GameMain()
{
static int window_closed;
// Make sure we don't execute again
if(window_closed)
{
return(0);
}
// Going somewhere?
if(KEYDOWN(VK_ESCAPE))
{
SendMessage(main_window_handle, WM_CLOSE, 0, 0);
window_closed = 1;
}
// Cleanout, and setup surface description
// This will be filled with info when surface is locked
DDRAW_INIT_STRUCT(ddsd);
// Lock the back buffer surface
if(FAILED(lpddsback->Lock(NULL, &ddsd, DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT, NULL)))
{
WriteError("\nFailed to lock back buffer surface");
return(0); // ERROR!
}
//
// Now ddsd has:
// - ddsd.lpSurface : pointer to surface memory
// - ddsd.lPitch : bytes per line for plotting calculations
//
// Get pointer to surface
UCHAR *back_buffer = (UCHAR *)ddsd.lpSurface;
// Clear out buffer (wipe screen)
if(ddsd.lPitch == SCREEN_WIDTH)
{
// Just wipe it
memset(back_buffer, 0, SCREEN_WIDTH*SCREEN_HEIGHT*4);
}
else
{
// Copy video pointer
UCHAR *dest_ptr = back_buffer;
// Clear out memory one line at a time
for(int y = 0; y < SCREEN_HEIGHT; y++)
{
memset(dest_ptr, 0, SCREEN_WIDTH*4);
// advance to next line
dest_ptr += ddsd.lPitch;
}
}
//
// Switch Game states
//
switch(game_state)
{
case GAME_LOGO:
{
RECT dest, src;
dest.left = 80;
dest.top = 60;
dest.bottom = 540;
dest.right = 700;
src.left = 0;
src.top = 0;
src.bottom = 480; // -1?
src.right = 640;
//
// DOESNT WORK!
//
if(FAILED(lpddsback->Blt(&dest, lpddsLogo, &src, DDBLT_WAIT, NULL)))
{
WriteError("\nFailed to Blit logo image");
return(0); // ERROR!
}
/* WriteError("%d\n", bitmap.buffer[65]);
for(int y=0; y<bitmap.bitmapinfoheader.biHeight; y++)
{
for(int x=0; x<bitmap.bitmapinfoheader.biWidth; x++)
{
//back_buffer[(x*4)+(y*ddsd.lPitch)] = 0;
//back_buffer[(x*4)+(y*ddsd.lPitch)+1] =
// bitmap.buffer[(x*3)+(y*bitmap.bitmapinfoheader.biWidth)+2];
//back_buffer[(x*4)+(y*ddsd.lPitch)+2] =
// bitmap.buffer[(x*3)+(y*bitmap.bitmapinfoheader.biWidth)+1];
//back_buffer[(x*4)+(y*ddsd.lPitch)+3] =
// bitmap.buffer[(x*3)+(y*bitmap.bitmapinfoheader.biWidth)];
UCHAR blue = bitmap.buffer[(x*4)+(y*bitmap.bitmapinfoheader.biWidth)],
green = bitmap.buffer[(x*4)+(y*bitmap.bitmapinfoheader.biWidth) + 1],
red = bitmap.buffer[(x*4)+(y*bitmap.bitmapinfoheader.biWidth) + 2];
back_buffer[(x*4)+(y*ddsd.lPitch)] = 0;
back_buffer[(x*4)+(y*ddsd.lPitch)+1] = red;
back_buffer[(x*4)+(y*ddsd.lPitch)+2] = green;
back_buffer[(x*4)+(y*ddsd.lPitch)+3] = blue;
}
}
for(int i=0; i < 5000; i++)
{
int x = (rand()%SCREEN_WIDTH);
int y = rand()%SCREEN_HEIGHT;
back_buffer[(x*4)+(y*ddsd.lPitch)] = 0;
back_buffer[(x*4)+(y*ddsd.lPitch)+1] = 60;
back_buffer[(x*4)+(y*ddsd.lPitch)+2] = 60;
back_buffer[(x*4)+(y*ddsd.lPitch)+3] = 60;
}*/
} break;
default:
break;
}
/*/ Plot some pixels!
UINT pixel = _RGB32BIT(255,60,60,60);
back_buffer[600] = pixel;
back_buffer[604] = pixel;
back_buffer[608] = pixel;*/
// Unlock the back buffer surface
if(FAILED(lpddsback->Unlock(NULL)))
{
WriteError("\nFailed to unlock back buffer surface");
return(0); // ERROR!
}
// Preform the flip to primary!
while(FAILED(lpddsprimary->Flip(NULL, DDFLIP_WAIT)));
//
// ???
//
//Sleep(500);
return(1);
}
//-------------------------------------------------------//
int CreateSurface(LPDIRECTDRAWSURFACE7 *lpSource, int xs, int ys)
{
DDRAW_INIT_STRUCT(ddsd);
ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT;
ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY;
ddsd.dwWidth = xs;
ddsd.dwHeight = ys;
if(FAILED(lpdd->CreateSurface(&ddsd, lpSource, NULL)))
{
WriteError("\nFailed to create surface");
return(0); // Error!
}
return(1);
}
void CreateBitmapSurface(LPDIRECTDRAWSURFACE7 lpSurface, char *fname, int xs, int ys)
{
HBITMAP hBitmap;
CreateSurface(&lpSurface, xs, ys);
hBitmap = (HBITMAP) LoadImage(NULL, fname, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
DrawBitmap(lpSurface, hBitmap, 0, 0, xs, ys);
DeleteObject(hBitmap);
}
void DrawBitmap(IDirectDrawSurface7 *lpSurface, HBITMAP hBitmap,
int x, int y, int width, int height)
{
HDC hdcImage;
HDC hdc;
BITMAP bm;
if (lpSurface == NULL || hBitmap == NULL)
{
WriteError("\nSurface or Bitmap invalid for Drawing");
return;
}
lpSurface->Restore();
hdcImage = CreateCompatibleDC(NULL);
SelectObject(hdcImage, hBitmap);
GetObject(hBitmap, sizeof(bm), &bm);
width = width == 0 ? bm.bmWidth : width;
height = height == 0 ? bm.bmHeight : height;
lpSurface->GetDC(&hdc);
BitBlt(hdc, x, y, width, height, hdcImage, 0, 0, SRCCOPY);
lpSurface->ReleaseDC(hdc);
DeleteDC(hdcImage);
}
//-------------------------------------------------------//
// OpenErrorFile
// - This creates the output error file
//-------------------------------------------------------//
int OpenErrorFile(char *filename, FILE *fp_override)
{
// is user requesting special file handle? stdout, stderr, etc.?
if (fp_override)
{
fp_error = fp_override;
}
else
{
// test if this file is valid
if ((fp_error = fopen(filename,"w"))==NULL)
{
return(0);
}
}
// get the current time
struct _timeb timebuffer;
char *timeline;
char timestring[280];
_ftime(&timebuffer);
timeline = ctime(&(timebuffer.time));
sprintf(timestring, "%.19s.%hu, %s", timeline, timebuffer.millitm, &timeline[20]);
// write out error header with time
WriteError("\nOpening Error Output File (%s) on %s\n",filename,timestring);
// now the file is created, re-open with append mode
if (!fp_override)
{
fclose(fp_error);
if ((fp_error = fopen(filename,"a+"))==NULL)
{
return(0);
}
}
// return success
return(1);
}
//-------------------------------------------------------//
//-------------------------------------------------------//
// CloseErrorFile
// - This closes the error file
//-------------------------------------------------------//
int CloseErrorFile(void)
{
if (fp_error)
{
// write close file string
WriteError("\n\nClosing Error Output File.");
if (fp_error!=stdout || fp_error!=stderr)
{
// close the file handle
fclose(fp_error);
}
fp_error = NULL;
// return success
return(1);
} // end if
else
{
return(0);
}
}
//-------------------------------------------------------//
//-------------------------------------------------------//
// WriteError
// - This prints out the error string to the error file
//-------------------------------------------------------//
int WriteError(char *string, ...)
{
char buffer[80]; // working buffer
va_list arglist; // variable argument list
// make sure both the error file and string are valid
if (!string || !fp_error)
{
return(0);
}
// print out the string using the variable number of arguments on stack
va_start(arglist,string);
vsprintf(buffer,string,arglist);
va_end(arglist);
// write string to file
fprintf(fp_error,buffer);
// flush buffer incase the system bails
fflush(fp_error);
// return success
return(1);
}
//-------------------------------------------------------//
//-------------------------------------------------------//
// WindowProc
// - 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;
}
// Process any messages that we didn't take care of
return (DefWindowProc(hwnd, msg, wparam, lparam));
} // end WinProc
//-------------------------------------------------------//
/////////////////////////////////////////////////////////////////////////////////////////////
// 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
PAINTSTRUCT ps; // Generic paintstruct
// 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);
// Register the window class
if (!RegisterClassEx(&winclass))
{
return(0);
}
// Create the window
if(!(hwnd = CreateWindowEx(NULL, // extended style
WINDOW_CLASS_NAME,
"Underground Assault",
WS_POPUP | WS_VISIBLE,
0,0,
SCREEN_WIDTH,SCREEN_HEIGHT,
NULL, // handle to parent
NULL, // handle to menu
hinstance, // instance of this application
NULL))) // extra creation parms
{
return(0);
}
// Save the window handle and instance in a global
main_window_handle = hwnd;
main_instance = hinstance;
// Initialize everything
GameInit();
// Enter main event loop
while(TRUE)
{
// Set time reference
start_time = GetTickCount();
// 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);
}
GameMain();
// Lock to 30ish fps (33 millisecs)
while((GetTickCount() - start_time) < 33);
}
// Thats all folks
GameShutdown();
// Return to Windows like this
return(msg.wParam);
} // end WinMain
//-----------------------------------------------------------------------------------------//
// Teh End
//-----------------------------------------------------------------------------------------//