如何保持连续重用HBITMAP和HDC?

时间:2018-08-21 13:45:53

标签: c++ sdl-2 gdi

出于性能原因,我正在尝试通过重用HBITMAP和HDC来使HBIPMAP工作。

这是我想做的一个小测试项目,以了解有关基于CPU的栅格化的更多信息。对于窗口,我使用SDL2。

如果我们注释掉,下面的代码将起作用:

DeleteDC(hdcMem);
hdcMem = CreateCompatibleDC(device);

我找不到2018年以上的任何例子。

mBackBuffer只是一个Vector(DWORD)

void Device::createDeviceFromHWND(const HWND& hwnd, const int& width, const int& height)
{
    // This is hacked code for an example.

    auto device = GetDC(hwnd);
    DWORD colorSize = 4;    // ARGB;

    // Create page section
    // https://docs.microsoft.com/en-us/windows/desktop/memory/creating-named-shared-memory
    HANDLE hMapFile;
    LPCTSTR pBuf;
    // https://docs.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-createfilemappinga
    hMapFile = CreateFileMappingA
    (
        INVALID_HANDLE_VALUE,
        NULL,
        PAGE_READWRITE,
        0,
        width * height * colorSize,
        NULL
    );

    if (hMapFile == NULL)
    {
        return;
    }

    DWORD* buffer = (DWORD*)MapViewOfFile(
        hMapFile, 
        FILE_MAP_ALL_ACCESS, 
        0, 
        0, 
        width * height * colorSize
    );

    BITMAPINFOHEADER header;
    memset(&header, 0, sizeof(BITMAPINFOHEADER));
    // https://msdn.microsoft.com/en-us/02f8ed65-8fed-4dda-9b94-7343a0cfa8c1
    header.biSize = sizeof(BITMAPINFOHEADER);
    header.biWidth = width;
    header.biHeight = height;
    header.biPlanes = 1;
    header.biBitCount = 32;
    header.biCompression = BI_RGB;
    header.biSizeImage = width * height * sizeof(BYTE);
    header.biXPelsPerMeter = 0;
    header.biYPelsPerMeter = 0;
    header.biClrUsed = 0;
    header.biClrImportant = 0;


    tagBITMAPINFO bitmap;
    memset(&bitmap, 0, sizeof(tagBITMAPINFO));
    // https://docs.microsoft.com/en-us/windows/desktop/api/wingdi/ns-wingdi-tagbitmapinfo
    tagRGBQUAD RGBQUAD;
    memset(&RGBQUAD, 0, sizeof(tagRGBQUAD));

    bitmap.bmiHeader = header;
    bitmap.bmiColors[0] = RGBQUAD;

    LPVOID p;
    // https://docs.microsoft.com/en-us/windows/desktop/api/wingdi/nf-wingdi-createdibsection
    auto hBitMap = CreateDIBSection
    (
        device,
        &bitmap,
        DIB_RGB_COLORS,
        &p,
        hMapFile,
        0
    );

    for (DWORD i = 0; i < width * height; ++i)
    {
        buffer[i] = 0xFF0000;
    }

    HDC hdcMem = CreateCompatibleDC(device);
    auto oldHBITMAP = (HBITMAP)SelectObject(hdcMem, hBitMap);

    BitBlt(
        device,
        0,
        0,
        width,
        height,
        hdcMem,
        0,
        0,
        SRCCOPY
    );

    DeleteDC(hdcMem);

    for (DWORD i = 0; i < width * height; ++i)
    {
        buffer[i] = 0;
    }

    hdcMem = CreateCompatibleDC(device);

    BitBlt(
        device,
        400,
        300,
        width,
        height,
        hdcMem,
        0,
        0,
        SRCCOPY
    );
}

输出为红色屏幕,但您应该在右上角看到黑色部分。

3 个答案:

答案 0 :(得分:1)

这种方法是错误的。当目标窗口显示为WM_PAINT时,您的所有工作将被撤消。

始终使用WM_PAINT和BeginPaint按照您希望的方式绘制窗口。

答案 1 :(得分:1)

这里有几个问题,有些与位图无关。

GetDC上的句柄应该在不再需要时由ReleaseDC清理。

CreateFileMapping的句柄应该由CloseHandle清理,MapViewOfFile的{​​{1}}应该清理。

UnmapViewOfFile句柄必须由HBITMAP清理

建议在DeleteObject之后通过调用SelectObject

进行清理

如果不还原旧的位图,并尝试删除SelectOject(hMemDC, oldHBitmap),则Windows无法满足该请求,因为在设备上下文中选择了另一个位图。 Windows将尝试修复此错误,但是如果代码太复杂,它可能会失败。

请注意Windows为您提供了10,000个GDI句柄的限制。如果您没有正确管理这些句柄,该应用程序将很快崩溃。有关这些功能,请参考WinAPI文档。如有疑问,请使用任务管理器监视程序的“ GDI句柄”。

修复这些问题后,代码应可以按预期工作,请参见下面的示例。

这当然仅用于演示。在实际的应用程序中,您可能希望将hMemDC和其他值保存在堆中,而不是堆栈中。您想减少重复创建这些句柄的次数。

如其他答案和评论所述,应响应HBITMAP进行绘制,在此您将从WM_PAINT中获得HDC(并用BeginPaint进行清理)。因此,您应避免使用EndPaint / GetDC

ReleaseDC

旁注,通过将引用运算符void Device::createDeviceFromHWND(const HWND& hwnd, const int& width, const int& height) { auto hdc = GetDC(hwnd); auto hMapFile = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, width * height * sizeof(DWORD), NULL); auto buffer = (DWORD*)MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, width * height * sizeof(DWORD)); BITMAPINFOHEADER biheader = { sizeof(biheader), width, height, 1, 32, BI_RGB }; LPVOID bits; auto hbitmap = CreateDIBSection(hdc, (BITMAPINFO*)&biheader, DIB_RGB_COLORS, &bits, hMapFile, 0); for(int i = 0; i < width * height; ++i) buffer[i] = 0xFF0000; auto memdc = CreateCompatibleDC(hdc); auto oldhbitmap = SelectObject(memdc, hbitmap); BitBlt(hdc, 0, 0, width, height, memdc, 0, 0, SRCCOPY); for(int i = 0; i < width * height; ++i) buffer[i] = 0; BitBlt(hdc, 0, 0, 100, 100, memdc, 0, 0, SRCCOPY); SelectObject(memdc, oldhbitmap); //<- ***EDIT*** //oldhbitmap is selected in to memdc, now we can destroy hbitmap and memdc DeleteObject(hbitmap); DeleteDC(memdc); ReleaseDC(hwnd, hdc); UnmapViewOfFile(buffer); CloseHandle(hMapFile); } 用于常量值不会获得任何收益。只需按如下所示更改函数原型:

&

此外,无需使用void createDeviceFromHWND(const HWND hwnd, const int width, const int height); 即可完成此操作,并使用下面显示的CreateFileMapping。只要buffer有效,buffer就会有效。

hbitmap

答案 2 :(得分:0)

您不在此处编辑位图:

for (unsigned int i = 0; i < width * height; ++i)
{
    mBackBuffer[i] = 0;
}

仅填充数组,即可从中创建位图,并且为空。