Click here to Skip to main content
14,643,058 members
Articles » Multimedia » GDI » Device Contexts
Article
Posted 20 Nov 1999

Stats

998.7K views
17K downloads
304 bookmarked

Flicker Free Drawing In MFC

Rate this:
4.96 (168 votes)
Please Sign up or sign in to vote.
4.96 (168 votes)
25 Mar 2002CPOL
A simple animation example which is used to show CMemDC in several modes

Sample Image

A simple animation example which is used to show CMemDC in several mode

Introduction

Removing flicker from an MFC application is well-covered territory. You can find the topic addressed in books, and on-line. However, the techniques presented are somewhat complicated and are usually difficult to add to an existing application. One often-presented technique is called double buffering. Double buffering allows the new screen to be drawn in off-screen memory, and then the completed screen is bit-blited back onto the physical screen.

This article presents a class called CMemDC that encapsulates most of the issues associated with writing to off-screen buffers. Adding CMemDC to an existing application or MFC Active X control is nearly trivial.

Modifying an MFC Application to Use CMemDC

  • Add the file memdc.h in your project.
  • Add the line #include "memdc.h" to stdafx.h.
  • Add a windows message handler for WM_ERASEBKGND.
  • Change the code in the message handler as follows:
    // Change this code
    BOOL CExampleView::OnEraseBkgnd(CDC* pDC) 
    {
          // TODO: Add your message handler code here and/or call default
          return CView::OnEraseBkgnd(pDC);
    }
     
    // To this code
    BOOL CExampleView::OnEraseBkgnd(CDC* pDC) 
    {
          return FALSE;
    }
  • Change your OnDraw code to the following:
    void CExampleView::OnDraw(CDC* dc)
    {
        CMemDC pDC(dc);
        CExampleDoc* pDoc = GetDocument();
        ASSERT_VALID(pDoc);
        // TODO: add draw code for native data here - use pDC 
         //as the device context to draw to
    }

Compile your code after you've made these changes and you will notice that the flicker you had seen before is gone.

Modifying a MFC Active X Control to Use CMemDC

To add CMemDC support, you follow the instruction for adding the support to an application, however you make one small change in the OnDraw function.

void CParticleTestCtlCtrl::OnDraw(CDC* pdc, const CRect& rcBounds, 
                                  const CRect& rcInvalid)
{
    CMemDC pDC(pdc, &rcBounds);
    // TODO: add draw code for native data here
    // - use pDC as the device context to draw 
}

The only substantial difference is that the rcBounds is passed to the CMemDC constructor.

Source Code

#ifndef _MEMDC_H_
#define _MEMDC_H_
 
//////////////////////////////////////////////////
// CMemDC - memory DC
//
// Author: Keith Rule
// Email:  keithr@europa.com
// Copyright 1996-2002, Keith Rule
//
// You may freely use or modify this code provided this
// Copyright is included in all derived versions.
//
// History - 10/3/97 Fixed scrolling bug.
//               Added print support. - KR
//
//       11/3/99 Fixed most common complaint. Added
//            background color fill. - KR
//
//       11/3/99 Added support for mapping modes other than
//            MM_TEXT as suggested by Lee Sang Hun. - KR
//
//       02/11/02 Added support for CScrollView as supplied
//             by Gary Kirkham. - KR
//
// This class implements a memory Device Context which allows
// flicker free drawing.
 
class CMemDC : public CDC {
private:       
    CBitmap    m_bitmap;        // Offscreen bitmap
    CBitmap*       m_oldBitmap; // bitmap originally found in CMemDC
    CDC*       m_pDC;           // Saves CDC passed in constructor
    CRect      m_rect;          // Rectangle of drawing area.
    BOOL       m_bMemDC;        // TRUE if CDC really is a Memory DC.
public:
    
    CMemDC(CDC* pDC, const CRect* pRect = NULL) : CDC()
    {
        ASSERT(pDC != NULL); 
 
        // Some initialization
        m_pDC = pDC;
        m_oldBitmap = NULL;
        m_bMemDC = !pDC->IsPrinting();
 
        // Get the rectangle to draw
        if (pRect == NULL) {
             pDC->GetClipBox(&m_rect);
        } else {
             m_rect = *pRect;
        }
 
        if (m_bMemDC) {
             // Create a Memory DC
             CreateCompatibleDC(pDC);
             pDC->LPtoDP(&m_rect);
 
             m_bitmap.CreateCompatibleBitmap(pDC, m_rect.Width(), 
                                                  m_rect.Height());
             m_oldBitmap = SelectObject(&m_bitmap);
 
             SetMapMode(pDC->GetMapMode());
 
             SetWindowExt(pDC->GetWindowExt());
             SetViewportExt(pDC->GetViewportExt());
 
             pDC->DPtoLP(&m_rect);
             SetWindowOrg(m_rect.left, m_rect.top);
        } else {
             // Make a copy of the relevant parts of the current 
             // DC for printing
             m_bPrinting = pDC->m_bPrinting;
             m_hDC       = pDC->m_hDC;
             m_hAttribDC = pDC->m_hAttribDC;
        }
 
        // Fill background 
        FillSolidRect(m_rect, pDC->GetBkColor());
    }
    
    ~CMemDC()      
    {          
        if (m_bMemDC) {
             // Copy the offscreen bitmap onto the screen.
             m_pDC->BitBlt(m_rect.left, m_rect.top, 
                           m_rect.Width(),  m_rect.Height(),
                  this, m_rect.left, m_rect.top, SRCCOPY);            
             
             //Swap back the original bitmap.
             SelectObject(m_oldBitmap);        
        } else {
             // All we need to do is replace the DC with an illegal
             // value, this keeps us from accidentally deleting the 
             // handles associated with the CDC that was passed to 
             // the constructor.              
             m_hDC = m_hAttribDC = NULL;
        }       
    }
    
    // Allow usage as a pointer    
    CMemDC* operator->() 
    {
        return this;
    }       
 
    // Allow usage as a pointer    
    operator CMemDC*() 
    {
        return this;
    }
};
 
#endif

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Keith Rule
Software Developer (Senior)
United States United States
I work at Tektronix in Beaverton OR. I've been programming for fun since 1975 (I started while in a Computer Explorer Scout group in Spokane WA). I've been programming in C since 1979 and I've been working professionally since 1983.

I really enjoy www.codeproject.com. It has saved me an incredible amount of time. I only hope my small contributions have given back some of what I've taken.

Comments and Discussions

 
GeneralRe: Doesn't work in my application derived from CScrollView Class,why? Pin
AniMatrix20-Jun-07 10:11
MemberAniMatrix20-Jun-07 10:11 
Generalgreat Pin
ed2321-Sep-06 9:59
Membered2321-Sep-06 9:59 
QuestionCreateCompatibleDC weirdness Pin
ZeroG_021-Sep-06 7:52
MemberZeroG_021-Sep-06 7:52 
GeneralUseful MemDC Pin
tangyuwu1237-Sep-06 20:14
Membertangyuwu1237-Sep-06 20:14 
GeneralFanstastic Pin
Dave Kerr16-Aug-06 4:06
MemberDave Kerr16-Aug-06 4:06 
GeneralRe: Fanstastic Pin
Keith Rule17-Aug-06 6:52
professionalKeith Rule17-Aug-06 6:52 
QuestionNice but very slow Pin
edwardking20-Jul-06 1:03
Memberedwardking20-Jul-06 1:03 
AnswerRe: Nice but very slow Pin
Keith Rule21-Jul-06 6:10
professionalKeith Rule21-Jul-06 6:10 
edwardking wrote:
Is there anyway to speed this up ? eg, some of the initialization done in the constructor, could this be done just once, instead of with every re-draw.


I've done roughly what you suggested in the past and I'm sure others have too.

My experience (though my memory is vague because I haven't used MFC in several years) was that off-screen drawing is much slower than on-screen drawing. I presume is because you don't benefit from any graphic hardware acceleration with off-screen rendering.

I found that caching the previous rendered data was quite valuable for dealing with redraws when nothing changed which required rerendering. Also, prerendering static portions of the screen into a bitmap and then blitting it into the bitmap before rendering the non-static elements has been helpful to me too.

Another helpful thing to do is only draw inside the clip rect. This limits rendering to a much smaller area.

As I recall, my experience was that these types of changes had much more effect than caching the memdc.

I'd encourage you to post your experience here and if you come up with a nice generalized implementation then please write a codeproject article.


Keith Rule


-- modified at 12:10 Friday 21st July, 2006
QuestionFlicker Free Dialog? Pin
Apollo815-Jul-06 12:52
MemberApollo815-Jul-06 12:52 
AnswerRe: Flicker Free Dialog? Pin
sissyneck25-Jul-07 23:41
Membersissyneck25-Jul-07 23:41 
GeneralVery useful! Pin
FrankSpecht13-Jul-06 10:12
MemberFrankSpecht13-Jul-06 10:12 
Questionan OpenGL-CView [modified] Pin
commander.tank3-Jul-06 4:36
Membercommander.tank3-Jul-06 4:36 
AnswerRe: an OpenGL-CView Pin
Jim Crafton3-Jul-06 6:24
MemberJim Crafton3-Jul-06 6:24 
AnswerRe: an OpenGL-CView Pin
Apollo815-Jul-06 12:58
MemberApollo815-Jul-06 12:58 
GeneralHats off to you !! Pin
revram19-Jun-06 21:21
Memberrevram19-Jun-06 21:21 
GeneralRe: Hats off to you !! Pin
Paolo Bozzoli4-Jul-06 6:19
professionalPaolo Bozzoli4-Jul-06 6:19 
GeneralRe: Hats off to you !! Pin
Aleš Cernivec8-Aug-06 5:15
MemberAleš Cernivec8-Aug-06 5:15 
GeneralSignificance of OnEraseBkgnd Pin
juraitwaluzka6-Jun-06 20:05
Memberjuraitwaluzka6-Jun-06 20:05 
GeneralRe: Significance of OnEraseBkgnd Pin
Keith Rule7-Jun-06 20:16
professionalKeith Rule7-Jun-06 20:16 
GeneralRe: Significance of OnEraseBkgnd Pin
bob1697227-Jun-06 3:31
Memberbob1697227-Jun-06 3:31 
Generalhelp me Pin
tatabaya1-Jun-06 1:42
Membertatabaya1-Jun-06 1:42 
QuestionIssues with RTL Layouts (WS_EX_RTLLAYOUT)? Pin
_Stilgar_30-Mar-06 9:26
Member_Stilgar_30-Mar-06 9:26 
QuestionSetting the background Pin
Arboluva19-Mar-06 3:51
MemberArboluva19-Mar-06 3:51 
GeneralHatchBrush Pin
voy3-Jan-06 21:43
Membervoy3-Jan-06 21:43 
GeneralUn millón de gracias (thanks a lot !!!) Pin
Tron300030-Oct-05 10:59
MemberTron300030-Oct-05 10:59 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.