• 沒有找到結果。

大家知道电影或动画片是由一张张图像组成的,它利用人眼不能够分辨出时间间隔在 25 毫秒内的动态图像变化这一特性,在这些连续图像被放映时,从视觉效果上给人以动的感觉。

所以在计算机屏幕上产生运动的效果需要动画技术。

4.3.1 采用延迟与清屏交错的实现方法

这种方法利用 cleardevice()和 delay()函数相互配合,先画一幅图形,让它延迟一段时间,

然后清屏,再画另一幅,如此反复,就形成动态效果。

例 4-16 通过函数 graphone()、graphtwo()和 graphthree()实现了 3 个简单的动画画面,这 3 个画面不停地进行切换。

#include<graphics.h>

#include<stdlib.h>

#include<dos.h>

int x,y,maxcolor;

void graphone(char *str); /*使字符串 str 左右运动,线条上下运动*/

void graphtwo(char *str); /*使字符串 str 上下运动,线条左右运动*/

void graphthree(char *str); /*使字符串 str 由小变大,再由大变小,直线也随之变化*/

main()

{ int i,driver,mode;

char *str="W E L C O M E !";

driver=DETECT;

mode=0;

initgraph(&driver,&mode,""); /*系统初始化*/

cleardevice(); /*清屏*/

settextjustify(CENTER_TEXT,CENTER_TEXT);

x=getmaxx(); /* 返回当前图形模式下的最大有效的 x 值*/

y=getmaxy(); /* 返回当前图形模式下的最大有效的 y 值*/

maxcolor=getmaxcolor(); /* 返回当前图形模式下最大有效的颜色值*/

while(!kbhit())

{ graphone(str); /* 第一个动画*/

graphtwo(str); /* 第二个动画*/

graphthree(str); /* 第三个动画*/

} getch();

closegraph(); /* 关闭图形模式*/

}

void graphone(char *str) { int i;

for(i=0;i<40;i++) { setcolor(1);

settextstyle(1,0,4);

setlinestyle(0,0,3);

cleardevice();

line(150,y-i*15,150,y-300-i*15);

void graphtwo(char *str) { int i;

void graphthree(char *str) { int i,j,color,width;

line((x-width)/2+10*(8-i),y/2+i*15-70,(x+width)/2-10*(8-i),y/2+i*15-70);

line((x-width)/2+5*(8-i),y/2+i*15-60,(x+width)/2-5*(8-i),y/2+i*15-60);

line((x-width)/2,y/2+i*15-50,(x+width)/2,y/2+i*15-50);

line((x-width)/2,y/2+i*15-20,(x+width)/2,y/2+i*15-20);

line((x-width)/2+5*(8-i)-10,y/2+i*15-10,(x+width)/2-5*(8-i),y/2+i*15-10)

line((x-width)/2+10*(8-i),y/2+i*15,(x+width)/2-10*(8-i),y/2+i*15);

delay(8000);

}

for(i=7;i>=0;i--) /*字符串由大变小*/

{

cleardevice(); /*清屏*/

settextstyle(1,0,i);

outtextxy(x/2,y/2-i*10-100,str);

outtextxy(x/2,y/2+i*10-100,str);

width=textwidth(str);

setlinestyle(0,0,1);

line((x-width)/2+10*(8-i),y/2+i*15-70,(x+width)/2-10*(8-i),y/2+i*15-70);

line((x-width)/2+5*(8-i),y/2+i*15-60,(x+width)/2-5*(8-i),y/2+i*15-60);

line((x-width)/2,y/2+i*15-50,(x+width)/2,y/2+i*15-50);

line((x-width)/2,y/2+i*15-20,(x+width)/2,y/2+i*15-20);

line((x-width)/2+5*(8-i),y/2+i*15-10,(x+width)/2-5*(8-i),y/2+i*15-10);

line((x-width)/2+10*(8-i),y/2+i*15,(x+width)/2-10*(8-i),y/2+i*15);

delay(8000);

} }

程序中用到的库有 graphics.h、dos.h 和 stdlib.h,其中 graphics.h 中的图形函数,除 initgraph()、cleardevice()、closegraph()、settextjustify()、settextstyle()、setlinestyle()、outtextxy()、

setcolor()和 line()外,还包括:

void far getmaxx (void);

功能:返回当前图形模式下的最大有效的 x 值(即最大的横坐标)。

void far getmaxy (void);

功能:返回当前图形模式下的最大有效的 y 值(即最大的纵坐标)。

void far getmaxcolor(void);

功能:返回当前图形模式下的最大有效的颜色值。

void far textwidth(char far *str);

功能:以像素为单位,返回由 str 所指向的字符串宽度,针对当前字符的字体与大小。该 程序用到的 dos.h 中的库函数有 delay(),其原型说明为:

void delay(unsigned milliseconds);

功能:该函数将程序的执行暂停一段时间(毫秒)。

void far random(int num);

功能:此函数返回一个 0~num 范围内的随机数。该函数在 stdlib.h 的库中。

4.3.2 动态开辟图视窗口的方法

还可以利用图视窗口设置技术来实现图视窗口动画效果。具体方法是:在不同图视窗口 中设置同样的图像,然后让图视窗口沿 x 轴方向移动设置,这次出现前要清除上次图视窗口的 内容,这样就会出现图像沿 x 轴移动的效果。也就是说,在位置动态变化,但大小不变的图视 窗口中[用 setviewpot()函数],设置固定图形(也可是微小变化的图像),这样虽呈现在观察 者面前的是当前图视窗口位置在动态变化,但视觉上却像是看到图像在屏幕上动态变化一样。

例 4-17 就是这样做的,不断地沿 x 轴开辟图视窗口,就像一个大小一样的窗口沿 x 轴在 移动,由于总有 clearviewport 函数清除上次窗口的相同立方体,因而视觉效果上,就像一个立 方体从左向右移动一样。程序中定义的 movebar 函数作用是开辟一个图视窗口,并画一个填 色的立方体,保留一阵[delay(250000)]然后清除它,主程序不断调用它,因每次顶点 x 坐标 在增加,因而效果是立方体沿 x 轴从左向右在运动。

例 4-17 动态开辟图视窗口。

#include <graphics.h>

#include <dos.h>

main()

{ int i,driver,mode;

graphdriver=DETECT;

initgraph(&driver,&mode,"");

for(i=0;i<25;i++) { setfillstyle(1, i);

movebar(i * 20);

}

closegraph();

}

movebar(int xorig) /*设窗口并画填色小立方体*/

{ setviewport(xorig,0,639,199,1);

setcolor(5);

bar3d(10,120,60,150,40,1);

floodfill(70,130,5);

floodfill(30,110,5);

delay(250000);

clearviewport();

}

采用上面的两种方法对较复杂图形不宜,一则画图形要占较长时间,二则图视窗口位置 切换的时间就变得较长,因而动画效果就会变差。

4.3.3 屏幕图像存储再放的方法

在图形方式下,与文本方式类似,除了清屏函数 cleardevice()外,还有其他的对屏幕图像 操作的函数,其中一类是屏幕图像存储和显示函数,包括:

1.存屏幕图像到内存区

void far getimage(int x1,int y1,int x2,int y2,void far *bitmap);

该函数将把屏幕左上角为(x1,y1),右下角为(x2,y2)矩形区内的图像保存到指针 bitmap 指向的内存区去。为了能开辟一个内存缓冲区,使它恰能存下所指矩形区中的图像,则必须首 先要知道所存图像占多少字节,则内存缓冲区也可设这样多的字节,这可用下面的函数:

2.测定图像所占字节数的函数

unsigned far imagesize(int x1,int y1,int x2,int y2);

该函数将得到屏幕上左上角为(x1,y1),右下角为(x2,y2)矩形区内图像所占的字节数。

3.将所存图像显示的函数

void far putimage(int x1,int y1,void far *bitmap,int op);

该函数将把指针 bitmap 指向的内存区中所装图像,与屏上现有左上角为(xl,y1)的矩形 区内图像进行 op 规定的操作(参见表 4-13)。该函数进行各种图像的逻辑操作如同二进制操 作一样。

表 4-13 op 逻辑操作结果

符号名 含义

COPY_PUT 0 复制

XOR_PUT 1 进行异或操作

OR_PUT 2 进行或操作

AND_PUT 3 进行与操作

NOT_PUT 4 进行非操作

例 4-18 演示了表 4-13 中的逻辑操作,for 循环用来在屏幕上方产生连续的 5 个方框,方 框中套一用洋红色填充的小方块,5 个图像全一样。循环结束后,又在屏幕下方画出两个框,

小框用洋红色填充并在大框内。程序运行后,立即在屏上显示出上述图案,当按任一键后,则 由函数 imagesize 得到屏幕下方大框套一填充框区域内图像所占字节数,然后由 malloc 函数按 字节数分配一内存缓冲区 buffer,再由 getimage 函数将图像存到 buffer 中,然后复制到屏幕 上方左边第一个框位置。按任一键后,又将 buffer 中图像和第二个框图像进行与操作后显示,

再按任一键,buffer 中的图像又和第三个方框内图像进行或操作并显示,如此重复,则可将 5 种逻辑操作结果均显示在屏上。注意 COPY 和 NOT 操作将与原来屏上的图像无关,buffer 中 图像经过这两种操作,将覆盖掉原屏上图像,并将结果进行显示。

例 4-18 表 4-13 中的逻辑操作。

#include <graphics.h>

main()

{ int i,j,driver,mode,size;

void *buffer;

driver=DETECT;

initgraph(&driver,&mode,"");

setbkcolor(BLUE);

cleardevice();

setcolor(YELLOW);

setlinestyle(0,0,1); /*用细实线 */

setfillstyle(1,5); /* 用洋红实填充 */

for(i=0;i<5;i++) /* 产生连续的 5 个方框中套小框的*/

{ j=i *110;

rectangle(80+j,100,130+j,150); /* 产生小框且用洋红色填充 */

floodfill(110+j,140,YELLOW);

rectangle(50+j,100,130+j,180); /*画大框*/

}

rectangle(50,340,100,420); /* 产生一个小框 */

floodfill(80,360,YELLOW); /* 用洋红色填充 */

rectangle(50,340,130,420); /* 产生一个大框 */

getch();

size=imagesize(40,300,132,430); /*取得(40,300)右下角(132,430)区域图像字节数*/

buffer=malloc(size); /*分配缓冲区(按字节数)*/

getimage(40,300,132,430,buffer); /*存图像*/

putimage(40,60,buffer,COPY_PUT); /*重新复制*/

getch();

j=110;

putimage(40+j,60,buffer,AND_PUT); /*和屏上的图与操作*/

getch();

putimage(40+2*j,60,buffer,OR_PUT);

getch();

putimage(40+3*j,60,buffer,XOR_PUT);

getch();

putimage(40+4*j,60,buffer,NOT_PUT);

getch();

closegraph();

}

同制作幻灯片一样,将整个动画过程变成一个个片段,然后存到显示缓冲区内,当把它 们按顺序重放到屏幕上时,就出现了动画效果,这可以用 getimage()和 putimage()函数来实现,

这种方法较快,因它已事先将要重放的画面画好了。余下的问题,就是计算应在什么位置重放 的问题了。

例 4-19 演示了利用这种方法产生的两个洋红色小球碰撞、弹回、又碰撞的动画效果。

#include <graphics.h>

main()

{ int i,driver,mode,size;

void *buffer;

driver=DETECT;

initgraph(&driver,&mode,"");

setbkcolor(BLUE);

cleardevice();

setcolor(YELLOW);

setlinestyle(0,0,1);

setfillstyle(1,5);

circle(100,200,30);

floodfill(100,200,YELLOW); /*填充圆*/

size=imagesize(69,169,131,231); /*指定图像占字节数*/

buffer=malloc(size); /*分配缓冲区(按字节数)*/

getimage(69,169,131,231,buffer); /*存图像*/

putimage(500,169,buffer,COPY_PUT); /*重新复制*/

do{

for(i=0;i<185;i++)

{ putimage(70+i,170,buffer,COPY_PUT); /*左边球向右运动*/

putimage(500-i,170,buffer,COPY_PUT); /*右边球向左运动*/

}/*两球相撞后循环停止*/

for(i=0;i<185;i++)

{ putimage(255-i,170,buffer,COPY_PUT); /*左边球向左运动*/

putimage(315+i,170,buffer,COPY_PUT); /*右边球向右运动*/

}

}while (!kbhit()); /*当不按键时重复上述过程*/

getch();

closegraph();

}

4.3.4 利用页交替的方法

对屏幕图像操作的函数,还有一类是设置显示页函数。曾在前面提到了显示适配器的显 示存储器 VRAM,图形方式下存储在 VRAM 中的一满屏图像信息称为一页。每个页一般为 64KB,VRAM 可以存储要显示的图像几个页(视 VRAM 容量而定,最大可达 8 页),Turbo C 2.0 支持页的功能有限,按在图形方式下显示的模式最多支持 4 页(EGALO 显示方式),一 般为两页(注意对 CGA 仅有一页),因存储图像的页显示时,一次只能显示一页,因此必须 设定某页为当前显示的页(又称可视页),缺省时定为 0 页,如图 4-3 所示。

图 4-3 显示页

正在由用户编辑图形的页称为当前编辑页(又称激活的页),这个页不等于显示页,即若 用户不设定该页为当前显示页时,在该页上编辑的图形将不会在屏上显示出来。缺省时,设定 0 页为当前编辑页,即若不用下述的页设置函数进行设置,就认定 0 页既是编辑页又是当前 显示页。

设置激活页和显示页的函数如下:

void far setactivepage(int pagenum);

void far setvisualpage(int pagenum);

这两个函数只能用于 EGA、VGA 等显示适配器。前者设置由 pagenum 指出的页为激活 的页,后者设置可显示的页。当设定了激活的页,即编辑页后,则程序中其后的画图操作均在 该页进行,若它不定为显示页,则其上的图像信息并不会在屏上显示出来。

例 4-20 下面的程序演示了设置显示页函数的应用。首先用 setactivepage(1)设置 l 页为 编辑页,在上面画出一个红色边框、用淡绿色填充的圆,此图并不显示出来(因缺省时,定义 0 页为可视页)。接着又定义 0 页为编辑页并清屏(即清 0 页),也定义 0 页为可视页,并 在其上画出一个用洋红色填充的方块,该方块将在屏上显示出来。接着进入 do 循环,设置 l 页为可视页,因而其上的圆便在屏上显示出来,方块的图像消失,用 delay(2000)将圆图像保 持 2000 毫秒即 2 秒,当不按键时,下一次循环又将 0 页设为可视页,因而方块的图像显示 出来,圆图像又消失。保持 2 秒后,又重复刚开始的过程。这样就会看到:屏上同一位置洋 红色圆和淡绿色方块交替出现,若将 delay 时间变少,将会出现动画的效果。

#include <graphics.h>

#include <dos.h>

CRT 显示器 位面 0

位面 1 位面 2 位面 3

显示存储器

main()

{ int i,graphdriver,graphmode,size,page;

graphdriver=DETECT;

initgraph(&graphdriver,&graphmode,"");

cleardevice();

setactivepage(1); /*设置 l 页为编辑页 */

setbkcolor(BLUE);

setcolor(RED);

setfillstyle(1,10);

circle(130,270,30); /* 画圆*/

floodfill(130,270,4); /*用淡绿色填充圆 */

setactivepage(0); /*设置 0 页为编辑页 */

cleardevice(); /*清 0 页 */

setfillstyle(1,5);

bar(100,210,160,270); /*画方块并填充洋红色 */

setvisualpage(0); /*设置 0 页为可视页*/

page=1;

do

{ setvisualpage(page); /*显示设定页的图像*/

delay(2000); /*延迟 2000ms */

page=page-1;

if(page<0) page=1;

} while(!kbhit());

getch();

closegraph();

}

正如上面的程序所示,将当前显示页和编辑页分开[用 setvisualpage()和 setactivepage()函 数],在编辑页上画好图形后,立即令该页变为显示页显示,然后在上次的显示页上(现在变 为编辑页)进行画图,画好后,又再次交换,如此编辑页和显示页反复地交换,在观察者的视 觉上,就出现了动画的效果。要让页的交替速度快,唯一的办法是缩短在页上的画图时间。

相關文件