[win32 GDI] Displaying bitmaps... again

Started by
5 comments, last by Colin Jeanne 17 years, 2 months ago
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.
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 :)
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.
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
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.
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);
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.

This topic is closed to new replies.

Advertisement