12

我有一个应用程序可以从数据库中加载一些 blob 数据,这些数据可以表示各种位图和图标的 png 格式或原始二进制数据。这被存储在一个std::vector<unsigned char>

我正在使用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。所有这些最终都创建了可爱的黑色位图而不是以前的模糊位图,所以我一定又做错了,我的猜测是因为这个位图创建是在一个没有屏幕直流或窗口概念的对象中完成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)。有谁知道CBitmap从 a 初始化对象的简单方法std::vector<unsigned char>

4

3 回答 3

6

我会使用CreateCompatibleBitmap, 然后调用SetDIBits以用您的数据填充它。这些是我见过的函数,SetDIBits 非常灵活,虽然很冗长。

在我的 MFC 年代,CreateBitmap由于怀疑性能问题而被避免。

于 2011-01-04T22:22:44.660 回答
3

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

于 2011-01-06T15:13:28.743 回答
1

AJG85提供的答案略有不同,它使用SHCreateMemStream代替. 在 Windows Vista 中作为公共 API 引入,并提供了在内存中创建一个现有区域的好处,从而避免了额外的内存分配:CreateStreamOnHGlobalSHCreateMemStreamIStream

#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来释放。

于 2020-07-06T10:03:04.443 回答