简体   繁体   中英

How can I keep reusing HBITMAP and HDC continually?

I am trying to get HBIPMAP working by reusing the HBITMAP and the HDC for performance reasons.

This is a small test project I wanted to do to learn more about CPU based Rasterization. For the window, im using SDL2.

The below code works if we comment out:

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

I can not find any example in the year 2018+.

mBackBuffer is just a 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
    );
}

The output is a red screen, but you should see the black section in corner right.

This approach is wrong. When the target window gets WM_PAINT all your work will be undone.

Always paint your windows the way you want them with WM_PAINT and BeginPaint.

There are several issues here, some not related to bitmap.

The handle from GetDC should be cleaned up by ReleaseDC when the handle is no longer needed.

The handle from CreateFileMapping should be cleaned up by CloseHandle , and MapViewOfFile should be cleaned up by UnmapViewOfFile .

HBITMAP handle must be cleaned up by DeleteObject

It is recommend to cleanup after SelectObject by calling SelectOject(hMemDC, oldHBitmap)

If you don't restore the old bitmap, and try to delete hMemDC , Windows cannot fulfill the request because there is another bitmap selected in device context. Windows will try to fix this error but it may fail if the code is too convoluted.

Note that Windows gives you a limit of 10,000 GDI handles. The application will crash very quickly if you don't manage these handles properly. Refer to WinAPI documentation for these functions. If in doubt, use the task manager to monitor "GDI handles" for your program.

The code should work as expected once you fix these problems, see the example below.

This of course is for demonstration only. In real world application you probably want to save the HBITMAP in the heap, instead of stack, as well as some other values. You want to minimize repeated creating these handles.

As noted in other answers and comments, painting should be done in response to WM_PAINT , where you get HDC from BeginPaint (and cleanup with EndPaint ). Therefore you should avoid 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);
}

Side note, you don't gain anything by using reference operator & for constant values. Just change the function prototype as follows:

void createDeviceFromHWND(const HWND hwnd, const int width, const int height);

Also, this can be done without CreateFileMapping , and use buffer shown below. buffer will be valid as long as hbitmap is valid.

void test(const HWND hwnd, const int w, const int h)
{
    auto hdc = GetDC(hwnd);

    //use the negative value of height, so bitmap bits are not upside-down
    BITMAPINFOHEADER bi = { sizeof(bi), w, -h, 1, 32, BI_RGB };

    DWORD* buffer;
    auto hbitmap = CreateDIBSection(hdc, (BITMAPINFO*)&bi, DIB_RGB_COLORS,
        (void**)&buffer, NULL, 0);

    auto memdc = CreateCompatibleDC(hdc);
    auto oldbmp = SelectObject(memdc, hbitmap);

    for(int i = 0; i < w * h; ++i) buffer[i] = 0xFF0000;
    BitBlt(hdc, 0, 0, w, h, memdc, 0, 0, SRCCOPY);

    //draw black square on top-left
    for(int y = 0; y < 100; y++)
        for(int x = 0; x < 100; x++)
            buffer[y * w + x] = 0;
    BitBlt(hdc, 0, 0, 100, 100, memdc, 0, 0, SRCCOPY);

    //cleanup:
    SelectObject(memdc, oldbmp);
    DeleteObject(hbitmap); //<- buffer is not valid after hbitmap is destroyed
    DeleteDC(memdc);
    ReleaseDC(hwnd, hdc);
}

You are not editing the bitmap here:

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

just filling the array, the bitmap was created from, with nulls.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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