从 bmp 文件数据创建 MFC CBitmap
Create MFC CBitmap from bmp file data
我有一个指向从 *.bmp 文件中读取的数据的空指针。 bmp 文件不再存在(现在有一个文件包含数百个这样的位图文件数据集)。如何使用此数据初始化 MFC CBitmap
?
我看到了 CBitmap::Create*
函数(例如,CreateBitmap()
、CreateCompatibleBitmap()
等),但它们要求我知道位图的高度和宽度,才能访问数据位等。我可以将数据写入磁盘,然后使用 ::LoadImage()
和 CBitmap::Attach()
加载位图,但我想在内存中执行此操作以提高性能。
谢谢!
更新(#2):
这是我的代码,根据 Constantine Georgiou 的评论和 post(谢谢!)的建议和简化。 CBitmap::CreateBitmap() 不再失败,但位图显示为黑色。
// 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;
以下代码将相同的数据写入文件,然后使用 ::LoadImage() 加载位图。这行得通。
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;
}
}
这是关于上述内容的跟踪消息。
创建图像前检查内存中的文件数据:
dwFileBytes = 3128.
- BMP HEADER:
biSize = 40
biWidth = 32
biHeight = 32
biPlans = 1
biBitCount = 24
biCompression = 0
biSizeImage = 3074
调用 CreateBitmap(32, 32, 1, 24, lpBits) 后检查 BITMAP:
bmType = 0
bmWidth = 32
bmHeight = 32
bmPlanes= 1
bmWidthBytes = 96
bmBitsPixel = 24
bmBits = 0
(此位图显示为黑色。)
写入文件并调用 LoadImage() 后检查 BITMAP:
bmType = 0
bmWidth = 32
bmHeight = 32
bmPlanes= 1
bmWidthBytes = 128
bmBitsPixel = 32
bmBits = 0
(此位图显示正确。)
我意识到我正在深入了解细节。道歉!我被难住了。
你的代码是正确的,尽管我宁愿稍微简化它——不需要复制这些结构,因为它们已经存在——只是一些类型转换和指针算法。例如:
// 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 (!m_IconBitmap.CreateBitmap(pBmpHdr->biWidth, pBmpHdr->biHeight, pBmpHdr->biPlanes,
pBmpHdr->biBitCount, lpBits))
{
// Handle the Error
}
(还没有真正测试过这段代码,但应该可以)
我不得不提一下,图像格式很不寻常,即每像素 16 位可能是 BGR 5-5-5 格式(每个分量 5 位)。每个像素两个字节,图像大小应该是 2048 字节 (32 x 32 x 2)。像这样的位图文件,由 Microsoft 工具创建,正好是 2102 字节,这是正确的(54 字节用于结构加上 2048 用于位图数据 - 没有调色板)。也许你应该将数据存储到一个文件中,然后用十六进制编辑器检查它们。
如果可以选择使用 GDI+,您可以使用 this answer 从内存数据缓冲区构建 HBITMAP
,最后将返回的 HBITMAP
附加到 MFC CBitmap
。这是原代码:
#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();
}
下面是关于如何将返回的 HBITMAP
附加到 CBitmap
的片段:
#include <afxwin.h>
// ...
CBitmap bitmap {};
bitmap.Attach(from_data(data));
我有一个指向从 *.bmp 文件中读取的数据的空指针。 bmp 文件不再存在(现在有一个文件包含数百个这样的位图文件数据集)。如何使用此数据初始化 MFC CBitmap
?
我看到了 CBitmap::Create*
函数(例如,CreateBitmap()
、CreateCompatibleBitmap()
等),但它们要求我知道位图的高度和宽度,才能访问数据位等。我可以将数据写入磁盘,然后使用 ::LoadImage()
和 CBitmap::Attach()
加载位图,但我想在内存中执行此操作以提高性能。
谢谢!
更新(#2):
这是我的代码,根据 Constantine Georgiou 的评论和 post(谢谢!)的建议和简化。 CBitmap::CreateBitmap() 不再失败,但位图显示为黑色。
// 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;
以下代码将相同的数据写入文件,然后使用 ::LoadImage() 加载位图。这行得通。
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;
}
}
这是关于上述内容的跟踪消息。
创建图像前检查内存中的文件数据:
dwFileBytes = 3128.
- BMP HEADER:
biSize = 40
biWidth = 32
biHeight = 32
biPlans = 1
biBitCount = 24
biCompression = 0
biSizeImage = 3074
调用 CreateBitmap(32, 32, 1, 24, lpBits) 后检查 BITMAP:
bmType = 0
bmWidth = 32
bmHeight = 32
bmPlanes= 1
bmWidthBytes = 96
bmBitsPixel = 24
bmBits = 0
(此位图显示为黑色。)
写入文件并调用 LoadImage() 后检查 BITMAP:
bmType = 0
bmWidth = 32
bmHeight = 32
bmPlanes= 1
bmWidthBytes = 128
bmBitsPixel = 32
bmBits = 0
(此位图显示正确。)
我意识到我正在深入了解细节。道歉!我被难住了。
你的代码是正确的,尽管我宁愿稍微简化它——不需要复制这些结构,因为它们已经存在——只是一些类型转换和指针算法。例如:
// 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 (!m_IconBitmap.CreateBitmap(pBmpHdr->biWidth, pBmpHdr->biHeight, pBmpHdr->biPlanes,
pBmpHdr->biBitCount, lpBits))
{
// Handle the Error
}
(还没有真正测试过这段代码,但应该可以)
我不得不提一下,图像格式很不寻常,即每像素 16 位可能是 BGR 5-5-5 格式(每个分量 5 位)。每个像素两个字节,图像大小应该是 2048 字节 (32 x 32 x 2)。像这样的位图文件,由 Microsoft 工具创建,正好是 2102 字节,这是正确的(54 字节用于结构加上 2048 用于位图数据 - 没有调色板)。也许你应该将数据存储到一个文件中,然后用十六进制编辑器检查它们。
如果可以选择使用 GDI+,您可以使用 this answer 从内存数据缓冲区构建 HBITMAP
,最后将返回的 HBITMAP
附加到 MFC CBitmap
。这是原代码:
#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();
}
下面是关于如何将返回的 HBITMAP
附加到 CBitmap
的片段:
#include <afxwin.h>
// ...
CBitmap bitmap {};
bitmap.Attach(from_data(data));