在本章,我们就课程设计中涉及的一些知识点作一个概述。这些知识点主要包括图形 基础与图形函数、文件操作知识、动画技术、中断知识和发声技术等,是在TurboC 下的操 作方式,不适用于VC++。 3.图形知识 1 C语言中提供了丰富的图形处理函数,本节将对本书中涉及的图形知识点作一个简要 回顾,主要包括图形模式的初始化、屏幕设置函数、颜色相关函数、画图函数、填充函数、文本 输出函数,以及与这些函数相关的各项参数等。 3.1 图形模式的初始化 1. 1.voidfarinitgrapintfar*gdriveintfar*gmodchar*pat h(r,e,h) 功能:显示模式控制。gdriver和gmode分别表示图形驱动器和模式,path是指图形驱 动程序所在的目录路径。有关图形驱动器、图形模式及分辨率的值如表3. 1所示。 表3.1 图形驱动器、图形模式及分辨率参照表 图形驱动器(gdriver) 图形模式(gmode) 色调分辨率 符号常数数值符号常数数值 CGAC0 0 C0 320×200 CGAC1 1 C1 320×200 CGA 1 CGAC2 2 C2 320×200 CGAC3 3 C3 320×200 CGAHI 4 2色640×200 MCGAC0 0 C0 320×200 MCGA 2 MCGAC1 1 C1 320×200 MCGAC2 2 C2 320×200 MCGAC3 3 C3 320×200 续表 图形驱动器(gdriver) 图形模式(gmode) 色调分辨率 符号常数数值符号常数数值 MCGA 2 MCGAMED 4 2色640×200 MCGAHI 5 2色640×480 EGA 3 EGALO 0 16 色640×200 EGAHI 1 16 色640×350 EGA64 4 EGA64LO 0 16 色640×200 EGA64HI 1 4色640×350 EGAMON 5 EGAMONHI 0 2色640×350 IBM8514 6 IBM8514LO 0 256 色640×480 IBM8514HI 1 256 色1024×768 HERC 7 HERCMONOHI 0 2色720×348 PC3270 10 PC3270HI 0 2色720×350 DETECT 0 用于硬件测试 ATT400C0 0 C0 320×200 ATT400C1 1 C1 320×200 ATT400 8 ATT400C2 2 C2 320×200 ATT400C3 3 C3 320×200 ATT400MED 4 2色320×200 ATT400HI 5 2色320×200 VGAL0 0 16 色640×200 VGA 9 VGAMED 1 16 色640×350 VGAHI 2 16 色640×480 h(r,e) 2.voidfardetectgrapint*gdrive*gmod 功能:自动检测显示器硬件。gdriver和gmode与initgraph() 函数中的意义一样,仍然 分别表示图形驱动器和模式。 3.2 屏幕颜色相关函数 1. r(r) 1.voidfarsetbkcolointcolo 功能:设置背景色。其中,r表示作图的颜色,可以用颜色的符号常量表示,也可以 colo 用代表该颜色的数值来表示,颜色符号常量和对应数值的关系如表3. 2所示。 47 表3.2 颜色符号常量与其数值对应表 符号常量数 值含 义符号常量数 值含 义 BLACK 0 黑色DARKGRAY 8 深灰色 BLUE 1 蓝色LIGHTBLUE 9 淡蓝色 GREEN 2 绿色LIGHTGREEN 10 淡绿色 CYAN 3 青色LIGHTCYAN 11 淡青色 RED 4 红色LIGHTRED 12 淡红色 MAGENTA 5 洋红色LIGHTMAGENTA 13 淡洋红色 BROWN 6 棕色YELLOW 14 黄色 LIGHTGRAY 7 淡灰色WHITE 15 白色 2.voidfarsetcolor(intcolor) 功能:设置前景色,color的取值参见表3.2。 3.intfargetbkcolor(void) 功能:返回当前背景色。 4.intfargetcolor(void) 功能:返回当前背景色。 5.intfargetmaxcolor(void) 功能:返回当前可用的最大颜色值。 3.1.3 图形窗口和图形屏幕函数 1.图形窗口操作 1)voidfarsetviewport(intx0,inty0,intx1,inty1,intclipflag) 设定一个以(x0,y0)为左上角、以(x1,y1)为右下角的图形窗口,其中,x0、y0、x1、y1是 相对于整个屏幕的坐标。如果clipflag为1,则超出窗口的输出图形自动被裁剪掉,即所有 作图限制于当前图形窗口之内;如果clipflag为0,则不进行裁剪,即作图将无限制地扩展于 窗口边界之外,直到屏幕边界。 2)voidfarclearviewport(void) 清除当前图形窗口,并把光标从当前位置移到原点(0,0)。 3)voidfargetviewsettings(structviewporttypefar*viewport) 获得关于现行窗口的信息,并将其存于viewporttype定义的结构变量viewport中。其 中,viewporttype的结构说明如下: struct viewporttype 48 { int left,top,right,bottom; int clipflag; }; 2.图形屏幕操作 1)voidfarsetactivepage(intpagenum) 为图形输出选择激活页,即后续图形的输出被写到函数选定的pagenum 页面,该页面 并不一定可见。 2)voidfarsetvisualpage(intpagenum) 使pagenum 所指定的页面变成可见页,页面从0开始。如果先用setactivepage()函数 在不同页面上画出一幅幅图像,再用setvisualpage()函数交替显示,就可以实现一些动画的 效果。 3)unsinedfarimagesize(intx0,inty0,intx1,inty1) 该函数一般在保存指定范围内的像素时使用,它计算要保存从左上角为(x0,y0)到右 下角为(x1,y1)的图形屏幕区域所需的字节数。 4)voidfargetimage(intx0,inty0,intx1,inty1,voidfar*buf) 将从左上角为(x0,y0)到右下角为(x1,y1)的图形屏幕区域的图像保存到buf所指向 的内存空间,该内存空间的大小由imagesize()函数计算。 5)voidfarputimage(intx,inty,void* mapbuf,intop) 将保存的图像输出到左上角点(x,y)的位置上,op规定了如何释放内存中的图像,图像 输出方式如表3.3所示。 表3.3 图像输出方式 符号常数数值含 义符号常数数值含 义 COPY_PUT 0 复制AND_PUT 3 与屏幕图像与后复制 XOR_PUT 1 与屏幕图像异或的复制NOT_PUT 4 复制反像的图形 OR_PUT 2 与屏幕图像或后复制 6)voidfarcleardevice(void) 清除屏幕内容。 3.1.4 画图函数 1.画点 1)画点函数 (1)voidfarputpixel(intx,inty,intcolor) 在坐标(x,y)处以color所代表的颜色画一点。 (2)intfargetpixel(intx,inty) 获得点(x,y)处的颜色值。 2)有关坐标位置的函数 (1)intfargetmaxx(void) 返回x轴的最大值 。 (2)intfargetmaxy(void) 返回y轴的最大值 。 (3)intfargetx(void) 返回光标所在位置的横坐标。 (4)voidfargety(void) 返回光标所在位置的纵坐标 。 (5)viavtitx, n odfrmoeo(nity) 移动光标到(x,y)点 。 (6)vl(x, oidfarmovereintdintdy) 把光标从当前位置(x,y)移动到位置(x+dx,y+dy) 。 2.画线 1)画线函数 (1)viaie(n0,nitxity1) odfrlnitxity0,n1, n 从点(x0,y0)到点(x1,y1)画直线 。 (2)vdfrlnto(nity) oiaieitx, n 画一条从当前位置到(x,y)的直线 。 (3)vodfrlnrl(nx, n iaieeitditdy) 画一条从当前位置(x,y)到位置(x+dx,y+dy)之间的直线 。 (4)viaice(nititrdu odfrcrlitx,ny,nais) 以(x,y)为圆心、radius为半径画一个圆 。 (5)viaritx,nitsaglitednge,nais) odfrac(nity,ntne,nnalitrdu 以(x,y)为圆心、radius为半径,画一段从stangle(角度)到endangle(角度)的圆弧线。 6)vodepitx,nitsagitednlnrdunrdu (e(e,e,s,s) 以(x,y)为中心,以xradius、yradius分别为x轴和y轴半径,画一段从角stangle到 endangle的椭圆线 odfrr 。 ca 当 gstean( gnle= 10, 、 nendangiletx=360 it 时,画出一个完整的椭圆。 (7)viaetnlitxity1,n2,ny2) 以(x1,y1)为左上角、(x2,y2)为右下角画一个矩形框。 oidfardrawpolintnumpointintfar*polypoint ilisnity,ntnlnnagitxaiityai (8)vy(s,s) 画一个顶点数为numpoints、各顶点坐标由polypoints给出的多边形 。 2)设定线型函 数 (1)vodfrstietlitlnsye,nindupten,nhcns) iaelnsye(nietlusgearittike 该函数用来设置线的有关信息,其中linestyle表示线的形状(线形状的取值及含义见 表3.4),tike表示线的宽度( 5),对于uar hcns 线宽度的取值及含义见表3.pten,只有 linestyle选USERBITLINE时才有意义(选其他线型,upatern取0即可)。此处upatern 的16位二进制数的每一位代表一个像元,如果为1,则该像元打开,否则该像元关闭。 表3.线形状的取值及含义 4 符号常数数值含义符号常数数值含义 SOLID_LINE 0 实线DASHED_LINE 3 点画线 DOTTED_LINE 1 点线USERBIT_LINE 4 用户定义线 CENTER_LINE 2 中心线 表3.线宽的取值及含义 5 符号常数数值含义 NORM_WIDTH 1 一点宽 THIC_WIDTH 3 三点宽 (2)viarsewriemoe(nd odfttditmoe) 该函数规定画线的方式。mode的取值为1或0。当取0时,表示画线时将所画位置的 原来信息覆盖了;当取1时,则表示画线时用现在特性的线与所画之处原有的线进行异或 (XOR)操作,实际上画出的线是原有线与现在规定的线进行异或后的结果。因此,当线的 特性不变,进行两次画线操作相当于没有画线。 3.5 封闭图形的填充 1. 1.先画轮廓再填充 1)vodfrbr(ntx1,nty1,itxity2) iaaiin2,n 先画一个以(y1,y1)为左上角、(x2,y2)为右下角的矩形窗口,再按规定模式和颜色 填充。 2)vodfrbritxity0,n1,nitdph,nofa iaa3d(n0,nitxity1,netittplg) 当topflag为非0时,画出一个三维的长方体;当topflag为0时,三维图形不封顶。 3)vodfrpelcitx,nitsage,nnage,nais) iaisie(nity,ntnlitednlitrdu 先画一个以(x,y)为圆心、radius为半径、从角度stangle到角度endangle的扇形,再按 规定方式填充。 4)vodfrscoitx,nitsage,nednlitxais,nrdu iaetr(nity,ntnlitnage,nrduityais) 先画一个以(x,y)为圆心,以xradius、yradius分别为x轴和y轴半径,从角度stangle 到角度endangle的椭圆扇形,再按规定方式填充。 2.任意封闭图形的填充 vodfrfodiitx,nitbre:该函数可对任意封闭图形进行填充。其中 ialofl(nity,nodr) (x,y)为封闭图形内的任意一点,border为边界的颜色,border指定的颜色值必须与图形轮 廓的颜色值相同。 提示:(x,y)必须在所要填充的封闭图形内部,否则不能进行填充。如果不是封闭图 形,则填充会从没有封闭的地方溢出去,填满其他地方。 51 3.设定填充方式 1)voidfarsetfillstyle(intpattern,intcolor) 以pattern为填充模式和以color为填充颜色对指定图形进行填充。填充模式pattern 的取值和含义如表3.6所示。 表3.6 填充模式pattern的取值和含义 符号常数数值含 义符号常数数值含 义 EMPTY_FILL 0 以背景色填充HATCH_FILL 7 直线网格填充 SOLID_FILL 1 实填充XHATCH_FILL 8 斜网格填充 LINE_FILL 2 直线填充INTTERLEAVE_FILL 9 间隔点填充 LTSLASH_FILL 3 斜线填充WIDE_DOT_FILL 10 稀疏点填充 SLASH_FILL 4 粗斜线填充CLOSE_DOS_FILL 11 密集点填充 BKSLASH_FILL 5 粗反斜线填充USER_FILL 12 用户自定义填充 LTBKSKASH_FILL 6 反斜线填充 2)voidfarsetfillpattern(char* upattern,intcolor) 该函数用于设置用户定义的填充图形的颜色,以供对封闭图形进行填充。其中, upattern是一个指向8字节的指针。这8字节定义了8×8点阵的图形。每字节的8 位二 进制数表示水平8点,8字节表示8行,然后以此为模型向各封闭区域填充。 3)voidfargetfillpattern(char* upattern) 该函数将用户定义的填充图模存入upattern指针指向的内存区域。 4)voidfargetfillsetings(structfillsettingstypefar*fillinfo) 获得当前图形模式的颜色并将其存入结构指针变量fillinfo中。其中,fillsettingstype 结构定义如下: struct fillsettingstype { int pattern; int color; }; 其中,pattern表示当前的填充模式,color表示填充的颜色。 3.1.6 图形模式下的文本输出 1.文本输出函数 1)voidfarouttext(charfar*text) 该函数输出字符串指针text所指的文本所在的位置。 2)voidfarouttextxy(intx,inty,charfar*text) 该函数在指定位置(x,y)输出字符串指针text所指的文本。 2.文本参数设置函数 1)vodfrstooitclr) iaeclr(noo 该函数用于设置输出文本的颜色,color表示要设置的颜色 。 2)vy(z,t) oidfarsetextjustifinthoriintver 该函数用于设置显示的方位。对使用outextxy()函数所输出的字符串,其中,哪个点 对应于坐标(在Tu0中是有规定的。如果把一个字符串看成一个长方形的图 x,y) rboC2. 形,在水平方向显示时,字符串长方形在垂直方向就有顶部、中部和底部三个位置,水平方向 就有左、中、右三个位置,两者结合所确定的位置就对准函数中的(x,y)位置。 setextjustify()函数中的参数horiz指出水平方向的位置(即左、中、右中的一个),参数 vert指出垂直方向的位置(即顶部、中部、底部中的一个)。有关参数horiz和vert的取值及 含义见表3. 7。 表3.7 参数horiz和vert的取值及含义 符号常数数值含义符号常数数值含义 LEFT_TEXT 0 水平TOP_TEXT 2 垂直 RIGHT_TEXT 2 水平CENTER_TEXT 1 水平或垂直 BOTTOM_TEXT 0 垂直 e(t,n,e) 该函数用于设置输出字符的字体font(见表3.方向directioharsize 3)voidfarsetextstylintfonintdirectiointcharsiz 8)、n和字体大小c(见表3.9)。其中方向direction的取值有HORIZ_DIR(用数值0表示)和VERT_DIR(用 数值1表示)两个,分别表示从左向右和从底向顶输出字符。 表3.字体ft的取值及含义 8 on 符号常数数值含义 DEFAULT_FONT 0 默认字体,8×8点阵字体 TRIPLEX_FONT 1 三倍笔画字体 SMALL_FONT 2 小号笔画字体 SANSSERIF_FONT 3 无衬线笔画字体 GOTHIC_FONT 4 黑体笔画字体 表3.字体大小ce的取值及含义 9 harsiz 符号常数或数值含义 1 8×8点阵 2 16×16点阵 3 24×24点阵 4 32×32点阵 53 续表 符号常数或数值含 义 5 40×40点阵 6 48×48点阵 7 56×56点阵 8 64×64点阵 9 72×72点阵 10 80×80点阵 USER_CHAR_SIZE=0 用户定义的字符大小 3.2 文件操作知识 本节我们将对文件操作的基本知识作一个简要回顾,包括文件的打开与关闭、文件的读 写、文件的状态判断及文件的定位。 3.2.1 文件的打开与关闭 1.文件的打开 C语言中打开文件的函数是fopen(),其一般调用形式如下: FILE *fp fp=fopen(filename, method) 其中,fp是一个指向FILE 类型结构体的指针变量,filename表示要打开的文件的名 字,method表示文件的使用方式,表3.10列出了文件的使用方式及其含义。 表3.10 文件的使用方式及其含义 文件的使用方式含 义文件的使用方式含 义 r,只读为输入打开一个文本文件r+,读写为读/写打开一个文本文件 w,只写为输出打开一个文本文件w+,读写为读/写建立一个新的文本文件 a,追加向文本文件尾增加数据a+,读写为读/写打开一个文本文件 rb,只读为输入打开一个二进制文件rb+,读写为读/写打开一个二进制文件 wb,只写为输出打开一个二进制文件wb+,读写为读/写建立一个新的二进制文件 ab,追加向二进制文件尾增加数据ab+,读写为读/写打开一个二进制文件 提示:如果不能打开一个文件,即打开文件失败,则fopen()函数将返回一个空指针 NULL。通常可以通过判断fopen()返回的值来决定是否打开成功。 54 2.文件的关闭 在使用完一个文件后应该关闭它,以防止它被误用。关闭文件的函数是fclose(),其一 般调用形式如下: fclose(fp) fp即为文件指针,是我们前面打开文件时创建的fp。fclose()函数也有返回值,如果顺 利关闭的话则返回0;否则返回-1,可以用ferror()函数(见3.2.3节)来测试。 3.2.2 文件的读写 对文件的读写,可以有多种方式,可以读写一个字符、一个字符串、一块数据或者一个整 数,有时候也需要进行格式化输入和输出等。 1.从文件读出 1)fgetc() fgetc()函数用于从一个指定的文件中读出一个字符,该文件必须是以读或者读写方式 打开的,其一般调用形式如下: ch=fgetc(fp) fp为文件型指针变量,ch为读出的字符变量。 2)fgets() fgets()函数用于从一个指定的文件中读出一个字符串,其一般调用形式如下: fgets(str, num, fp) num 为需要读取的字符个数,fp为文件型指针变量。从fp指向的文件中读取num-1 个字符,然后在最后加一个\' 0'字符,再把它们放入数组str中。 3)getw() getw()函数表示从一个磁盘文件中读取一个整数,其一般调用形式如下: getw(fp) fp为文件型指针变量。 4)fread() fread()函数用于读取一组数据,其一般调用形式如下: fread(buffer, size, count, fp) buffer是读取数据所存放的地方,size表示要读写的字节数,count表示要进行读写多 少字节的数据项,fp为文件型指针。 5)fscanf() fscanf()函数用于从磁盘文件中读取ASCII字符,并将读取的数据存入指定变量中,其 一般调用形式如下: 55 fscanf(fp, format string, in_list) fp为文件型指针,formatstring是格式字符串,in_list表示输入表列。 2.写入文件 1)fputc() fputc()函数表示将一个字符写入指定的磁盘文件中,其一般调用形式如下: fputc(ch, fp) ch为要写入的字符,fp为文件型指针变量。 2)fputs() fputs()函数表示向指定文件写入一个字符串,其一般调用形式如下: fputs(str, fp) str表示要写入的字符串,fp为文件型指针变量。 3)putw() putw()函数用于将一个整数写入指定的文件,其一般调用形式如下: putw(digit, fp) digit表示要写入的整数,fp为文件型指针变量。 4)fwrite() fwrite()用来将一组数据写入指定的文件,其一般调用形式如下: fwrite(buffer, size, count, fp) 其各个参数的意义和fread()函数中的相同,只是这里的buffer存放的是要写入文件的 数据。 5)fprintf() fprintf()函数用于格式化输出字符串到指定的文件,其一般调用形式如下: fprintf(fp, format string, out_list) fp为文件型指针,formatstring是格式字符串,out_list表示输出表列。我们举例来说 明如下: fprintf(fp,"%d, %2.2f", i, j) 本语句的作用是将整型变量i和实型变量j的值按照%d和%2.2f的形式输出到fp所 指向的文件上。 3.2.3 文件的状态 1.文件结束判断 在ANSI标准中,使用feof()函数来判断文件是否结束,其一般调用形式如下: 56 feof(fp) fp为文件型指针,如果遇到文件结束符,即表示文件结束返回非0,否则返回0。 2.错误检测及清除 1)ferror() 在调用各种输入输出函数时,如果出现了错误,可以用ferror()函数来检测,其一般调 用形式如下: ferror(fp) 如果ferror返回0,则表示没有出错。 2)clearer() clearer()函数用于将文件错误标志和文件结束标志置为0,其一般调用形式如下: clearer(fp) 如果在调用一个输入输出函数出错后,再调用该函数,即可将ferror()值清为0。 3.2.4 文件的定位 文件中有一个位置指针指向当前读写的位置。有时我们需要获取位置指针的位置或者 改变当前的指向位置,C语言中提供了几个函数来实现这些功能。 1.rewind() rewind()函数将位置指针重新返回到文件的开头,其一般调用形式如下: rewind(fp) 2.ftell() ftell()函数用于获取流式文件中的当前位置,用相对于文件开头的位移量来表示,其一 般调用形式如下: ftell(fp) 如果返回正整数,则表示当前的存放位置,如果返回值为-1L,则表示出错。 3.fseek() fseek()函数用于实现改变位置指针所指向的位置,其一般调用形式如下: (文件类型指针,位移量,起始点) 起始点有三个值,分别是文件开始(SEEK_SET,用数字0表示)、文件当前位置(SEEK _CUR,用数字1表示)和文件末尾(SEEK_END,用数字2表示);位移量是以起始点为基点 而移动的字节数,如果字节数为正表示向前移动,如果为负则表示向后移动。ANSIC标准 要求位移量是long型数据,并规定在数字的末尾加一个字母L,表示long型。 57 举例说明,假定有如下的调用: fseek(fp, 100L, 1) 该语句表示将位置指针向前移动到离当前位置100字节处。 3.3 动画技术 我们知道电影或动画片是由一张张图像组成的,它利用人眼不能够分辨出时间间隔在 25毫秒内的动态图像变化这一特性,在这些连续图像被放映时,从视觉效果上给人以动的 感觉。所以在计算机屏幕上产生运动的效果需要动画技术。 3.3.1 采用延迟与清屏交错的实现方法 这种方法利用cleardevice()和delay()函数相互配合,先画一幅图形,让它延迟一段时 间,然后清屏,再画另一幅,如此反复,就形成动态效果。本小节的例3-1分别通过函数 graphone()、graphtwo()和graphthree()实现了三幅简单的动画画面,这三幅画面不停地进 行切换。 例3-1 #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); /*第三个动画*/ } 58 getch(); closegraph(); /*关闭图形模式*/ }v oid 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); line(170,y-i*15-50,170,y-350-i*15); line(130,y-i*15-50,170,y-i*15-50); line(150,y-300-i*15,190,y-300-i*15); line(x-150,i*15,x-150,300+i*15); line(x-170,i*15-50,x-170,250+i*15); line(x-150,i*15,x-190,i*15); line(x-130,250+i*15,x-170,250+i*15); outtextxy(i*25,150,str); outtextxy(x-i*25,y-150,str); delay(5000); } }v oid graphtwo(char *str) { int i; for(i=0;i<30;i++) { setcolor(5); cleardevice(); settextstyle(1,1,4); line(i*25,y-100,300+i*25,y-100); line(i*25,y-120,300+i*25,y-120); line(x-i*25,100,x-300-i*25,100); line(x-i*25,120,x-300-i*25,120); outtextxy(150,i*25,str); outtextxy(x-150,y-i*25,str); delay(5000); } }v oid graphthree(char *str) { int i,j,color,width; 59 color=random(maxcolor); /*随机得到颜色值*/ setcolor(color); settextstyle(1,0,1); /*设置字符串的格式*/ outtextxy(x/2,y/2-100,str); /*显示字符串*/ delay(8000); for(i=0;i<8;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)-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 中的图形函数,除 60 initgraph()、cleardevice()、closegraph()、settextjustify()、settextstyle()、setlinestyle()、 outtextxy()、setcolor()、line()外,还包括如下: void far textwidth(char far *str): 功能:以像素为单位,返回由str所指向的字符串宽度,针对当前字符的字体与大小。 该程序用到的dos.h中的库函数有delay(),其原型说明如下: void delay(unsigned milliseconds) 功能:该函数将程序的执行暂停一段时间(毫秒)。 void far random(int num) 功能:此函数返回一个0~num 范围内的随机数。该函数在stdlib.h库中。 3.3.2 动态开辟视图窗口的方法 我们还可以利用视图窗口设置技术来实现视图窗口动画效果,具体方法是:在不同视 图窗口中设置同样的图像,然后让视图窗口沿x轴方向移动设置,这次出现前要清除上次视 图窗口的内容,这样就会出现图像沿x轴移动的效果。也就是说,在位置动态变化但大小不 变的视图窗口中(用setviewport()函数),设置固定图形(也可是微小变化的图像),这样虽 呈现在观察者面前的是当前视图窗口位置在动态变化,但视觉上却像是看到图像在屏幕上 动态变化一样。 例3-2就是这样做的,不断地沿x轴开辟视图窗口,就像一个大小一样的窗口沿x轴在 移动,由于总有clearviewport函数清除上次窗口的相同立方体,因而视觉效果上,就像一个 立方体从左向右移动一样。程序中定义的movebar函数作用是开辟一个视图窗口,并画一 个填色的立方体,保留一阵(delay(250000))然后清除它,主程序不断调用它,因每次顶点x 坐标在增加,因而效果是立方体沿x轴从左向右在运动。 例3-2 #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(); }m ovebar(int xorig) /*设窗口并画填色小立方体*/ 61 { 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(); } 采用上面的两种方法对较复杂图形不适宜,一则画图形要占较长时间,二则视图窗口位 置切换的时间变得较长,因而动画效果就会变差。 3.3.3 屏幕图像存储再放的方法 在图形方式下,与文本方式类似,除了清屏函数cleardevice()外,还有其他的对屏幕图 像操作的函数。其中一类是屏幕图像存储和显示函数,包括:存屏幕图像到内存区 getimage()函数,测定图像所占字节数的imagesize()函数,将所存图像显示的putimage() 函数,函数详情见3.1.3节。 例3-3演示了动画的工作方式,for循环用来在屏幕上方产生连续的五个方框,方框中 套用洋红色填充的小方块,五个图像全一样。循环结束后,又在屏幕下方画出两个小框,小 框用洋红色填充并在大框内。程序运行后,立即在屏幕上显示出上述图案,当按任意键后, 则由函数imagesize()得到屏幕下方大框区域内图像所占字节数,然后由malloc()函数按字 节数分配内存缓冲区buffer,再由getimage()函数将图像存到buffer中,然后复制到屏幕上 方左边第一个框位置。按任意键后,又将buffer中图像和第二个框图像进行与操作后显 示,再按任意键,buffer中的图像又和第三个方框内图像进行或操作并显示,如此重复,则可 将5种逻辑操作结果均显示在屏幕上。 注意:COPY和NOT操作将与原来屏幕上的图像无关,buffer中图像经过这两种操 作,将覆盖掉原屏幕上图像,并将结果进行显示。 例3-3 #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++) /*产生连续的五个方框中套小框*/ 62 { j=i *110; rectangle(80+j,100,130+j,150); /*产生小框且用洋红色填充*/ floodfill(110+j,140,YELLOW); rectangle(50+j,100,130+j,180); /*画大框*/ }r ectangle(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()函数来 实现,这种方法较快,因它已事先将要重放的画面画好了。余下的问题,就是计算应在什么 位置重放的问题了。 例3-4演示了利用这种方法产生的两个洋红色小球碰撞、弹回然后又碰撞的动画效果。 例3-4 #include<graphics.h> main() { int i,driver,mode,size; void *buffer; driver=DETECT; initgraph(&driver,&mode,""); setbkcolor(BLUE); cleardevice(); setcolor(YELLOW); 63 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(); } 3.3.4 利用页交替的方法 对屏幕图像操作的函数,还有一类是设置显示页函数。显示适配器的显示存储器为 VRAM,图形方式下存储在VRAM 中的一满屏图像信息称为一页。每页一般为64K字节, 图3.1 显示页 VRAM 可以存储要显示的图像几页(视VRAM 容量而定,最大可达8页),TurboC2.0支持页的 功能有限,按在图形方式下显示的模式最多支持 4页(EGALO 显示方式),一般为2 页(注意对 CGA,仅有1页),因存储图像的页显示时,一次 只能显示1页,因此必须设定某页为当前显示的 页(又称可视页),缺省时定为0 页,如图3.1 所示。正 在由用户编辑图形的页称为当前编辑页(又称激活的页),这个页不等于显示页,即若 用户不设定该页为当前显示页时,在该页上编辑的图形将不会在屏幕上显示出来。缺省时, 设定0页为当前编辑页,即若不用下述的页设置函数进行设置,就认定0页既是编辑页,又 是当前显示页。 设置激活页和显示页的函数为setactivepage()和setvisualpage(),详情见3.1.3节。这 两个函数只能用于EGA、VGA 等显示适配器。前者设置由pagenum 指出的页为激活的 64 页,后者设置可显示的页。当设定了激活的页,即编辑页后,则程序中其后的画图操作均在 该页进行,若它不定为显示页,则其上的图像信息并不会在屏幕上显示出来。 例3-5的程序演示了设置显示页函数的应用。首先用setactivepage(1)设置l页为编 辑页,在上面画出一个红色边框、用淡绿色填充的圆,此图并不显示出来(因缺省时,定义0 页为可视页)。接着又定义0页为编辑页并清屏(即清0页),也定义0页为可视页,并在其 上画出一个用洋红色填充的方块,该方块将在屏幕上显示出来。接着进入do循环,设置l 页为可视页,因而其上的圆便在屏幕上显示出来,方块的图像消失,用delay(2000)将圆图像 保持2000毫秒即2秒,当不按键时,下一次循环又将0页设为可视页,因而方块的图像显示 出来,圆图像又消失。保持2秒后,又重复刚开始的过程。这样我们就会看到:屏幕上同一 位置洋红色方块和淡绿色圆交替出现,若将delay时间变少,将会出现动画的效果。 例3-5 #include<graphics.h> #include<dos.h> 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(); }