Search code examples
c++mfcondrawbitblt

How do I draw an array made bitmap on screen using bitblt?


Instead of drawing using Moveto and LineTo, I want to use a hand made bitmap, that I will fill with an array that I will create myself, and use that to fill the screen.

Right now, the array is just filled with red, But when I draw to the screen I get all black.

This is the code:

void CCGWorkView::OnDraw(CDC* pDC)
{
    CCGWorkDoc* pDoc = GetDocument();
    ASSERT_VALID(pDoc);
    if (!pDoc)
        return;

    pDCToUse->FillSolidRect(&r, world.bg_color);

    BITMAPINFO bminfo;
    CPaintDC hdc(this);
    CRect rect;
    GetWindowRect(&rect);

    int h = rect.bottom - rect.top,
        w = rect.right - rect.left;
    int *bits = new int[w * h];

    HDC hdcMem = CreateCompatibleDC(hdc);
    HBITMAP bm = CreateCompatibleBitmap(hdc, w, h);

    SelectObject(hdcMem, bm);

    bminfo.bmiHeader.biSize = sizeof(bminfo.bmiHeader);
    bminfo.bmiHeader.biWidth = w;
    bminfo.bmiHeader.biHeight = h;
    bminfo.bmiHeader.biPlanes = 1;
    bminfo.bmiHeader.biBitCount = 32;
    bminfo.bmiHeader.biCompression = BI_RGB;
    bminfo.bmiHeader.biSizeImage = 0;
    bminfo.bmiHeader.biXPelsPerMeter = 1;
    bminfo.bmiHeader.biYPelsPerMeter = 1;
    bminfo.bmiHeader.biClrUsed = 0;
    bminfo.bmiHeader.biClrImportant = 0;

    for (int i = 0; i < w * h; i++) {
        bits[i] = RGB(255, 0, 0);
    }

    SetDIBits(hdcMem, bm, 0, h, bits, &bminfo, 0);

    BitBlt(hdc, rect.left, rect.top, w, h, hdcMem, rect.left, rect.top, SRCCOPY);

    DeleteDC(hdcMem);
    DeleteObject(bm);
    delete bits;
}

Solution

  • There are a couple of issues in your code.

    First, you don’t need CPaintDC, as the pDC is passed into your OnDraw() function. This may have actually caused your black display, as the newly created DC has a one pixel black-and-white bitmap selected into it, and when you call CreateCompatibleBitmap(), that bitmap is also monochrome.

    Second, SetDIBits() expect RGBQUAD colors (https://learn.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-rgbquad), NOT RGB.

    And, as Constantine Georgiou suggested, you should de-select your bitmap out of DC before deleting it to avoid resource leaks, even if MFC handles it for you.

    Here is modified code:

    void CMFCApplication1View::OnDraw(CDC* pDC)
    {
        CMFCApplication1Doc* pDoc = GetDocument();
        ASSERT_VALID(pDoc);
        if (!pDoc)
            return;
    
        //pDCToUse->FillSolidRect(&r, world.bg_color);
    
        BITMAPINFO bminfo;
        CRect rect;
        GetClientRect(&rect);
    
        int h = rect.bottom - rect.top,
            w = rect.right - rect.left;
        int* bits = new int[w * h];
    
        HDC hdcMem = CreateCompatibleDC(pDC->m_hDC);
        HBITMAP bm = CreateCompatibleBitmap(pDC->m_hDC, w, h);
    
        HGDIOBJ hOld = SelectObject(hdcMem, bm);
    
        bminfo.bmiHeader.biSize = sizeof(bminfo.bmiHeader);
        bminfo.bmiHeader.biWidth = w;
        bminfo.bmiHeader.biHeight = h;
        bminfo.bmiHeader.biPlanes = 1;
        bminfo.bmiHeader.biBitCount = 32;
        bminfo.bmiHeader.biCompression = BI_RGB;
        bminfo.bmiHeader.biSizeImage = 0;
        bminfo.bmiHeader.biXPelsPerMeter = 1;
        bminfo.bmiHeader.biYPelsPerMeter = 1;
        bminfo.bmiHeader.biClrUsed = 0;
        bminfo.bmiHeader.biClrImportant = 0;
    
        RGBQUAD red = { 0, 0, 255, 0 };
        for (int i = 0; i < w * h; i++) {
            bits[i] = *((int*)&red);
        }
    
        SetDIBits(hdcMem, bm, 0, h, bits, &bminfo, DIB_RGB_COLORS);
    
        BitBlt(pDC->m_hDC, rect.left, rect.top, w, h, hdcMem, rect.left, rect.top, SRCCOPY);
    
        SelectObject(hdcMem, hOld);
        DeleteDC(hdcMem);
        DeleteObject(bm);
        delete[] bits;
    }