Click here to Skip to main content
Licence 
First Posted 24 Nov 2007
Views 20,862
Downloads 599
Bookmarked 19 times

Double Buffering in a Win32 API Program

By Markus Koppensteiner | 24 Nov 2007
An article describing how to do animations with double buffering
3 votes, 100.0%
1

2

3

4

5
1.00/5 - 4 votes
μ 1.00, σa 1.10 [?]
Screenshot - image001.jpg

Using the Win32 API and the GDI to develop a program may be outdated, but it has its advantages too. Win32 programs are very slim, and in my opinion they are not more complicated than MFC programs (though I must admit that I am not an expert in MFC).

What is the whole thing about? I will mainly describe how to do animations with double buffering. The article is directed to programmers on a more or less advanced level (as I hope to be more or less). You should be familiar with C++ and the structure of a Win32 API program. For basic information, please read some beginners lessons on this website, or look for information on MSDN or download the Forgers Win32 tutorial. There you will find a more detailed discussion of the topics I try to explain here. The comments in the source code might help you too.

Introduction

I think it is very well known that movies consist of single pictures, so called frames. When you present these pictures at a rate of 24 frames per second, the human eye interprets the rapid succession of images as movements. Animations on the computer can be created in a similar manner. You draw a picture, then you delete it, and you draw the next one, then you delete it and so on. This way of doing animations has one big disadvantage. Deleting a picture after it has been drawn, leads to an unpleasant flickering. The remedy for this eye-torturing side-effect is double buffering. How is it done? In double buffering we do not directly draw our pictures onto the screen. Instead we do all the drawing in memory (=the buffer) first and then copy the content of the memory onto the screen. Therefore the whole procedure changes: Draw into memory, copy onto screen, draw into memory, copy onto screen and so on. The delete section has disappeared and the flickering, too. The animations run smoother.

How to Use the Code

Because it is a nuisance to use the API functions, I have developed an -easy to use- class, that simplifies the process of double buffering. You find the corresponding code in Bitmap_OP.cpp and in Bitmap_OP.h. I will not explain the procedures and objects within the class, only how to apply the methods. If you are interested in more details, please look them up on the websites I've mentioned above.

First of all go to WM_CREATE in the main program loop. There you find an instance of the Bitmap_Operations class. The command biop->Initialize_Buffers(hWnd,1) is necessary to define how many buffers you intend to use. Don't forget that all the drawing will be done in these buffers. With biop->CreateBuffer(0) you allocate memory for one buffer (buffer 0).

The SetTimer function is required to tell Windows that something has to happen periodically. We need this function to do the animations.

The WM_TIMER event in the message loop will be called when you set a timer. Look for WM_TIMER and see what happens there. Also look for KillTimer.

The function Draw_With_Buffering() demonstrates how double buffering is done. To understand the details read the comments.

void Draw_With_Buffering()
{
    HBRUSH brush = CreateSolidBrush(RGB(200,170,20));
    HBRUSH background_brush = CreateSolidBrush(RGB(255,255,255));
    
    // brush is applied to the buffer we've created 
    // with biop->CreateBuffer(0) we get the 
    // device context of the buffer (my_DC_Buffer)
    // by calling biop->Get_DC_Buffer(0)
    SelectObject(biop->Get_DC_Buffer(0),brush);

    //FillRect fills the buffer with the colour white
    FillRect(biop->Get_DC_Buffer(0),&rect, background_brush);

    // the drawing function ellipse is applied to the buffer (my_DC_Buffer)
    // we draw the ellipse onto the surface of the buffer stored in memory
    Ellipse(biop->Get_DC_Buffer(0),left + growth ,top - growth  
                    ,right - growth,bottom+ growth);

    // the content of the buffer(the ellipse) is copied onto the
    // screen, strictly speaking onto the device context (hDC) of the
    // main window
    biop->Copy_to_Screen(0);

    // release memory of the brushes
    DeleteObject(background_brush);
    DeleteObject(brush);
}

Please note that we draw into the buffer by using biop->Get_DC_Buffer(0). This method returns the device contexts of the buffers (only one in our case) that we've initialized and created. To display the content of a buffer in our main window, we have to invoke biop->Copy_to_Screen(0).

Let's turn to the function Draw_Without_Buffering(). What do we find there? With InvalidateRect you clear up the main window, by filling it with the color white. Instead of using the methods of the Bitmap_Operations class, we draw the ellipse directly onto the device context of the main window.

void Draw_Without_Buffering(HWND handle)
{
    // window is deleted by filling it up with the current
    // background color (=white)
    InvalidateRect(handle,NULL,true);
    UpdateWindow(handle);
          
    // Get the device context for the main window
    HDC hDC = GetDC(handle);
    HBRUSH brush = CreateSolidBrush(RGB(200,170,20));
    SelectObject(hDC,brush);
        
    // Ellipse function is applied to the device context of the
    // main window
    Ellipse(hDC,left + growth ,top - growth  
            ,right - growth,bottom+ growth); 
            
    // Release memory
    DeleteObject(brush);
    ReleaseDC(handle,hDC);
}

When you run the program, you can switch between double buffering and no buffering. Just look for it in the Mode menu. Compare the two modes and you will easily understand what all the fuss is about. When you try more complex animations, the difference between the two modes will be much more pronounced.

I did not mention all the methods the Bitmap_Operations class contains. But they have names that betray their functions and if you play with them, you might easily find out what they are for. But remember to keep to the correct order when calling the methods. Always use Initialize_Buffers first, then create the number of buffers you need with CreateBuffer.

Afterwards, you can do some drawing operations in the device context of the buffers. To retrieve the device context use Get_DC_Buffer. And do not produce a memory leak by not using Free_Buffer or Free_Buffers (look for WM_DESTROY in my program to find the command).

Feel free to improve the program and try to remove traps, if there are any.

Just one last comment: If you want to do first class animations, don't use GDI commands and also avoid the WM_TIMER event (It is too slow and not accurate enough.).

About Markus Koppensteiner

I am currently working on my PhD in human ethology. Ethologists analyse human and animal behaviour and I've focused on the investigation of body movements. Programming is not more than a hobby for me, but one that has already helped me to solve some tricky problems.

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

Markus Koppensteiner



Austria Austria

Member


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. (secure sign-in)
 
Search this forum  
 FAQ
    Noise  Layout  Per page   
  Refresh
Generalgood stuff PinmemberMisterlolguy0:32 14 Oct '10  

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

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

Permalink | Advertise | Privacy | Mobile
Web01 | 2.5.120206.1 | Last Updated 24 Nov 2007
Article Copyright 2007 by Markus Koppensteiner
Everything else Copyright © CodeProject, 1999-2012
Terms of Use
Layout: fixed | fluid