Sign in to follow this  

Saving a Bitmap

This topic is 4744 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

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);

?

Share this post


Link to post
Share on other sites
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 want
GlobalUnlock(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,

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
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,

Share this post


Link to post
Share on other sites
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?

Share this post


Link to post
Share on other sites
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,

Share this post


Link to post
Share on other sites
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:
[code]
WriteFile(file, pvBits, bmpi.bmiHeader.biSizeImage, &numwrittenthree, NULL);[code]

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,

Share this post


Link to post
Share on other sites

This topic is 4744 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.

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