Monday, January 10, 2011

Converting a compressed BITMAP to a Magick++ Image

One problem that you may encounter while working with the Magick++ API (an API for both GraphicsMagick and ImageMagick) is trying to convert an compressed Windows BITMAP to a Magick++ image.
If you are interested in converting uncompressed BITMAPs, please see this guide.

The most practical approach is to use a Magick::Blob. Although there is overhead associated with allocating extra memory, the overhead associated with decompressing the pixel data is likely much higher.
Here is the code:



//! Transfers the given windows DIB (BGR format, no compression) to a Magick++ Image
/*!
* bmiHeader specifies information about the bitmap image (may have color table entries at end)\n
* bmihLength specifies the length of the bitmap header (which may be longer than
* sizeof(BITMAPINFOHEADER) because of the optional color table entries at the end)\n
* pixels contains the pixel buffer of the bitmap. For maximal efficiency, it is assumed that no other process modifies these pixels while this function is running\n
* length specifies the length of the pixel buffer (pixels)\n
* mImage is a pointer to the Magick++ image that will get the bitmap data. It is assumed that no other process can modify the image while this function is running\n
* \n
* \n
* A Magick++ exception may be thrown
*/
void CompressedBitmapToMagick(const BITMAPINFOHEADER* bmiHeader, size_t bmihLength, const BYTE* restrict pixels, size_t length, Magick::Image* magickImage)
{
assert(bmiHeader->biCompression != BI_RGB); //use the functions specified in this guide for much higher efficiency
assert(bmiHeader->biSizeImage != 0); //must be specified for compressed images


//! Since compression is a big overhead and manually dealing with it is /extremely/ difficult, we just use a BLOB

//We need align the data so that bitmap header is directly before the bitmap data (in DIB format)
void* pDIB = malloc(bmihLength + length);
if (pDIB == NULL)
{ //ran out of memory
throw "out of memory";
}
memcpy(pDIB, bmiHeader, bmihLength);
memcpy((void*)((char*)pDIB + bmihLength), (void*)pixels, length); //copy the pixel data

//! Read the DIB via a blob
Magick::Blob blob;
//Let this Blob "steal" the memory we just allocated
blob.updateNoCopy(pDIB, bmihLength + length, Magick::Blob::Allocator(Magick::Blob::MallocAllocator));
magickImage->read(blob);

//Note that the Blob calls free() for us
return;
}

No comments:

Post a Comment