Steve A
Steve A

Reputation: 2001

Create MFC CBitmap from bmp file data

I have a void pointer to data that was read from a *.bmp file. The bmp file no longer exists (there is now one file that contains hundreds of these bitmap file datasets). How can I initialize an MFC CBitmap using this data?

I see the CBitmap::Create* functions (e.g., CreateBitmap(), CreateCompatibleBitmap(), etc), but they require that I know the bitmap's height and width, can get access to the data bits, etc. I can write the data to disk and then use ::LoadImage() and CBitmap::Attach() to load the bitmap, but I want to do this in memory to improve performance.

Thanks!

UPDATE (#2):

Here is my code, as suggested and simplified by Constantine Georgiou's comments and post (thank you!). CBitmap::CreateBitmap() no longer fails, but the bitmap displays as black.

// Bitmap File Header
LPBITMAPFILEHEADER pFileHdr = (LPBITMAPFILEHEADER)pFileData;
// Bitmap Info Header
LPBITMAPINFOHEADER pBmpHdr = (LPBITMAPINFOHEADER)((PCHAR)pFileData + sizeof(BITMAPFILEHEADER));
// Image Data
LPVOID lpBits = (LPVOID)((PCHAR)pFileData + pFileHdr->bfOffBits);
if(!bitmap.CreateBitmap(pBmpHdr->biWidth, pBmpHdr->biHeight, pBmpHdr->biPlanes, pBmpHdr->biBitCount, lpBits))
    bool bummer = true;

Here is code that writes the same data to a file and then loads the bitmap using ::LoadImage(). This works.

CFile file;
if(file.Open(sFilename, CFile::modeCreate | CFile::modeReadWrite))
{
    file.Write(pFileData, dwFileBytes);
    file.Close();

    HBITMAP hBitmap = (HBITMAP)::LoadImage(NULL, sFilename, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
    if(hBitmap)
    {
        if(!bitmap.Attach(hBitmap))
            bool ahHeck = true;
    }
}

Here are trace messages regarding the above.

Inspecting the file data in memory before creating the image:

Inspecting BITMAP after calling CreateBitmap(32, 32, 1, 24, lpBits):

(This bitmap displays as black.)

Inspecting BITMAP after writing to file and calling LoadImage():

(This bitmap displays correctly.)

I realize I'm getting into the weeds with details. Apologies! I'm stumped.

Upvotes: 0

Views: 1816

Answers (2)

IInspectable
IInspectable

Reputation: 51355

If using GDI+ is an option, you can construct an HBITMAP from an in-memory data buffer using essentially this answer, and finally attaching the returned HBITMAP to an MFC CBitmap. This is the original code:

#include <Shlwapi.h>
#include <atlimage.h>
#include <comdef.h>
#include <comip.h>

#include <vector>

#pragma comment(lib, "Shlwapi.lib")
#if defined(_DEBUG)
#    pragma comment(lib, "comsuppwd.lib")
#else
#    pragma comment(lib, "comsuppw.lib")
#endif


HBITMAP from_data(std::vector<unsigned char> const& data)
{
    if (data.empty())
    {
        _com_issue_error(E_INVALIDARG);
    }

    auto const stream { ::SHCreateMemStream(&data[0], static_cast<UINT>(data.size())) };
    if (!stream)
    {
        _com_issue_error(E_OUTOFMEMORY);
    }
    _COM_SMARTPTR_TYPEDEF(IStream, __uuidof(IStream));
    IStreamPtr sp_stream { stream, false };

    CImage img {};
    _com_util::CheckError(img.Load(sp_stream));

    return img.Detach();
}

And here is a snippet on how to attach the returned HBITMAP to a CBitmap:

#include <afxwin.h>

// ...

CBitmap bitmap {};
bitmap.Attach(from_data(data));

Upvotes: 0

Constantine Georgiou
Constantine Georgiou

Reputation: 3401

Your code is correct, although I would rather simplify it a little - no need to make copies of these structures, as they are already there - just some typecasting and pointer arithmetic. For example:

// Bitmap File Header
LPBITMAPFILEHEADER pFileHdr = (LPBITMAPFILEHEADER)pFileData;

// Bitmap Info Header
LPBITMAPINFOHEADER pBmpHdr = (LPBITMAPINFOHEADER)((PCHAR)pFileData + sizeof(BITMAPFILEHEADER));

// Image Data
LPVOID lpBits = (LPVOID)((PCHAR)pFileData + pFileHdr->bfOffBits);

The documentatiom clearly mentions that "bit count" is the number of bits per pixel. So, the call would be:

if (!m_IconBitmap.CreateBitmap(pBmpHdr->biWidth, pBmpHdr->biHeight, pBmpHdr->biPlanes, 
    pBmpHdr->biBitCount, lpBits))
{
    // Handle the Error
}

(haven't really tested this code, but it should work)

I have to mention though that the image format is rather unusual, ie 16 bits per pixel is probably a BGR 5-5-5 format (5 bits for each component). And with two bytes per pixel, the image size should rather be 2048 bytes (32 x 32 x 2). A bitmap file like this, created by Microsoft tools would be 2102 bytes exactly, which is correct (54 bytes for the structures plus 2048 for the bitmap data - no palette). Maybe you should store the data to a file and examine them with a hex editor.

Upvotes: 1

Related Questions