1
votes

I want to use a bitmap to read the current view window, make some changes to it and output the bitmap back to the view. The program is supposed to read a white window and draw a square against this background. However, the image that I see in the final window is a square in a black background.

void CScribbleView::OnLButtonDown(UINT, CPoint point) 
{
    //CClientDC dc(this);
    //OnPrepareDC(&dc);
    HBITMAP initialMap;
    RECT t;
    BITMAP bmpScreen;
    GetClientRect(&t);
    initialMap = CreateCompatibleBitmap(GetDC()->m_hDC,t.right-t.left,t.bottom-t.top);        
    HDC tempDC = CreateCompatibleDC(GetDC()->m_hDC);
    SelectObject(tempDC,initialMap);
    SelectObject(tempDC,getStockObject(BLACK_PEN);
    SelectObject(tempDC,GetStockObject(WHITE_BRUSH));
    Rectangle(tempDC,100,100,200,200);
    BitBlt(GetDC()->m_hDC,clientR.left,clientR.top,clientR.right-clientR.left,t.bottom-clientR.top,tempDC,0,0,SRCCOPY);
}

This is the output:

I suspect that the bitmap was not properly read from my original DC. So I later decided to use a different way of retrieving DC, the CClientDC, instead of GetClientDC()->m_hDC.

void CScribbleView::OnLButtonDown(UINT, CPoint point) 
{
    CClientDC dc(this);
    OnPrepareDC(&dc);
    HBITMAP initialMap;
    RECT t;
    BITMAP bmpScreen;
    GetClientRect(&t);
    initialMap = CreateCompatibleBitmap(dc.m_hDC,t.right-t.left,t.bottom-t.top);        
    HDC tempDC = CreateCompatibleDC(dc.m_hDC);
    SelectObject(tempDC,initialMap);
    SelectObject(tempDC,getStockObject(BLACK_PEN);
    SelectObject(tempDC,GetStockObject(WHITE_BRUSH));
    Rectangle(tempDC,100,100,200,200);
    BitBlt(dc.m_hDC,clientR.left,clientR.top,clientR.right-clientR.left,t.bottom-clientR.top,tempDC,0,0,SRCCOPY);
}

Now, the new program doesn't show anything; it is the same white background as I had originally started with. What is the difference between these two DC's and how can I fix my problem?

1
Okay, it seems that createCompatibleBitmap function does not create a bitmap that contains the same image that is in the client window. Instead, it just creates an image that is the size of the client window with all the pixel colors initialized to zero (which is black). - varimax
Reading the current view window is a bad idea anyway. What are you actually trying to achieve ? - Jabberwocky
It's a long story. I am doing a drawing program like Paint. Right now I am working on drawing rectangles. Originally, when I press and drag my mouse, I would draw a new rectangle in OnMouseMove function. However, that leaves a bunch of lagged rectangles behind. So I decided to save the client view in a bitmap the moment the mouse is pressed. In OnMouseMove function I would just draw on top of the view I had saved, covering up the previous rectangle created in the dragging action. - varimax
It's probably best if you did the whole drawing into an off screen buffer and blit that buffer back to your view window. - Jabberwocky
Yeah that was what I was trying to do - varimax

1 Answers

1
votes

These WinAPI functions all require cleanup: ::CetDC, ::CreateCompatibleDC, ::CreateCompatibleBitmap. See documentation for each of them. Without cleanup, your program could quickly use its 10,000 GDI resource limit and crash.

You don't have to worry about cleanup if you use the MFC version of these WinAPI functions (you should still see the docs to make sure). This is MFC example for double-buffering:

void foo::OnLButtonDown(UINT nFlags, CPoint point)
{
    CWnd::OnLButtonDown(nFlags, point);

    CClientDC dc(this); 

    CRect rect; 
    GetClientRect(&rect); 

    //create memory dc 
    CDC memdc;
    memdc.CreateCompatibleDC(&dc);
    CBitmap bitmap; 
    bitmap.CreateCompatibleBitmap(&dc, rect.Width(), rect.Height()); 
    memdc.SelectObject(bitmap); 

    //some random drawings:
    memdc.SelectObject(GetStockObject(BLACK_PEN)); 
    memdc.SelectObject(GetStockObject(GRAY_BRUSH)); 
    memdc.Rectangle(10, 10, 100, 100); 

    //draw memory DC to destination DC
    dc.BitBlt(0, 0, rect.Width(), rect.Height(), &memdc, 0, 0, SRCCOPY);
}; 

Your goal is probably to draw on screen buffer, as suggested in comments. In which case you declare memdc and bitmap as member data:

//declare member data
CDC m_memdc;
CBitmap m_bitmap;
CRect m_rect;
CPoint m_point;

void foo::initialize_once()
{
    ASSERT(IsWindow(m_hWnd));
    GetClientRect(&m_rect);
    //create memdc
    m_memdc.CreateCompatibleDC(0);
    CBitmap bitmap;
    bitmap.CreateCompatibleBitmap(&m_memdc, m_rect.Width(), m_rect.Height());
    m_memdc.SelectObject(bitmap);
    //initialize memdc background color
    m_memdc.FillSolidRect(m_rect, RGB(255,255,255));
}

void foo::OnLButtonDown(UINT nFlags, CPoint point)
{
    __super::OnLButtonDown(nFlags, point);
    m_point = point;
    m_memdc.MoveTo(point);
};

void OnMouseMove(UINT nFlags, CPoint point)
{
    __super::OnMouseMove(nFlags, point);
    if (!(nFlags & MK_LBUTTON)) return;
    m_point = point;
    m_memdc.LineTo(point);
    CClientDC dc(this);
    dc.BitBlt(0, 0, m_rect.Width(), m_rect.Height(), &m_memdc, 0, 0, SRCCOPY);
}