//------------------------------------------------------------------- // tweening.cpp // // This program demonstrates the application of an animation // technique, known as "tweening", in which a figure appears // to move from one position to another by selecting certain // "key" points which define the figure in its two positions // and using linear interpolations to construct the figure's // intervening positions. // // programmer: ALLAN CRUSE // written on: 19 APR 1992 // revised on: 25 OCT 2003 -- ported demo from MS-DOS to Linux // revised on: 29 OCT 2003 -- remove SiS-specific dependencies //------------------------------------------------------------------- #include // for printf(), perror() #include // for open() #include // for exit() #include // for close() #include // for tcgetattr(), tcsetattr() #include // for mmap() #include // for iopl() #define VRAM_BASE_ADDRESS 0xA0000000 #define VGA_MEMORY_LENGTH ( 4 << 20) #define MAXVERT 20 typedef struct { float x, y; } point_t; typedef struct { int numverts; point_t vert[ MAXVERT ]; } polyline_t; unsigned char *vram = (unsigned char*)VRAM_BASE_ADDRESS; const int display_mode = 0x4101, hres = 640, vres = 480; void copy_page( int src_page, int dst_page ) { int pagesize = hres * vres; long *srcp = (long*)&vram[ src_page * pagesize ]; long *dstp = (long*)&vram[ dst_page * pagesize ]; pagesize /= 4; for (int i = 0; i < pagesize; i++) dstp[ i ] = srcp[ i ]; } int map_system_memory( int base, int size ) { int fd = open( "/dev/vram", O_RDWR ); if ( fd < 0 ) { perror( "/dev/vram" ); return -1; } int prot = PROT_READ | PROT_WRITE; int flag = MAP_FIXED | MAP_SHARED; void *mm = (void*)base; if ( mmap( mm, size, prot, flag, fd, 0 ) == MAP_FAILED ) { perror( "mmap" ); return -1; } close( fd ); } void draw_pixel( int x, int y, int color, int page ) { unsigned char *dstn = &vram[ (hres*vres) * page ]; if (( x < 0 )||( x >= hres )) return; if (( y < 0 )||( y >= vres )) return; dstn[ y*hres + x ] = color; } void draw_line( int x1, int y1, int x2, int y2, int color, int page ) { int deltax = x2 - x1; int deltay = y2 - y1; int xinc = ( deltax < 0 ) ? -1 : 1; int yinc = ( deltay < 0 ) ? -1 : 1; if ( deltax < 0 ) deltax = - deltax; if ( deltay < 0 ) deltay = - deltay; draw_pixel( x1, y1, color, page ); if (( deltax == 0 )&&( deltay == 0 )) return; else if ( deltay <= deltax ) { int error_term = - deltax; while ( x1 != x2 ) { error_term += 2*deltay; if ( error_term >= 0 ) { y1 += yinc; error_term -= 2*deltax; } x1 += xinc; draw_pixel( x1, y1, color, page ); } } else { int error_term = - deltay; while ( y1 != y2 ) { error_term += 2*deltax; if ( error_term >= 0 ) { x1 += xinc; error_term -= 2*deltay; } y1 += yinc; draw_pixel( x1, y1, color, page ); } } } void draw_rectangle( int x, int y, int h, int v, int color, int page ) { unsigned char *dstn = &vram[ (hres*vres) * page ]; int minx = 0, miny = 0, maxx = hres-1, maxy = vres-1; do { dstn[ y*hres + x ] = color; ++x; } while ( x < maxx ); do { dstn[ y*hres + x ] = color; ++y; } while ( y < maxy ); do { dstn[ y*hres + x ] = color; --x; } while ( x > minx ); do { dstn[ y*hres + x ] = color; --y; } while ( y > miny ); } void wipe_page( int page ) { int pagesize = hres * vres; long *dstn = (long*)&vram[ page * pagesize ]; pagesize /= 4; for (int i = 0; i < pagesize; i++) dstn[ i ] = 0; } void make_polyline( polyline_t &p, int numverts ) { p.numverts = numverts; for (int i = 1; i <= numverts; i++) { p.vert[i].x = 5 + rand()%(hres - 11); p.vert[i].y = 5 + rand()%(vres - 11); } p.vert[0].x = p.vert[numverts].x; p.vert[0].y = p.vert[numverts].y; } void inbetween( polyline_t &p, polyline_t &q, float t, polyline_t &b ) { if ( p.numverts != q.numverts ) return; b.numverts = p.numverts; for (int i = 0; i <= b.numverts; i++) { b.vert[i].x = t * p.vert[i].x + (1.0 - t) * q.vert[i].x; b.vert[i].y = t * p.vert[i].y + (1.0 - t) * q.vert[i].y; } } void draw_polyline( polyline_t &p, int color, int page ) { for (int i = 0; i < p.numverts; i++) { int x1 = (int)p.vert[i].x; int y1 = (int)p.vert[i].y; int x2 = (int)p.vert[i+1].x; int y2 = (int)p.vert[i+1].y; draw_line( x1, y1, x2, y2, color, page ); } } int main( int argc, char **argv ) { if ( iopl( 3 ) ) { perror( "iopl" ); exit(1); } system( "/sbin/insmod vram.o" ); map_system_memory( VRAM_BASE_ADDRESS, VGA_MEMORY_LENGTH ); struct termios otty; tcgetattr( 0, &otty ); struct termios tty = otty; tty.c_lflag &= ~( ICANON | ECHO | ISIG ); tty.c_cc[ VMIN ] = 1; tty.c_cc[ VTIME ] = 0; tcsetattr( 0, TCSAFLUSH, &tty ); char command[ 40 ]; sprintf( command, " clear ; mode3 %d ", display_mode ); system( command ); int white = 15, red = 12, green = 10, yellow = 14; draw_rectangle( 0, 0, hres, vres, white, 0 ); getchar(); polyline_t poly1, poly2, tween; int done = 0, numverts = 4; while ( !done ) { // setup and draw the initial and final polygons make_polyline( poly1, numverts ); make_polyline( poly2, numverts ); wipe_page( 1 ); draw_rectangle( 0, 0, hres, vres, white, 1 ); draw_polyline( poly1, red, 1 ); draw_polyline( poly2, green, 1 ); copy_page( 1, 0 ); getchar(); // perform the animated "in-betweening" int steps = 50; for (int step = 0; step <= steps; step++) { float t = (steps - step)/50.0; polyline_t tween; inbetween( poly1, poly2, t, tween ); wipe_page( 1 ); draw_rectangle( 0, 0, hres, vres, white, 1 ); draw_polyline( poly1, red, 1 ); draw_polyline( poly2, green, 1 ); draw_polyline( tween, yellow, 1 ); copy_page( 1, 0 ); } // await keypress from the user if ( getchar() == 0x1B ) done = 1; } system( " mode3 3 " ); tcsetattr( 0, TCSAFLUSH, &otty ); }