Sign in to follow this  
glMatt

DevC++ Image File Loading Problem

Recommended Posts

I wrote an OpenGL Checkers game program for Mac OS 10.4 using XCode 2.x. The game runs quite well, so I decided to also make one for Windows using DevC++. Just like with XCode, getting images to load and display is turning out to be a monumental task. The error is my own messageBox error saying that there is no hBitmap (!hBitmap). What could be causing this? The code is included below. ------------------------------------------------------------------------------ To start, a new C plus plus Windows project was created and saved in its own folder which also contains a 512x512 24 bit bmp image of a checkerboard (checkerboard.bmp). ------------------------------------------------------------------------------ A new resource file, "Images.rc" was created with the following information: CHECKERBOARD BITMAP "checkerboard.bmp" ------------------------------------------------------------------------------ main.cpp looks like this: #include <windows.h> #include <windowsx.h> #include <iostream> #include <gl/gl.h> #include <GL/gl.h> using namespace std; GLuint texture; static HINSTANCE hinst; HBITMAP hBitmap; BITMAP bitmap; bool determinePixelFormat(HDC *hDC, HGLRC *hRC, HWND hwnd); LRESULT CALLBACK WinProc(HWND, UINT, WPARAM, LPARAM); int WINAPI WinMain (HINSTANCE hThisInstance, HINSTANCE hPrevInstance, LPSTR lpszArgument, int nFunsterStil) { glClearColor(0.0, 0.0, 0.0, 0.0); glClearDepth(1.0); glShadeModel(GL_SMOOTH); glOrtho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); hBitmap = (HBITMAP) LoadImage(hinst, "CHECKERBOARD", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE | LR_CREATEDIBSECTION); if (!hBitmap) { MessageBox(NULL, "LoadImage failed", "ERROR", MB_OK); } int get_object; get_object = GetObject(hBitmap, sizeof(bitmap), &bitmap); if (!get_object) { MessageBox(NULL, "GetObject failed", "ERROR", MB_OK); } GLint bm_bits_pixel = (GLint) bitmap.bmBitsPixel; GLint internal_format; if (bm_bits_pixel < 32) { internal_format = GL_RGB; } else { internal_format = GL_RGBA; } GLuint width = (GLuint) bitmap.bmWidth; GLuint height = (GLuint) bitmap.bmHeight; glGenTextures(1, &texture); glBindTexture(GL_TEXTURE_2D, texture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexImage2D(GL_TEXTURE_2D, 0, internal_format, width, height, 0, internal_format, GL_UNSIGNED_BYTE, bitmap.bmBits); DeleteObject(hBitmap); bool quit_game = FALSE; WNDCLASSEX wincl; HWND hwnd; HDC hDC; HGLRC hRC; MSG messages; wincl.cbSize = sizeof(WNDCLASSEX); wincl.style = CS_OWNDC; wincl.lpfnWndProc = WinProc; wincl.cbClsExtra = 0; wincl.cbWndExtra = 0; wincl.hInstance = hThisInstance; wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION); wincl.hCursor = LoadCursor (NULL, IDC_CROSS); wincl.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wincl.lpszMenuName = NULL; wincl.lpszClassName = "Checkers"; wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION); if (!RegisterClassEx(&wincl)) { return 0; } hwnd = CreateWindowEx(NULL, "Checkers", "Checkers Beta 0.70", WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, 100, 100, 768, 600, NULL, NULL, hThisInstance, 0); if (!determinePixelFormat(&hDC, &hRC, hwnd)) { PostQuitMessage (0); } ShowWindow(hwnd, SW_SHOWNORMAL); UpdateWindow(hwnd); while (!quit_game) { if (PeekMessage(&messages, NULL, 0, 0, PM_REMOVE)) { if (messages.message == WM_QUIT) { quit_game = TRUE; } else { TranslateMessage(&messages); DispatchMessage(&messages); } } else { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glDisable(GL_DEPTH_TEST); glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, texture); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glBegin(GL_QUADS); glTexCoord2f(0.0, 0.0); glVertex3f(-1.0, -1.0, 0.0); glTexCoord2f(0.0, 1.0); glVertex3f(-1.0, 1.0, 0.0); glTexCoord2f(1.0, 1.0); glVertex3f(0.5, 1.0, 0.0); glTexCoord2f(1.0, 0.0); glVertex3f(0.5, -1.0, 0.0); glEnd(); glDisable(GL_TEXTURE_2D); glEnable(GL_DEPTH_TEST); glFlush(); SwapBuffers(hDC); } } wglMakeCurrent(NULL, NULL); wglDeleteContext(hRC); ReleaseDC(hwnd, hDC); return (messages.wParam); } bool determinePixelFormat(HDC *hDC, HGLRC *hRC, HWND hwnd) { PIXELFORMATDESCRIPTOR pfd; pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR); pfd.nVersion = 1; pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; pfd.dwLayerMask = PFD_MAIN_PLANE; pfd.iPixelType = PFD_TYPE_RGBA; pfd.cColorBits = 24; pfd.cDepthBits = 16; pfd.cAccumBits = 0; pfd.cStencilBits = 0; *hDC = GetDC(hwnd); int pixelformat; pixelformat = ChoosePixelFormat(*hDC, &pfd); if (!pixelformat) { MessageBox(NULL, "ChoosePixelFormat failed", "Error", MB_OK); return (FALSE); } bool set_pixel_format; set_pixel_format = SetPixelFormat(*hDC, pixelformat, &pfd); if (!set_pixel_format) { MessageBox(NULL, "SetPixelFormat failed", "Error", MB_OK); return (FALSE); } *hRC = wglCreateContext(*hDC); wglMakeCurrent(*hDC, *hRC); return (TRUE); } LRESULT CALLBACK WinProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_CREATE: break; case WM_CLOSE: DestroyWindow(hwnd); break; case WM_QUIT: DestroyWindow(hwnd); break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hwnd, message, wParam, lParam); } return 0; }

Share this post


Link to post
Share on other sites
You have to provide the full file name:

hBitmap = (HBITMAP) LoadImage(hinst,
"CHECKERBOARD.BMP",
IMAGE_BITMAP,
0, 0,
LR_LOADFROMFILE | LR_CREATEDIBSECTION);

Preferably you might add the full path to your game folder so you always load the correct file.

Share this post


Link to post
Share on other sites
[edit]Ha, Endurion is correct on that, I guess I fixed that and didn't realize it -- the error goes away, but the image will not display, bugs in the code :/

For future references, you can wrap your code in [source] code goes here [/source] tags for a cleaner presentation [wink]

Now as for your project. It compiles and runs fine for me, but it does not work. I just placed a 512x512 bmp in the same folder and it ran without that error. I however did get that error when the file was not there. Check to make sure that your bitmap is in the same folder and your project so it is loaded from where the .exe is compiled at. It should fix that.

Now as for your program, I would suggest not trying to do all of this from scratch again. There is a function done that does all of this + other formats as well by NeHe. It's called IPicture (http://nehe.gamedev.net/counter.asp?file=files/misc/nehe_ipicture.zip) I pasted in the link because hotlinking to the file does not work on the first try, so copy and paste it into your URL bar.

Ok, so in relation to your project. Basically you can rip out all of the BMP loading stuff and replace it with the function and this function call:O
    if(!BuildTexture("checkerboard.bmp", texture))
return -1;

And that's it! The function takes care of everything else for you. Of course you have to add in a few more header files and link in additional files, but they are all native Win32.

Ok, so here is a copy and paste modification of your code to show you what I have been talking about:

/*
Do not forget to link in:

-lmingw32
-lopengl32
-lglu32
-luser32
-lgdi32
-lole32
-loleaut32
-luuid

*/


#include <windows.h>
#include <windowsx.h>
#include <iostream>
#include <olectl.h> // Header File For The OLE Controls Library (Used In BuildTexture)
#include <math.h> // Header File For The Math Library (Used In BuildTexture)
#include <gl/gl.h>
#include <GL/gl.h>

using namespace std;

int BuildTexture(char *szPathName, GLuint &texid) // Load Image And Convert To A Texture
{
HDC hdcTemp; // The DC To Hold Our Bitmap
HBITMAP hbmpTemp; // Holds The Bitmap Temporarily
IPicture *pPicture; // IPicture Interface
OLECHAR wszPath[MAX_PATH+1]; // Full Path To Picture (WCHAR)
char szPath[MAX_PATH+1]; // Full Path To Picture
long lWidth; // Width In Logical Units
long lHeight; // Height In Logical Units
long lWidthPixels; // Width In Pixels
long lHeightPixels; // Height In Pixels
GLint glMaxTexDim ; // Holds Maximum Texture Size

if (strstr(szPathName, "http://")) // If PathName Contains http:// Then...
{
strcpy(szPath, szPathName); // Append The PathName To szPath
}
else // Otherwise... We Are Loading From A File
{
GetCurrentDirectory(MAX_PATH, szPath); // Get Our Working Directory
strcat(szPath, "\\"); // Append "\" After The Working Directory
strcat(szPath, szPathName); // Append The PathName
}

MultiByteToWideChar(CP_ACP, 0, szPath, -1, wszPath, MAX_PATH); // Convert From ASCII To Unicode
HRESULT hr = OleLoadPicturePath(wszPath, 0, 0, 0, IID_IPicture, (void**)&pPicture);

if(FAILED(hr)) // If Loading Failed
return FALSE; // Return False

hdcTemp = CreateCompatibleDC(GetDC(0)); // Create The Windows Compatible Device Context
if(!hdcTemp) // Did Creation Fail?
{
pPicture->Release(); // Decrements IPicture Reference Count
return FALSE; // Return False (Failure)
}

glGetIntegerv(GL_MAX_TEXTURE_SIZE, &glMaxTexDim); // Get Maximum Texture Size Supported

pPicture->get_Width(&lWidth); // Get IPicture Width (Convert To Pixels)
lWidthPixels = MulDiv(lWidth, GetDeviceCaps(hdcTemp, LOGPIXELSX), 2540);
pPicture->get_Height(&lHeight); // Get IPicture Height (Convert To Pixels)
lHeightPixels = MulDiv(lHeight, GetDeviceCaps(hdcTemp, LOGPIXELSY), 2540);

// Resize Image To Closest Power Of Two
if (lWidthPixels <= glMaxTexDim) // Is Image Width Less Than Or Equal To Cards Limit
lWidthPixels = 1 << (int)floor((log((double)lWidthPixels)/log(2.0f)) + 0.5f);
else // Otherwise Set Width To "Max Power Of Two" That The Card Can Handle
lWidthPixels = glMaxTexDim;

if (lHeightPixels <= glMaxTexDim) // Is Image Height Greater Than Cards Limit
lHeightPixels = 1 << (int)floor((log((double)lHeightPixels)/log(2.0f)) + 0.5f);
else // Otherwise Set Height To "Max Power Of Two" That The Card Can Handle
lHeightPixels = glMaxTexDim;

// Create A Temporary Bitmap
BITMAPINFO bi = {0}; // The Type Of Bitmap We Request
DWORD *pBits = 0; // Pointer To The Bitmap Bits

bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); // Set Structure Size
bi.bmiHeader.biBitCount = 32; // 32 Bit
bi.bmiHeader.biWidth = lWidthPixels; // Power Of Two Width
bi.bmiHeader.biHeight = lHeightPixels; // Make Image Top Up (Positive Y-Axis)
bi.bmiHeader.biCompression = BI_RGB; // RGB Encoding
bi.bmiHeader.biPlanes = 1; // 1 Bitplane

// Creating A Bitmap This Way Allows Us To Specify Color Depth And Gives Us Imediate Access To The Bits
hbmpTemp = CreateDIBSection(hdcTemp, &bi, DIB_RGB_COLORS, (void**)&pBits, 0, 0);

if(!hbmpTemp) // Did Creation Fail?
{
DeleteDC(hdcTemp); // Delete The Device Context
pPicture->Release(); // Decrements IPicture Reference Count
return FALSE; // Return False (Failure)
}

SelectObject(hdcTemp, hbmpTemp); // Select Handle To Our Temp DC And Our Temp Bitmap Object

// Render The IPicture On To The Bitmap
pPicture->Render(hdcTemp, 0, 0, lWidthPixels, lHeightPixels, 0, lHeight, lWidth, -lHeight, 0);

// Convert From BGR To RGB Format And Add An Alpha Value Of 255
for(long i = 0; i < lWidthPixels * lHeightPixels; i++) // Loop Through All Of The Pixels
{
BYTE* pPixel = (BYTE*)(&pBits[i]); // Grab The Current Pixel
BYTE temp = pPixel[0]; // Store 1st Color In Temp Variable (Blue)
pPixel[0] = pPixel[2]; // Move Red Value To Correct Position (1st)
pPixel[2] = temp; // Move Temp Value To Correct Blue Position (3rd)

// This Will Make Any Black Pixels, Completely Transparent (You Can Hardcode The Value If You Wish)
if ((pPixel[0]==0) && (pPixel[1]==0) && (pPixel[2]==0)) // Is Pixel Completely Black
pPixel[3] = 0; // Set The Alpha Value To 0
else // Otherwise
pPixel[3] = 255; // Set The Alpha Value To 255
}

glGenTextures(1, &texid); // Create The Texture

// Typical Texture Generation Using Data From The Bitmap
glBindTexture(GL_TEXTURE_2D, texid); // Bind To The Texture ID
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); // (Modify This For The Type Of Filtering You Want)
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); // (Modify This For The Type Of Filtering You Want)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, lWidthPixels, lHeightPixels, 0, GL_RGBA, GL_UNSIGNED_BYTE, pBits); // (Modify This If You Want Mipmaps)

DeleteObject(hbmpTemp); // Delete The Object
DeleteDC(hdcTemp); // Delete The Device Context

pPicture->Release(); // Decrements IPicture Reference Count

return TRUE; // Return True (All Good)
}

GLuint texture;

static HINSTANCE hinst;

bool determinePixelFormat(HDC *hDC, HGLRC *hRC, HWND hwnd);
LRESULT CALLBACK WinProc(HWND, UINT, WPARAM, LPARAM);

int WINAPI WinMain (HINSTANCE hThisInstance,
HINSTANCE hPrevInstance,
LPSTR lpszArgument,
int nFunsterStil)
{
int get_object;

bool quit_game = FALSE;
WNDCLASSEX wincl;
HWND hwnd;
HDC hDC;
HGLRC hRC;
MSG messages;

wincl.cbSize = sizeof(WNDCLASSEX);
wincl.style = CS_OWNDC;
wincl.lpfnWndProc = WinProc;
wincl.cbClsExtra = 0;
wincl.cbWndExtra = 0;
wincl.hInstance = hThisInstance;
wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
wincl.hCursor = LoadCursor (NULL, IDC_CROSS);
wincl.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wincl.lpszMenuName = NULL;
wincl.lpszClassName = "Checkers";
wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);

if (!RegisterClassEx(&wincl))
{
return 0;
}

hwnd = CreateWindowEx(NULL,
"Checkers",
"Checkers Beta 0.70",
WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
100,
100,
768,
600,
NULL,
NULL,
hThisInstance,
0);

if (!determinePixelFormat(&hDC, &hRC, hwnd))
{
PostQuitMessage (0);
}

ShowWindow(hwnd, SW_SHOWNORMAL);
UpdateWindow(hwnd);

/* OpenGl code goes here! After you have setup the Win32 window + context */
glClearColor(0.0, 0.0, 0.0, 0.0);
glClearDepth(1.0);
glShadeModel(GL_SMOOTH);
glOrtho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);

// here is where you load the bmp
if(!BuildTexture("checkerboard.bmp", texture))
return -1;

// The rest is all you!
while (!quit_game)
{
if (PeekMessage(&messages, NULL, 0, 0, PM_REMOVE)) {
if (messages.message == WM_QUIT) {
quit_game = TRUE;
} else {
TranslateMessage(&messages);
DispatchMessage(&messages);
}
} else {

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glDisable(GL_DEPTH_TEST);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, texture);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glBegin(GL_QUADS);
glTexCoord2f(0.0, 0.0); glVertex3f(-1.0, -1.0, 0.0);
glTexCoord2f(0.0, 1.0); glVertex3f(-1.0, 1.0, 0.0);
glTexCoord2f(1.0, 1.0); glVertex3f(0.5, 1.0, 0.0);
glTexCoord2f(1.0, 0.0); glVertex3f(0.5, -1.0, 0.0);
glEnd();
glDisable(GL_TEXTURE_2D);
glEnable(GL_DEPTH_TEST);

glFlush();
SwapBuffers(hDC);
}
}
wglMakeCurrent(NULL, NULL);
wglDeleteContext(hRC);
ReleaseDC(hwnd, hDC);
return (messages.wParam);
}

bool determinePixelFormat(HDC *hDC, HGLRC *hRC, HWND hwnd)
{
PIXELFORMATDESCRIPTOR pfd;
pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
pfd.nVersion = 1;
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
pfd.dwLayerMask = PFD_MAIN_PLANE;
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.cColorBits = 24;
pfd.cDepthBits = 16;
pfd.cAccumBits = 0;
pfd.cStencilBits = 0;
*hDC = GetDC(hwnd);
int pixelformat;
pixelformat = ChoosePixelFormat(*hDC, &pfd);
if (!pixelformat) {
MessageBox(NULL, "ChoosePixelFormat failed", "Error", MB_OK);
return (FALSE);
}
bool set_pixel_format;
set_pixel_format = SetPixelFormat(*hDC, pixelformat, &pfd);
if (!set_pixel_format) {
MessageBox(NULL, "SetPixelFormat failed", "Error", MB_OK);
return (FALSE);
}
*hRC = wglCreateContext(*hDC);
wglMakeCurrent(*hDC, *hRC);
return (TRUE);
}

LRESULT CALLBACK
WinProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
switch (message) {
case WM_CREATE:

break;
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_QUIT:
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, message, wParam, lParam);
}
return 0;
}



Note that you will have to now link in the following libraries:
-lmingw32
-lopengl32
-lglu32
-luser32
-lgdi32
-lole32
-loleaut32
-luuid


You should not need a resource file either now. See if this helps, if you have any questions on it feel free to ask! The NeHe function is at the top of your program, so you do not have to download the IPicture example if you do not want to.

Share this post


Link to post
Share on other sites
hBitmap = (HBITMAP) LoadImage(hinst,
"CHECKERBOARD",
IMAGE_BITMAP,
0, 0,
LR_LOADFROMFILE | LR_CREATEDIBSECTION);
if (!hBitmap) {
MessageBox(NULL, "LoadImage failed", "ERROR", MB_OK);
}

I want to make the checkerboard .bmp file tied in with the .exe program as a resource. That is why I am trying to use the "CHECKERBOARD" handle in the .rc file. This worked for me in a different DevC++ project, but not this one.

DevC++ shows examples where this is being done.


-------------------------------------------------------------------------------
With the file loaded this way, I no longer get the error. I don't think
the image will be part of the .exe program, though:

hBitmap = (HBITMAP) LoadImage(hinst,
"checkerboard.bmp",
IMAGE_BITMAP,
0, 0,
LR_LOADFROMFILE | LR_CREATEDIBSECTION);
if (!hBitmap) {
MessageBox(NULL, "LoadImage failed", "ERROR", MB_OK);
}

And there are also OpenGL related problems. The image does not show up. Maybe version 4.9.9.2 has problems with .rc files.

Share this post


Link to post
Share on other sites
If you want to load the image from the resources you have to

A) include the bmp in the resources (i think you already did that)
B) remove the LR_LOADFROMFILE flag (otherwise LoadBitmap tries to load from a file)

If you included the bmp in your resources you ought to have an ID for it. Use that as the second parameter in one of the both ways:

LoadBitmap( hinst, "ID_DEFINE", ...

or

LoadBitmap( hinst, MAKEINTRESOURCE( ID_DEFINE ), ...

Share this post


Link to post
Share on other sites
Thanks for the code, DREW_BENTON. The checkerboard image displays. No modifications needed.

It would be nice if there was some sort of handbook for DevC++.

It still bugs me that DevC++ 4.9.9.2 beta will not recognize .rc files.

Share this post


Link to post
Share on other sites
Quote:
Original post by glMatt


It still bugs me that DevC++ 4.9.9.2 beta will not recognize .rc files.


Thats news to me...

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this