Click here to Skip to main content
Click here to Skip to main content
Go to top

Double Buffering in a Win32 API Program

, 5 Mar 2012
Rate this:
Please Sign up or sign in to vote.
An article describing how to do animations by using buffers
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 by using buffers. 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 using buffers. How is it done? When using buffers 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 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 to use buffers for drawing. 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 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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

I try to be a behavioral scientist who uses his programming 'skills' to solve problems arising in the field of nonverbal communication.

Comments and Discussions

 
Generalgood stuff PinmemberMisterlolguy13-Oct-10 23:32 

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.

| Advertise | Privacy | Mobile
Web04 | 2.8.140916.1 | Last Updated 5 Mar 2012
Article Copyright 2007 by Markus Koppensteiner
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid