TC图形函数库:#include <graphics.h>
平台:watcom
参考资料:  
本文多数例子来自上述材料,自己额外添加注释,方便理解
初始化
void far initgraph(int far *gdriver, int far *gmode, char *path);  
far类型
这是早期的dos编程中使用的内存分段模式
far类型表示代码段采用远跳转
near类型表示代码段采用近跳转
但现在的win都采用平坦内存模式,不存在近跳转和远跳转,所以可以不用考虑  
参数
- gdriver为驱动器
 可以采用常量或数值- CGA(1)
- EGA(3)
- VGA(9)
- DETECT(0)
 
- gmode为模式
 不同的驱动器提供的不同的模式
 可以采用常量或数值
- path为图形驱动程序所在的目录
 一般传入空字符串
- 一般自动检测即可 
 即- gdriver = 0
 此时- gmode的参数将被自动检测,但还是得传递一个变量,可以不赋值
 如:- int gdriver = 0, gmode; initgraph(&gdriver, &gmode, ""); //此时gmode并没有赋值
- 注意: 
 函数的形参都是指针,不要直接传递整型数据
错误捕捉示例
通过graphresult()函数返回图形模式初始化结果
与常量grOk进行比较
注意:graphresult()必须在initgraph()之后调用  
int gdriver = 0, gmode;
initgraph(&gdriver, &gmode, "");
errorcode = graphresult();
if(errorcode != grOk)
{
    printf("Graphics error: %s\n", grapherrormsg(errorcode));
    printf("Press any key to halt:");
    getchar();
    exit(1);
}
图形的坐标
文本模式
屏幕一般分为25或40或50行,80或40列
以左上角为原点(1,1),向右为x正方向,向下为y正方向  
图形模式
屏幕被划分为像素,每个像素为一个点,像素的个数与视频适配器的类型和适配器的工作方式有关
系统默认以左上角为原点(0,0),向右为x正方向,向下为y正方向
注意:文本模式跟图形模式的原点坐标不同  
获取坐标最大值
getmaxx()函数返回当前图形模式下的最大x坐标(整型)getmaxy()函数返回当前图形模式下的最大y坐标(整型)  
坐标轴变换
选择一个原点,建立以右为x正方向,以上为y正方向的坐标系(符合我们的习惯)
假设选定新的坐标原点(origin_x, origin_y)
原先坐标系的点(before_x, before_y)与新坐标系上的对应点(later_x, later_y)存在以下关系
before_x = origin_x + later_x
before_y = origin_y - later_y  
文本模式与图形模式的切换
模式间切换不需要重复关闭、初始化图形模式
获取当前图形模式
int far getgraphmode(voide);
注意:前提是已经成功调用过initgraph()函数  
恢复为图形初始化前的模式(也就是文本模式)
void far restorecrtmode(void);  
切换到文本模式(bhh)
closegraph();text_mode();textmode(C80);  
将系统设置为指定的图形模式并清屏
void far setgraphmode(int mode);  
示例:
//......省略图形初始化的代码
gmode = getgraphmode();  //获取当前图形模式
restorecrtmode();  //恢复为文本模式  
setgraphmode(gmode);  //设置(恢复)为之前的图形模式并清屏
关闭图形系统
void far closegraph(void);
所有的有关图形的程序段必须在initgraph()和closegraph()之间(类型于文件操作)  
背景色和作图色设置
背景色: setbkcolor(int color);
作图色: setcolor(int color);  
参数color
可以使用常量或数值表示
- 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)
9-13为1-5对应的浅色
基本图形函数
画点函数
void far putpixel(int x, int y, int pixelcolor);  
坐标位置函数
获取最大x坐标:int far getmaxx(void);
获取最大y坐标:int far getmaxy(void);
获取当前x坐标:int far getx(void);
获取当前y坐标:int far gety(void);
移动画笔到绝对坐标:void far moveto(int x, int y);
移动画笔到相对坐标:void far moverel(int dx, int dy);  
图形模式下的文本输出函数
只支持英文输出,且文字大小固定,为8*16点阵
在当前位置输出文本:void far outtext(char far *textstring);
在指定位置输出文本:void far outtextxy(int x, int y, char *textstring);  
画线函数
指定直线起止点:void far line(int x0, int y0, int x1, int x2);
以当前位置为直线起点,指定终点(绝对坐标),并移动画笔:void far lineto(int x, int y);
以当前位置为直线起点,指定终点(相对坐标),并移动画笔:void far linerel(int dx, int dy);  
画圆函数,画圆弧函数
画圆:void far circle(int x, int y, int radius);
画圆弧:void far arc(int x, int y, int stangle, int endangle, int radius);stangle和endangle参数采用角度制(0-360),以x轴正方向为0度,以逆时针为正方向  
画椭圆(弧)函数
void far ellipse(int x, int y, int stangle, int endangle, int xradius, int yradius);  
画矩形函数
void far rectangle(int left, int top, int right, int bottom);
指定左上角的坐标为(left, top),右下角的坐标为(right, bottom)  
线型设定函数
void far setlinestyle(int linestype, unsigned upattern, int thick);  
- linestype为直线类型(常量或数值)  - 实线(缺省):SOLID_LINE(0)
- 点线:DOTTED_LINE(1)
- 中心线:CENTER_LINE(2)
- 虚线:DASHED_LINE(3)
- 用户自定义线型:USERBIT_LINE(4)
 
- upattern为自定义线型(十六进制数)
 若linestype不为USERBIT_LINE或4,则该参数取0即可
- thick为直线粗细(单位:px)
 相关常量:
 NORM_WIDTH(1),缺省
 THICK_WIDTH(3)
填充函数
设置填充模式和颜色
void far setfillsytle(int pattern, int color);
pattern可以采用常量或数值:  
- 用背景色:EMPTY_FILL(0)
- 用指定颜色:SOLID_FILL(1)
- 用线:LINE_FILL(2)
- 用斜线:LTSLASH_FILL(3)
- 用粗斜线:SLASH_FILL(4)
- 用粗反斜线:BKLASH_FILL(5)
- 用反斜线:LTKLASH_FILL(6)
- 用网格线:HATCH_FILL(7)
- 用斜交线:XHATCH_FILL(8)
- 用间隔线:INTERLEAVE_FILL(9)
- 用稀疏点:WIDE_DOT_FILL(10)
- 用密集点:CLOSE_DOT_FILL(11)
- 用用户自定义模式:USER_FILL(12)
有界区域填充
void far floodfill(int x, int y, int bcolor);
(x, y)为所要填充的有界区域的任意点
bcolor不是填充色,而是边框颜色  
椭圆填充
void far fillellipse(int x, int y, int xradius, int yradius);  
椭圆扇区填充
void far sector(int x, int y, int stangle, int endangle, int xradius, int yradius);
多边形填充
void far fillpoly(int numpoints, int far *polypoints);
polypoints参数为各个点的坐标,存放规则如下:(假设一个poly数组)
(poly[0], poly[1]) 为第一个顶点
(poly[2], poly[3]) 为第二个顶点
…………以此类推  
图片输出
8bit图片输出
void load_8bit_bmp(int x, int y, char *path);
#include <graphics.h>
#include <stdio.h>
main()
{
   int x, y;
   int driver=0, mode=VESA_1024x768x8bit;
   initgraph(&driver, &mode, "");
   load_8bit_bmp(0, 0, "pic256.bmp");
   load_8bit_bmp(200, 200, "hello.bmp");
   getchar();
   closegraph();
}
24bit图片输出(系统没有提供函数)
通过读取bmp文件的二进制数据输出图片
#include <graphics.h>
#include <stdio.h>
#include <mem.h>
int load_24bit_bmp(int x, int y, char *filename)
{
   FILE *fp = NULL;
   byte *p = NULL; /* pointer to a line of bmp data */
   byte *vp = _vp + (y*_width + x) * (_color_bits/8);
   dword width, height, bmp_data_offset, bytes_per_line, offset;
   int i;
   p = malloc(1024L * 3);  /* memory for holding a line of bmp data */
   if(p == NULL)  /* cannot allocate enough memory for drawing 1 line */
      goto display_bmp_error;
   fp = fopen(filename, "rb");
   if(fp == NULL) /* cannot open bmp file */
      goto display_bmp_error;
   fread(p, 1, 0x36, fp);     /* read BMP head */
   if(*(word *)p != 0x4D42)   /* check BMP signature */
      goto display_bmp_error; /* not a BMP file */
   if(*(word *)(p+0x1C) != 24)
      goto display_bmp_error; /* not a 24-bit-color BMP file */
   width = *(dword *)(p+0x12);
   height = *(dword *)(p+0x16);
   bmp_data_offset = *(dword *)(p+0x0A);
   fseek(fp, bmp_data_offset, SEEK_SET); /* skip BMP head */
   bytes_per_line = (width * 3 + 3) / 4 * 4; /* must be multiple of 4 */
   for(i=height-1; i>=0; i--)          /* draw from bottom to top */
   {
      fread(p, 1, bytes_per_line, fp); /* read a line of bmp data */
      offset = i * 1024 * 3;
      memcpy(vp+offset, p, width*3);
   }
   free(p);
   fclose(fp);
   return 1;
display_bmp_error:
   if(p != NULL)
      free(p);
   if(fp != NULL)
      fclose(fp);
   return 0;
}
main()
{
   int driver=0, mode=VESA_1024x768x24bit;
   initgraph(&driver, &mode, "");
   load_24bit_bmp(0, 0, "pic.bmp");
   getchar();
   closegraph();
}
中文输出
点阵字库
字库有固定的字体大小
读取字库文件中对应文字的16*16点阵共32字节的信息,若为1则在对应的位置上输出一个点,否则跳过
示例:(读取16*16的字库,并将其放大四倍打印)  
#include <graphics.h>
void draw_hz(byte buf[16][2]);
main()
{
   int driver=0, mode=VESA_640x480x8bit;
   FILE *fp;
   byte hz[3]="我", buf[16][2];    //每个汉字占用两个字节 
   long offset;
   int x, y,  button;
   initgraph(&driver, &mode, "");
   fp = fopen("hzk16", "rb"); // 打开16x16点阵字库文件
   if(fp == NULL)
   {
      puts("Cannot open file hzk16.");
      exit(0);
   }
   // 计算"我"字点阵的偏移位置
   offset = ((hz[0] - 0xA1) * 94 + (hz[1] - 0xA1)) * 0x20;         //呃?这是啥?莫非是固定的计算公式? 
   fseek(fp, offset, SEEK_SET); // 移动文件指针到该偏移位置
   fread(buf, 1, 2*16, fp);     // 读取32字节点阵数据
   fclose(fp);
   draw_hz(buf); // 画"我"字
   getchar();
   closegraph();
}
void draw_hz(byte buf[16][2])
{
   word sixteen_dots;
   int x0=_width/2-32, y0=_height/2-32;  //打印的起始位置
   //居中显示,注意我们把字库的字体放大了四倍,也就是实际上打印出来是64*64的点阵汉字 
   int x, y;    //当前打印的“点”的坐标 
   int i, j;
   x = x0;
   y = y0;
   for(i=0; i<16; i++) // 16行
   {
      sixteen_dots = buf[i][0]<<8 | buf[i][1]; // 把两个一字节的word合并为一个两字节的int,共16位,代表1行16个点 
      for(j=0; j<16; j++) // 16列
      {
         if(sixteen_dots  &  1<<(15-j)) // 判断第(15-j)位是否为1, 为1表示有笔画
         //假设j=14,则1<<(15-j)的结果为0000 0000 0000 0010
         //假设sixteen_dots为...........0101 0101 0101 0101
         //取位“与”即可判断第2位是否为1 
         {
            setfillstyle(SOLID_FILL, RED);
            bar(x, y, x+3, y+3); // 画一个4*4的红色方块代替一个前景点
         }
         else // 第(15-j)位为0,为0表示没笔画
         {
            setfillstyle(SOLID_FILL, BLACK);
            bar(x, y, x+3, y+3); // 画一个4*4的黑色方块代替一个背景点
         }
         x += 4;    //把“画笔”移动到当前行的下一个4*4的“点”的位置 
      }
      //把“画笔”移动到下一行第一个4*4的“点”的位置
      x = x0;
      y += 4;     
   }
}
缺陷:当字体放大时,会出现明显的锯齿形
TTF曲线字库
有两种方式——
1、将多行字符串转换为多幅图片再输出,速度较快
2、逐行输出字符串  
相关类型——
typedef struct _PIC      // 照片结构类型
{
   struct picture *img;  // 照片的图像
   struct picture *mask; // 照片的掩码
   int width, height;    // 照片的宽度及高度
} PIC;
相关函数——
- 字符串转换为图片 - PIC * get_ttf_text_pic(char text[], char fontname[], int fontsize);
 text为要转换的字符串
 fontname为使用的曲线字库
 fontsize为字体大小
 返回指向图片(PIC)的指针
- 绘制图片 - void draw_picture(int x, int y, PIC *p);
 p为指向所要绘制的图片的指针
- 销毁图片,释放资源 - void destroy_picture(PIC *p);
 只有一个参数(指针),为指向所要销毁的图片的指针
- 直接打印字符串 - dword out_ttf_text_xy(int x0, int y0, char text[], char fontname[], int fontsize);
 返回32位整型,高16位记录图片宽度,低16位记录图片高度
示例:
#include <stdio.h>
#include <graphics.h>
main()
{   
   int driver=0, mode=VESA_1024x768x24bit;
   short int x=0, y=100;
   int i;
   PIC *p[4];
   initgraph(&driver, &mode, "");
   /* TTF字体输出演示1: 先把多行字符串转化成多幅照片, 再输出, 速度较快 */
   setcolor(0xFF0000); // 红色
   p[0] = get_ttf_text_pic("A QUICK BROWN FOX JUMPS", "courier.ttf", 40);
          // 把字符串转化成照片, 其中"courier.ttf"是TTF字库名, 40是字体大小
          // p[0]用来接收函数返回的照片指针
   setcolor(0xFFFF00); // 黄色
   p[1] = get_ttf_text_pic("a quick brown fox jumps", "courier.ttf", 36);
   setcolor(0x00FF00); // 绿色
   p[2] = get_ttf_text_pic("0123456789", "courier.ttf", 32);
   setcolor(0x0000FF); // 蓝色
   p[3] = get_ttf_text_pic("`~!@#$%^&*()-=_+[]{}\\|;:'\",.<>/?", "courier.ttf", 28);
   for(i=0; i<4; i++)
   {  
      draw_picture(x, y, p[i]); // 在(x,y)坐标输出照片p[i]
      y += p[i]->height;        // 计算下一行的y坐标
      destroy_picture(p[i]);    // 销毁已画照片占用的资源
   }
   getchar();
   /* TTF字体输出演示2: 逐行输出字符串, 速度较慢 */
   driver=0;
   mode=VESA_1024x768x8bit;
   initgraph(&driver, &mode, "");
   y = 100;
   setcolor(LIGHTRED);
   y += out_ttf_text_xy(x, y, "夏天的飞鸟,飞到我窗前唱歌,又飞去了。", "simyahei.ttf", 40);
   // 函数的返回值 = (照片宽度<<16) | 照片高度
   // 故类型为short int的y实际仅加上照片高度值, 从而得到下行的y坐标
   y += out_ttf_text_xy(x, y, "Stray birds of summer come to my window", "courier.ttf", 32);
   y += out_ttf_text_xy(x, y, "to sing and fly away.", "courier.ttf", 32);
   setcolor(YELLOW);
   y += out_ttf_text_xy(x, y, "秋天的黄叶,它们没什么可唱,", "simyahei.ttf", 36);
   y += out_ttf_text_xy(x, y, "And yellow leaves of autumn", "courier.ttf", 28);
   y += out_ttf_text_xy(x, y, "which have no songs", "courier.ttf", 28);
   setcolor(LIGHTGREEN);
   y += out_ttf_text_xy(x, y, "只叹息一声,", "simyahei.ttf", 32);
   y += out_ttf_text_xy(x, y, "Flutter and fall there", "courier.ttf", 24);
   setcolor(LIGHTBLUE);
   y += out_ttf_text_xy(x, y, "飞落在那里。", "simyahei.ttf", 28);
   y += out_ttf_text_xy(x, y, "with a sigh.", "courier.ttf", 20);
   getchar();
   closegraph();
}
声音输出
扬声器输出
过于机械麻烦,这里跳过
声卡输出
只支持midi音乐文件和wav波形文件
相关类型——
知道有这两个类型即可,具体成员不必深究  
typedef struct midi // MIDI file data structure
{                 
   word format;                       // 0 or 1
   word num_tracks;                   // 1 - 32
   word divisions;                    // number ticks per quarter note
   byte  *track[16];                  // Track data pointers
   word repeat;
   struct midi *chain;
   void  (*chainfunc)(struct midi *);
} MIDI;
typedef struct wave 
{
   dword id;
   dword sample_rate;
   dword byte_rate;
   byte *data;
   dword chunk_size;                   // size of this chunk in bytes
   dword sample_size;                  // size of entire linked sample
   struct wave  *next;                 // points to next link.
   struct wave  *head;                 // points to top link.
} WAVE;
相关函数——
- 初始化int initsound(void);
- 载入WAVE * load_wave(char *filename);MIDI * load_midi(char *filename);
- 播放void play_wave(WAVE *p, int repeat);void play_midi(MIDI *p, int repeat);
 若repeat为-1,表示无限循环
- 释放void free_wave(WAVE *p);void free_midi(MIDI *p);
- 关闭void closesound(void);
示例:
#include <graphics.h>
main()
{
   MIDI *pmidi;
   WAVE *pwave;
   initsound(); // 初始化声卡
   pmidi = load_midi("bach.mid");  // 载入midi文件, 返回MIDI指针
   pwave = load_wave("sheep.wav"); // 载入wav文件, 返回WAVE指针
   play_midi(pmidi, -1); // repeat forever 播放midi音乐
   play_wave(pwave, 3);  // repeat 3 times 播放wav声音
   puts("Press a key to continue.\n");
   getchar();
   free_midi(pmidi); // 释放midi音乐占用的资源
   free_wave(pwave); // 释放wav声音占用的资源
   closesound(); // 关闭声卡
   return 1;
}
详细函数——(包括一些控制函数)
int  initsound(void);                  // 声卡初始化
void closesound(void);                 // 关闭声卡
WAVE * load_wave(char *filename);      // 载入wav文件
void play_wave(WAVE *p, int repeat);   // 播放wav, repeat为循环次数, repeat=-1为无限循环
void stop_wave(WAVE *p);               // 停止播放wav
void pause_wave(WAVE *p);              // 暂停播放wav
void resume_wave(WAVE *p);             // 继续播放wav
void free_wave(WAVE *p);               // 释放wav占用的资源
MIDI * load_midi(char *filename);      // 载入midi文件
void play_midi(MIDI *p, int repeat);   // 播放midi, repeat为循环次数, repeat=-1为无限循环
void stop_midi(MIDI *p);               // 停止播放midi
void pause_midi(MIDI *p);              // 暂停播放midi
void resume_midi(MIDI *p);             // 继续播放midi
void free_midi(MIDI *p);               // 释放midi占用的资源  
键盘输入:bioskey()
- bioskey(0)
 若键盘缓存区为空,将等待用户输入
 若键盘缓存区不为空,将直接读走其中的键
- bioskey(1)
 若键盘缓存区为空,返回0
 若键盘缓存区不为空,返回非零
什么是键盘缓冲区?
CPU能在运行程序时处理外部中断(interrupt)。当用户按下某个键时,键盘会向CPU发出一个中断请求,此时CPU会暂停正在运行的程序而转去处理键盘中断,处理的结果是把当前按住的键读走并存放到键盘缓冲区中。键盘缓冲区是一个队列(queue),相当于一个数组。
鼠标输入
int x, y, b;
union REGS r;
r.w.ax = 0x0003;    /*调用0003号功能*/
int86(0x33, &r, &r);
x = r.w.cx; /* 当前鼠标的x坐标 */
y = r.w.dx; /* 当前鼠标的y坐标 */
b = r.w.bx; /* 当前鼠标的按键状态 */
其他常见功能:
- 0000
 检测鼠标驱动是否安装
- 0004
 把鼠标定位到(x,y)
- 0007
 设置水平方向的鼠标移动范围
- 0008
 设置竖直方向的鼠标移动范围
详见中断大全
watcom平台提供的VESA图形库(bhh)
通过直接给坐标对应的内存赋值画图
一些程序可能会出现byte, word, dword类型
其实这都是自定义类型byte对应charword对应shortdword对应int
相关的全局变量
- _vp: 指向坐标(0,0)的指针
- _width: 屏幕宽度
- _height: 屏幕高度
- _color_bits: 每个像素占用内存的位(bit)数
- _visual_page: 当前显示画面(前景)对应的页号
- _active_page: 当前输出画面对应的页号
- _back_page: 后台画面(背景)对应的页号
- _page_gap: 后台画面与显示画面的距离
- _mode_index: 当前视频模式的编号
- _mode: 当前视频模式的内部编号
- _draw_color: 前景色
- _back_color: 背景色
- _fill_color: 填充前景色
- _fill_mask_index: 填充掩码
- _x: 当前x坐标
- _y: 当前y坐标
指向(x,y)的指针p,指向char类型数据公式:p = _vp + ( y * _width + x ) * ( _color_bits / 8 )
解释:((x, y)的指针) = (原点指针) + (跳过的元素总数) * (每个元素占用的字节) 
文本模式
每个元素占用两个字节——
- 第一个字节为文本内容
- 第二个内容为文本样式
 高四位为背景色,低四位为前景色
每个指针指向一个一字节(8位)的数据
示例(输出一个蓝底红字的A):
char *p = _vp;
*p = 'A';  
*(p+1) = ( BLUE << 4 ) + RED;  //或*(p+1) = 0x14;
图形模式
gmode常量
watcom平台提供的VESA图形库中相关常量:
包含8bit,24bit, 32bit三种颜色模式   
- VESA_320x200x8bit
- VESA_320x200x24bit
- VESA_320x200x32bit
- VESA_640x480x8bit
- VESA_640x480x32bit
- VESA_800x600x8bit
- VESA_800x600x24bit
- VESA_800x600x32bit
- VESA_1024x768x8bit
- VESA_1024x768x24bit
- VESA_1024x768x32bit 以上为图形模式
- TEXT_80x25x16bit 文本模式
8bit颜色模式
每个点占用显卡一个字节,每个点有2^8(256)种颜色,前十六种(0~15)颜色定义如下:
enum COLORS
{
   BLACK,       /* dark colors */
   BLUE,
   GREEN,
   CYAN,
   RED,
   MAGENTA,
   BROWN,
   LIGHTGRAY,
   DARKGRAY,    /* light colors */
   LIGHTBLUE,
   LIGHTGREEN,
   LIGHTCYAN,
   LIGHTRED,
   LIGHTMAGENTA,
   YELLOW,
   WHITE
};
24bit颜色模式
每个点占用显卡三个字节,分别代表RGB的蓝色(B),绿色(G),红色(R),(没错,就是逆序的),每个点有2^24种颜色。
示例:  
char *p;
p = _vp;
*p = 0x56;    //Blue
*(p+1) = 0x34;    //Green
*(p+2) = 0x12;    //Red
32bit颜色模式
每个点占用显卡四个字节,前三个字节表示RGB(同24bit),第四个字节通常为0x00  
前景页和背景页
显示页面存在两个层次,前景和背景_active_page为当前输出的页面_visual_page为当前显示的页面,非显示页面将被隐藏_back_page为当前的背景页setviualpage()函数能够设置显示页面setactivepage()函数能够设置输出页面
示例:  
setvivualpage(_back_page);    //表示将背景页设置为当前显示页面  
stactivepage(_back_page);    //表示将背景页设置为当前输出页面
