An Efficient Animation Algorithm

Last reviewed: November 2, 1995
Article ID: Q75431
The information in this article applies to:
  • Microsoft Windows Software Development Kit (SDK) for Windows versions 3.1 and 3.0
  • Microsoft Win32 Application Programming Interface (API) included with:

        - Microsoft Windows NT versions 3.5 and 3.51
        - Microsoft Windows 95 version 4.0
    

SUMMARY

An application that shows an animated image cannot rely solely on Windows graphical device interface (GDI) functions because they will be too slow. Instead, the application must create its own set of bitmaps. This article discusses the process required and provides tips to improve performance and memory use.

This information applies to any type of animation or fast drawing, from painting the game pieces in Reversi to updating the time each second in Clock.

MORE INFORMATION

There are three major steps to this process:

  1. Allocate the bitmap.

It is preferable to allocate a single bitmap to store all the different "cels"--the components of the animated scene. The contents of the bitmap should be arranged in a column that is wide enough to hold a single cel; the height is determined by the number of cels. To improve memory usage, the bitmap should be discardable.

For example, given the definitions of the three constants below, the following code allocates the correct size bitmap:

   X_SIZE = width of the cel
   Y_SIZE = height of the cel
   NUM_CELS = number of cels in the animated sequence

   HBITMAP hbm;

   hbm = CreateDiscardableBitmap(hDC, X_SIZE, NUM_CELS * Y_SIZE);
   if (!hbm)
       {
       // error - could not allocate bitmap
       }

  • Prepare the bitmaps.

    To draw into the bitmap, it must be selected into a display context (DC). Allocate a (temporary) compatible DC for this purpose:

       if (hTmpDC = CreateCompatibleDC(hDC))
           {
           HBITMAP hOldBm;
    
           hOldBm = SelectObject(hTmpDC, hbm);
           // and so forth
           }
    
    
    In many cases, all cels will share the same background. Rather than drawing this background several times onto the bitmap, draw it once onto the first cel and copy it to the other cels, as the following code demonstrates:

       // GDI calls to draw to hbm from (0, 0) to (X_SIZE, Y_SIZE)
    
       for (i = 1; i < NUM_CELS; i++)  // Perform the copy
           BitBlt(hTmpDC, 0, i * Y_SIZE, X_SIZE, Y_SIZE, hTmpDC, 0, 0,
                     SRCCOPY);
    
    
    After the background is copied, draw the foreground on each cel, using regular GDI calls (in TRANSPARENT drawing mode). The coordinates for cel "i" in bitmap hbm are:

       x_pos: 0 to (X_SIZE - 1)
       y_pos: (i * Y_SIZE) to (((i + 1) * Y_SIZE) - 1)
    
    
    If the cels in the bitmap contain sequential images, animating to the screen is simplified.

    To finish this step, release the temporary DC.

       SelectObject(hTmpDC, hOldBm);
       DeleteDC(hTmpDC);
    
    

  • Animate.

    A temporary, off-screen DC is required to allow the application to select the bitmap. Note that selecting the object may fail if the bitmap has been discarded. If this has occurred, another bitmap must be allocated (if memory allows) and the bitmap must be initialized (as outlined in step 2, above).

       if ((hTmpDC = CreateCompatibleDC(hDC)) != NULL)
           {
           HBITMAP hOldBm;
    
           if (!(hOldBm = SelectObject(hTmpDC, hbm))
               // must re-allocate bitmap. Note that this MAY FAIL!!!
    
    
    At this point, call the BitBlt() function to copy the various stages of the animation sequence to the screen. If the cels in the bitmap contain sequential images, a simple loop will do the job nicely, as the following code demonstrates:

           for (i = 0; i < NUM_CELS; i++)
               {
               BitBlt (hDC, x_pos, y_pos, X_SIZE, Y_SIZE, hTmpDC, 0,
                            i * Y_SIZE, SRCCOPY);
    
               // Some form of delay goes here. A real-time wait, based on
               // clock ticks, is recommended.
               }
    
    
    When the drawing is done, delete the temporary DC:

       SelectObject(hTmpDC, hOldBm);
       DeleteDC(hTmpDC);
    
    
    It is important to cancel the selection of the bitmap between passes through the for loop. This allows the bitmap to be discarded if the system runs low on memory.


  • Additional reference words: 3.00 3.10 3.50 4.00 95 animation
    KBCategory: kbgraphic
    KBSubcategory: GdiBmp


    THE INFORMATION PROVIDED IN THE MICROSOFT KNOWLEDGE BASE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. MICROSOFT DISCLAIMS ALL WARRANTIES, EITHER EXPRESS OR IMPLIED, INCLUDING THE WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL MICROSOFT CORPORATION OR ITS SUPPLIERS BE LIABLE FOR ANY DAMAGES WHATSOEVER INCLUDING DIRECT, INDIRECT, INCIDENTAL, CONSEQUENTIAL, LOSS OF BUSINESS PROFITS OR SPECIAL DAMAGES, EVEN IF MICROSOFT CORPORATION OR ITS SUPPLIERS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. SOME STATES DO NOT ALLOW THE EXCLUSION OR LIMITATION OF LIABILITY FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES SO THE FOREGOING LIMITATION MAY NOT APPLY.

    Last reviewed: November 2, 1995
    © 1998 Microsoft Corporation. All rights reserved. Terms of Use.