Jump to content
  • Advertisement
Sign in to follow this  
sipickles

[win32 GDI] Displaying bitmaps... again

This topic is 4221 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi, I am trying to draw a heightmap bitmap created by my world building tool. I can create a bitmap, draw colours onto it and save it, then see the results in windows explorer like this: I want to display this bitmap in the tool app, but am having trouble showing it. I first tried this in the main windows messageHandler:
		case WM_PAINT:
			if ( g_bitmap )
			{
				hdc = BeginPaint(hWnd, &ps);
				hdcMem = CreateCompatibleDC(hdc);
				SelectObject(hdcMem, g_bitmap->GetHandle());
				GetObject(g_bitmap->GetHandle(), sizeof(bm), &bm);
				BitBlt(hdc, 100, 100, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCCOPY);
				DeleteDC(hdcMem);
				EndPaint(hWnd, &ps);
			}
			break;


Using debug, this code is reached and all variables seem initialised, but nothing displays. My hunch is that g_bitmap->GetHandle() returns something incorrect. It simply returns the handle provided by this:
BITMAPINFO	bmi;

    ZeroMemory( &bmi.bmiHeader,  sizeof(BITMAPINFOHEADER) );
    bmi.bmiHeader.biSize        = sizeof(BITMAPINFOHEADER);
    bmi.bmiHeader.biWidth       =  g_numVerts;
    bmi.bmiHeader.biHeight      = -g_numVerts;
    bmi.bmiHeader.biPlanes      = 1;
    bmi.bmiHeader.biCompression = BI_RGB;
    bmi.bmiHeader.biBitCount    = 16;

    VOID *pvBits;
	
	// Create a DC and a bitmap
	m_hbmDC		= CreateCompatibleDC( NULL );
    m_hBitmap	= CreateDIBSection( m_hbmDC,
									&bmi,
									DIB_RGB_COLORS,
									&pvBits,
									NULL,
									0 );

    SetMapMode( m_hbmDC, MM_TEXT );

	SelectObject( m_hbmDC, m_hBitmap );

	g_log->Log( "Bitmap Created\n" );


Or is my use of CreateDIBSection incorrect? I am wondeering whether my bitmap and window format are not compatible, but i thought bitblt took care of that. Any suggestions welcome.

Share this post


Link to post
Share on other sites
Advertisement
Since this didn't work, I then tried creating a child window like this:

HWND bmhWnd = CreateWindow(	"STATIC",NULL,
WS_CHILD | WS_VISIBLE | WS_BORDER | SS_BITMAP,
x, y,
w, h,
g_hWnd,(HMENU)IDC,g_hInstance,NULL);



Then trying to set the image of the static like this:

SendMessage (bmhWnd, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM) g_bitmap->GetHandle());



No good either, but I get the outline of the bitmap, just nothing in it :)

Share this post


Link to post
Share on other sites
Your GetHandle() method isnt getting the handle to the bitmap you want. CreateDIBSection() is used when you want to create a bitmap and set its bits directly not when you want the HBITMAP of an existing bitmap. Presumably you already have an HBITMAP of the bitmap you want to display (either from LoadImage() or one that your program has made dynamically), so just return that. Right now, your GetHandle() method is probably leaking GDI resources as well since it's not deleting the previous m_hbmDC or m_hBitmap and instead overwrites their previous values. Leaking GDI resources will eventually lead to the entire OS behaving strangely. Hopefully, however, you wont need to create a DC or use CreateDIBSection() in this call so that will no longer be a problem.

Also, please remember that SelectObject() returns the previously selected object. Before you delete a DC you must select this original object back into the DC.

Share this post


Link to post
Share on other sites
Thanks Colin

In the above code CreateDIBSection is creating the bitmap, not retrieving the handle of an existing one.


What method should I use to create a new blank bitmap then? I don't want to load one in. I want a new one that I can draw to, display then save.

Ta

Share this post


Link to post
Share on other sites
The CreateDIBSection() might be what you need. CreateDIBSection() is useful if you are going to set the bits of the bitmap directly. If, on the other hand, you're going to select the HBITMAP into a DC and then use the GDI drawing functions to create the image then a better function might be CreateBitmap() or CreateCompatibleBitmap(). However, you dont want to do this in GetHandle().

What you probably want to do it create the bitmap when the class with the GetHandle() member is first created. GetHandle() would then be a simple getter function for the HBITMAP.

Share this post


Link to post
Share on other sites
Yes, I think that is what I am doing already. Here's some code which may help:


#include "cBitmap.h"

cBitmap::cBitmap()
{
// Prepare to create a bitmap
BITMAPINFO bmi;

ZeroMemory( &bmi.bmiHeader, sizeof(BITMAPINFOHEADER) );
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = g_numVerts;
bmi.bmiHeader.biHeight = -g_numVerts;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biCompression = BI_RGB;
bmi.bmiHeader.biBitCount = 16;

VOID *pvBits;

// Create a DC and a bitmap
m_hbmDC = CreateCompatibleDC( NULL );
m_hBitmap = CreateDIBSection( m_hbmDC,
&bmi,
DIB_RGB_COLORS,
&pvBits,
NULL,
0 );

SetMapMode( m_hbmDC, MM_TEXT );

SelectObject( m_hbmDC, m_hBitmap );

g_log->Log( "Bitmap Created\n" );

}

cBitmap::~cBitmap()
{
g_log->Log( "Bitmap Deleted\n" );
DeleteObject( m_hbmDC );
}



///////////////////////////////////

void cBitmap::DrawGreyPixel( int x, int y, WORD intensity )
{
/// a WORD is passed to enable future modification...
if ( intensity > 255 )
intensity = 255;

COLORREF ret = SetPixel( m_hbmDC, x, y, RGB((BYTE)intensity, (BYTE)intensity, (BYTE)intensity ) );

if ( ret == 1 )
MessageBox( NULL, "Error", "cBitmap::DrawGreyPixel", MB_OK );

}


void cBitmap::SaveBitmap()
{
g_log->Log( "Bitmap saved\n" );
PBITMAPINFO pbmi = CreateBitmapInfoStruct();
CreateBMPFile( "output.bmp", pbmi );
}

HBITMAP cBitmap::GetHandle()
{
return m_hBitmap;
}

PBITMAPINFO cBitmap::CreateBitmapInfoStruct()
{
BITMAP bmp;
PBITMAPINFO pbmi;
WORD cClrBits;

// Retrieve the bitmap color format, width, and height.
GetObject(m_hBitmap, sizeof(BITMAP), (LPSTR)&bmp);

// Convert the color format to a count of bits.
cClrBits = (WORD)(bmp.bmPlanes * bmp.bmBitsPixel);
if (cClrBits == 1)
cClrBits = 1;
else if (cClrBits <= 4)
cClrBits = 4;
else if (cClrBits <= 8)
cClrBits = 8;
else if (cClrBits <= 16)
cClrBits = 16;
else if (cClrBits <= 24)
cClrBits = 24;
else cClrBits = 32;

// Allocate memory for the BITMAPINFO structure. (This structure
// contains a BITMAPINFOHEADER structure and an array of RGBQUAD
// data structures.)

if (cClrBits != 24)
pbmi = (PBITMAPINFO) LocalAlloc(LPTR,
sizeof(BITMAPINFOHEADER) +
sizeof(RGBQUAD) * (1<< cClrBits));

// There is no RGBQUAD array for the 24-bit-per-pixel format.

else
pbmi = (PBITMAPINFO) LocalAlloc(LPTR,
sizeof(BITMAPINFOHEADER));

// Initialize the fields in the BITMAPINFO structure.

pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
pbmi->bmiHeader.biWidth = bmp.bmWidth;
pbmi->bmiHeader.biHeight = bmp.bmHeight;
pbmi->bmiHeader.biPlanes = bmp.bmPlanes;
pbmi->bmiHeader.biBitCount = bmp.bmBitsPixel;
if (cClrBits < 24)
pbmi->bmiHeader.biClrUsed = (1<<cClrBits);

// If the bitmap is not compressed, set the BI_RGB flag.
pbmi->bmiHeader.biCompression = BI_RGB;

// Compute the number of bytes in the array of color
// indices and store the result in biSizeImage.
// For Windows NT, the width must be DWORD aligned unless
// the bitmap is RLE compressed. This example shows this.
// For Windows 95/98/Me, the width must be WORD aligned unless the
// bitmap is RLE compressed.
pbmi->bmiHeader.biSizeImage = ((pbmi->bmiHeader.biWidth * cClrBits +31) & ~31) /8
* pbmi->bmiHeader.biHeight;
// Set biClrImportant to 0, indicating that all of the
// device colors are important.
pbmi->bmiHeader.biClrImportant = 0;
return pbmi;
}

void cBitmap::CreateBMPFile( LPTSTR pszFile, PBITMAPINFO pbmi )
{
HANDLE hf; // file handle
BITMAPFILEHEADER hdr; // bitmap file-header
PBITMAPINFOHEADER pbih; // bitmap info-header
LPBYTE lpBits; // memory pointer
DWORD dwTotal; // total count of bytes
DWORD cb; // incremental count of bytes
BYTE *hp; // byte pointer
DWORD dwTmp;

pbih = (PBITMAPINFOHEADER) pbmi;// pbi;
lpBits = (LPBYTE) GlobalAlloc(GMEM_FIXED, pbih->biSizeImage);


// Retrieve the color table (RGBQUAD array) and the bits
// (array of palette indices) from the DIB.
GetDIBits(m_hbmDC, m_hBitmap, 0, (WORD) pbih->biHeight, lpBits, pbmi, DIB_RGB_COLORS);

// Create the .BMP file.
hf = CreateFile(pszFile,
GENERIC_READ | GENERIC_WRITE,
(DWORD) 0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
(HANDLE) NULL);

hdr.bfType = 0x4d42; // 0x42 = "B" 0x4d = "M"
// Compute the size of the entire file.
hdr.bfSize = (DWORD) (sizeof(BITMAPFILEHEADER) + pbih->biSize + pbih->biClrUsed * sizeof(RGBQUAD) + pbih->biSizeImage);
hdr.bfReserved1 = 0;
hdr.bfReserved2 = 0;

// Compute the offset to the array of color indices.
hdr.bfOffBits = (DWORD) sizeof(BITMAPFILEHEADER) + pbih->biSize + pbih->biClrUsed * sizeof (RGBQUAD);

// Copy the BITMAPFILEHEADER into the .BMP file.
WriteFile(hf, (LPVOID) &hdr, sizeof(BITMAPFILEHEADER), (LPDWORD) &dwTmp, NULL) ;

// Copy the BITMAPINFOHEADER and RGBQUAD array into the file.
WriteFile(hf, (LPVOID) pbih, sizeof(BITMAPINFOHEADER) + pbih->biClrUsed * sizeof (RGBQUAD), (LPDWORD) &dwTmp, ( NULL)) ;

// Copy the array of color indices into the .BMP file.
dwTotal = cb = pbih->biSizeImage;
hp = lpBits;

WriteFile(hf, (LPSTR) hp, (int) cb, (LPDWORD) &dwTmp,NULL);

// Close the .BMP file.

CloseHandle(hf);

// Free memory.
GlobalFree((HGLOBAL)lpBits);
}





You can see cBitmap::GetHandle() just returns the HBITMAP m_hBitmap.

To use it I do this:

cBitmap* g_bitmap = NULL;
g_bitmap = new cBitmap();
g_bitmap->DrawGreyPixel(10,10, 255 ); // white dot

// attempt to get the handle for using in WM_PAINT
case WM_PAINT:
if ( g_bitmap )
{
hdc = BeginPaint(hWnd, &ps);
hdcMem = CreateCompatibleDC(hdc);
SelectObject(hdcMem, g_bitmap->GetHandle());
GetObject(g_bitmap->GetHandle(), sizeof(bm), &bm);
BitBlt(hdc, 100, 100, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCCOPY);
DeleteDC(hdcMem);
EndPaint(hWnd, &ps);
}
break;

g_bitmap->SaveBitmap();
SAFE_DELETE(g_bitmap);

Share this post


Link to post
Share on other sites
Ok, given that you are using SetPixel(), CreateDIBSection() is overkill for your purposes. CreateCompatibleBitmap() is probably just fine (and if that doesnt work then CreateBitmap()).

As for why GetHandle() isnt working in your WM_PAINT handler: a bitmap may only be selected into a single DC at a time. In WM_PAINT you are trying to select your bitmap into hdcMem even though it is already selected into m_hbmDC. I suggest adding a member function to your bitmap class called Draw() or somesuch where you pass it a DC that you wont to draw the bitmap to. In this case m_hbmDC is already acting as your memory DC.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!