从内存缓冲区创建 HBITMAP

发布于 2024-10-10 01:34:53 字数 1608 浏览 0 评论 0 原文

我有一个应用程序,它从数据库中加载一些 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>?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

一身仙ぐ女味 2024-10-17 01:34:53

我将使用 CreateCompatibleBitmap,然后调用 SetDIBits 用您的数据填充它。这些是我见过的有效函数,SetDIBits 非常灵活,尽管很冗长。

在我的 MFC 岁月里,由于怀疑存在性能问题,CreateBitmap 被避免使用。

I'd use CreateCompatibleBitmap, and then call SetDIBits 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.

樱桃奶球 2024-10-17 01:34:53

使用 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 进行了更改

Using GdiPlus I got something that works pretty well and doesn't involve pulling any teeth!

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();
}

Edit: Changed per Jegatheesh

时间你老了 2024-10-17 01:34:53

答案略有不同">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 归调用者所有,需要通过调用 删除对象

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 an IStream over an existing region in memory, thus avoiding an additional memory allocation:

#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();
}

This implementation either throws a _com_error, or returns an HBITMAP 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文