如何使用 Direct2D 从 BITMAPINFOHEADER 和 BYTE 渲染位图

Posted

技术标签:

【中文标题】如何使用 Direct2D 从 BITMAPINFOHEADER 和 BYTE 渲染位图【英文标题】:How to Render a Bitmap from BITMAPINFOHEADER and BYTE using Direct2D 【发布时间】:2020-10-11 07:38:07 【问题描述】:

我正在尝试创建一个 C++ 应用程序,它实际上从放大镜中捕获位图并使用 Direct 2d 进行渲染。

我目前拥有将位图从放大镜保存到文件的代码。但我需要做的是使用直接 2d 将该位图绘制到我的窗口,而不是将其保存到文件中。

放大镜以 MAGIMAGEHEADER 的形式将图像作为结构返回,我能够从中获取 BITMAPINFOHEADER 和字节。我需要使用直接 2D 将其渲染到窗口。

这是用于从 Magnifier API 获取 BITMAPINFOHEADER 和字节的代码

BOOL MagImageScaling(HWND hwnd, void *srcdata, MAGIMAGEHEADER srcheader, void *destdata, MAGIMAGEHEADER destheader,RECT unclipped, RECT clipped, HRGN dirty)

    // Setup the bitmap info header
    bmif.biSize = sizeof(BITMAPINFOHEADER);
    bmif.biHeight = srcheader.height;
    bmif.biWidth = srcheader.width;
    bmif.biSizeImage = srcheader.cbSize;
    bmif.biPlanes = 1;
    bmif.biBitCount = (WORD)(bmif.biSizeImage / bmif.biHeight / bmif.biWidth * 8);
    bmif.biCompression = BI_RGB;

    // Prepare the buffer
    if (pData != NULL)
    
        delete pData;
        pData = NULL;
    
    pData = (BYTE*)malloc(bmif.biSizeImage);
    memcpy(pData, srcdata, bmif.biSizeImage);

    // The data bit is in top->bottom order, so we convert it to bottom->top order
    LONG lineSize = bmif.biWidth * bmif.biBitCount / 8;
    BYTE* pLineData = new BYTE[lineSize];
    BYTE* pStart;
    BYTE* pEnd;
    LONG lineStart = 0;
    LONG lineEnd = bmif.biHeight - 1;
    while (lineStart < lineEnd)
    
        // Get the address of the swap line
        pStart = pData + (lineStart * lineSize);
        pEnd = pData + (lineEnd * lineSize);
        // Swap the top with the bottom
        memcpy(pLineData, pStart, lineSize);
        memcpy(pStart, pEnd, lineSize);
        memcpy(pEnd, pLineData, lineSize);

        // Adjust the line index
        lineStart++;
        lineEnd--;
    
    delete pLineData;
    // Set the flag to say that the callback function is finished
    bCallbacked = TRUE;
    return TRUE;

这里的变量 bmif 是 BITMAPINFOHEADER 并且 pData 是字节数 有什么方法可以实现吗?

【问题讨论】:

代码具有未定义的行为,因为您将deletemalloc 混合在一起。在调用new [] 时,您还使用了错误形式的delete。应该是delete[] 实际上这段代码是有效的,但我需要实现的是不同的。我添加它以显示示例 对于 Direct2D,您必须创建一个 Direct2D 位图:ID2D1Bitmap。您可以从 Direct2D 渲染目标创建它,例如,当内存中有像素时,docs.microsoft.com/en-us/windows/win32/api/d2d1/…。你的 Direct2D 代码在哪里? 我还没有完成直接二维码,因为我不知道如何将此数据转换为 ID2D1 位图。无论如何,代码与我的其他问题之一相同。***.com/questions/63381368/… 使用适当的格式(如 DXGI_FORMAT_R8G8B8A8_UNORM 左右)调用 renderTarget.CreateBitmap,然后从像素缓冲区调用 bitmap.CopyFromMemory,然后调用 renderTarget.DrawBitmap 【参考方案1】:

如果你有HBITMAP 句柄,你可以这样做: 图片的大小使用:::GetObject(hBmp, sizeof(BITMAP), &amp;bmpSizeInfo);

像这样填写BITMAPINFO

memset(&bmpData, 0, sizeof(BITMAPINFO));
bmpData.bmiHeader.biSize = sizeof(bmpData.bmiHeader);
bmpData.bmiHeader.biHeight = -bmpSizeInfo.bmHeight;
bmpData.bmiHeader.biWidth = bmpSizeInfo.bmWidth;
bmpData.bmiHeader.biPlanes = bmpSizeInfo.bmPlanes;
bmpData.bmiHeader.biBitCount = bmpSizeInfo.bmBitsPixel;

创建足够的堆内存来保存位图的数据:

pBuff = new char[bmpSizeInfo.bmWidth * bmpSizeInfo.bmHeight * 4];

像这样获取位图数据:

::GetDIBits(hDc, hBmp, 0, bmpSizeInfo.bmHeight, (void*)pBuff, &bmpData, DIB_RGB_COLORS);

创建一个D2D1_BITMAP_PROPERTIES 并像这样填充它:

bmpPorp.dpiX = 0.0f;
bmpPorp.dpiY = 0.0f;
bmpPorp.pixelFormat.format = DXGI_FORMAT_B8G8R8A8_UNORM;
bmpPorp.pixelFormat.alphaMode = D2D1_ALPHA_MODE_IGNORE;

使用您的渲染目标将数据转换为ID2D1Bitmap

pRT->CreateBitmap(bmpSize, pBuff, 4 * bmpSizeInfo.bmWidth, bmpPorp, &pBmpFromH);

【讨论】:

让我试试这个 我已经从这个链接尝试过这个方法:***.com/questions/26566849/… 通过下面的方法我能够获得 HBITMAP 句柄,但我无法使用直接 x 渲染它:***.com/questions/15930528/… 你能展示你的渲染代码吗?我的意思是直接二维码 完整代码:firebasestorage.googleapis.com/v0/b/quizbox-f1bb1.appspot.com/o/…【参考方案2】:

以下是如何将 mag 的位图与 Direct2D 一起使用。您不需要 BITMAPINFOHEADER,因为 mag 格式与 DXGI_FORMAT_B8G8R8A8_UNORM 相同:

BOOL MagImageScaling(HWND hwnd, void* srcdata, MAGIMAGEHEADER srcheader, void* destdata, MAGIMAGEHEADER destheader, RECT unclipped, RECT clipped, HRGN dirty)

    // note: all this (dc, surface, targte) can be created only once as long as the D3D device isn't reset
    ComPtr<ID2D1DeviceContext> dc;
    HR(d2Device->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE, dc.GetAddressOf()));

    ComPtr<IDXGISurface2> surface;
    HR(swapChain->GetBuffer(0, IID_PPV_ARGS(&surface)));

    ComPtr<ID2D1Bitmap1> target;
    HR(dc->CreateBitmapFromDxgiSurface(surface.Get(), NULL, target.GetAddressOf()));
    dc->SetTarget(target.Get());

    D2D1_BITMAP_PROPERTIES properties = ;
    properties.pixelFormat.alphaMode = D2D1_ALPHA_MODE_PREMULTIPLIED;

    // note: this is ok as srcheader.format (GUID_WICPixelFormat32bppRGBA) is compatible
    properties.pixelFormat.format = DXGI_FORMAT_B8G8R8A8_UNORM; 

    D2D1_SIZE_U size = ;
    size.width = srcheader.width;
    size.height = srcheader.height;

    ComPtr<ID2D1Bitmap> bitmap;
    HR(dc->CreateBitmap(size, properties, bitmap.GetAddressOf()));
    HR(bitmap->CopyFromMemory(NULL, srcdata, srcheader.stride));

    dc->BeginDraw();

    // note: we don't call this because we draw on the whole render target
    //dc->Clear();

    dc->DrawBitmap(bitmap.Get());

    HR(dc->EndDraw());
    HR(swapChain->Present(1, 0));
    return TRUE;

【讨论】:

你的回答很好,很有说服力,再次感谢你两次救了我。

以上是关于如何使用 Direct2D 从 BITMAPINFOHEADER 和 BYTE 渲染位图的主要内容,如果未能解决你的问题,请参考以下文章

Direct2D教程渲染位图

如何使用 Direct2D 创建自定义窗口镶边?

Skia 或 Direct2D 如何使用 GPU 渲染线条或多边形?

Direct2D如何打开共享纹理

使用 Direct2D 在非客户区绘图

Direct2D - 如何绘制尽可能接近 GDI 渲染的文本