[英]Creating HBITMAP from memory buffer
我有一个应用程序,它从数据库中加载一些 blob 数据,该数据库可以表示各种位图和图标的 png 格式或原始二进制数据。 这被存储在std::vector<unsigned char>
我正在使用CImageList
对象在树视图、工具栏图像等中显示各种图像,但问题是从内存中的数据创建位图变得模糊,好像在执行以下操作时缺少像素:
std::vector<unsigned char> bits;
HBITMAP hbitmap = CreateBitmap(16, 16, 1, 32, bits.data());
为了暂时解决这个问题,我只是将向量中的 data() 写入临时文件,然后使用 LoadImage 将其读回并从中创建 HBITMAP。 这非常有效,但是这无疑是一种无耻的黑客行为,我希望完全没有必要。
我在网上环顾四周,但没有找到任何关于如何从内存“正确”创建 hbitmaps 的很好的例子。 如果可能的话,我希望能够创建这些位图以添加到图像列表中,而无需任何文件 i/o 和有限数量的数据复制。
寻找最好的方法来做到这一点,显然 Windows 特定的代码很好。
更新:
根据 jdv 的回答,我开始使用 CreateCompatibleBitmap、CreateDIBitmap,最后是 CreateDIBSection。 所有这些最终都创建了可爱的黑色位图而不是之前的模糊位图,所以我一定又做错了,我的猜测是因为这个位图创建是在一个没有屏幕 dc 或窗口概念的对象中完成的,使用GetDC(NULL)
和CreateCompatibleDC(NULL)
不好。 示例代码:
BITMAPINFO bmi;
ZeroMemory(&bmi, sizeof(BITMAPINFO));
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biHeight = 16;
bmi.bmiHeader.biWidth = 16;
bmi.bmiHeader.biPlanes = 1;
HDC dc = CreateCompatibleDC(NULL);
HBITMAP hbitmap = CreateDIBSection(dc, &bmi, DIB_RGB_COLORS, (void**)blobData.GetMember<FILEDATAFIELD_DATA>().data(), NULL, 0);
我现在当然认为必须有一种更简单的方法来解决这个问题,也许是完全避免使用 HBITMAP 并直接使用CBitmap
类? 归结为将图像添加到CImageList
我CImageList
在使用CBitmap::FromHandle(HBITMAP hbitmap, COLORREF mask)
。 有谁知道从std::vector<unsigned char>
初始化CBitmap
对象的简单方法吗?
我会使用CreateCompatibleBitmap
,然后调用SetDIBits
用您的数据填充它。 这些是我见过的功能,SetDIBits 非常灵活,虽然冗长。
在我的 MFC 年中,由于可疑的性能问题,避免使用CreateBitmap
。
使用 GdiPlus 我得到了一些效果很好的东西,不需要拔牙!
Gdiplus::Bitmap* pBitmap = NULL;
IStream* pStream = NULL;
HRESULT hResult = ::CreateStreamOnHGlobal( NULL, TRUE, &pStream );
if(hResult == S_OK && pStream)
{
hResult = pStream->Write(&bits[0], ULONG(bits.size()), NULL);
if(hResult == S_OK)
pBitmap = Gdiplus::Bitmap::FromStream(pStream);
pStream->Release();
}
编辑:根据 Jegatheesh 更改
AJG85提供的答案略有不同,它使用SHCreateMemStream代替CreateStreamOnHGlobal
。 SHCreateMemStream
是作为 Windows Vista 中的公共 API 引入的,它提供了在内存中的现有区域上创建IStream
的好处,从而避免了额外的内存分配:
#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();
}
这个实现要么抛出一个_com_error
,要么返回一个HBITMAP
,它指的是从内存数据构造的图像。
当函数返回时,可以安全地释放内存缓冲区。 返回的HBITMAP
归调用者所有,需要通过调用DeleteObject来释放。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.