Saving a Bitmap

Started by
10 comments, last by Emmanuel Deloget 19 years, 4 months ago
Alright this topic has been covered, but i've searched and searched and haven't found a decent answer, or at least anything that's worked for me. I'm using some code that I snagged from CodeGuru here. it crashes on the line "int nColors = 1 << lpbi->biBitCount;" Anyway here's the full code im trying to use:

BOOL WriteDIB( LPTSTR szFile, HANDLE hDIB)
{
	BITMAPFILEHEADER	hdr;
	LPBITMAPINFOHEADER	lpbi;

	if (!hDIB)
		return FALSE;

	HANDLE file = CreateFile(szFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

	lpbi = (LPBITMAPINFOHEADER)hDIB;

	int nColors = 1 << lpbi->biBitCount;

	// Fill in the fields of the file header 
	hdr.bfType		= ((WORD) ('M' << 8) | 'B');	// is always "BM"
	hdr.bfSize		= GlobalSize (hDIB) + sizeof( hdr );
	hdr.bfReserved1 	= 0;
	hdr.bfReserved2 	= 0;
	hdr.bfOffBits		= (DWORD) (sizeof( hdr ) + lpbi->biSize +
						nColors * sizeof(RGBQUAD));

	// Write the file header 
	DWORD numwritten;
	WriteFile(file, &hdr, sizeof(hdr), &numwritten, NULL);

	// Write the DIB header and the bits 
	DWORD numwrittentwo;
	WriteFile(file, lpbi, GlobalSize(hDIB), &numwrittentwo, NULL);

	CloseHandle(file);

	return TRUE;
}


//// Here's where I call the function


	BITMAPINFO info;
	ZeroMemory( &info.bmiHeader, sizeof(BITMAPINFOHEADER) );
	info.bmiHeader.biWidth=500; // Set size you need
	info.bmiHeader.biHeight=500; // Set size you need
	info.bmiHeader.biPlanes=1;
	info.bmiHeader.biBitCount=24; // Can be 8, 16, 32 bpp or
	// other number
	info.bmiHeader.biSizeImage=0;
	info.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
	info.bmiHeader.biClrUsed= 0;
	info.bmiHeader.biClrImportant= 0;
	
	VOID *pvBits;
	HDC tempdc = CreateCompatibleDC(hdc);
	HBITMAP dib = CreateDIBSection(tempdc, &info, DIB_RGB_COLORS, &pvBits, NULL, 0);
	SelectObject(tempdc, dib);
	BitBlt(tempdc, 0, 0, 32, 32, bmpdc, 0, 0, SRCCOPY);
	WriteDIB("out.bmp", dib);

?
Advertisement
Hello,


You cannot treat a handle as a memory location. If hDib was a handle to some memory (allocated with GlobalAlloc()) you would have to call GlobalLock() (and GlobalUnlock() when you do not need it anymore) i.e. replacing
lpbi = (LPBITMAPINFOHEADER)hDIB;

with
lpbi = (LPBITMAPINFOHEADER)(GlobalLock(hDIB));// ... do whatever you wantGlobalUnlock(hDIB);



In your case, the hDib is not even a handle to some global memory. It is a GDI object - something completely different. It will be easier to direcly use you BITMAPINFO structure and your pvBits pointer. If you don't want to do this then you'll have to call GetDIBits() to get the needed informations in your WriteDIB() function - which is silly since you have these informations when you create your hDib.

HTH,
Ok now i have

BOOL WriteDIB( LPTSTR szFile, HBITMAP hbmp, HDC hdc, BITMAPINFO bmpi){	BITMAPFILEHEADER	hdr;	if (!hbmp)		return FALSE;	HANDLE file = CreateFile(szFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);	int nColors = 1 << bmpi.bmiHeader.biBitCount;	// Fill in the fields of the file header 	hdr.bfType		= ((WORD) ('M' << 8) | 'B');	// is always "BM"	hdr.bfSize		= GlobalSize (hbmp) + sizeof( hdr );	hdr.bfReserved1 	= 0;	hdr.bfReserved2 	= 0;	hdr.bfOffBits		= (DWORD) (sizeof( hdr ) + bmpi.bmiHeader.biSize +						nColors * sizeof(RGBQUAD));	// Write the file header 	DWORD numwritten, numwrittentwo, numwrittenthree;	WriteFile(file, &hdr, sizeof(hdr), &numwritten, NULL);	// Write the DIB header and the bits 	WriteFile(file, &bmpi.bmiHeader, sizeof(bmpi.bmiHeader), &numwrittentwo, NULL);	LPVOID *pvBits;	GetDIBits(hdc, hbmp, 0, 32, pvBits, &bmpi, DIB_RGB_COLORS);	WriteFile(file, pvBits, sizeof(pvBits), &numwrittenthree, NULL);	CloseHandle(file);	return TRUE;}


but it only seems to print the header, it's one line starting with BM and the file is like 1 kb, opens to default blank view in mspaint
You're writing sizeof( pvBits ) bytes to the file. As pvBits is a pointer you're writing only (in your case) 4 bytes.

You need to calculate the actual byte size of the image data. Don't forget to take the line padding in account!

Fruny: Ftagn! Ia! Ia! std::time_put_byname! Mglui naflftagn std::codecvt eY'ha-nthlei!,char,mbstate_t>

so let's see, for a 32x32 bitmap, at 24 bit, (since right now i have info.bmiHeader.biSizeImage=0;) i should have info.bmiHeader.biSizeImage = (32*sizeof(RGBQUAD))*(32*sizeof(RGBQUAD))??
Quote:Original post by Funkymunky
so let's see, for a 32x32 bitmap, at 24 bit, (since right now i have info.bmiHeader.biSizeImage=0;) i should have info.bmiHeader.biSizeImage = (32*sizeof(RGBQUAD))*(32*sizeof(RGBQUAD))??


No.

The correct formula to compute the size of an Windows DIB is :

int linesize = ((info.bmiHeader.biWidth + 3) & (~3));info.bmiHeader.biSizeImage = linesize * info.bmiHeader.biHeight * info.bmiHeader.biBitCount / 8;


linesize is the smallest multiple of 4 which is equal or greater than width - the line padding Endurion just talked about. This is one of the funny thing about Windows DIB.

HTH,
Ok it's still only spitting out
BM□□□□□□□□6□□□(□□□ □□ □□□□□□□□□□□□
□□□□□□□□□□□□□□□□□□

in mspaint it's a 32 by 32 24 color plain white bitmap (bits dont get written?)

in paint shop pro it's a torn image, a bunch of static

*UPDATE*

ive changed WriteFile(file, pvBits, sizeof(pvBits), &numwrittenthree, NULL); to WriteFile(file, &pvBits, bmpi.bmiHeader.biSizeImage, &numwrittenthree, NULL);

now it spits out a 4k image, same results (white and torn) but now there's some garbage after those first two lines.

...help?
Quote:Original post by Funkymunky
*UPDATE*

ive changed WriteFile(file, pvBits, sizeof(pvBits), &numwrittenthree, NULL); to WriteFile(file, &pvBits, bmpi.bmiHeader.biSizeImage, &numwrittenthree, NULL);

now it spits out a 4k image, same results (white and torn) but now there's some garbage after those first two lines.

...help?


&pvBits points tp pvBits, while pvBits points to your bits. You should use:

WriteFile(file, pvBits, bmpi.bmiHeader.biSizeImage, &numwrittenthree, NULL);<s>WriteFile(file, &pvBits, bmpi.bmiHeader.biSizeImage, &numwrittenthree, NULL);</s>


HTH,
tried that, it just writes the 1k file with only the two lines.
Ok...

Let's do it again:

Quote:Original post by Funkymunky
Ok now i have

BOOL WriteDIB( LPTSTR szFile, HBITMAP hbmp, HDC hdc, BITMAPINFO bmpi){  BITMAPFILEHEADER	hdr;    if (!hbmp) return FALSE;    HANDLE file = CreateFile(szFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);    int nColors = 1 << bmpi.bmiHeader.biBitCount;  // Fill in the fields of the file header   hdr.bfType		= ((WORD) ('M' << 8) | 'B');	// is always "BM"  hdr.bfSize		= GlobalSize (hbmp) + sizeof( hdr );  hdr.bfReserved1 	= 0;  hdr.bfReserved2 	= 0;  hdr.bfOffBits		= (DWORD) (sizeof( hdr ) + bmpi.bmiHeader.biSize +						nColors * sizeof(RGBQUAD));  // Write the file header   DWORD numwritten, numwrittentwo, numwrittenthree;  WriteFile(file, &hdr, sizeof(hdr), &numwritten, NULL);  // Write the DIB header and the bits   WriteFile(file, &bmpi.bmiHeader, sizeof(bmpi.bmiHeader), &numwrittentwo, NULL);  LPVOID *pvBits;  GetDIBits(hdc, hbmp, 0, 32, pvBits, &bmpi, DIB_RGB_COLORS); // WRONG (1)  WriteFile(file, pvBits, sizeof(pvBits), &numwrittenthree, NULL); // WRONG (2)  CloseHandle(file);  return TRUE;}

but it only seems to print the header, it's one line starting with BM and the file is like 1 kb, opens to default blank view in mspaint


FIRST: I assume that you correctly computed bmpi.bmiHeader.biSizeImage before you entered in the function.

WRONG (1) : GetDIBits() won't allocate the memory for you. For some reason, your program do not crash - because the bitmap is small enough, probably, meaning that you are crashing your memory. You'll have to do:
pvBits = (LPVOID) new char[bmpi.bmiHeader.biSizeImage];

Then you can call GetDIBits() as you called it. Don't forget to delete [] the memory.

WRONG (2) : sizeof(pvBits) is the size of the pointer - which is really sizeof(LPVOID) - on intel 32 architectures, it is equal to 4. You must use bmpi.bmiHeader.biSizeImage again here:

WriteFile(file, pvBits, bmpi.bmiHeader.biSizeImage, &numwrittenthree, NULL);

LAST: as I already said, since you are creating your hBitmap right before calling the function, you might as well do:

BOOL WriteDIB(LPCTSTR szFileName, LPBITMAPINFO pBm, LPVOID pBits){  HFILE hFile = CreateFile(szFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);  // make sure pBm->bmiHeader.biSizeImage is computed  int ls = (pBm->bmiHeader.biWidth + 3) & (~3);  pBm->bmiHeader.biSizeImage = ls * pBm->bmiHeader.biHeight * pBm->bmiHeader.biBitCount / 8;  // if we are writing a 8 bit bitmap we must remember of the palette  // - 256 RGBQUAD that are stored after pBm->bmiHeader. We are not handling  // them in this code  BITMAPFILEHEADER hdr;  hdr.bfType		= ((WORD) ('M' << 8) | 'B');	// is always "BM"  hdr.bfSize		= sizeof(hdr) + sizeof(BITMAPINFOHEADER);  hdr.bfReserved1 	= 0;  hdr.bfReserved2 	= 0;  hdr.bfOffBits		= hdr.bfSize;  DWORD dwByteCount;  WriteFile(hFile, &hdr, sizeof(hdr), &dwByteCount, NULL);  WriteFile(hFile, pBm, pBm->bmiheader.biSize, &dwByteCount, NULL);  WriteFile(hFile, pBits, pBm->bmiheader.biSizeImage, &dwByteCount, NULL);  CloseHandle(hFile);  return TRUE;}

You'll want to add the error handling code.
HTH,

This topic is closed to new replies.

Advertisement