Page 1 of 1

How to load Windows DIB in Magick++? [SOLVED]

Posted: 2010-05-11T21:25:58-07:00
by anotherprogrammer123
Hi,

I am new to Windows programming and was given a HANDLE to a Windows DIB image and a BITMAPINFOHEADER structure.
How can I load this DIB image into an ImageMagick image using Magick++ on Visual C++ 2008 in Windows Vista (using less memory if possible)?

Thanks in advance!

Re: How to load Windows DIB?

Posted: 2010-05-13T10:45:52-07:00
by anotherprogrammer123
*bump*

Re: How to load Windows DIB?

Posted: 2010-05-13T11:10:02-07:00
by fmw42
looks like none of the users know about this. so try posting to the developers forum

Re: How to load Windows DIB?

Posted: 2010-05-13T16:09:34-07:00
by billbell52
I am fairly sure the dib and bmp image format are the same thing. VS recognizes bmp format. Not sure on dib. Try changing the type to bmp and load it into paint to be sure.

Re: How to load Windows DIB?

Posted: 2010-05-13T16:33:18-07:00
by fmw42
yes, they can be the same. see http://en.wikipedia.org/wiki/BMP_file_format

so just load it with .bmp into IM just like any other image format.

I am not a Windows or API programmer (I script command line IM on Mac), so I cannot help further.

Re: How to load Windows DIB?

Posted: 2010-05-13T16:42:58-07:00
by anotherprogrammer123
Thanks for the responses, but ImageMagick gives me a "resource limit" error when I try to load the handle into a BLOB.
My machine had 2GB of RAM so something is wrong. If no one knows, I can try posting in the developer's forum.

Re: How to load Windows DIB?

Posted: 2010-05-13T16:54:41-07:00
by fmw42
try loading a known good image and see if you are using the right API calls. perhaps it is not the image, but your code.

Re: How to load Windows DIB?

Posted: 2010-08-19T20:12:32-07:00
by anotherprogrammer123
Hi guys,

I am still totally confused on this.
First, I define this function:

Code: Select all

unsigned char *LoadBitmapFile(char *filename, BITMAPINFOHEADER *bitmapInfoHeader)
{
FILE *filePtr; //our file pointer
BITMAPFILEHEADER bitmapFileHeader; //our bitmap file header
unsigned char *bitmapImage;  //store image data
int imageIdx=0;  //image index counter
unsigned char tempRGB;  //our swap variable

//open filename in read binary mode
filePtr = fopen(filename,"rb");
if (filePtr == NULL)
return NULL;

//read the bitmap file header
fread(&bitmapFileHeader, sizeof(BITMAPFILEHEADER),1,filePtr);

//verify that this is a bmp file by check bitmap id
if (bitmapFileHeader.bfType !=0x4D42)
{
fclose(filePtr);
return NULL;
}

//read the bitmap info header
fread(bitmapInfoHeader, sizeof(BITMAPINFOHEADER),1,filePtr);

//move file point to the begging of bitmap data
fseek(filePtr, bitmapFileHeader.bfOffBits, SEEK_SET);

//allocate enough memory for the bitmap image data
bitmapImage = (unsigned char*)malloc(bitmapInfoHeader->biSizeImage);

//verify memory allocation
if (!bitmapImage)
{
free(bitmapImage);
fclose(filePtr);
return NULL;
}

//read in the bitmap image data
fread(bitmapImage,bitmapInfoHeader->biSizeImage,1,filePtr);

//make sure bitmap image data was read
if (bitmapImage == NULL)
{
fclose(filePtr);
return NULL;
}

//swap the r and b values to get RGB (bitmap is BGR)
for (imageIdx = 0;imageIdx < bitmapInfoHeader->biSizeImage;imageIdx+=3)
{
tempRGB = bitmapImage[imageIdx];
bitmapImage[imageIdx] = bitmapImage[imageIdx + 2];
bitmapImage[imageIdx + 2] = tempRGB;
}

//close file and return bitmap image data
fclose(filePtr);
return bitmapImage;
}

Next, I run this code:

Code: Select all

BITMAPINFOHEADER* header = new BITMAPINFOHEADER;
unsigned char* pixels = LoadBitmapFile("D:/img.bmp", header);
Magick::Image img;
img.read(Magick::Blob(pixels, header->biSizeImage));
img.write("D:/twain.bmp");
Which gives me access violation errors while img.read() is being run.

So, I think that the BITMAPINFOHEADER and pixels need to be contiguous and loaded together, so I do this:

Code: Select all

unsigned char* fullImg = (unsigned char*)malloc(sizeof(BITMAPINFOHEADER) + header->biSizeImage);
memcpy((void*)fullImg, (void*)header, sizeof(BITMAPINFOHEADER));
memcpy((void*)(fullImg + sizeof(BITMAPINFOHEADER)), pixels, header->biSizeImage);
unsigned char* pixelscpy = fullImg + sizeof(BITMAPINFOHEADER);
;copy to imagemagick
Magick::Image img;
img.read(Magick::Blob(fullImg, sizeof(BITMAPINFOHEADER) + header->biSizeImage));
img.write("D:/twain.bmp");
This time, no errors happen, but twain.bmp is an empty file!


Can anybody help me out?

Thank you in advance!

Re: How to load Windows DIB?

Posted: 2010-08-20T08:56:12-07:00
by el_supremo
This is an edited version of a C function I wrote to save a screen capture image (which is received as a handle to a bitmap).
Perhaps you can massage this to do what you need.

Pete

Code: Select all

#include <windows.h>
#include <wand/magick_wand.h>

void  SaveBitmap(char *filename,HBITMAP hBitmap)
{
	HDC        hdc=NULL;
	unsigned char *pBuf=NULL;
	BITMAPINFO bmp_inf;

	do {
		hdc = GetDC(NULL);
		ZeroMemory(&bmp_inf,sizeof(BITMAPINFO));
		bmp_inf.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
		// Passing a NULL gets just the header info containing 
		// the width and height which are used to compute the 
		// size required for a malloc
		GetDIBits(hdc,hBitmap,0,0,NULL,&bmp_inf,DIB_RGB_COLORS);

		if(bmp_inf.bmiHeader.biSizeImage <= 0)
			bmp_inf.bmiHeader.biSizeImage=bmp_inf.bmiHeader.biWidth*abs(bmp_inf.bmiHeader.biHeight)*(bmp_inf.bmiHeader.biBitCount+7)/8;

		if((pBuf = malloc(bmp_inf.bmiHeader.biSizeImage+sizeof(BITMAPINFOHEADER))) == NULL)
		{
			MessageBox( NULL, "Unable to Allocate Bitmap Memory", "Error", MB_OK|MB_ICONERROR);
			break;
		}
		bmp_inf.bmiHeader.biCompression=BI_RGB;
		// Now get the whole image
		GetDIBits(hdc,hBitmap,0,bmp_inf.bmiHeader.biHeight,pBuf+sizeof(BITMAPINFOHEADER), &bmp_inf, DIB_RGB_COLORS);
		{
			int i;
			unsigned char *p,*q;
			MagickWand *wand;

			// Copy the separate bmp_inf into the DIB info header which is 
			// now contiguous with the image data in pBuf
			p = (unsigned char *)&bmp_inf;
			q = pBuf;
			for(i=0;i<sizeof(BITMAPINFOHEADER);i++)*q++ = *p++;

			MagickWandGenesis();

			wand = NewMagickWand();
			MagickReadImageBlob(wand,pBuf,bmp_inf.bmiHeader.biSizeImage+sizeof(BITMAPINFOHEADER));

			// Force it OFF - shouldn't need this but IM's DIB coder 
			// forces matte on if the bit depth is 32 (which it is with a screencap). 
			MagickSetImageMatte(wand,MagickFalse);


			// Write the file
			MagickWriteImage(wand,filename);

			wand = DestroyMagickWand(wand);
			MagickWandTerminus();
		}
	} while(0);
	if(hdc)     ReleaseDC(NULL,hdc); 
	if(pBuf)    free(pBuf); 

}

Re: How to load Windows DIB?

Posted: 2010-08-20T12:37:59-07:00
by anotherprogrammer123
Thanks so much guys.

I figured out the problem! I think I did pointer arithmetic wrong. I was also not taking into account the RGBQUAD structures.

Here is the new function. Hope it helps future searches out:

Code: Select all

int handleToImage(HANDLE bitmap, MYIMAGE &image) const
{
	//Get the header of the bitmap (stored at the start of the handle's address)
	BITMAPINFOHEADER* header = (BITMAPINFOHEADER*)GlobalLock(bitmap);

	//We will store the bitmap into a magick++ image and transfer this to the image
	Magick::Image magickImg;
	//calculates the total number of bytes stored in 'header' (the structure is: BITMAPINFOHEADER, one RGBQUAD structure for each possible color, pixel data)
	int length = header->biSize + (00000001 << header->biBitCount)*sizeof(RGBQUAD) + header->biSizeImage; //note that if we are working with uncompressed bitmaps (32 bits), header->biSizeImage might be 0
	//read the bitmap into a magick++ using a BLOB
	try { magickImg.read(Magick::Blob(header, length)); }
	catch (...) { return 1; }

	//Load the magick++ object into the image
	if (image.load(magickImg) != 0)
	{
		return 1; //failed reading
	}

	//Free the bitmap handle now that it is loaded into the image
	GlobalUnlock(bitmap); GlobalFree(bitmap);

	return 0;
}
It is working now!
Lesson: don't forget about the 2^biBitCount RGBQUAD structures.


Thanks again everyone.