我有一个应用程序,它从数据库中加载一些 blob 数据,这些数据可以表示 png 格式或各种位图和图标的原始二进制数据。这存储在 std::vector
中,
我正在使用 CImageList
对象在树视图、工具栏图像等中显示各种图像,但问题是从内存中的数据创建位图会变得模糊,就像在执行如下操作时缺少像素一样:
std::vector<unsigned char> bits;
HBITMAP hbitmap = CreateBitmap(16, 16, 1, 32, bits.data());
为了暂时解决此问题,我只需将向量中的 data() 写入临时文件,然后使用 LoadImage将其读回并从中创建 HBITMAP。这工作得很好,但是这无疑是一种无耻的黑客行为,我希望这应该是完全没有必要的。
我在网上浏览过,但没有找到任何关于如何“正确”从内存创建 hbitmap 的真正好的例子。我希望能够创建这些位图并将其添加到图像列表中,而无需任何文件 I/O,并且如果可能的话,也无需复制有限数量的数据。
寻找最好的方法来做到这一点,显然 Windows 特定的代码是可以的。
更新:
根据 jdv 的回答,我开始使用 CreateCompatibleBitmap、CreateDIBitmap,最后是 CreateDIBSection。所有这些最终都创建了可爱的黑色位图,而不是之前的模糊位图,所以我一定又做错了什么,我的猜测是,因为这个位图创建是在一个没有屏幕 dc 或使用 < 的窗口概念的对象中完成的。 code>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
时,我无论如何都会使用 CBitmap::FromHandle(HBITMAP hbitmap, COLORREF mask)
。有谁知道从 std::vector
初始化 CBitmap
对象的简单方法吗?
I have an application which loads some blob data out of a database which can represent png formatted or raw binary data for various bitmaps and icons. This is being stored in a std::vector<unsigned char>
I'm using CImageList
objects to display various images in tree views, toolbar images, etc but the problem is creating bitmaps from the data in memory are coming out fuzzy as if it's missing pixels when doing something like below:
std::vector<unsigned char> bits;
HBITMAP hbitmap = CreateBitmap(16, 16, 1, 32, bits.data());
To work around this issue for now I am simply writing out the data() in the vector to a temporary file and then using LoadImage to read it back in and creating the HBITMAP from that. This works perfectly however that is admittedly a shameless hack and should be completely unnecessary I would hope.
I've looked around online but haven't found any really good examples of how to "properly" create hbitmaps from memory. I would like to be able to create these bitmaps to be added to the image list without any file i/o and limited amounts of copying the data around if possible.
Looking for the best way to do this and obviously windows specific code is fine.
UPDATE:
Based on jdv's answer I began playing with CreateCompatibleBitmap, CreateDIBitmap, and finally CreateDIBSection. All of these ended up creating lovely black bitmaps instead of the previous fuzzy bitmaps so I must be doing something wrong again and my guess is since this bitmap creation is being done in an object that has no concept of the screen dc or window that using GetDC(NULL)
and CreateCompatibleDC(NULL)
are no good. Sample code:
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);
I am now of course thinking there has got to be a simpler way to go about this perhaps by avoiding the HBITMAP altogether and working directly with CBitmap
class? When it comes down to adding the image to the CImageList
I'm using CBitmap::FromHandle(HBITMAP hbitmap, COLORREF mask)
anyway. Does anyone know a simple way to initialize a CBitmap
object from a std::vector<unsigned char>
?
发布评论
评论(3)
我将使用
CreateCompatibleBitmap
,然后调用SetDIBits
用您的数据填充它。这些是我见过的有效函数,SetDIBits 非常灵活,尽管很冗长。在我的 MFC 岁月里,由于怀疑存在性能问题,
CreateBitmap
被避免使用。I'd use
CreateCompatibleBitmap
, and then callSetDIBits
to fill it with your data. These are functions I have seen to work, and SetDIBits is quite flexible, although verbose.In my MFC years,
CreateBitmap
was avoided due to suspected performance issues.使用 GdiPlus,我得到了一些效果非常好的东西,而且不需要拔掉任何牙齿!
编辑:根据 Jegatheesh 进行了更改
Using GdiPlus I got something that works pretty well and doesn't involve pulling any teeth!
Edit: Changed per Jegatheesh
与 答案略有不同">AJG85,采用 SHCreateMemStream 代替 CreateStreamOnHGlobal。
SHCreateMemStream
是在 Windows Vista 中作为公共 API 引入的,它提供了在内存中的现有区域上创建IStream
的优点,从而避免了额外的内存分配:此实现要么抛出
_com_error
,或返回一个HBITMAP
,它引用从内存数据构造的图像。当函数返回时,内存缓冲区可以被安全地释放。返回的 HBITMAP 归调用者所有,需要通过调用 删除对象。
A slight variation on the answer provided by AJG85, that employs SHCreateMemStream in place of
CreateStreamOnHGlobal
.SHCreateMemStream
was introduced as a public API in Windows Vista, and provides the benefit of creating anIStream
over an existing region in memory, thus avoiding an additional memory allocation:This implementation either throws a
_com_error
, or returns anHBITMAP
that refers to the image constructed from the in-memory data.When the function returns, the memory buffer can be safely freed. The returned
HBITMAP
is owned by the caller, and needs to be released with a call to DeleteObject.