Advertisement

MFC, DC and Bitmap question...

Started by April 20, 2002 11:17 PM
5 comments, last by Matthew02 22 years, 8 months ago
I am trying to save the contents of an SDI document window to a .bmp file. I know nothing about MFC, so this is just what I figured should work (and I'm sure I'm wrong). I've created a menu item called to save the bitmap and I've added a function for it in the CView class of my application. I don't know if this is the right class to put it in or not. Also, I'm not sure if I am getting the right DC. I have found that the GetPixel calls always return -1 and that leads me to believe that I am using the wrong DC. Can someone tell me what is wrong with this code or if there is a more simple way to do this? The j loop goes through about 315 iterations and then the program crashes. If I try to break out of the loop at that time, the third call to WriteFile fails. In case you need to know, the width and height are usually 1260 and 945 respectively. I really don't have a clue how to do what I want to do and this was the best that I could come up with. Thanks so much for your help.
            
void msg(const int x, const char* message = NULL);
void CPowerNetworkCaptureView::OnFileSaveBitmap() 
{
	// grab the device context and get it's dimensions

	CClientDC ClientDC(this);
	OnPrepareDC (&ClientDC);
	OnDraw(&ClientDC);
	CSize ClientSize = ClientDC.GetWindowExt();
	DWORD width = ClientSize.cx;
	DWORD height = ClientSize.cy;
	DWORD pitch = width * 3;
	
	
	// set up some bitmap information

	WORD  fileID   = ((WORD) ('M' << 8) | 'B');
	DWORD fileSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + (height * pitch);
	WORD  infoBPP  = 24;
	
	
	// initialize bmfHeader and bmiHeader structures

	BITMAPFILEHEADER bmfHeader;
	BITMAPINFOHEADER bmiHeader;
	
	memset(&bmfHeader, 0, sizeof(BITMAPFILEHEADER));
	memset(&bmiHeader, 0, sizeof(BITMAPINFOHEADER));
	
	bmfHeader.bfType = fileID;
	bmfHeader.bfSize = fileSize;
	bmfHeader.bfOffBits = 54;
	
	bmiHeader.biSize = 40;
	bmiHeader.biWidth = width;
	bmiHeader.biHeight = height;
	bmiHeader.biBitCount = infoBPP;
	
	
	// get pixel data from the DC

	COLORREF color;		// stored as 0x00bbggrr

	BYTE (*pixels)[3] = new BYTE[width*height][3];
	
	int i=0;
	for (int j=height-1; j>=0; j--)	{
		for (int k=0; k<width; k++)
		{
			BYTE* pixel = pixels[i*pitch + k];
			color = ClientDC.GetPixel(k, j);
			*pixel++ = ((BYTE) (color >> 16) & 0xff);
			*pixel++ = ((BYTE) (color >> 8) & 0xff);
			*pixel   = ((BYTE) color & 0xff);
		}
		i++;
	}
	
	
	// write the bitmap to a file

	DWORD dwWritten;
	HANDLE fh;
	LPSTR lpFileName = "c:\\test.bmp";
	
	fh = CreateFile(lpFileName,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL|FILE_FLAG_SEQUENTIAL_SCAN,NULL);
	if (fh == INVALID_HANDLE_VALUE)
	{
		MessageBox("Unable to create bitmap file.", "Error!", MB_ICONERROR);
		return;
	}
	
	WriteFile(fh, (LPSTR)&bmfHeader, sizeof(BITMAPFILEHEADER), &dwWritten, NULL);
	WriteFile(fh, (LPSTR)&bmiHeader, sizeof(BITMAPINFOHEADER), &dwWritten, NULL);
	WriteFile(fh, pixels, width * pitch, &dwWritten, NULL);
	
	CloseHandle(fh);
	
	
	// memory cleanup

	delete [] pixels;
	pixels = 0;
}
  
[edited by - matthew02 on April 21, 2002 1:39:48 AM]
Can''t you just use SelectObject to retrieve the current bitmap in the DC, then GetObject to get BITMAP for it, and use the bitmap bits in that struct instead of reading the bitmap pixel-by-pixel?

I can''t be bothered to see what''s wrong with the posted code at this moment.
---visit #directxdev on afternet <- not just for directx, despite the name
Advertisement
Thanks for your suggestion. The problem is, I really don''t know how to use SelectObject and GetObject. I don''t know anything about MFC and the help files aren''t doing their job for me. The Win32 counterparts aren''t helping me either. I''ve tried what you recommended, but I''m not really sure that I''m doing it correctly. If you have time later, maybe you could take a look at what I came up with. Thanks.

  void CPowerNetworkCaptureView::OnFileSaveBitmap() {	// grab the device context and get it''s dimensions	CClientDC ClientDC(this);	OnPrepareDC(&ClientDC);	OnDraw(&ClientDC);	CSize ClientSize = ClientDC.GetWindowExt();	CBitmap ClientBitmap;	ClientBitmap.CreateCompatibleBitmap(&ClientDC, ClientSize.cx, ClientSize.cy);	ClientDC.SelectObject(&ClientBitmap);	BITMAP bitmap;	ClientBitmap.GetObject(sizeof(BITMAP), (LPVOID) &bitmap);		DWORD width  = bitmap.bmWidth;;	DWORD height = bitmap.bmHeight;	DWORD pitch  = bitmap.bmWidthBytes;	// size of one scan line (stride)	WORD  bpp    = bitmap.bmBitsPixel;			// set up some bitmap information	WORD  fileID   = ((WORD) (''M'' << 8) | ''B'');	DWORD fileSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + (height * pitch);			// initialize bmfHeader and bmiHeader structures	BITMAPFILEHEADER bmfHeader;	BITMAPINFOHEADER bmiHeader;		memset(&bmfHeader, 0, sizeof(BITMAPFILEHEADER));	memset(&bmiHeader, 0, sizeof(BITMAPINFOHEADER));		bmfHeader.bfType = fileID;	bmfHeader.bfSize = fileSize;	bmfHeader.bfOffBits = 54;		bmiHeader.biSize = 40;	bmiHeader.biWidth = width;	bmiHeader.biHeight = height;	bmiHeader.biBitCount = bpp;			// write the bitmap to a file	DWORD dwWritten;	HANDLE fh;	LPSTR lpFileName = "c:\\test.bmp";		fh = CreateFile(lpFileName,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL|FILE_FLAG_SEQUENTIAL_SCAN,NULL);	if (fh == INVALID_HANDLE_VALUE)	{		MessageBox("Unable to create bitmap file.", "Error!", MB_ICONERROR);		return;	}		WriteFile(fh, (LPSTR)&bmfHeader, sizeof(BITMAPFILEHEADER), &dwWritten, NULL);	WriteFile(fh, (LPSTR)&bmiHeader, sizeof(BITMAPINFOHEADER), &dwWritten, NULL);	WriteFile(fh, bitmap.bmBits, height * pitch, &dwWritten, NULL);	    CloseHandle(fh);}  
SelectObject (the Win32 version, not MFC one) returns the old object. You need to use GetObject on that bitmap, not on the bitmap you just created.

May I also suggest you use windowsx.h and replace SelectObject with SelectBitmap and so on? Look into that header to see what it does for you.
---visit #directxdev on afternet <- not just for directx, despite the name
I am still having problems. I've looked through windowsx.h, but I don't know what functions to use or how to use them to get what I want. My call to GetObject() always fails and GetLastError() returns 0 for the error code. I've posted the relevant code below. Could you take a look at it and see if I am doing the right thing? Thanks.


      void msg(const int x, const char* message = NULL);void CPowerNetworkCaptureView::OnFileSaveBitmap() {	// grab the device context and get it's dimensions	CClientDC ClientDC(this);	OnPrepareDC(&ClientDC);	OnDraw(&ClientDC);	BITMAP bitmap;	HGDIOBJ hGdiObj = SelectObject(ClientDC.GetSafeHdc(), NULL);	if (!GetObject(hGdiObj, sizeof(BITMAP), (LPVOID) &bitmap))	{		DWORD dwError = GetLastError();		msg(dwError, "Error!");		return;	}   ...}    


[edited by - matthew02 on April 21, 2002 1:38:38 PM]
Is hGdiObj NULL? If yes, create and select a compatible bitmap like you did earlier just to keep Windows happy.
---visit #directxdev on afternet <- not just for directx, despite the name
Advertisement
hGdiObj is NULL, but even when I create a compatible bitmap, it remains NULL. hBitmap is not NULL, so I''m not sure exactly what the problem is. Thanks.


  void msg(const int x, const char* message = NULL);void CPowerNetworkCaptureView::OnFileSaveBitmap() {	// grab the device context and get it''s dimensions	CClientDC ClientDC(this);	OnPrepareDC(&ClientDC);	OnDraw(&ClientDC);	CSize ClientSize = ClientDC.GetWindowExt();	HBITMAP hBitmap = CreateCompatibleBitmap(ClientDC.GetSafeHdc(), ClientSize.cx, ClientSize.cy);msg(hBitmap == NULL, "hBitmap == NULL");	HGDIOBJ hGdiObj = SelectObject(ClientDC.GetSafeHdc(), hBitmap);msg(hGdiObj == NULL, "hGdiObj == NULL");	BITMAP bitmap;	if (!GetObject(hGdiObj, sizeof(BITMAP), (LPVOID) &bitmap))	{		DWORD dwError = GetLastError();		msg(dwError, "Error!");		return;	}  

This topic is closed to new replies.

Advertisement