/* GAME PROGRAMMING TUTORIAL Copyright (C) 1996 Emmanuel Lagare DOUBLE BUFFERING The problem with writing directly to the video buffer is flicker. This is caused by the viewer actually seeing your sprites being erased and redrawn on the screen. A solution is double buffering. We buffer data before it is sent to the video buffer so that the viewer only sees the completed image. There is of course a speed trade-off since the viewer no longer sees the changes as quickly. In fact, double buffering can be very slow on systems equipped with slow display cards (such as mine). To do double buffering we first allocate a 64,000 byte buffer (320 x 200). Then we modify put_pixel, bit_blt, reverse_bitblt, and transparent_bitblt by changing all references to video_buffer into double_buffer. And while we're at it, let's define a function for clearing a buffer: void clear(char *buffer, char color) { memset(buffer,color,64000); } Then we add a function which simply memcpy the contents of the double buffer to the video buffer. Ooops. Not that simple, there is still shearing to consider. Shearing is caused by the video hardware updating the screen when you are still memcpying the double buffer to the video buffer. The result is you see the old image and the new image at the same time ___________ old screen | | shear line | | new screen | | |___________| To prevent shearing, we must update the video buffer only at the time when the video hardware is not accessing the video buffer. This happens during the vertical retrace period, the time when the video hardware has finished refreshing the last line of the screen and is rushing back to the first line for the next refresh. The information we need can be found at port 0x3da, bit 3 (4th bit). If this bit is 0 then there is no retrace in progess. If this is 1 then a retrace in progress. If a retrace is in progress we do the following: 1. wait until the retrace stops 2. wait until the retrace starts If not we simply wait until the retrace starts. All that done, we might want to retain the capability to access the video buffer directly. We have two options: a. Create a new set of functions (put_pixel_v, bitblt_v, etc.) b. Modify the value of the double_buffer pointer at run-time (i.e. make it point to the double buffer or the video buffer) It all depends on what you want to do. Below is the usual example. */ #include #include #include #include #include #define GRAPHICS 0x013 #define TEXT 0x03 char far *video_buffer = MK_FP(0xa000, 0x0000); char far *double_buffer; extern void vsync(void); typedef struct sprite_type { unsigned x, y; /* location */ unsigned x_vel, y_vel; /* velocity */ char width, height; /* dimensions */ char num_frames; /* number of frames */ char cur_frame; /* current frame */ char far *background, far **frame; } sprite; void set_video_mode(int mode) { union REGS regs; regs.x.ax = mode; int86(0x10, ®s, ®s); } void put_pixel(short int x, short int y, char color) { double_buffer[(y << 8) + (y << 6) + x] = color; } void bit_blt(int x, int y, char far *bitmap) { int yindex, offset = (y << 8) + (y << 6) + x; char width = bitmap[0], height = bitmap[1]; bitmap += 2; /* skip the first two bytes */ for (yindex = 0; yindex < height; yindex++) { _fmemcpy((char far *)double_buffer+offset,bitmap,width); offset += 320; /* next line of double buffer */ bitmap += width; /* next line of bitmap */ } } void reverse_bit_blt(int x, int y, char far *bitmap) { int yindex, offset = (y << 8) + (y << 6) + x; char width = bitmap[0], height = bitmap[1]; bitmap += 2; /* skip the first two bytes */ for (yindex = 0; yindex < height; yindex++) { _fmemcpy(bitmap,(char far*)double_buffer+offset,width); offset += 320; /* next line of double buffer */ bitmap += width; /* next line of bitmap */ } } void transparent_bit_blt(int x, int y, char far *buffer) { int xindex, yindex, offset = (y << 8) + (y << 6) + x; char width = buffer[0], height = buffer[1]; buffer += 2; /* skip the first two bytes */ for (yindex = 0; yindex < height; yindex++) { for (xindex = 0; xindex < width; xindex++) { if (buffer[xindex]) { double_buffer[offset+xindex] = buffer[xindex]; } } offset += 320; /* next line of double buffer */ buffer += width; /* next line of bitmap */ } } /* new */ void clear(char far *buffer, char color) { _fmemset(buffer,color,64000); } /* new */ void show_double_buffer(void) { vsync(); _fmemcpy((char far*)video_buffer, (char far*)double_buffer, 64000); } void draw_sprite(sprite *sprite) { sprite->x += sprite->x_vel; sprite->y += sprite->y_vel; reverse_bit_blt(sprite->x,sprite->y,sprite->background); transparent_bit_blt(sprite->x,sprite->y,sprite->frame[sprite->cur_frame]); } void erase_sprite(sprite *sprite) { bit_blt(sprite->x,sprite->y,sprite->background); } char far bitmap[102] = { 10,10, /* width and height of block */ 15,15,15,15,15,15,15,15,15,15, /* strip 1 */ 15,00,00,00,00,00,00,00,00,15, 15,00,00,00,00,00,00,00,00,15, 15,00,00,00,00,00,00,00,00,15, 15,00,00,00,00,00,00,00,00,15, 15,00,00,00,00,00,00,00,00,15, 15,00,00,00,00,00,00,00,00,15, 15,00,00,00,00,00,00,00,00,15, 15,00,00,00,00,00,00,00,00,15, 15,15,15,15,15,15,15,15,15,15 /* strip 10 */ }; void main(void) { unsigned count, i; sprite sprite; /* setup double buffer */ double_buffer = (char far*)farmalloc(64000); clear(double_buffer, 0); /* go to graphics mode */ set_video_mode(GRAPHICS); /* initialize our sprite */ sprite.x = 0; sprite.y = 95; sprite.x_vel = 1; sprite.y_vel = 0; sprite.width = 10; sprite.height = 10; sprite.num_frames = 1; sprite.cur_frame = 0; sprite.background = (char far *)farmalloc(sprite.width*sprite.height+2); sprite.background[0] = 10; sprite.background[1] = 10; sprite.frame = (char far **)farmalloc(sizeof(char far*)*sprite.num_frames); sprite.frame[0] = (char far*)&bitmap; /* fill up the screen with random pixels */ for(count = 0; count < 32000; count++) { put_pixel(rand() % 320, rand() % 200, rand() % 256); } show_double_buffer(); /* move sprite from left to right */ for(count = 0; count < 310; count++) { draw_sprite(&sprite); show_double_buffer(); vsync(); erase_sprite(&sprite); } /* go back to text mode */ set_video_mode(TEXT); /* reset double buffer */ farfree(double_buffer); } .