Reputation: 2001
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:
dwFileBytes = 3128.
biSize = 40
biWidth = 32
biHeight = 32
biPlans = 1
biBitCount = 24
biCompression = 0
biSizeImage = 3074
Inspecting BITMAP after calling CreateBitmap(32, 32, 1, 24, lpBits):
bmType = 0
bmWidth = 32
bmHeight = 32
bmPlanes= 1
bmWidthBytes = 96
bmBitsPixel = 24
bmBits = 0
(This bitmap displays as black.)
Inspecting BITMAP after writing to file and calling LoadImage():
bmType = 0
bmWidth = 32
bmHeight = 32
bmPlanes= 1
bmWidthBytes = 128
bmBitsPixel = 32
bmBits = 0
(This bitmap displays correctly.)
I realize I'm getting into the weeds with details. Apologies! I'm stumped.
Upvotes: 0
Views: 1816
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
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