Click here to Skip to main content
Click here to Skip to main content

Flicker free redrawing and background buffering of drawing with two simple classes

By , 27 Mar 2000
 
  • Download source files - 5 Kb

    Introduction

    I present here two classes which can be used for:

    1. Flicker free drawing
    2. Buffering drawing, for example the background

    The advantages of this two classes are:

    • There are multiple buffers possible for several purposes. Unlike the code of several other CMemDC classes at other sites there is a distinction between the buffers and the code which is used to access these buffers.
    • When we use GetSafeCDC() for all drawing operations we never get a problem during printing or previewing because we simply bypass in this case the pointer to CDC which is given to OnDraw. This simple method will never fail.
    • 16 and 32 bit compatible
    • The buffers can be allocated once. If we have a large screen we do not allocate a new big bitmap for each OnDraw event. If only a part of the screen needs to be redrawn it is still possible to declare and use a temporary buffer.
    • If the buffer allocation fails due to limited system resources all drawings will be done unbuffered
    • If we have drawing operations which do last a long time it is possible to show the user at regular time intervalls (for example each second) intermediate results. The implementation for this is simple: Just call CopyToScreen(1000) during these operations. This will copy all seconds the current content of the background buffer to the screen.

    Note: During preview the CDC supplied to OnDraw is internally a CPreviewDC. This is an internal MFC class. This class does have some functions which are overloaded and not virtual. So when i have used other CMemDCs implementation I had errors during printing and previewing because those CMemDC did not call the functions of CPreviewDC but called athe corresponding functions of CDC.

    Using the classes

    There are a lot of possibilities how to use this classes. Look at the declarations in MemDc.h.

    An example of usage, explained step by step:

    1. Define one or more CBufferMemDC in the header of your CView derived class. In my case I have defined two buffers. One for the general background which is static and one for the information which I had to redraw more often on the background. It is also possible to allocate an additional buffer for the final result of the drawing to buffer the total window client area for the case where the user does switch between the applications for example with Alt-Tab.

      Example:

      CBufferMemDC m_BufferBackground;
      // a buffer for the part of drawing which does not change
      
      CBufferMemDC m_BufferFlickerFreeDrawing;
      // a buffer which is used for flicker free redrawing on top of
      // the background
      
    2. Add the following function to your CView derived class:
      static void CalcSizeTotalAreaWnd( CDC* pDC, const YourView* pView,
                                        CRect& totalAreaWnd ) {
      	if ( pDC->IsPrinting() ){
      	
      // if we are printing we get the total area from the clip box. 
      // Perhaps there is a better way to get this information
      
      		pDC->GetClipBox(&totalAreaWnd);
      	
      	} else {
      
      // if we don't print we just take the client area of the window
      
      		pView->GetClientRect( totalAreaWnd );
      	}
      }
      
    3. Put the implementation of your OnDraw function in another function. For example
      OnRedraw( CDC* pDC).
    4. Modify your OnDraw function:
      void MyView::OnDraw( CDC* pDC )
      {
      	CRect totalAreaWnd;
      	CalcSizeTotalAreaWnd( pDC, this, totalAreaWnd );
      
      	CMemDC memDC( pDC, totalAreaWnd, totalAreaWnd,
                           &m_BufferBackground );
      
      	if ( pDC->IsPrinting() || m_BufferBackground.IsDirty()){
      	
      // draw what you want to draw, for example a line. We simply call here
      // our routine, which we have defined above. It is important not to
      // use memDC, but GetSafeCDC. See the descriptions of this function
      // in the header file. Note: OnRedraw is only called when the
      // background buffer is dirty or when we are printing. Printing
      // processes are not buffered because this can
      // create very big bitmaps if we use for example A0-printers
      
      		OnRedraw( memDC.GetSafeCDC() );	
      			
      // when we have drawn all, we have all information in the bitmap but 
      // not on the screen. So we copy the contents of the buffer to the
      // screen, to show the user what has happened. The user will see only
      // the result and so we have realized the flicker free redrawing
      
      		memDC.CopyToScreen(0);
      	}
      }
      

    My own implementation is more complex because I had to buffer not only the background but also several intermediate states during drawing. But this was no problem with these two classes because these two classes do solve the two main parts of such problems: How to handle a buffer and how to do the buffering.

    At the end I have to say that I was astonished how small the final code was.

    Ernst Versteeg, Switzerland
    ernst@asit.ch

  • License

    This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

    A list of licenses authors might use can be found here

    About the Author

    Ernst Versteeg
    Switzerland Switzerland
    Member
    No Biography provided

    Sign Up to vote   Poor Excellent
    Add a reason or comment to your vote: x
    Votes of 3 or less require a comment

    Comments and Discussions

     
    You must Sign In to use this message board.
    Search this forum  
        Spacing  Noise  Layout  Per page   
    QuestionHow to use in a dialog-based app?memberHawks22 Aug '05 - 7:49 
    Great work but how to use it in a dialog-based app?
    GeneralCFormViewmembermervick6 Jul '04 - 23:58 
    Does this apply to CFormView as well? I can't seem to work it out with CFOrmView. It keeps on flickering when i resize the window. Please do help.
    Generalmemory leak in CBufferMemDC::SetSizeBuffermembershettelbus16 Jun '03 - 13:33 
    Thought I would comment that I found a potential memory leak in this class in the function CBufferMemDC::SetSizeBuffer. The section
    m_pBitmap = new CBitmap;
    if (!m_pBitmap->CreateCompatibleBitmap(pDC, m_SizeBitmap.Width(), m_SizeBitmap.Height())){
    	AfxMessageBox("...");
    	m_pBitmap = NULL;
    }
    should instead read
    m_pBitmap = new CBitmap;
    if (!m_pBitmap->CreateCompatibleBitmap(pDC, m_SizeBitmap.Width(), m_SizeBitmap.Height())){
    	AfxMessageBox("...");
    	delete m_pBitmap;
    	m_pBitmap = NULL;
    }
    Otherwise there will be a memory leak if the call to CreateCompatibleBitmap fails.
     
    Regards,
    shettelbus
    GeneralMemory Leaks!memberklm25 Jul '02 - 20:36 
    Hello,
     
    the function of your class is good, but when i exit my programm i get this messages:
     
    Detected Memory Leaks!
     
    (A lot of CBitmap-Object)

    GeneralRe: Memory Leaks!memberklm25 Jul '02 - 23:52 
    Hello,
     
    the error was in my own code, in the dialog where i have created a member-variable of your classes.
     
    There are no memory leaks in your classes!
     

    Sorry!
    QuestionCool But?memberjack Mesic14 Apr '02 - 20:25 
    cool,but it is too slow.
     
    Regards
    http://www.ucancode.net/
    QuestionWhat OnRedraw()???memberRolee713 Sep '01 - 16:24 
    How to code "OnRedraw()"?
    Give me Example source plz (Anybody...)
     
    PS, Sorry i can't english well... T.T
    Roll eyes | :rolleyes:
    GeneralCMemDCmemberAnonymous19 Aug '01 - 22:27 
    >...CMemDC did not call the functions of CPreviewDC
    Why would you use CMemDC or CBufferMemDC when printing?
    CMyView::OnDraw(CDC*pDC)
    {
    CMemDC* pMemDC = NULL;
    if (!pDC->IsPrinting())
    {
    pDC = pMemDC = new CMemDC(pDC);
    }
    ...draw using pDC
    delete pMemDC;
    }

    QuestionBlack background???memberHassan4 May '01 - 0:13 
    hi!
    I am using these classes in my application. When i used these classes, the background becomes Black.
    What may be the problem??
    Hoping to hear from u soon.
    regards'
    Hassan
     
    Hassan
    AnswerRe: Black background???sussAnonymous8 Mar '03 - 10:52 
    HI,
     
    If any one found remedx of this problem, please do post.
     
    Thanks.
    Jabran
    GeneralRe: Black background???memberTydia-kun12 Aug '06 - 4:16 
    I do believe I have a solution to this problem if you still need it.
    What I did was fill the buffer (CBufferMemDC) with everything that should be drawn on the screen first. Then you can do whatever you want with it.
    GeneralUse with CScrollViewsussRichard25 Sep '00 - 7:47 
    I get these 2 classes to works great with a normal CView class (many thanks to the author). But when I use it with CSCrollView it gives problems.
     
    The screen is redrawn incorrectly when the screen is scrolled and then resized.
     
    Any idea how to fix this?
    Richar
    GeneralRe: Use with CScrollViewmemberAshutosh21 Oct '01 - 23:58 
    Hi Richard,
     
    You could use the following piece of code in your CScrollView-derived class's OnDraw()
     
    CYourCSView::OnDraw(CDC* pDC)
    {
    // initalize your MemDC object
    ...
    // add following piece of code
    CPoint point;
    point = GetScrollPosition();// this is CScrollView's fxn that returns the point scrolled
     
    if( memDC.GetWindowOrigin() != point )
    {
    memDC.SetWindowOrigin(point);
    }
    ...
    // your drawing code here
    }
     
    // declarations of Set and Get functions used above in the memDC class's .h file
    private:
    CPoint m_ptOrigin;
    ...
     
    public:
    CPoint GetWindowOrigin();
    void SetWindowOrigin(CPoint ptOrg);
    ...
    // definitions of Set and Get functions used above in the memDC class's .cpp file
    void CMemDC::SetWindowOrigin(CPoint ptOrg)
    {
    if ( m_bMemDC )
    {
    m_ptOrigin = ptOrg;
    SetWindowOrg(m_ptOrigin.x, m_ptOrigin.y);
    SetBrushOrg(m_ptOrigin.x % 8, m_ptOrigin.y % 8);
    }
    }
     
    CPoint CMemDC::GetWindowOrigin()
    {
    return m_ptOrigin;
    }
    ///////////////////////////////////////////////////////////////////////
     
    Hope this helps.
     
    Regards,
    Smile | :)
     
    Ashutosh
    Generalproblems copying data between bufferssussYosi Keller28 Aug '00 - 10:11 
    I tried using this code, it workrd fine with one buffer
    (ofcourse I did not get any speed increase).
    But when I added another buffer I could not copy the data
    from the background buffer to the current buffer.
    Can you please Emai/post a simple 2 buffer running demo
     

    GeneralRe: problems copying data between bufferssussErnst Versteeg28 Aug '00 - 12:00 
    I wrote this classes some time ago as helper classes for a big application. I didn't have problems with
    them and so i don't have a simple demo example. I now don't have the time to write such an example
    and i am not very interested in such code because i now don't work on Microsoft Windows any more
    because the future is not based on Microsoft any more..
     
    If you have a simple example which does not work i can look where the error is and then we can publish
    it here
    GeneralSpeed IncreasesussDavid Gallagher21 Aug '00 - 13:48 
    If you change the code in
    BOOL CBufferMemDC::SetSizeBuffer( CDC *pDC, CRect rect )
     
    to read
     
    if ( (m_SizeBitmap.Width() < rect.Width() ) | (m_SizeBitmap.Height() < rect.Height() ))
    Reset();
     
    instead of
    if (m_SizeBitmap != rect)
    Reset();
     
    The buffer is only re-allocated when it is too small for the current view.
    GeneralRe: Speed IncreasememberHassan7 May '01 - 1:13 
    I have tried it. I am pleased to mention that there is no more flickering in my drawing. But there is a problem, now the drawing becomes pretty slow.
    I am drawing various maps on the screen. Whenever i resize or drag some custom figure (i.e. rectangle, triangle etc.), drawing becomes pretty slow.
    Can any one suggest any idea of improving the speed of drawing?
    Thanx in advance.
     

    Hassan Zia
    GeneralOnEraseBgnd !sussDavid Gallagher21 Aug '00 - 13:01 
    Good Solution, but don't forget to override
    OnEraseBkgnd(CDC* pDC)
     
    BOOL CMyView::OnEraseBkgnd(CDC* pDC)
    {
    return FALSE;
    }
     
    this stops MFC from clearing the background during sizing etc
    Generalyes, it's good idea.memberAndy Byron7 Dec '03 - 16:26 
    yes, it's good idea.

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

    Permalink | Advertise | Privacy | Mobile
    Web04 | 2.6.130523.1 | Last Updated 28 Mar 2000
    Article Copyright 2000 by Ernst Versteeg
    Everything else Copyright © CodeProject, 1999-2013
    Terms of Use
    Layout: fixed | fluid