简体   繁体   English

从 bmp 文件数据创建 MFC CBitmap

[英]Create MFC CBitmap from bmp file data

I have a void pointer to data that was read from a *.bmp file.我有一个指向从 *.bmp 文件中读取的数据的 void 指针。 The bmp file no longer exists (there is now one file that contains hundreds of these bitmap file datasets). bmp 文件不再存在(现在有一个文件包含数百个这样的 bitmap 文件数据集)。 How can I initialize an MFC CBitmap using this data?如何使用此数据初始化 MFC CBitmap

I see the CBitmap::Create* functions (eg, CreateBitmap() , CreateCompatibleBitmap() , etc), but they require that I know the bitmap's height and width, can get access to the data bits, etc. I can write the data to disk and then use ::LoadImage() and CBitmap::Attach() to load the bitmap, but I want to do this in memory to improve performance.我看到CBitmap::Create*函数(例如CreateBitmap()CreateCompatibleBitmap()等),但它们要求我知道位图的高度和宽度,可以访问数据位等。我可以写入数据到磁盘,然后使用::LoadImage()CBitmap::Attach()加载 bitmap,但我想在 memory 中执行此操作以提高性能。

Thanks!谢谢!

UPDATE (#2):更新(#2):

Here is my code, as suggested and simplified by Constantine Georgiou's comments and post (thank you.): CBitmap:,CreateBitmap() no longer fails.这是我的代码,正如 Constantine Georgiou 的评论和帖子(谢谢)所建议和简化的那样: CBitmap:,CreateBitmap() 不再失败。 but the bitmap displays as black.但 bitmap 显示为黑色。

// Bitmap File Header
LPBITMAPFILEHEADER pFileHdr = (LPBITMAPFILEHEADER)pFileData;
// Bitmap Info Header
LPBITMAPINFOHEADER pBmpHdr = (LPBITMAPINFOHEADER)((PCHAR)pFileData + sizeof(BITMAPFILEHEADER));
// Image Data
LPVOID lpBits = (LPVOID)((PCHAR)pFileData + pFileHdr->bfOffBits);
if(!bitmap.CreateBitmap(pBmpHdr->biWidth, pBmpHdr->biHeight, pBmpHdr->biPlanes, pBmpHdr->biBitCount, lpBits))
    bool bummer = true;

Here is code that writes the same data to a file and then loads the bitmap using::LoadImage().这是将相同数据写入文件然后使用::LoadImage() 加载 bitmap 的代码。 This works.这行得通。

CFile file;
if(file.Open(sFilename, CFile::modeCreate | CFile::modeReadWrite))
{
    file.Write(pFileData, dwFileBytes);
    file.Close();

    HBITMAP hBitmap = (HBITMAP)::LoadImage(NULL, sFilename, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
    if(hBitmap)
    {
        if(!bitmap.Attach(hBitmap))
            bool ahHeck = true;
    }
}

Here are trace messages regarding the above.以下是有关上述内容的跟踪消息。

Inspecting the file data in memory before creating the image:在创建镜像之前检查 memory 中的文件数据:

  • dwFileBytes = 3128.
  • BMP HEADER: BMP HEADER:
  • biSize = 40
  • biWidth = 32
  • biHeight = 32
  • biPlans = 1
  • biBitCount = 24
  • biCompression = 0
  • biSizeImage = 3074

Inspecting BITMAP after calling CreateBitmap(32, 32, 1, 24, lpBits):调用 CreateBitmap(32, 32, 1, 24, lpBits) 后检查 BITMAP:

  • bmType = 0
  • bmWidth = 32
  • bmHeight = 32
  • bmPlanes= 1
  • bmWidthBytes = 96
  • bmBitsPixel = 24
  • bmBits = 0

(This bitmap displays as black.) (此 bitmap 显示为黑色。)

Inspecting BITMAP after writing to file and calling LoadImage():写入文件并调用 LoadImage() 后检查 BITMAP:

  • bmType = 0
  • bmWidth = 32
  • bmHeight = 32
  • bmPlanes= 1
  • bmWidthBytes = 128
  • bmBitsPixel = 32
  • bmBits = 0

(This bitmap displays correctly.) (此 bitmap 显示正确。)

I realize I'm getting into the weeds with details.我意识到我正在深入了解细节。 Apologies.道歉。 I'm stumped.我难住了。

Your code is correct, although I would rather simplify it a little - no need to make copies of these structures, as they are already there - just some typecasting and pointer arithmetic.您的代码是正确的,尽管我宁愿将其简化一点 - 无需复制这些结构,因为它们已经存在 - 只是一些类型转换和指针算术。 For example:例如:

// Bitmap File Header
LPBITMAPFILEHEADER pFileHdr = (LPBITMAPFILEHEADER)pFileData;

// Bitmap Info Header
LPBITMAPINFOHEADER pBmpHdr = (LPBITMAPINFOHEADER)((PCHAR)pFileData + sizeof(BITMAPFILEHEADER));

// Image Data
LPVOID lpBits = (LPVOID)((PCHAR)pFileData + pFileHdr->bfOffBits);

The documentatiom clearly mentions that "bit count" is the number of bits per pixel.该文档清楚地提到“位数”是每个像素的位数。 So, the call would be:因此,电话将是:

if (!m_IconBitmap.CreateBitmap(pBmpHdr->biWidth, pBmpHdr->biHeight, pBmpHdr->biPlanes, 
    pBmpHdr->biBitCount, lpBits))
{
    // Handle the Error
}

(haven't really tested this code, but it should work) (还没有真正测试过这段代码,但它应该可以工作)

I have to mention though that the image format is rather unusual, ie 16 bits per pixel is probably a BGR 5-5-5 format (5 bits for each component).我不得不提一下,图像格式相当不寻常,即每像素 16 位可能是 BGR 5-5-5 格式(每个组件 5 位)。 And with two bytes per pixel, the image size should rather be 2048 bytes (32 x 32 x 2).每个像素两个字节,图像大小应该是 2048 字节(32 x 32 x 2)。 A bitmap file like this, created by Microsoft tools would be 2102 bytes exactly, which is correct (54 bytes for the structures plus 2048 for the bitmap data - no palette).像这样由 Microsoft 工具创建的 bitmap 文件将是 2102 字节,这是正确的(结构的 54 字节加上 bitmap 数据的 2048 字节 - 没有调色板)。 Maybe you should store the data to a file and examine them with a hex editor.也许您应该将数据存储到文件中并使用十六进制编辑器检查它们。

If using GDI+ is an option, you can construct an HBITMAP from an in-memory data buffer using essentially this answer , and finally attaching the returned HBITMAP to an MFC CBitmap .如果使用 GDI+ 是一个选项,您可以基本上使用此 answer从内存数据缓冲区构造HBITMAP ,最后将返回的HBITMAP附加到 MFC CBitmap This is the original code:这是原始代码:

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

And here is a snippet on how to attach the returned HBITMAP to a CBitmap :这是一个关于如何将返回的HBITMAP附加到CBitmap的片段:

#include <afxwin.h>

// ...

CBitmap bitmap {};
bitmap.Attach(from_data(data));

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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