### Animation {#Heading6} Gosh. There's just no way I can discuss high-level animation fundamentals in any detail here; I could spend an entire (and entirely separate) book on animation techniques alone. You might want to have a look at Chapters 43 through 46 before attacking the code in this chapter; that will have to do us for the present volume. (I will return to *3-D* animation in the next chapter.) Basically, I'm going to perform page flipped animation, in which one page (that is, a bitmap large enough to hold a full screen) of display memory is displayed while another page is drawn to. When the drawing is finished, the newly modified page is displayed, and the other—now invisible—page is drawn to. The process repeats ad infinitum. For further information, some good places to start are *Computer Graphics*, by Foley and van Dam (Addison-Wesley); *Principles of Interactive Computer Graphics*, by Newman and Sproull (McGraw Hill); and "Real-Time Animation" by Rahner James (January 1990, *Dr. Dobb's Journal*). Some of the code in this chapter was adapted for Mode X from the code in Chapter 44—yet another reason to read that chapter before finishing this one. ### Mode X Animation in Action {#Heading7} Listing 49.5 ties together everything I've discussed about Mode X so far in a compact but surprisingly powerful animation package. Listing 49.5 first uses solid and patterned fills and system-memory-to-screen-memory masked copying to draw a static background containing a mountain, a sun, a plain, water, and a house with puffs of smoke coming out of the chimney, and sets up the four alignments of a masked kite image. The background is transferred to both display pages, and drawing of 20 kite images in the nondisplayed page using fast masked copying begins. After all images have been drawn, the page is flipped to show the newly updated screen, and the kites are moved and drawn in the other page, which is no longer displayed. Kites are erased at their old positions in the nondisplayed page by block copying from the background page. (See the discussion in the previous chapter for the display memory organization used by Listing 49.5.) So far as the displayed image is concerned, there is never any hint of flicker or disturbance of the background. This continues at a rate of up to 60 times a second until Esc is pressed to exit the program. See Figure 49.1 for a screen shot of the resulting image—add the animation in your imagination. ![](images/49-01.jpg)\ **Figure 49.1**  *An animated Mode X screen.* **LISTING 49.5 L49-5.C** /* Sample mode X VGA animation program. Portions of this code first appeared in PC Techniques. Compiled with Borland C++ 2.0 in C compilation mode. */ #include #include #include #include #include "maskim.h" #define SCREEN_SEG 0xA000 #define SCREEN_WIDTH 320 #define SCREEN_HEIGHT 240 #define PAGE0_START_OFFSET 0 #define PAGE1_START_OFFSET (((long)SCREEN_HEIGHT*SCREEN_WIDTH)/4) #define BG_START_OFFSET (((long)SCREEN_HEIGHT*SCREEN_WIDTH*2)/4) #define DOWNLOAD_START_OFFSET (((long)SCREEN_HEIGHT*SCREEN_WIDTH*3)/4) static unsigned int PageStartOffsets[2] = {PAGE0_START_OFFSET,PAGE1_START_OFFSET}; static char GreenAndBrownPattern[] = {2,6,2,6, 6,2,6,2, 2,6,2,6, 6,2,6,2}; static char PineTreePattern[] = {2,2,2,2, 2,6,2,6, 2,2,6,2, 2,2,2,2}; static char BrickPattern[] = {6,6,7,6, 7,7,7,7, 7,6,6,6, 7,7,7,7,}; static char RoofPattern[] = {8,8,8,7, 7,7,7,7, 8,8,8,7, 8,8,8,7}; #define SMOKE_WIDTH 7 #define SMOKE_HEIGHT 7 static char SmokePixels[] = { 0, 0,15,15,15, 0, 0, 0, 7, 7,15,15,15, 0, 8, 7, 7, 7,15,15,15, 8, 7, 7, 7, 7,15,15, 0, 8, 7, 7, 7, 7,15, 0, 0, 8, 7, 7, 7, 0, 0, 0, 0, 8, 8, 0, 0}; static char SmokeMask[] = { 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0}; #define KITE_WIDTH 10 #define KITE_HEIGHT 16 static char KitePixels[] = { 0, 0, 0, 0,45, 0, 0, 0, 0, 0, 0, 0, 0,46,46,46, 0, 0, 0, 0, 0, 0,47,47,47,47,47, 0, 0, 0, 0,48,48,48,48,48,48,48, 0, 0, 49,49,49,49,49,49,49,49,49, 0, 0,50,50,50,50,50,50,50, 0, 0, 0,51,51,51,51,51,51,51, 0, 0, 0, 0,52,52,52,52,52, 0, 0, 0, 0, 0,53,53,53,53,53, 0, 0, 0, 0, 0, 0,54,54,54, 0, 0, 0, 0, 0, 0, 0,55,55,55, 0, 0, 0, 0, 0, 0, 0, 0,58, 0, 0, 0, 0, 0, 0, 0, 0, 0,59, 0, 0, 0, 0,66, 0, 0, 0, 0,60, 0, 0,64, 0,65, 0, 0, 0, 0, 0,61, 0, 0,64, 0, 0, 0, 0, 0, 0, 0,62,63, 0,64}; static char KiteMask[] = { 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1}; static MaskedImage KiteImage; #define NUM_OBJECTS 20 typedef struct { int X,Y,Width,Height,XDir,YDir,XOtherPage,YOtherPage; MaskedImage *Image; } AnimatedObject; AnimatedObject AnimatedObjects[] = { { 0, 0,KITE_WIDTH,KITE_HEIGHT, 1, 1, 0, 0,&KiteImage}, { 10, 10,KITE_WIDTH,KITE_HEIGHT, 0, 1, 10, 10,&KiteImage}, { 20, 20,KITE_WIDTH,KITE_HEIGHT,-1, 1, 20, 20,&KiteImage}, { 30, 30,KITE_WIDTH,KITE_HEIGHT,-1,-1, 30, 30,&KiteImage}, { 40, 40,KITE_WIDTH,KITE_HEIGHT, 1,-1, 40, 40,&KiteImage}, { 50, 50,KITE_WIDTH,KITE_HEIGHT, 0,-1, 50, 50,&KiteImage}, { 60, 60,KITE_WIDTH,KITE_HEIGHT, 1, 0, 60, 60,&KiteImage}, { 70, 70,KITE_WIDTH,KITE_HEIGHT,-1, 0, 70, 70,&KiteImage}, { 80, 80,KITE_WIDTH,KITE_HEIGHT, 1, 2, 80, 80,&KiteImage}, { 90, 90,KITE_WIDTH,KITE_HEIGHT, 0, 2, 90, 90,&KiteImage}, {100,100,KITE_WIDTH,KITE_HEIGHT,-1, 2,100,100,&KiteImage}, {110,110,KITE_WIDTH,KITE_HEIGHT,-1,-2,110,110,&KiteImage}, {120,120,KITE_WIDTH,KITE_HEIGHT, 1,-2,120,120,&KiteImage}, {130,130,KITE_WIDTH,KITE_HEIGHT, 0,-2,130,130,&KiteImage}, {140,140,KITE_WIDTH,KITE_HEIGHT, 2, 0,140,140,&KiteImage}, {150,150,KITE_WIDTH,KITE_HEIGHT,-2, 0,150,150,&KiteImage}, {160,160,KITE_WIDTH,KITE_HEIGHT, 2, 2,160,160,&KiteImage}, {170,170,KITE_WIDTH,KITE_HEIGHT,-2, 2,170,170,&KiteImage}, {180,180,KITE_WIDTH,KITE_HEIGHT,-2,-2,180,180,&KiteImage}, {190,190,KITE_WIDTH,KITE_HEIGHT, 2,-2,190,190,&KiteImage}, }; void main(void); void DrawBackground(unsigned int); void MoveObject(AnimatedObject *); extern void Set320x240Mode(void); extern void FillRectangleX(int, int, int, int, unsigned int, int); extern void FillPatternX(int, int, int, int, unsigned int, char*); extern void CopySystemToScreenMaskedX(int, int, int, int, int, int, char *, unsigned int, int, int, char *); extern void CopyScreenToScreenX(int, int, int, int, int, int, unsigned int, unsigned int, int, int); extern unsigned int CreateAlignedMaskedImage(MaskedImage *, unsigned int, char *, int, int, char *); extern void CopyScreenToScreenMaskedX(int, int, int, int, int, int, MaskedImage *, unsigned int, int); extern void ShowPage(unsigned int); void main() { int DisplayedPage, NonDisplayedPage, Done, i; union REGS regset; Set320x240Mode(); /* Download the kite image for fast copying later. */ if (CreateAlignedMaskedImage(&KiteImage, DOWNLOAD_START_OFFSET, KitePixels, KITE_WIDTH, KITE_HEIGHT, KiteMask) == 0) { regset.x.ax = 0x0003; int86(0x10, ®set, ®set); printf("Couldn't get memory\n"); exit(); } /* Draw the background to the background page. */ DrawBackground(BG_START_OFFSET); /* Copy the background to both displayable pages. */ CopyScreenToScreenX(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0, BG_START_OFFSET, PAGE0_START_OFFSET, SCREEN_WIDTH, SCREEN_WIDTH); CopyScreenToScreenX(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0, BG_START_OFFSET, PAGE1_START_OFFSET, SCREEN_WIDTH, SCREEN_WIDTH); /* Move the objects and update their images in the nondisplayed page, then flip the page, until Esc is pressed. */ Done = DisplayedPage = 0; do { NonDisplayedPage = DisplayedPage ^ 1; /* Erase each object in nondisplayed page by copying block from background page at last location in that page. */ for (i=0; iX + ObjectToMove->XDir; Y = ObjectToMove->Y + ObjectToMove->YDir; if ((X < 0) || (X > (SCREEN_WIDTH - ObjectToMove->Width))) { ObjectToMove->XDir = -ObjectToMove->XDir; X = ObjectToMove->X + ObjectToMove->XDir; } if ((Y < 0) || (Y > (SCREEN_HEIGHT - ObjectToMove->Height))) { ObjectToMove->YDir = -ObjectToMove->YDir; Y = ObjectToMove->Y + ObjectToMove->YDir; } /* Remember previous location for erasing purposes. */ ObjectToMove->XOtherPage = ObjectToMove->X; ObjectToMove->YOtherPage = ObjectToMove->Y; ObjectToMove->X = X; /* set new location */ ObjectToMove->Y = Y; }