Search code examples
c++winapidrawing

How can I display a bitmap (array with color values) in a window using Window API?


I want move a buffer of color values to a window using BitBlt, only the window shows up empty. When I compile and run the code from How can I load a bitmap inside my window? (with my own example .bmp file), the window also shows up empty.

After some testing, the problem appears to be with SelectObject(). According to the documentation, when the return value is NULL an error has occered: https://learn.microsoft.com/en-us/windows/desktop/api/wingdi/nf-wingdi-selectobject. The return value is NULL, but GetLastError() gives 0, indicating there has not been an error. What is the problem here?

    case WM_CREATE:
        std::fill(arr, arr + sizeof(arr), RGB(255,0,0));
        hBitmap = CreateBitmap(240, 120, 1, sizeof(COLORREF), (void*) arr);
        UpdateWindow(hwnd);
        break;
    case WM_PAINT:
        PAINTSTRUCT ps;
        BITMAP bitmap;
        HGDIOBJ oldBitmap;
        HDC hdcMem;
        HDC hdc;

        hdc = BeginPaint(hwnd, &ps);

        hdcMem = CreateCompatibleDC(hdc);
        oldBitmap = SelectObject(hdcMem, hBitmap);
        std::cout << (oldBitmap == NULL) << std::endl;
        std::cout << GetLastError();

        GetObject(hBitmap, sizeof(bitmap), &bitmap);
        BitBlt(hdc, 0, 0, bitmap.bmWidth, bitmap.bmHeight, hdcMem, 0, 0, SRCCOPY);

        SelectObject(hdcMem, oldBitmap);
        DeleteDC(hdcMem);

        EndPaint(hwnd, &ps);
        break;

(BTW: I use main() instead of WinMain() and GetModuleHandle(NULL) each time I need the hInstance. As the window functions fine, I suspect it has nothing to do with this, but I'll mention it anyway.)

---SOLVED!---

I've got it working now :) For others seeing this, this is what I've changed:

    case WM_CREATE:
        std::fill(arr, arr + 240 * 120, RGB(255,0,0));
        hBitmap = CreateBitmap(240, 120, 1, sizeof(COLORREF) * 8, (void*) arr);
        UpdateWindow(hwnd);
        break;

Solution

  • If arr is a pointer, use the total number of elements (240 * 120)

    If arr is an array, sizeof will return the total size in bytes. Instead use sizeof(arr)/sizeof(*arr) to find the count of array (total number of array's elements, or total number of pixels). For example, assuming arr's element is 32-bit, then you are looking at 240 * 120 pixels, where each pixel is 4 bytes or 32 bits.

    CreateBitmap's 4th parameter expects the size in bits, so it should be sizeof(*arr) * 8 or just 32.

    uint32_t arr[240 * 120];
    std::fill(arr, arr + sizeof(arr)/sizeof(*arr), RGB(255, 0, 0));
    //or std::fill(arr, arr + 240 * 120, RGB(255, 0, 0));
    hBitmap = CreateBitmap(240, 120, 1, 32, (void*)arr);
    

    Note that this will produce a blue bitmap, not red, because it is using BGR format instead of RGB.

    It appears you already know how to use memory dc. You can create a bitmap with CreateCompatibleBitmap, then select that bitmap in to memory dc, and use the standard GDI functions such as FillRect. This will avoid the pitfalls with counting the bits and bytes.