繁体   English   中英

从内存缓冲区创建 HBITMAP

[英]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类? 归结为将图像添加到CImageListCImageList在使用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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM