锐单电子商城 , 一站式电子元器件采购平台!
  • 电话:400-990-0325

高级编程技术--tc图形界面

时间:2022-11-06 20:30:00 1245n4no接近传感器

高级编程技术
介绍如何使用TC 实现菜单设计、图形绘制、动画播放、音乐
的演奏、汉字的显示、图片的显现等技术,在讲述时,以问题-解答的方式来逐渐阐明。
1 屏幕输出和键盘输入... 1
1.1 屏幕输出文本...1
1.2 键盘输入...8
1.3 问题实现...11
1.4 高级应用-菜单实现...14
实验一...23
2 图形显示和鼠标输入...23
2.1 图形显示...23
2.2 鼠标的使用...52
2.3 问题实现...58
实验二...62
3 屏幕图像和动画技术...63
3.1 实现方法简单...63
3.2 利用动态开放图视口的方法...66
3.3 利用屏幕图像存储和重放的方法...67
3.4 使用页面交替的方法...70
3.5 问题实现...71
实验三...74
4 中断技术...74
4.1 编写自己的中断程序...76
4.2 问题实现...78
4.3 其他应用-硬中断演示秒表程序...84
实验四...87
5 发声技术...87
5.1 声音函数...87
5.2 计算机乐谱...88
5.3 问题实现...89
实验五...93
6 汉字显示技术...93
6.1 汉字编码...93
6.2 问题实现...95
实验六...97
1
使用过Windows 系统用户感受到图形用户界面的直观性和高效性。Windows 系
统的应用程序都拥有相同或相似的基本外观,包括窗口、菜单、工具条、状态栏等。用户只
学习其他软件并不难,从而降低学习成本和难度。Windows 是一
允许用户同时操作多个应用程序,或者在一个程序中同时做几件事。
例如,我们可以欣赏它MP3 的音乐边IE 冲浪,可以运行WORD 同时编辑多个文档等。
用户直接使用鼠标或键盘,或在不同的应用程序之间切换,非常方便。
这些都是单任务和命令行界面DOS 作系统所无法比拟的。TC2.0 或TC3.0 均是在DOS
环境下运行的C 系统。不过,无论采用TC,还是VC、BC,所产生的C 可执行程序都是基
于DOS 系统的。
C 语言发展如此迅速,而且成为最受欢迎的语言之一,主要因为它具有强大的功能。C
是一种“中”级语言,它把高级语言的基本结构和语句与低级语言的实用性结合起来。C 语
言可以对位、字节和地址进行操作,而这三者是计算机最基本的工作单元。C 语言具有各种
各样的数据类型,并引入了指针概念,可使程序效率更高。另外C 语言也具有强大的图形
功能,支持多种显示器和驱动器。而且计算功能、逻辑判断功能也比较强大,可以实现决策
目的。C 系统提供了大量的功能各异的标准库函数,减轻了编程的负担。所以要用C 语言
实现具有类Windows 系统应用程序界面特征的、或更生动复杂的DOS 系统的程序,就必须
掌握更高级的编程技术。这些技术与微机的硬件密切联系,除了在第一章介绍的内容外,更
深入的知识将在接口和汇编这门后期课程中学习。
1 文本的屏幕输出和键盘输入
[问题的提出] 编制一个程序,将屏幕垂直平分成两个窗口,左边窗口为蓝色背景,白色
前景,右边窗口为绿色背景,红色前景。两个窗口都设计为文本输入,即在窗口中可以输入
文字,在窗口屏幕中显示出来。使用tab 键在左右两个窗口中切换,每个窗口都有光标,活
动窗口光标进行闪烁。
[分析] 在这个问题中我们遇到了在初学C 时不曾接触到的新概念,如文本窗口、前景色、
背景色,以及围绕它们要解决的新问题:
(1) 如何在屏幕中开文本输入的窗口?
(2) 如何设置窗口的前景色、背景色或闪烁等显示属性?
(3) 如何通过按键来控制窗口的切换?
[解答] 要解决这一编程问题,要求有两方面的学习过程:一是对于分析中的前两个问题,
要求大家了解有关文本的屏幕输出的知识;二是对于第3 个问题,要求对键盘的输入有所了
解。下面先就这两个方面的内容做一介绍。
1.1 文本的屏幕输出
显示器的屏幕显示方式有两种:文本方式和图形方式。文本方式就是显示文本的模式,
它的显示单位是字符而不是图形方式下的象素,因而在屏幕上显示字符的位置坐标就用行和
列表示。Turbo C 的字符屏幕函数主要包括文本窗口大小的设定、窗口颜色的设置、窗口文
本的清除和输入输出等函数。这些函数的有关信息(如宏定义等)均包含在conio.h 头文件中,
因此在用户程序中使用这些函数时,必须用include 将conio.h 包含进程序。
1) 文本窗口的定义
Turbo C 默认定义的文本窗口为整个屏幕,共有80 列25 行的文本单元。如图3-1 所示,
规定整个屏幕的左上角坐标为(1,1),右下角坐标为(80,25),并规定沿水平方向为X
2
轴,方向朝右;沿垂直方向为Y 轴,方向朝下。每个单元包括一个字符和一个属性,字符
即ASCII 码字符,属性规定该字符的颜色和强度。除了这种默认的80 列25 行的文本显示
方式外,还可由用户通过函数:
void textmode(int newmode);
来显式地设置Turbo C 支持的5 种文本显示方式。该函数将清除屏幕,以整个屏幕为当前窗
口,并移光标到屏幕左上角。newmode 参数的取值见表3-1,既可以用表中指出的方式代码,
又可以用符号常量。LASTMODE 方式指上一次设置的文本显示方式,它常用于在图形方式
到文本方式的切换。
(1列,1行)
(80列,25行)
第一行
第一列
第80列
第25行
图3-1 屏幕文本显示坐标
表3-1 文本显示方式
方式 符号常量 显示列×行数和颜色
0 BW40 40×25 黑白显示
1 C40 40×25 彩色显示
2 BW80 80×25 黑白显示
3 C80 80×25 彩色显示
7 MONO 80×25 单色显示
-1 LASTMODE 上一次的显示方式
Turbo C 也可以让用户根据自己的需要重新设定显示窗口,也就是说,通过使用窗口设
置函数window()定义屏幕上的一个矩形域作为窗口。window()函数的函数原型为:
void window(int left, int top, int right, int bottom);
函数中形式参数(int left,int top)是窗口左上角的坐标,(int right,int bottom)是窗口的
右下角坐标,其中(left,top)和(right,bottom)是相对于整个屏幕而言的。例如,要定
义一个窗口左上角在屏幕(20,5)处,大小为30 列15 行的窗口可写成:
window(20, 5, 50, 25);
若window()函数中的坐标超过了屏幕坐标的界限,则窗口的定义就失去了意义,也就是说
定义将不起作用,但程序编译链接时并不出错。
窗口定义之后,用有关窗口的输入输出函数就可以只在此窗口内进行操作而不超出窗口
的边界。
另外,一个屏幕可以定义多个窗口,但现行窗口只能有一个(因为DOS 为单任务操作
系统)。当需要用另一窗口时,可将定义该窗口的window()函数再调用一次, 此时该窗口便
成为现行窗口了。
2) 文本窗口颜色和其它属性的设置
文本窗口颜色的设置包括背景颜色的设置和字符颜色(既前景色)的设置,使用的函数
及其原型为:
3
设置背景颜色函数:void textbackground(int color);
设置字符颜色函数:void textcolor(int color);
有关颜色的定义见表3-2。表中的符号常数与相应的数值等价,二者可以互换。例如设定兰
色背景可以使用textbackground(1),也可以使用textbackground(BLUE),两者没有任何区别,
只不过后者比较容易记忆,一看就知道是兰色。
表3-2 颜色表
符号常数 数值 含义 背景或背景
BLACK 0 黑 前景、背景色
BLUE 1 蓝 前景、背景色
GREEN 2 绿 前景、背景色
CYAN 3 青 前景、背景色
RED 4 红 前景、背景色
MAGENTA 5 洋红 前景、背景色
BROWN 6 棕 前景、背景色
LIGHTGRAY 7 淡灰 前景、背景色
DARKGRAY 8 深灰 用于前景色.
LIGHTBLUE 9 淡蓝 用于前景色
LIGHTGREEN 10 淡绿 用于前景色
LIGHTCYAN 11 淡青 用于前景色
LIGHTRED 12 淡红 用于前景色
LIGHTMAGENTA 13 淡洋红 用于前景色
YELLOW 14 黄 用于前景色
WHITE 15 白 用于前景色
BLINK 128 闪烁 用于前景色
Turbo C 另外还提供了一个函数,可以同时设置文本的字符和背景颜色,这个函数是
文本属性设置函数:void textattr(int attr);
参数attr 的值表示颜色形式编码的信息,每一位代表的含义如下:
字节低四位cccc 设置字符颜色,4~6 三位bbb 设置背景颜色,第7 位B 设置字符是否闪烁。
假如要设置一个兰底黄字,定义方法如下:
textattr(YELLOW+(BLUE<<4));
若再要求字符闪烁,定义变为:
textattr(128+YELLOW+(BLUE<<4);
注意:
(1) 对于背景只有0 到7 共八种颜色,取大于7 小于15 的数,则代表的颜色与减7 后的值
对应的颜色相同;
(2) 用textbackground()和textcolor()函数设置了窗口的背景与字符颜色后,在没有用clrscr()
函数清除窗口之前,颜色不会改变,直到使用了函数clrscr(),整个窗口和随后输出到窗
口中的文本字符才会变成新颜色。
4
(3) 用textattr()函数时背景颜色应左移4 位,才能使3 位背景颜色移到正确位置;
例程3-1:这个程序使用了关于窗口大小的定义、颜色的设置等函数,在一个屏幕上不同位
置定义了7 个窗口,其背景色分别使用了7 种不同的颜色。
/*-------例程3-1-------*/
#include
#include
int main()
{
int i;
textbackground(0); /* 设置屏幕背景色,待clrscr 后起作用 */
clrscr(); /* 清除文本屏幕 */
for(i=1; i<8; i++)
{
window(10+i*5, 5+i, 30+i*5, 15+i); /* 定义文本窗口 */
textbackground(i); /* 定义窗口背景色 */
clrscr(); /* 清除窗口 */
}
getch();
return 0;
}
void highvideo(void);
该函数将设置用高亮度显示字符。
void lowvideo(void);
该函数将设置用低亮度显示字符。
void normvideo(void);
该函数将设置通常亮度显示字符。
3) 窗口内文本的输入输出函数
? 窗口内文本的输出函数
我们以前介绍过的printf(),putc(),puts(),putchar()和输出函数以整个屏幕为窗口的,
它们不受由window 设置的窗口限制,也无法用函数控制它们输出的位置,但Turbo C 提供
了三个文本输出函数,它们受窗口的控制,窗口内显示光标的位置,就是它开始输出的位置。
当输出行右边超过窗口右边界时,自动移到窗口内的下一行开始输出,当输出到窗口底部边
界时,窗口内的内容将自动产生上卷,直到完全输出完为止,这三个函数均受当前光标的控
制,每输出一个字符光标后移一个字符位置。这三个输出函数原型为:
int cprintf(char *format,表达式表);
int cputs(char *str);
int putch(int ch);
它们的使用格式同printf(),puts()和putc(),其中cprintf()是将按格式化串定义的字符串或数
据输出到定义的窗口中,其输出格式串同printf 函数,不过它的输出受当前光标控制,且输
出特点如上所述,cputs 同puts,是在定义的窗口中输出一个字符串,而putch()则是输出一
个字符到窗口,它实际上是函数putc 的一个宏定义,即将输出定向到屏幕。
? 窗口内文本的输入函数
5
可直接使用stdio.h 中的getch 或getche 函数。需要说明的是,getche()函数从键盘上获
得一个字,在屏幕上显示的时候,如果字符超过了窗口右边界,则会被自动转移到下一行的
开始位置。
4) 有关屏幕操作的函数
void clrscr(void);
该函数将清除窗口中的文本,并将光标移到当前窗口的左上角,即(1, 1)处。
void clreol(void);
该函数将清除当前窗口中从光标位置开始到本行结尾的所有字符,但不改变光标原来的
位置。
void delline(void);
该函数将删除一行字符,该行是光标所在行。
void gotoxy(int x, int y);
该函数很有用,用来定位光标在当前窗口中的位置。这里x,y 是指光标要定位处的坐
标(相对于窗口而言)。当x, y 超出了窗口的大小时,该函数就不起作用了。
int movetext(int x1, int y1, int x2, int y2, int x3, int y3);
该函数将把屏幕上左上角为(xl,y1),右下角为(x2,y2)的矩形内文本拷贝到左上角为(x3,
y3)的一个新矩形区内。这里x,y 坐标是以整个屏幕为窗口坐标系,即屏幕左上角为(1,1)。
该函数与开设的窗口无关,且原矩形区文本不变。
int gettext(int xl, int yl, int x2, int y2, void *buffer);
该函数将把左上角为(xl,y1),右下角为(x2,y2)的屏幕矩形区内的文本存到由指针buffer
指向的一个内存缓冲区内,当操作成功,返回1;否则,返回0。
因一个在屏幕上显示的字符需占显示存储器VRAM 的两个字节,即第一个字节是该字
符的ASCII 码,第二个字节为属性字节,即表示其显示的前景、背景色及是否闪烁,所以
buffer 指向的内存缓冲区的字节总数的计算为:
字节总数=矩形内行数×每行列数×2
其中:矩形内行数=y2-y1+l,每行列数=x2-xl+1(每行列数是指矩形内每行的列数)。矩形内
文本字符在缓冲区内存放的次序是从左到右,从上到下,每个字符占连续两个字节并依次存
放。
int puttext(int x1, int y1, int x2, int y2, void *buffer);
该函数则是将gettext()函数存入内存buffer 中的文字内容拷贝到屏幕上指定的位置。
注意:
(1) gettext()函数和puttext()函数中的坐标是对整个屏幕而言的,即是屏幕的绝对坐标,而
不是相对窗口的坐标;
(2) movetext()函数是拷贝而不是移动窗口区域内容,即使用该函数后,原位置区域的文本
内容仍然存在。
例程3-2:下面的程序首先定义了一个字符数组,下标为64,表示用来存四行八列的文本。
由于没有用window 函数设置窗口,因而用缺省值,即全屏幕为一个窗口,程序开始设置80
列×25 行文本显示方式(C80),背景色为蓝色,前景色为红色,经clrscr 函数清屏后,设置
的背景色才使屏幕背景变蓝。gotoxy(10,10)使光标移到第10 行10 列,然后在(10,10)开
始位置显示L:load,接着在下面三行相同的列位置显示另外三条信息,13 行10 列显示的
E:exit 后面带有回车换行符,为的是将光标移到下一行开始处,好显示press any key to
continue。当按任一键后,gettext 函数将(10,l0,18,13)矩形区的内容存到ch 缓存区内。
ch 即上述的四行八列信息,接着设置一个窗口,并纵向写上1,2,3,4,然后用movetext(),
6
将此窗口内容复制到另一区域,由于此区域包括背景色和显示的字符,所以被复制到另一区
域的内容也是相同的背景色和文本。当按任一键后,又出现提示信息,再按键,则存在ch
缓冲区内的文本由puttext()又复制到开设的窗口内了,注意上述的函数movetext(),gettext(),
puttext()均与开设的窗口内坐标无关,而是以整个屏幕为参考系的。
/*-------例程3-2-------*/
#include
main()
{
int i;
char ch[4*8*2]; /* 定义ch 字符串数组作为缓存区 */
textmode(C80);
textbackground(BLUE);
textcolor(RED);
clrscr();
gotoxy(10,10);
cprintf("L:load");
gotoxy(10,11);
cprintf("S:save");
gotoxy(10,12);
cprintf("D:delete");
gotoxy(10,13);
cprintf("E:exit/r/n");
cprintf("Press any key to continue");
getch();
gettext(10,10,18,13,ch); /* 存矩形区文存到ch 缓存区 */
clrscr();
textbackground(1);
textcolor(3);
window(20,9,34,14); /* 开一个窗口 */
clrscr();
cprintf("1./r/n2./r/n3./r/n4./r/n");/* 纵向写1,2,3,4 */
movetext(20,9,34,14,40,10); /* 将矩形区文本复制到另一区域 */
puts("hit any key");
getch();
clrscr();
cprintf("press any key to put text");
getch();
clrscr();
puttext(23,10,31,13,ch); /* 将ch 缓存区所存文本在屏上显示 */
getch();
}
5) 状态查询函数
有时需要知道当前屏幕的显示方式,当前窗口的坐标、当前光标的位置,文本的显示
7
属性等,Turbo C 提供了一些函数得到屏幕文本显示有关信息的函数:
void gettextinfo(struct text_info *f);
这里的text_info 是在conio.h 头文件中定义的一个结构,该结构的定义是
structtext_info(
unsigned char winleft; /* 窗口左上角x 坐标 */
unsigned char wintop; /* 窗口左上角y 坐标 */
unsigned char winright; /* 窗口右下角x 坐标 */
unsigned char winbottom; /* 窗口右下角y 坐标 */
unsigned char attributes; /* 文本属性 */
unsigned char normattr; /* 通常属性 */
unsigned char currmode; /* 当前文本方式 */
unsigned char screenheight; /* 屏高 */
unsigned char screenwidth; /* 屏宽 */
unsigned char curx; /* 当前光标的x 值 */
unsigned char cury; /* 当前光标的y 值 */
};
例程3-3:下面的程序将屏幕设置成80 列彩色文本方式,并开了一个window(1,5,70,20)
的窗口,在窗口中显示了current information of window,然后用gettextinfo 函数得到当前窗
口的信息,后面的cprintf()函数将分别显示出结构text_info 各分量的数值来,即:
current information of window
Left corner of window is l,5 Right corner of window is 70,20 Text window attribute
is 29 Text window normal attribute is 29 Current video mode is 3 window height and
width is 25,80 Row cursor pos is 2,Column pos is 1
/*-------例程3-3-------*/
#include
main()
{
struct text_info current;
textmode(C80);
textbackground(1);
textcolor(13);
window(1,5,70,20);
clrscr();
cputs("Current information of window/r/n");
gettextinfo(¤t);
cprintf("Left corner of window is %d,%d",
current.winleft,current.wintop);
cprintf("Right corner of window is %d,%d",
current.winright,current.winbottom);
cprintf("Text window attribute is%d",
current.attribute);
cprintf("Text window normal attribute",
current.normattr);
8
cprintf("Current video mode is%d",current.currmode);
cprintf("Window height and width is%d,%d",
current.screenheight,current.screenwidth);
cprintf("Row cursor pos is %d,Column pos is %d",
current.cury,current.curx);
getch();
}
1.2 键盘输入
当我们按下键盘上某键时,系统如何知道某键被按下呢?它的奥妙在于计算机键盘是一
个智能化的键盘,在键盘内有一个微处理器,它用来扫描和检测每个键的按下和拾起状态。
然后以程序中断的方式(INT 9)与主机通信。ROM 中BIOS 内的键盘中断处理程序,会将
一个字节的按键扫描码(扫描码的0~6 位标识了每个键在键盘上的位置,最高位标识按键的
状态,0 对应该键是被按下;1 对应松开。它并不能区别大小写字母,而且一些特殊键如
PrintScreen 等不产生扫描码直接引起中断调用)翻译成对应的ASCII 码。
由于ASCII 码仅有256 个(28),它不能将PC 键盘上的键全部包括,因此有些控制键如
CTRL,ALT,END,HOME,DEL 等用扩充的ASCII 码表示,扩充码用两个字节的数表示。
第一个字节是0,第二个字节是0~255 的数,键盘中断处理程序将把转换后的扩充码存放在
Ax 寄存器中,存放格式如表3-3 所示。对字符键,其扩充码就是其ASCII 码。
表3-3 键盘扫描码
键名 AH AL
字符键 扩充码=ASCII 码 ASCII 码
功能键/组合键 扩充码 0
是否有键按下,何键按下,简单的应用中可采用两种办法:一是直接使用Turbo C 提供
的键盘操作函数bioskey()来识别,二是通过第一章1.2.4.3 节介绍的int86()函数,调用BIOS
的INT 16H,功能号为0 的中断。它将按键的扫描码存放在Ax 寄存器的高字节中。
函数bioskey()的原型为:
int bioskey(int cmd);
它在bios.h 头文件中进行了说明,参数cmd 用来确定bioskey()如何操作:
cmd 操作
0 bioskey()返回按健的键值,该值是2 个字节的整型数。若没有键按下,则该函数一
直等待,直到有键按下。当按下时,若返回值的低8 位为非零,则表示为普通键,
其值代表该键的ASCII 码。若返回值的低8 位为0,则高8 位表示为扩展的ASCII
码,表示按下的是特殊功能键。
1 bioskey()查询是否有键按下。若返回非0 值,则表示有键按下,若为0 表示没键按
下。
2 bioskey()将返回一些控制键是否被按过,按过的状态由该函数返回的低8 位的各位
值来表示:
字节位 对应的16 进制数 含义
0 0x01 右边的shift 键被按下
1 0x02 左边的shift 键被按下
2 0x04 Ctrl键被按下
9
3 0x08 Alt 键被按下
4 0x10 Scroll Lock 已打开
5 0x20 Num Lock 已打开
6 0x40 Caps Lock 已打开
7 0x80 Inset 已打开
当某位为l 时,表示相应的键已按,或相应的控制功能已有效,如选参数cmd 为2,
若key 值为0x09,则表示右边的shift 键被按,同时又按了Alt 键。
函数bioskey()的原型为:
int int86(int intr_num,union REGS *inregs,union REGS *outregs);
这个函数在bios.h 头文件中进行了说明,它的第一个参数intr_num 表示BIOS 调用类型
号,相当于int n 调用的中断类型号n,第二个参数表示是指向联合类型REGS 的指针,它
用于接收调用的功能号及其它一些指定的入口参数,以便传给相应的寄存器,第三个参数也
是一个指向联合类型REGS 的指针,它用于接收功能调用后的返回值,即出口参数,如调
用的结果,状态信息,这些值从相关寄存器中得到。
例程3-4:第二章扫雷游戏中,我们定义上,下,左,右键用来移动雷区光标的位置,回车或者
空格键用来挖开光标当前指向的雷区方块,F 和f 标记当前光标指向的方块有地雷,Q 和q
在光标指向方块打问号,表示可能有地雷,A 和a 用来自动挖开光标周围的方块,ESC 退出
游戏。在实现时,我们调用bioskey(0)来获得按键值,然后经过判断转入相应的处理。下面
让我们再来回顾一下2.2.3.4 节中的扫雷游戏源程序片段,其中key.c 文件仅有一个函数
getKey(),它用biosky(0)读取键盘输入,读到一个有用键(上,下,左,右键、回车或者空格键、
F、f、Q、q、A、a、ESC)时返回该键值。
/*key.c——扫雷游戏的按键获取*/
#include
/*define key-value*/
#define ENTER 0x1c0d
#define UP 0x4800
#define DOWN 0x5000
#define LEFT 0x4b00
#define RIGHT 0x4d00
#define ESC 0x011b
#define SPACE 0x3920
#define LOWERF 0x2166
#define UPPERF 0x2146
#define LOWERA 0x1e61
#define UPPERA 0x1e41
#define LOWERQ 0x1071
#define UPPERQ 0x1051
10
int getKey(void){
while(1){
int key=bioskey(0);
switch(key){
case ENTER:
case UP:
case DOWN:
case LEFT:
case RIGHT:
case ESC:
case SPACE:
case LOWERF:
case UPPERF:
case LOWERA:
case UPPERA:
case LOWERQ:
case UPPERQ: return key;
}
}
}
/*------------------------------------------End of key.c------------------------------------------*/
例程3-5:本程序仅仅演示了通过int86()获取按键的扫描码。在此注意扫描码和bioskey()返
回的码值是不同的。
#include
#include
/* define Keys scan code */ /* 定义各键的扫描码 */
#define Key_ESC 1
#define Key_A 30
int getKeySCode();
main()
{
int acount=0, ky;
do
{
ky= int getKeySCode();; /* 得到按键的扫描码 */
switch(ky){
case Key_A: /* A and a key */
++account; break;
case Key_ESC:
printf(“/nEnd the program”);
exit(0);
11
default:
break;
}
printf(“/nDuring the program, you press A and a %d times”,acount);
}
/* read char on key,return scan code */
int getKeySCode() /* 读键函数 */
{
union REGS rg;
rg.h.ah=0;
int86(0x16,&rg,&rg);
return rg.h.ah;
}
1.3 问题实现
在了解了上两小节的内容后,你就可以解决我们最初提出的问题了(见例程3-6)。
我们的设计思想是首先用文本窗口函数window (int x1, int y1, int x2, int y2)画出两个窗
口,用textcolor (int color),textbackground (int color),clrscr (void)等进行窗口属性的设置。
用tab 键进行两个窗口间的循环切换,在每次切换前先调用gettext (int left, int top, int right, int
bottom, void * buf)函数把当前矩形窗口上的字符拷贝到由buf 所指向的内存中,在切换到另
一个窗口后调用puttext (int left, int top, int right, int bottom, void * buf)把先前存储在该窗口
buf 所指向的内存中的字符拷贝到当前窗口中,并用gotoxy (int x, int y)把光标移到原先所在
位置,因此可以接着先前的文本继续编辑。
程序中运用一个临时变量turn 的值0 和1 来进行两个窗口间的循环切换,当值为1 时
调用draw_left_win()函数重绘左边窗口,当值为0时调用draw_right_win()函数重绘右边窗口。
程序用bioskey(0)来读取键盘输入的文本并存在内存中,然后用putch(key)输出。程序运行
(已再次切换到左窗口,光标在问号后闪烁)的界面如图3-2 所示。
在这个程序中我们调用了process.h 中的void exit(int status)函数,它清除和关闭
所有打开的文件,写出任何缓冲输出,并终止程序。参数为0,则认为程序正常终止;若为
非零值,则说明存在执行错误。
/*例程3-6*/
图3-2 3.1 节问题实现后程序运行界面
12
#include
#include
#include
/*切换时保存左窗口文本*/
char leftbuf[40*25*2];
/*切换时保存右窗口文本*/
char rightbuf[40*25*2];
/*切换时保存左窗口当前坐标*/
int leftx, lefty;
/*切换时保存右窗口当前坐标*/
int rightx, righty;
/*重绘左边窗口*/
void draw_left_win();
/*重绘右边窗口*/
void draw_right_win();
int main()
{
int key;
int turn;
textmode(C80);
textbackground(0);
textcolor(WHITE);
clrscr();
gotoxy(60,1);
cprintf("Press Esc to Quit");
window(41,2,79,24); /*右边窗口为绿色背景,红色前景*/
textbackground(2);
textcolor(4);
clrscr();
gettext(41,2,79,24, rightbuf);
window(2,2,40,24); /*左边窗口为蓝色背景,白色前景*/
textbackground(1);
textcolor(15);
clrscr();
gettext(2,2,40,24, leftbuf);
turn = 0; /*初始激活左窗口*/
for(;;)
{
key=bioskey(0);
13
if(key == 0x11b)
exit(0);
key=key&0xff; /*获取窗口输入的文本的ASCII 码值*/
if(key == '/t')
{
if(turn == 0) /*切换到左窗口*/
{
gettext(2,2,40,24, leftbuf);
leftx = wherex();
lefty = wherey();
draw_right_win();
turn = 1;
}
else if(turn == 1) /*切换到右窗口*/
{
gettext(41,2,79,24, rightbuf);
rightx = wherex();
righty = wherey();
draw_left_win();
turn = 0;
}
}
else
putch(key); /*当前光标处显示新输入的文本字符*/
}
}
void draw_right_win()
{
window(41,2,79,24);
textbackground(2);
textcolor(4);
clrscr();
puttext(41,2,79,24, rightbuf);
gotoxy(rightx, righty);
}
void draw_left_win()
{
window(2,2,40,24);
textbackground(1);
textcolor(15);
clrscr();
puttext(2,2,40,24, leftbuf);
14
gotoxy(leftx, lefty);
}
1.4 高级应用——菜单实现
1.4.1 一个弹出式菜单
这个程序(例程3-7)是一个文本方式下的菜单程序,它生成一个弹出式菜单,如图3-3
所示。程序运行时,首先弹出一个带洋红色框的蓝底菜单,并有一红色光条压在第一项上,
当按E 键时,程序回到系统,压Down 键时,光条移到第二项,压A 时,则列出当前日录
下的各文件目录,再压任意键后则又弹出菜单,当压B 键时,则列出目录,当屏满后,暂
停,按键后,又继续列出,它实际上就是执行dir /p 命令,当按回车键后,又出现菜单,当
按C 键后,便执行dir /w 的dos 命令,以宽行格式列出目录,按回车键后又回到菜单用UP
键可使光条上移。
程序开始时,首先用do 循环反复运行下面的程序,直到按E 键后为止,window(7,8,
19,15)定义了一个用洋红色填充的窗口,接着又定义一个蓝色窗口,它套在洋红色窗口内,
因而形成一个带洋红色粗边框的蓝色窗口,在窗口内写上各菜单项,接着又调用光条上移函
数upbar(y—1),使红色光条压在第一菜单项上,下面的do 循环则用来构成一个反复检查按
键,并转去执行相应的功能操作,do 循环内的第一个switch(ky)语句用来判断按的是何键,
按键值赋给y 的相应值,并使ky=key_enter,以结束do 循环,跟着第二个switch(y)语句则
按y 值转去执行相应的菜单项功能。只有当菜单项功能键是E 时,才使得外层的do 循环结
束而回到系统。
upbar(int y)函数是产生一个上移光条(用gettext()函数),实际上它第一次被调用时,则用
gettext(i,y,i,y,&t)连续8 次将菜单项8 个字符长的区域(蓝底无字)存入&t 中,用蓝底白
字又放回(用puttext()函数),而第2 个gettext()函数则将下移一行的8 个字符长区域(即第一
个菜单项,存入t 中,又以白字红底方式放回原处,即产生一个红色光条压在第一菜单项上。
以后的调用(当按UP 键时)则随y 值不同,而红色光条压在不同菜单项上。
downbar()函数产生下移光条,当按Down 键时,便调用该函数将红底白字的光条压在下
一条菜单项上。
在Turbo C 的stdlib.h 中提供了一个能够执行DOS 命令的函数system (),其原型为:
int system(char *command);
参数command 所指向的字符串正是DOS 的命令。在这个程序中我们通过调用system("dir"),
system("dir/p"),system("dir/w")使程序根据菜单选项A,B,C 来分别先执行DOS 命令dir
图3-3 弹出式菜单
15
(列出当前日录下的各文件目录),dir/p(列出目录,当屏满后,暂停,按键后,又继续列
出),dir/w(以宽行格式列出目录)后再返回程序。
/*例程3-7*/
#include
#include
#include
#include
#include
/* define Key Values /* 定义各键的bioskey(0)的按键返回值 */
#define Key_DOWN 0x5100
#define Key_UP 0x4900
#define Key_A 0x1e41
#define Key_a 0x1e61
#define Key_B 0x3042
#define Key_b 0x3062
#define Key_C 0x2e43
#define Key_c 0x2e63
#define Key_E 0x1245
#define Key_e 0x1265
#define Key_ENTER 0x1c0d
main()
{
int ky,y;
char ch;
textbackground(0);
clrscr();
do
{
textmode(C80);
textbackground(13);
textcolor(RED);
window(7,8,19,15); /* 开一个窗口 */
clrscr();
textbackground(1);
textcolor(RED);
window(8,9,18,14); /* 再开一个当前窗口,套在上一个窗口之中*/
clrscr();
gotoxy(3,3);
cprintf("E:exit/r/n"); /* 窗口中写上红色的菜单项 */
gotoxy(3,4);
cprintf("A:dir/r/n");
gotoxy(3,5);
16
cprintf("B:dir/p/r/n");
gotoxy(3,6);
cprintf("C:dir/w/r/n");
y=10;
upbar(y-1); /* 调用光条上移函数 */
do
{
ky=bioskey(0); /* 得到按键的键值 */
switch(ky)
{
case Key_A: case Key_a:/* A and a key */
{
y=12;
ky=Key_ENTER;
break;
}
case Key_B:case Key_b:/* B and b key */
{
y=13;
ky=Key_ENTER;
break;
}
case Key_C: case Key_c:/* C and c key */
{
y=14;
ky=Key_ENTER;
break;
}
case Key_E:case Key_e:/* E and e key */
{
y=11;
ky=Key_ENTER;
break;
}
case Key_DOWN:/* Cursor down key */
{
if ( y<13 )
{
upbar(y);
y++;
}
break;
}
case Key_UP: /* Cursor up key */
17
{
if(y>10)
{
downbar(y);
y--;
}
break;
}
}
} while (ky !=Key_ENTER ); /* Enter key */
textcolor(WHITE);
switch(y)
{
case 11:
{
ch='%'; /* 返回系统 */
break;
}
case 12:
{
system("dir");
getch(); /* 列目录后等待按键 */
break;
}
case 13:
{
system("dir /p");
getch(); /* 列目录屏满后暂停,按任意键继续 */
break;
}
case 14:
{
system("dir /w");
getch(); /* 用宽行格式列目录 */
break;
}
}
if(ch=='%')
break;
} while(1);
clrscr(); /* 清屏 */
}
18
/* read bar down */
upbar(int y) /* 光条上移函数 */
{
int i;
typedef struct texel_struct {
unsigned char ch;
unsigned char attr;
} texel;
texel t;
for(i=9;i<=17; i++)
{
gettext(i,y,i,y,&t);
t.attr=0x1f; /* 字符为白色,背景为蓝色 */
puttext(i,y,i,y,&t);
gettext(i,y+1,i,y+1,&t);
t.attr=0x4f; /* 字符为白色,背景为红色 */
puttext(i,y+1,i,y+1,&t);
}
gotoxy(3,y+1);
return;
}
/* red bar up */ /* 光条下移函数 */
downbar(int y)
{
int i;
typedef struct texel_struct {
unsigned char ch;
unsigned char attr;
} texel;
texel t;
for(i=9;i<=17;i++)
{
gettext(i,y,i,y,&t);
t.attr=0x1f; /* 字符为白色,背景为蓝色 */
puttext(i,y,i,y,&t);
gettext(i,y-1,i,y-1,&t);
t.attr=0x4f ; /* 字符为白色,背景为红色 */
puttext(i,y-1,i,y-1,&t);
}
gotoxy(3,y-1);
return;
}
19
1.4.2 一个下拉式菜单
该程序(例程3-8)在文本方式下产生一个下拉式菜单,程序运行时首先在屏幕顶行产
生一个浅灰底黑字的主菜单,各菜单项的第一个字母加红,表示为热键,如图3-4 所示,当
选择主菜单第一项,即按ALT_F 时,便产生一个下拉式子菜单,可用UP 和DOWN 键使压
在第一个子菜单项上的黑色光条上下移动,当压在某子菜单项上,且按回车后,程序便转去
执行相应子菜单项的内容,由于篇幅关系,该程序仅是一个演示程序,只作了第一个主菜单
项和对应的子菜单,且子菜单项对应的操作只在程序相应处作了说明,并无具体内容,对主
菜单项其它各项未作选它时相应的子菜单,但作法和第一项File 的相同,故不赘述。
程序中用指针数组munu[]存放主菜单各项,red[]存放各项的热键字符(即主菜单项各项
的第一个字母),f[]存放主菜单第一项file 的子菜单各项。定义字符数组buf 存放原子菜单所
占区域的内容,buf1 存放一个子菜单项区域内容,由于一个字符占两个字节,故所占列数
均乘了2。外层循环处理主菜单,第一步显示主菜单界面,即先使整个屏幕的背景色为蓝色,
然后开辟显示主菜单项的窗口(window(1,1,80,1)),用浅灰底黑字依次显示出主菜单各
项,用红色字母再重现各项的第一个字母,并使光标定位在主菜单项的第一项File 的F 处;
第二步用键盘管理函数bioskey()获取菜单选项,当按ALT_X 键时,则退出本程序,若按
ALT_F 键时,则执行弹出子菜单的操作:首先加黑主菜单的File 项显示,将子菜单的区域
内容保存到buf 缓冲区内(用gettext(5,2,20,12,buf)),这样当子菜单项消失时,用它来恢复原
区域的内容。然后设置一个作子菜单的浅灰色窗口(window(5,2,20,9)),调用作框函数box,
在浅灰色底上面输出一个矩形框来,接着的for 循环则在框内显示出子菜单各项内容。接着
是处理File 的子菜单项的内层循环:首先获取按键,当为ALT_X 时退出本程序;当为ESC
键时直接返回到外层循环,即返回到主界面;当为UP 或DOWN 键时,则产生黑色光条的
上下移动,当光条在第一项上时,若再按UP 键,则光条移到最后一项,若光条原来就在最
后一项,再按DOWN 键,则光条退回到第一子菜单项去,这由y=y==2?6:y-1 和y=y==6?2:
y+l 来实现,当光条压在某子菜单项上,且当按键为ENTER 时程序则转去执行相应的子菜
单项指明的操作,它们由switch(y-1)语句来实现,当光条压在第一子菜单项上,且按回车后,
则执行case l 后的操作,由于是示范程序,具体操作没有指出,要变为实用菜单,则需在此
处填上操作内容,转去作相应的处理,处理之后返回到外层循环。
用画框子程序box()画框,实际上是由符号┌(ASCII 为0xda),─,└,│,┘,┐,
拼成了一个矩形框,仍然是文本输出,这是和图形方式下画框不同之处。
/*例程3-8*/
#include
#include
图3-4 下拉式菜单
20
#include
#define Key_DOWN 0x5100
#define Key_UP 0x4900
#define Key_ESC 0x011b
#define Key_ALT_F 0x2100
#define Key_ALT_X 0x2d00
#define Key_ENTER 0x1c0d
void box(int startx,int starty,int high,int width);
main()
{
int i,key,x,y,l;
char *menu[] = {"File","Edit","Run","Option","Help","Setup","Zoom","Menu"};
/* 主菜单各项 */
char *red[] = { "F","E","R","O","H","S","Z","M" }; /* 加上红色热键 */
char *f[] = {"Load file", "Save file", "Print", "Modify ", "Quit A1t_x"};
/* File 项的子菜单 */
char buf[16*10*2],buf1[16*2]; /* 定义保存文本的缓冲区 */
while(1)
{
textbackground(BLUE);
clrscr();
textmode(C80);
window(1,1,80,1);/* 定义显示主菜单的窗口 */
textbackground(LIGHTGRAY);
textcolor(BLACK);
clrscr();
gotoxy(5,1);
for(i=0,l=0;i<8;i++)
{
x=wherex(); /* 得到当前光标的坐标 */
y=wherey();
cprintf("%s",menu[i]); /* 显示各菜单项 */
l=strlen(menu[i]); /* 得到菜单项的长度 */
gotoxy(x,y);
textcolor(RED);
cprintf("%s",red[i]); /* 在主菜单项各头字符写上红字符 */
x=x+l+5;
gotoxy(x,y);
textcolor(BLACK); /* 为显示下一个菜单项移动光标 */
}
gotoxy(5,1);
21
key=bioskey(0);
switch (key){
case Key_ALT_X:
exit(0); /* ALT_X 则退出 */
case Key_ALT_F:
{
textbackground(BLACK);
textcolor(WHITE);
gotoxy(5,1);
cprintf("%s",menu[0]); /* 加黑File 项 */
gettext(5,2,20,12,buf); /* 保存窗口原来的文本 */
window(5,2,20,9);/* 设置作矩形框的窗口 */
textbackground(LIGHTGRAY);
textcolor(BLACK);
clrscr();
box(1,1,7,16); /* 调用作框函数 */
for(i=2;i<7;i++) /* 显示子菜单各项 */
{ gotoxy(2,i);
cprintf("%s",f[i-2]);
}
gettext(2,2,18,3,buf1); /*将下拉菜单的内容保存在buf1*/
textbackground(BLACK);
textcolor(WHITE);
gotoxy(2,2);
cprintf("%s",f[0]);/*加黑下拉菜单的第一项load file*/
gotoxy(2,2);
y=2;
while ((key=bioskey(0))!=Key_ALT_X) /* 等待选择下拉菜单项*/
{
if ((key==Key_UP)||(key==Key_DOWN))
{
puttext(2,y,18,y+1,buf1); /* 恢复原先的项 */
if (key==Key_UP)
y=y==2?6:y-1;
else
y=y==6?2:y+1;
gettext(2,y,18,y+1,buf1);/*保存要压上光条的子菜单项*/
textbackground(BLACK);
textcolor(WHITE);
gotoxy(2,y);
cprintf("%s",f[y-2]); /* 产生黑条压在所选项上 */
gotoxy(2,y);
22
}
else
if (key==Key_ENTER)/* 若是回车键,判断是哪一子菜单按的回
车,在此没有相应的特殊处理*/
{
switch ( y-1 ){
case 1: /* 是子菜单项第一项:Load file */
break;
case 2: /* Save file */
break;
case 3: /* print */
break;
case 4: /* modify */
break;
case 5:
exit(0);
default:
break;
}
break;
}
else
if (key==Key_ESC)
break; /* 是Esc 键,返回主菜单 */
}
if (key==Key_ALT_X) exit(0);
break;
}
}
}
}
void box(int startx,int starty,int high,int width) /* 画矩形框函数 */
{ int i;
gotoxy(startx,starty);
putch(0xda); /* 画┌ */
for (i=startx+1;iputch(0xbf); /* 画┐ */
for( i=starty+1;i{
gotoxy(startx,i);putch(0xb3); /* 画│ */
gotoxy(width,i);putch(0xb3); /* 画│ */
}
gotoxy(startx,high);
23
putch(0xc0); /* 画└ */
for (i=startx+1;iputch(0xd9); /* 画┘ */
return ;
}
实验一
(1) 编写一个类似tt.exe 中的游戏,计算机控制一些单词从屏幕上边下落,用户输入准确的
单词时,由屏幕下方发出一个箭头,击毁该单词。
(2) 针对第2 章提出的扫雷游戏问题,参考Windows 系统自带的扫雷游戏,采用本节介绍
的知识,实现图3-5 所示的主菜单和相应的下拉菜单,并且尝试完成部分菜单功能。
2 图形显示方式和鼠标输入
[问题的提出] 编写程序,使用鼠标进行如下操作:按住鼠标器的任意键并移动,十字光
标将随鼠标而移动,根据按键的不同采用不同的形状来画出相应的移动轨迹:当仅按下左键
时,用圆圈;仅按下右键时,用矩形;其它按键情况用线条。
[分析] 在这个问题中我们看到输入的操作已不再是通过键盘,而是用鼠标。而且我们还要
响应鼠标的具体操作,在屏幕上画出点、矩形、圆等图形。
[解答] 要解决这一编程问题,将涉及两方面的内容:一是关于程序设计中较难且又最吸引
人的部分——计算机图形程序设计,即图形方式(另外一种显示器显示方式)的知识;二是
关于鼠标的知识。下面将对它们做具体的解释。
2.1 图形显示
图形方式和文本方式不同,我们可以在这种方式下画图,它的显示单位是象素。如同近
看电视的画面一样,显示器显示的图形也是由一些圆点组成(其亮度、颜色不同),这些点称
为象素(或称象点)。满屏显示象素多少,则决定了显示的分辨率高低,可以看出象素越小(或
个数越多),则显示的分辨率越高。象素在屏幕上的位置则可由其所在的x,y 坐标来决定。
图3-5 Windows 扫雷游戏的菜单
图3-6 (a)显示屏在640×480 分辨率下的坐标 (b)不同位置象素的坐标
24
显示屏的图形坐标系统就象一个倒置的直角坐标系(如图3-6 所示):定义屏幕的左上角为
原点,正x 轴右延伸,正y 轴向下延伸,即x 和y 坐标值均为非负整数,但其最大值则由显
示器的类型和显示方式来确定,也就是说,显示的象素大小可以通过设置不同的显示方式来
改变。例如在图3-6(a)所示的显示方式下,x,y 最大坐标是(639,399),即满屏显示的象素
个数为640×400。3-6(b)示出了不同位置象素的坐标,其最大的x,y 值(即行和列值)由程序
设置的显示方式来决定。我们称这种显示坐标为屏幕显示的物理坐标或绝对坐标,以便和图
视窗口(图视口)坐标相区别。图视窗口是指在物理坐标区间又开辟一个或多个区间,在这些
区间又可定义一个相对坐标系统,以后画图均可在此区间进行,以相对坐标来定义位置。如
在图3-6(a)所示的显示方式下,当定义了一个左上角坐标为(200,50),右下角坐标为(400,
150)的一个区域为图视口,则以后处理图形时,就以其左上角为坐标原点(0,0),右下角为
坐标(200,100)的坐标系来定位图形上各点位置。
Turbo C 为用户提供了一个功能很强的画图软件库,它又称为Borland 图形接口(BGI),
它包括图形库文件(graphics.lib),图形头文件(graphics.h)和许多图形显示器(图形终端)的驱动
程序(如CGA.BGI、EGAVGA.BGI 等)。还有一些字符集的字体驱动程序(如goth.chr 黑体字
符集等)。编写图形程序时用到的一些图形库函数均在graphics.lib 中,执行这些函数时,所
需的有关信息(如宏定义等)则包含在graphics.h 头文件中。因此用户在自己的画图源程序中
必须包括graphics.h 头文件,在进行目标程序连接时,要将graphics.lib 连接到自己的目标程
序中去。
由于计算机画图涉及到显示器和驱动它们工作的图形适配器(卡)等许多硬件知识,因而
有必要简单地介绍一下。
2.1.1 图形显示器与适配器
计算机中要显示的字符和图形均以数字形式存储在存储器中,而显示器接收的应是模拟
信号。插在PC 微机插槽中的图形卡(即适配器或显卡),其作用就是将要显示的字符和图形
以数字形式存储在卡上的视频存储器VRAM 中,再将其变成视频模拟信号送往相应适配的
显示器进行显示,也即适配器在计算机主机和显示器之间起到了信息转换和视频发送作用,
一般PC 机中适配器、主机、显示器之间的关系如图3-7 所示。
图3-7 适配器、主机、显示器之间的关系图
25
由于计算机配有的显示器种类不同,因而适配器种类不同,而且不同适配器又可支持不
同的分辨率显示方式、文本显示方式和颜色设置。表3-4 提供了Turbo C 支持的各种显示器
适配器和图形模式,其中常用的适配器是下面三种:
1) 彩色图形适配器(CGA)
这是PC/XT 等微机配用的显示器图形卡,它可以产生单色或彩色字符和图形。在图形
方式下,Turbo C 支持两种分辨率供选择:一种为高分辨方式(CGAHI),象素数为640×200,
这时背景色是黑的(当然也可重新设置),前景色可供选择,但前景色只是同一种,因而图形
只显示两色;另一种为中分辨显示方式,象素数为320×200,其背景色和前景色均可由用
户选择,但仅能显示四种颜色。在该显示方式下,可有四种模式供选择,即CGACO,CGACl,
CGAC2,CGAC3,它们的区别是显示的4 种颜色不同。
2) 增强型图形适配器〔EGA〕
该适配器与之配接的相应显示器,除支持CGA 的四种显示模式外,还增加了Turbo C
称为EGALO(EGA 低分辨显示方式,分辨率为640×200)的16 色显示方式,和640×350 的
EGAHI(EGA 高分辨显示方式,分辨率为640×350)的16 色显示方式。
表3-4 Turbo C 支持的适配器和图形模式
适配器Driver 模式Mode 分辨率 颜色数
页数
标识符
CGA 0
1
2
3
4
320×200
320×200
320×200
320×200
640×200
4
4
4
4
2
1
1
1
1
1
CGAC0
CGAC1
CGAC2
CGAC3
CGAHI
EGA 0
1
640×200
640×350
16
16
4
2
EGALO
EGAHI
EGA64 0
1
640×200
640×350
16
4
1
1
EGA64LO
EGA64HI
EGAMONO 0 640×350 2 1 EGAMONOHI
VGA 0
1
2
640×200
640×350
640×480
16
16
16
2
2
1
VGALO
VGAMED
VGAHI
MCGA 0
1
2
3
4
5
320×200
320×200
320×200
320×200
640×200
640×480
4
4
4
4
2
2
1
1
1
1
1
1
MCGA0
MCGA1
MCGA2
MCGA3
MCGAMED
MCGAHI
HREC 0 720×348 2 1 HERCMONOHI
ATT400 0
1
2
3
4
5
320×200
320×200
320×200
320×200
640×200
640×400
4
4
4
4
2
2
1
1
1
1
1
1
ATT400C0
ATT400C1
ATT400C2
ATT400C3
ATT400MED
ATT400HI
PC3270 0 720×350 2 1 PC3270HI
IBM8514 0
1
640×480
1024×768
256
256
IBM8514LO
IBM8514HI
26
3) 视频图形阵列适配器(VGA)
它支持CGA 和EGA 的所有显示方式,但自己还有640×480 的高分辨显示方式
(VGAHI)、640×350 的中分辨显示方式(VGAMED)和640×200 的低分辨显示方式(VGALO),
它们均可有16 种显示颜色可供选择。
众多生产厂家推出了许多性能优于VGA 但名字各异的图形显示系统,美国标准协会制
定了这样的系统应具有的主要性能标准,常将属于这类的显示适配卡统称为SVGA(即Super
VGA)。目前基本上使用的都属于SVGA,可以使用VGA 卡方式进行编程。
显示器的两种工作方式,即文本方式或称字符显示方式和图形显示方式,它们的主要差
别是显示存储器(VRAM)中存的信息不同。字符方式时,VRAM 存放要显示字符的ASCII
码,用它作为地址,取出字符发生器ROM(固定存储器)中存放的相应字符的图象(又称字模,
如图3-8(a)显示了C 的8×8 点阵字模),变成视频信号在显示器屏上进行显示。EGA、VGA
可以使用几种字符集,如EGA 下有三种字符集,VGA 有五种字符集。而当选择图形方式时,
则要显示的图形的图象直接存在VRAM 中,VRAM 中某地址单元存放的数就表示了相应屏
幕上某行和列上的象素及颜色。图3-8(b)是字符方式和CGA320×200 中分辨图形方式显示的
示意图,在CGA 的中分辨图形方式下,每字节代表4 个象素,即每2 位表示一个象素及颜
色。
上面介绍了一些编制图形程序的预备知识,下面将介绍编制图形程序的各种库函数和相
应的其它一些知识。
2.1.2 图形系统的初始化和关闭
在编制图形程序时,进入图形方式前,首先要在程序中对使用的图形系统进行初始化,
即要用什么类型的图形显示适配器的驱动程序,采用什么模式的图形方式(也就是相应程序
的入口地址),以及该适配器驱动程序的寻找路径名。所用系统的显示适配器一定要支持所
选用的显示模式,否则将出错。当图形系统初始化后,才可进行画图操作。
1) 图形系统的初始化函数
Turbo C 提供了函数initgraph 可完成图形系统初始化的功能。其原型是:
void far initgraph(int far *driver,int far *mode,char far *path_for_driver);
当我们使用的存储模式为tiny(微型)、small(小型)或medium(中型)时,不需要远指针,
因而可以将初始化函数调用格式写成如下形式(该说明适用于后面所述的任一函数):
initgraph(&graphdriver,&graphmode,””);
图3-8 (a)C 的8×8 点阵字模 (b)VRAM 和字符及图形显示关系
27
其中驱动程序目录路径为空字符””时,表示就在当前目录下,参数graphmode 用如表
3-4 所示的模式号或标识符来定义。参数graphdriver 是一个枚举变量,它属于显示器驱动程
序的枚举类型:
enum graphics_driver {DETECT,CGA,MCGA,EGA,EGA64,EGAMONO,IBM 8514,
HERCMONO,ATT400,VGA,PC3270};
其中枚举成员的值顺序为:DETECT 为0,CGA 为1,依次类推。当我们不知道所用显示适
配器名称时,可将graphdriver 设成DETECT,它将自动检测所用显示适配器类型,并将相
应的驱动程序装入,并将其最高的显示模式作为当前显示模式,如下面所列:
检测到的适配器 选中的显示模式
CGA 4(640×200,2 色即CGAHI)
EGA 1(640×350,16 色,即EGAHI)
VGA 2(640×480,16 色,即VGAHI)
一旦执行了初始化,显示器即被设置成相应模式的图形方式。
例程3-9:下面是一般画图程序的开始部分,它包括对图形系统的初始化:
#include
main()
{
int graphdriver=DETECT;
int graphmode;
initgraph(&graphdriver,&grapllmode,””);
?
}
上面初始化过程中,将由DETECT 检测所用适配器类型,并将当前目录录下相应的驱
动程序装入,并采用最高分辨率显示模式作为graphmode 的值。
若已知所用图形适配器为VGA 时,想采用640×480 的高分辨显示模式VGAHI,则图
形初始化部分可写成:
int graphdriver=VGA;
int graphmode=VGAHI;
initgraph(&graphdriver,&graphmode,””);
2) 图形系统检测函数
当graphdriver=DETECT 时,实际上initgraph 函数又调用了图形系统检测函数
detectgraph,它完成对适配器的检查并得到显示器类型号和相应的最高分辨率模式,若所设
适配器不是规定的那些类型,则返回-2,表示适配器不存在,该函数的原型说明是:
void far detectgraph(int far *graphdriver,int far *graphmode);
例程3-10:当想检测所用的适配器类型,但并不想用其最高分辨率显示模式,而想由自己
进行控制使用时,可采用这个函数来实现:
#include
main()
{
int graphdriver;
int graphmode;
detectgraph(&graphdriver,&graphmode);
switch(graphdriver) {
28
case CGA:graphmode=1; /* 设置成低分辨模式 */
break;
case EGA:graphmode=0; /* 设置成低分辨模式 */
break;
case VGA:graphmode=1; /* 设置成中分辨模式 */
break;
case –2:
printf(“/nGraphics adapter not installed”);
exit(1);
default:
printf(“/nGraphics adapter is not CGA,EGA,or VGA”);
}
initgraph(&graphdriver,&graphmode,””);
?
}
调用detectgraph 时,该函数将把检测到的适配器类型赋予graphdriver,再把该类型适
配器支持的最高分辨率模式赋给graphmode。
3) 清屏和恢复显示方式的函数
画图前一般需清除屏幕,使得屏幕如同一张白纸,以画最新最美的图画,因而必须使用
清屏函数。清屏函数的原型是:
void far cleardevice(void);
该函数作用范围为整个屏幕,如果用函数setviewport 定义一个图视窗口,则可用清除图视
口函数,它仅清除图视口区域内的内容,该函数的说明原型是:
void far clearviewport(void);
当画图程序结束,回到文本方式时,要关闭图形系统,回到文本方式,该函数的说明原
型是:
void far closegraph(void);
由于进入C 环境进行编程时,即进入文本方式,因而为了在画图程序结束后恢复原来
的最初状况,一般在画图程序结束前调用该函数,使其恢复到文本方式。
为了不关闭图形系统,使相应适配器的驱动程序和字符集(字库)仍驻留在内存,但又回
到原来所设置的模式,则可用恢复工作模式函数,它也同时进行清屏操作,它的说明原型是:
void far restorecrtmode(void);
该函数常和另一设置图形工作模式函数setgraphmode 交互使用,使得显示器工作方式
在图形和文本方式之间来回切换,这在编制菜单程序和说明程序时很有用处。
2.1.3 基本绘图函数
图形由点、线、面组成,Turbo C 提供了一些函数,以完成这些操作,而所谓面则可由
对封闭图形填上颜色来实现。当图形系统初始化后,在此阶段将要进行的画图操作均采用缺
省值作为参数的当前值,如画图屏幕为全屏,当前开始画图坐标为(0,0)(又称当前画笔位置,
虽然这个笔是无形的),又如采用画图的背景颜色和前景颜色、图形的填充方式,以及可以
采用的字符集(字库)等均为缺省值。
1) 画点函数
void far putpixel(int x,int y,int color);
该函数表示在指定的x,y 位置画一点,点的显示颜色由设置的color 值决定,关于颜
29
色的设置,将在设置颜色函数中介绍。
int far getpixel(int x,int y);
该函数与putpixel()相对应,它得到在(x,y)点位置上的象素的颜色值。
例程3-11:下面是一个画点的程序,它将在y=20 的恒定位置上,沿x 方向从x=200 开始,
连续画两个
锐单商城拥有海量元器件数据手册IC替代型号,打造电子元器件IC百科大全!

相关文章