Click here to Skip to main content
15,897,273 members
Articles / Web Development / HTML

Flicker Free Drawing In MFC

Rate me:
Please Sign up or sign in to vote.
4.96/5 (174 votes)
25 Mar 2002CPOL1 min read 1.2M   18K   314   234
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:
    C++
    // 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:
    C++
    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.

C++
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

C++
#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)


Written By
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: streaks and smudging Pin
parama_chakra23-Jun-05 19:17
parama_chakra23-Jun-05 19:17 
GeneralRe: streaks and smudging Pin
AnonymousBabe@usa.net9-Sep-05 17:39
AnonymousBabe@usa.net9-Sep-05 17:39 
GeneralRe: streaks and smudging Pin
parama_chakra9-Sep-05 18:17
parama_chakra9-Sep-05 18:17 
GeneralRe: streaks and smudging Pin
AnonymousBabe@usa.net9-Sep-05 17:51
AnonymousBabe@usa.net9-Sep-05 17:51 
GeneralRe: streaks and smudging Pin
Alberto_Canabal30-Nov-05 1:23
Alberto_Canabal30-Nov-05 1:23 
GeneralRe: streaks and smudging [modified] Pin
bob169724-Mar-06 1:05
bob169724-Mar-06 1:05 
GeneralRe: streaks and smudging [modified] Pin
bob1697216-Mar-06 16:56
bob1697216-Mar-06 16:56 
AnswerRe: streaks and smudging [modified] Pin
Matt Clarkson13-Aug-09 1:28
Matt Clarkson13-Aug-09 1:28 
Right so after 4 days of solid work on this i have finally found out what is wrong.

It resides in the rounding errors between DP and LP and that the offscreen bitmap and the DC get misaligned.

It eventually boiled down to SetWindowOrg(m_rect.left, m_rect.top); resulting in misalignment.

I replaced this with:

// Set the same viewport origin so they are aligned
CPoint viewportOrg = pDC->GetViewportOrg();
SetViewportOrg(viewportOrg);

I have added/removed bits to the class to make it work with my CScrollView. I got rid of the DPtoLP conversions as I was finding errors in the rounding.

You MUST pass it a rectangle that outlines the client box:

CRect clientRect;
GetClientRect(&clientRect);
CMemDC pMemDC(pDC, &clientRect);

Note: This works with my CScrollView. I haven't tested it in any other situations.

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_updateRect_LP;	// Rectangle of drawing area to update in logical units
	CRect		m_clientRect_DP;	// Rectangle of drawing area in device units
	BOOL		m_bMemDC;			// TRUE if CDC really is a Memory DC.

public:
	
	CMemDC(CDC* pDC, const CRect* pClientRect) : CDC()
	{
		ASSERT(pDC != NULL); 
		ASSERT(pClientRect != NULL);

		// Some initialization
		m_pDC = pDC;
		m_oldBitmap = NULL;
		m_bMemDC = !pDC->IsPrinting();

		// Get the client rect 
		m_clientRect_DP = *pClientRect;

		// Get the area of the display that needs updating.
		pDC->GetClipBox(&m_updateRect_LP);

		if (m_bMemDC) {

			// Create a Memory DC
			CreateCompatibleDC(pDC);

			// Create offscreen bitmap
			m_bitmap.CreateCompatibleBitmap(pDC, m_clientRect_DP.Width(), m_clientRect_DP.Height());

			// Set up the map mode
			SetMapMode(pDC->GetMapMode());

			// Set up the zoom
			SetWindowExt(pDC->GetWindowExt());
			SetViewportExt(pDC->GetViewportExt());

			// Store pointer to the bitmap
			m_oldBitmap = SelectObject(&m_bitmap);

			// Set the same viewport origin so they are aligned
			CPoint viewportOrg = pDC->GetViewportOrg();
			SetViewportOrg(viewportOrg);

                        // Set the same window origin so they are aligned
			CPoint windowOrg = pDC->GetWindowOrg();
			SetWindowOrg(windowOrg);
			
			// Inflate to avoid rounding errors
			m_updateRect_LP.InflateRect(1,1);

			// Reset the clip region for the memory DC
			SelectClipRgn(NULL);

			// Update the clip region
			IntersectClipRect(&m_updateRect_LP);

		}
		else
		{
			// Make a copy of the relevent parts of the current DC for printing
			m_bPrinting = pDC->m_bPrinting;
			m_hDC       = pDC->m_hDC;
			m_hAttribDC = pDC->m_hAttribDC;
		}
	}
	
	~CMemDC()	
	{		
		if (m_bMemDC)
		{
			// Copy the offscreen bitmap onto the screen.
			m_pDC->BitBlt(
				m_updateRect_LP.left,
				m_updateRect_LP.top,
				m_updateRect_LP.Width(),
				m_updateRect_LP.Height(),
				this,
				m_updateRect_LP.left,
				m_updateRect_LP.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 accidently 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;
	}
};

Matt Clarkson

"When the world says 'Give up',
Hope whispers 'Try it one more time.'"

Edit: Added SetWindowOrg.

modified on Wednesday, August 19, 2009 5:57 AM

Generalnice and a little question Pin
DaviziN12-May-05 1:04
DaviziN12-May-05 1:04 
GeneralRe: nice and a little question Pin
sohbear2-Jun-05 20:09
sohbear2-Jun-05 20:09 
GeneralUse with MM_ISOTROPIC Pin
daniel_lidstrom2-May-05 23:08
daniel_lidstrom2-May-05 23:08 
GeneralRe: Use with MM_ISOTROPIC Pin
parama_chakra19-Jun-05 18:50
parama_chakra19-Jun-05 18:50 
GeneralMemDC's font is not the same! Pin
Fanwy27-Mar-05 15:48
Fanwy27-Mar-05 15:48 
GeneralRe: MemDC's font is not the same! Pin
Keith Rule28-Mar-05 8:06
professionalKeith Rule28-Mar-05 8:06 
Questionneed to free memory resource ? Pin
panenhan21-Mar-05 15:59
panenhan21-Mar-05 15:59 
AnswerRe: need to free memory resource ? Pin
Keith Rule28-Mar-05 8:08
professionalKeith Rule28-Mar-05 8:08 
GeneralDoesn't work for most MM_xxx modes Pin
Vitaly Tomilov15-Mar-05 3:31
Vitaly Tomilov15-Mar-05 3:31 
GeneralRe: Doesn't work for most MM_xxx modes Pin
Keith Rule15-Mar-05 6:05
professionalKeith Rule15-Mar-05 6:05 
GeneralDoesn't work with clipped DC Pin
Vitaly Tomilov13-Jan-05 4:39
Vitaly Tomilov13-Jan-05 4:39 
QuestionHow does this work with Dialog Based Apps? Pin
Jinegelen30-Nov-04 14:15
Jinegelen30-Nov-04 14:15 
GeneralUpdate bug dragging window out of screen Pin
Jesper Knudsen9-Nov-04 2:59
Jesper Knudsen9-Nov-04 2:59 
GeneralKeith, thanks ! Pin
andré_k8-Nov-04 0:34
andré_k8-Nov-04 0:34 
Generalonly for very simple drawings Pin
gugul6-Nov-04 4:38
sussgugul6-Nov-04 4:38 
GeneralRe: only for very simple drawings Pin
Keith Rule9-Nov-04 8:05
professionalKeith Rule9-Nov-04 8:05 
GeneralTHNX!!! Pin
mark_sa5-Oct-04 14:20
mark_sa5-Oct-04 14:20 

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.