Using Bitmaps to Improve the Screen Display

You've seen an example program that displays a bitmap that originated outside the program. Now you'll see an example program that generates its own bitmap to support smooth motion on the screen. The principle is simple: you draw on a memory device context with a bitmap selected, and then you zap the bitmap onto the screen.

The EX11B Example

In the EX05C example in Chapter 5, the user dragged a circle with the mouse. As the circle moved, the display flickered because the circle was erased and redrawn on every mouse-move message. EX11B uses a GDI bitmap to correct this problem. The EX05C custom code for mouse message processing carries over almost intact; most of the new code is in the OnPaint and OnInitialUpdate functions.

In summary, the EX11B OnInitialUpdate function creates a memory device context and a bitmap that are compatible with the display. The OnPaint function prepares the memory device context for drawing, passes OnDraw a handle to the memory device context, and copies the resulting bitmap from the memory device context to the display.

Here are the steps to build EX11B from scratch:

  1. Run AppWizard to produce \vcpp32\ex11b\ex11b. Accept all the default settings but two: select Single Document and select CScrollView view as the base class for CEx11bView. The options and the default class names are shown here.

  2. Use ClassWizard to add CEx11bView message handlers. Add message handlers for the following messages:

  3. Edit the ex11bView.h header file. Add the private data members shown here to the CEx11bView class:

        const CSize m_sizeEllipse;
        CPoint   m_pointTopLeft;
        BOOL     m_bCaptured;
        CSize    m_sizeOffset;
        CDC*     m_pdcMemory;
        CBitmap* m_pBitmap;

  4. Code the CEx11bView constructor and destructor in ex11bView.cpp. You need a memory device context object and a bitmap GDI object. These are constructed in the view's constructor and destroyed in the view's destructor. Add the following boldface code:

    CEx11bView::CEx11bView() : m_sizeEllipse(100, -100),
                               m_pointTopLeft(10, -10),
                               m_sizeOffset(0, 0)
        m_bCaptured = FALSE;
        m_pdcMemory = new CDC;
        m_pBitmap   = new CBitmap;
        delete m_pBitmap; // already deselected
        delete m_pdcMemory;

  5. Add code for the OnInitialUpdate function in ex11bView.cpp. The C++ memory device context and bitmap objects are already constructed. This function creates the corresponding Windows objects. Both the device context and the bitmap are compatible with the display context dc, but you must explicitly set the memory device context's mapping mode to match the display context. You could create the bitmap in the OnPaint function, but the program runs faster if you create it once here. Add the boldface code shown here:

    void CEx11bView::OnInitialUpdate()
        CSize sizeTotal(800, 1050); // 8-by-10.5 inches
        CSize sizePage( / 2, / 2);
        CSize sizeLine( / 50, / 50);
        SetScrollSizes(MM_LOENGLISH, sizeTotal, sizePage, sizeLine);
        // creates the memory device context and the bitmap
        if (m_pdcMemory->GetSafeHdc() == NULL) {
            CClientDC dc(this);
            CRect rectMax(0, 0,,;
            // makes bitmap same size as display window
            m_pBitmap->CreateCompatibleBitmap(&dc, rectMax.right,

  6. Add code for the OnPaint function in ex11bView.cpp. Normally it isn't necessary to map the WM_PAINT message in your derived view class. The CView version of OnPaint contains the following code:

    CPaintDC dc(this);

    In this example, you will be using the OnPaint function to reduce screen flicker through the use of a memory device context. OnDraw is passed this memory device context for the display, and it is passed the printer device context for printing. Thus, OnDraw can perform tasks common to the display and to the printer. You don't need to use the bitmap with the printer because the printer has no speed constraint.

    The OnPaint function must perform, in order, the following three steps to prepare the memory device context for drawing:

    After the memory device context is prepared, OnPaint can call OnDraw with a memory device context parameter. Then the CDC::BitBlt function copies the updated rectangle from the memory device context to the display device context. Add the following boldface code:

    void CEx11bView::OnPaint()
        CPaintDC dc(this); // device context for painting
        CRect rectUpdate;
        CBitmap* pOldBitmap = m_pdcMemory->SelectObject(m_pBitmap);
        CBrush backgroundBrush((COLORREF) ::GetSysColor(COLOR_WINDOW));
        CBrush* pOldBrush = m_pdcMemory->SelectObject(&backgroundBrush);
                            rectUpdate.Width(), rectUpdate.Height(),
                  rectUpdate.Width(), rectUpdate.Height(),
                  m_pdcMemory, rectUpdate.left,,

  7. Code the OnDraw function in ex11bView.cpp. Copy the code from ex05cView.cpp. In EX11B, OnDraw is passed a pointer to a memory device context by the OnPaint function. For printing, OnDraw is passed a pointer to the printer device context.

  8. Copy the mouse message-handling code from ex05cView.cpp. Copy the functions shown below from ex05cView.cpp to ex11bView.cpp. Be sure to change the functions' class names from CEx05cView to CEx11bView.

  9. Change two lines in the OnMouseMove function in ex11bView.cpp. Change the following two lines:

    InvalidateRect(rectOld, TRUE);
    InvalidateRect(rectNew, TRUE);


    InvalidateRect(rectOld, FALSE);
    InvalidateRect(rectNew, FALSE);

    If the second CWnd::InvalidateRect parameter is TRUE (the default), Windows erases the background before repainting the invalid rectangle. That's what you needed in EX05C, but the background erasure is what causes the flicker. Because the entire invalid rectangle is being copied from the bitmap, you no longer need to erase the background. The FALSE parameter prevents this erasure.

  10. Build and run the application. Here is the EX11B program output.

    Click to view at full size.

    Is the circle's movement smoother now? The problem is that the bitmap is only 8-by-10.5 inches, and if the scrolling window is big enough, the circle goes off the edge. One solution to this problem is to make the bitmap as big as the largest display.

Windows Animation

EX11B is a crude attempt at Windows animation. What if you wanted to move an angelfish instead of a circle? Win32 doesn't have an Angelfish function (yet), so you'd have to keep your angelfish in its own bitmap and use the StretchBlt mask ROP codes to merge the angelfish with the background. You'd probably keep the background in its own bitmap, too. These techniques are outside the scope of this book. If you are interested in learning more about Windows Animation, run out and get Nigel Thompson's Animation Techniques in Win32 (Microsoft Press, 1995). After you read it, you can get rich writing video games for Windows!