一、汉字库结构及区位码
国家标准的汉字字符集(GB2312-80)在汉字系统中是以汉字库的形式提供的。汉字库中将汉字分成94个区,每个区有94个汉字,即所谓的“区”和“位”,每个汉字在字库中都有确定的区号和位号(用两个字节表示),这两个字节就叫做区位码。区位码的第一个字节表示区号,第二个字节表示位号。只要知道了汉字的区位码,就可以知道该汉字在汉字库中的位置。
二、汉字的内码
英文字符在计算机中是用一个字节的ASCII码表示的,该字节最高位一般用于奇偶校验,故实际是用7位码来代表128个字符的。即ASCII码的8个bit位中最高位是为0的,所以为了区分一个字节是汉字还是ASCII码,当汉字的国标码在机内表示时,将汉字国标码两字节的最高位均置1,以表示该码表示的是汉字。这个最高位置1的码就是常称的内码。
三、内码转换为区位码
区号 = 内码高位 - 0xa0
位号 = 内码低位 - 0xa0
若用十进制表示内码为c1c2,则区号(qh)和位号(wh)为:
qh = c1 - 160
wh = c2 - 160
(注: 160D = A0H)
即区位码(qw)为:
qw = 100 * (c1-160) + (c2-160)
四、汉字库的制作
UCDOS 7.0里面有个“特显”功能,能显示任意宽度和任意高度的汉字。有相应的演示文件和原码都在“D:/UCDOS/SRC/TX/C”目录下,但其中演示文件在运行前,必须先敲一个“TX”命令,启动特显功能。
制作汉字库可以用以下方法:将所有汉字显示到屏幕上,每显示一个汉字时,将屏幕上的汉字区域截下来,存成二进制文件。)(当然要自己写程序):
1、 根据上面讲到的区位码与内码的对应关系,用下面的双重循环就可以显示94个区乘94个位的所有汉字。
……
char ch[3];
for( i = 0 ; i < 94 ; i ++ )
{
for( j = 0 ; j < 94 ; j ++ )
{
ch[0] = i + 0xa0 ;
ch[1] = j + 0xa0 ;
ch[2] = '/0' ;
SetTextColor(15,0);//(UCDOS7.0特显功能的设置颜色的函数;)
OutTextxy( 0 , 0 , ch ) ; //(UCDOS7.0特显功能中的一个显示汉字的函数;)
}
}
……
很久以前写的一个做汉字库的小程序MKHZK.EXE,原代码没有找到,用得着的就来下载吧,由于这个破网站不能上传EXE文件,所以我把EXE文件放到了DOC文件里面,下载好DOC文件后打开来,再把里面的EXE文件复制出来就可以了.
以下是最近“翻箱捣柜”找到的几个函数,放阁这上面应该不会掉,否则下次就再也找不到了。
//
// //
//----------------------------------------------------------------------//
//------------西文DOS下以画点.写显存方式显示汉字----(测试程序)----------//
//----------------------------------------------------------------------//
// //
// [说明]: //
// 此程序所用的汉字库为MKHZK.EXE所生成,且文件名格式必须为 //
// //
// "..//hzk//zk(高度)x(宽度)(字体)" //
// //
// 即字库路径必须为工作目录的上一级 hzk 目录中, 字库名必 //
// 须为以上格式,如下例: //
// //
// "..//hzk//zk24x244" //
// //
// 表示 24x24 点阵汉字,字体为宋体. //
// //
// 上面的 puthz程序是画点显示函数(按照字模中的0与1的 //
// 格式向屏幕上画点,其特点是显示速度慢);下面的 disphz 是 //
// 直接写屏显示函数(直接将字模信息写入显存, 特点是显示速 //
// 度快). //
// //
// [作者]: 黄 礼 进 //
// //
// [日期]: 2001 年 12 月 14 日 //
// //
// ABCDEFGHIJKLMNOPQRSTUVWXYZ //
// [\/]^{|}~¢£ ̄¥0123456789<=> //
// ?#$%&@()^0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ //
//----------------------------------------------------------------------//
//
#include<graphics.h>
#include<alloc.h>
#include<stdio.h>
#include<stdlib.h>
#include<dos.h>
#include<string.h>
#include<fcntl.h>
#include<conio.h>
#include<io.h>
#include<mem.h>
//#program inline
///
//----------------------|| 画 屏 程 序 段 开 始 ||-----------------------//
///
void far Clrscr(int left,int top,int right,int bottom,int color)
{ // 以指定的color颜色清除屏幕上的从left行top列到right行bottom列;
setfillstyle(SOLID_FILL,color); // 设置图形的填充模式;
bar(left,top,right,bottom); // 在指定的区域画实心框;
}
///
void far Button(int x1,int y1,int x2, int y2,int fg)
{// 在指定的x1行y1列到x2行y2列,按fg所指的类型画按扭框;
int color1,color2;
setfillstyle(1,7);// 设置图形的填充模式;
bar(x1,y1,x2,y2); // 在指定的区域画实心框;
if(fg==1)
{
color1=15;
color2=8;
}
else
{
color1=8;
color2=15;
}
setcolor(color1);
line(x1,y1,x2,y1);
line(x1,y1,x1,y2);
line(x1,y1+1,x2-1,y1+1);
line(x1+1,y1,x1+1,y2);
setcolor(color2);
line(x1,y2,x2,y2);
line(x2,y1,x2,y2);
line(x1,y2-1,x2-1,y2-1);
line(x2-1,y1,x2-1,y2-1);
}
///
void InitialGraphics(void) //初始化图形模式;
{
int driver=DETECT,mode;
int errorcode;
registerbgidriver(&EGAVGA_driver);
initgraph(&driver,&mode,"");
errorcode=graphresult();
if(errorcode!=grOk)
{
printf("Graphics error:%s/n",grapherrormsg(errorcode));
printf("Press any key to halt:");
getch();
}
}
///
//-------------------------------------------------------------------
//================================//
// 下 为 显 示 汉 字 程 序 段 //
//================================//
//-------------------------------------------------------------------
///
//用屏幕画点的方法显示汉字的程序.
int puthz(int h,int w,int type,int space,
int isHorizontal,int slantingflag,int xPosition,
int yPosition,unsigned char *HZPTR,int color)
//w汉字的宽度;h汉字的高度;type字型号(字体);space字间距;
//isHorizontal竖向显示标致;slantingflag汉字倾斜标致;xPosition 列坐标位置;
//yPosition 行坐标位置;HZPTR为所要显示的字符串;color汉字的颜色;
{ ///slantingflag为斜体标致,1和2表示斜体(2比1斜得历害),0表示正体;
int HZKFILE; //汉字库文件句柄;
int HZSIZE,old_w; //字模数组长度;
unsigned char HZKMAT[970]; //存放字模的数组;
char hzkname[80]; //暂存库名的数组;
char ch[8]; //临时数组,用于整型数到字符型数的转换;
unsigned char qh,wh; //汉字的区号和位号;
unsigned long offset; //字库文件内部指针的定位标致;
int xx; //暂存x坐标的变量;
unsigned char mask[]={0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01};
register int i,j,k,x0,y0,pos; //只用于计数的变量;
char test[80];
unsigned char zm[16][2][8];
memset(HZKMAT,0,970); //将数组HZKMAT中的所有元素清零;
old_w=w; //将原始的 w 记下来,因为 w 的宽度不一定是汉字的宽度用于字间距;
memset(hzkname,0,80);//将 hzkname 中的所有元素清零,为稳定性而加,可不要;
strcpy(hzkname,"..//hzk//zk");// /
strcat(hzkname,itoa(h,ch,10));// |
strcat(hzkname,"x");// >--形成字库文件名;
strcat(hzkname,itoa(w,ch,10));// |
strcat(hzkname,itoa(type,ch,10));// /
if((HZKFILE=open(hzkname,O_RDONLY|O_BINARY))==-1) //共享方式打开汉字库;
{ //如果打不开汉字库则将最相似大小的字库打开并从中读取字模;
if((w%8)>=1)
w=(w/8)*8+8; //如果汉字的宽度模8大于等于1则算另外一个字节计算;
else //即将宽度加上一个字节;
w=w/8*8; //否则其小于1的话,将原字节数计算;
HZSIZE=w/8*h; //计算将要读取多少位字模;
memset(hzkname,0,80); //将hzkname各元素清0;
strcpy(hzkname,"..//hzk//zk"); //将存汉字库名的数组置最初的字符串;
strcat(hzkname,itoa(h,ch,10)); //将汉字的高度拷贝至其后;
strcat(hzkname,"x"); //再拷贝一个 x 字符;
strcat(hzkname,itoa(w,ch,10)); //将汉字的宽度拷贝至其后;
strcat(hzkname,itoa(type,ch,10));//将汉字的字体拷贝到尾部,文件名做好;
if((HZKFILE=open(hzkname,O_RDONLY|O_BINARY))==-1) //打开汉字库;
{ //若汉字库没有打开;
Clrscr(0,0,getmaxx(),getmaxy(),0); //清除全屏;
printf("/nCan not open hzk !"); //输出文件没有打开的提示信息;
getch(); //接受一个键盘输入(任意键);
closegraph();
exit(1); //函数返回 -1 出错标致;
}
}
else //如果汉字库打开了,则计算其宽度,以读取字模;
{
if((w%8)>=1)
w=(w/8)*8+8; //如果汉字的宽度模8大于等于1则算另外一个字节计算;
else //即将宽度加上一个字节;
w=w/8*8; //否则其小于1的话,将按原字节数计算;
HZSIZE=w/8*h; //计算将要读取多少位字模;
}
//---------------------------------------------------------------------
xx=xPosition; //将原始的列坐标暂存;
while(*HZPTR!=NULL)
{
if((*HZPTR&128)!=0) //判断其是否为汉字;
{
if(isHorizontal==0) //判断是否为横向显示;
{
if((xPosition+w+space)>=640&&*HZPTR!=NULL)
{ //如果当前的列坐标不能显示下一个汉字则换行;
yPosition+=h+4, //以上功能用当前x坐标加上汉字的宽度w再加
xPosition=0; //上字间距space来实现;
}
}
//如果列坐标超出了640,不能再显示下一个汉字时,而汉字又没有显示完,
//则将列坐标重新设置为零,横坐标加上此行汉字的高度再加上4,目的是为了美观;
if(isHorizontal==1) yPosition+=h+space,xPosition=xx;
qh=HZPTR[0]-0xa0;// /
wh=HZPTR[1]-0xa0;// |
offset=(long)(94*(qh)+(wh))*HZSIZE;// >--- 取字模;
// sprintf(test,"qh == 0x%x , wh == 0x%x , offset == %ld , HZSIZE == %d",qh,wh,offset,HZSIZE);
lseek(HZKFILE,offset,SEEK_SET);// |
read(HZKFILE,HZKMAT,HZSIZE);// /
y0=yPosition;//将汉字显示位置的行坐标暂存,以在显示下一汉字时用;
for( i=0 ; i<h ; i++ ) //从汉字的0行到汉字的高度循环;
{
x0=xPosition; //将汉字显示的列坐标暂存,以在下一条显示时使用;
pos=w/8*i; //此行中的pos用于字模数组HZKMAT中下标定位,以取字模;
for(j=0;j<w;j++) //从0到汉字的宽度循环;
{
if((mask[j%8]&HZKMAT[pos+j/8])!=NULL)//按位相与后不为0则画点;
if(slantingflag>=2)putpixel(x0+(h-i),y0,color);//断倾斜标致;
else if(slantingflag==1)putpixel(x0+(h/2-i/2),y0,color);
else putpixel(x0,y0,color); //屏幕当前坐标按颜色值画点;
x0++; //画好后将列坐标加上1;
}
y0++; //一行字模画完后将画下一行,y0++是将行数加1,到下一行;
}
xPosition+=old_w+space; //由于old_w是此函数的入口参数,是最标准;
HZPTR+=2; //的汉字宽度,HZPTR+=2是将要显示的字符串向后移两个字;
} //节,因为汉字占用两个字节;
else //如果不是汉字;
//-------------以下是对ASCII码的显示方法及其控制--------------//
{
if(isHorizontal==0) //判断是否为横向显示;
{
if((xPosition + w + space)>=640&&*HZPTR!=NULL)
{ //如果当前的列坐标不能显示下一个汉字则换行;
yPosition+=h+4;
xPosition=0; //以上功能用当前x坐标加上汉字的宽度w再加
} //上字间距space来实现;
}
//如果列坐标超出了640,不能再显示下一个汉字时,而汉字又没有显示完,
//则将列坐标重新设置为零,横坐标加上此行汉字的高度再加上4,目的是为了美观;
if(isHorizontal==1) yPosition+=h+space,xPosition=xx;
// offset=94*92+(HZPTR[0]-0x21);//取字库文件中ASCII码开始处偏移量;
offset = HZPTR[0]-0x21;
if(HZPTR[0] == ' ')//如果是空格跳转到一个没有字模的位置;
offset = 92 * 94 + HZSIZE;
lseek(HZKFILE,offset*HZSIZE*1L,SEEK_SET); //移文件指针到字库文
read(HZKFILE,HZKMAT,HZSIZE); //件ASCII码开始处;
y0=yPosition+h/8;//ASC显示位置的行坐标暂存,以在显示下一ASC时用;
//h/8 是为了将ASC字符显示位置降一点,以使其更美观;
for( i=0 ; i<h ; i++ ) //按ASC的高度循环;
{
x0=xPosition;//将ASC显示位置的列坐标暂存,以在显示下一条线时用;
pos=w/8*i; // pos用于HZKMAT数组中的下标定位;
for(j=0;j<w-1;j++)//按ASC的宽度循环, w-1是因为ASC没汉字那么宽;
{
if((mask[j%8]&HZKMAT[pos+j/8])!=NULL)//按位相与后不为0则画点;
if(slantingflag>=2)putpixel(x0+(h-i),y0,color);//断倾斜标致;
else if(slantingflag==1)putpixel(x0+(h/2-i/2),y0,color);
else putpixel(x0,y0,color); //屏幕当前坐标按颜色值画点;
x0++; //画好后将列坐标加上1;
}
y0++; //一行字模画完后将画下一行,y0++是将行数加1,到下一行;
}
xPosition+=old_w-old_w/2+space;//ASC没汉字宽,所以减去8以缩字距;
HZPTR+=1; //此处的字符指针只移一位,是因为ASC字符只占一个字节;
} //(汉字占两个字节,所以加2);
}
close(HZKFILE); //关闭汉字库文件;
return(0); //返加0值表示没有出错;
}
/
要想看的远,必须站的高,要站的高,必须基础牢!要不然摔下来就惨了......
[楼 主] | Posted: 2005-12-28 18:36
enjoyo
级别: 管理员
精华: 2
发帖: 1010
威望: 764 点
金钱: 4227 RMB
贡献值: 0 点
在线时间:326(小时)
注册时间:2005-08-29
最后登录:2006-05-12
/
//以直接写显存的方式,向屏幕显示汉字;
void disphz(int h,int w,int type,int space,
int isHorizontal,int Alignflag,int xPosition,
int yPosition,unsigned char *HZPTR,int color)
//w汉字的宽度;h汉字的高度;type字型号(字体);space字间距;
//isHorizontal竖向显示标致;xPosition 列坐标位置;yPosition 行坐标位置;
//HZPTR为所要显示的字符串;color汉字的颜色;
// 当Alignflag为 1 时,为左对齐,为 -1 时为右对齐(此项功能仅针对于纯ASCII
{ //码字符串),当汉字和ASCII码共处一串时,此功能在显示时可能不准确;
int HZKFILE; //汉字库文件句柄;
int HZSIZE; //字模数组长度;
int stringlen=0;
unsigned char HZKMAT[970]; //存放字模的数组;
char hzkname[80]; //暂存库名的数组;
char ch[8]; //临时数组,用于整型数到字符型数的转换;
unsigned char qh,wh; //汉字的区号和位号;
unsigned long offset; //字库文件内部指针的定位标致;
register int i,t;
register long j;
int s;
char far *ptr;
stringlen=strlen(HZPTR);
memset(HZKMAT,0,970); //将数组HZKMAT中的所有元素清零;
memset(hzkname,0,80);//将 hzkname 中的所有元素清零,为稳定性而加,可不要;
strcpy(hzkname,"..//hzk//zk");// /
strcat(hzkname,itoa(h,ch,10));// |
strcat(hzkname,"x");// >--形成字库文件名;
strcat(hzkname,itoa(w,ch,10));// |
strcat(hzkname,itoa(type,ch,10));// /
if((HZKFILE=open(hzkname,O_RDONLY|O_BINARY))==-1) //共享方式打开汉字库;
{ //如果打不开汉字库则将最相似大小的字库打开并从中读取字模;
if((w%8)>=1)
w=(w/8)*8+8; //如果汉字的宽度模8大于等于1则算另外一个字节计算;
//即将宽度加上一个字节;否则其小于1的话,将原字节数计算;
HZSIZE=w/8*h; //计算将要读取多少位字模;
memset(hzkname,0,80); //将hzkname各元素清0;
strcpy(hzkname,"..//hzk//zk"); //将存汉字库名的数组置最初的字符串;
strcat(hzkname,itoa(h,ch,10)); //将汉字的高度拷贝至其后;
strcat(hzkname,"x"); //再拷贝一个 x 字符;
strcat(hzkname,itoa(w,ch,10)); //将汉字的宽度拷贝至其后;
strcat(hzkname,itoa(type,ch,10));//将汉字的字体拷贝到尾部,文件名做好;
if((HZKFILE=open(hzkname,O_RDONLY|O_BINARY))==-1) //打开汉字库;
{ //若汉字库没有打开;
Clrscr(0,0,getmaxx(),getmaxy(),0); //清除全屏;
printf("/nCan not open hzk !"); //输出文件没有打开的提示信息;
getch(); //接受一个键盘输入(任意键);
closegraph();
exit(1); //函数返回 -1 出错标致;
}
}
else //如果汉字库打开了,则计算其宽度,以读取字模;
{
if((w%8)>=1)
w=(w/8)*8+8; //如果汉字的宽度模8大于等于1则算另外一个字节计算;
//即将宽度加上一个字节;否则其小于1的话,将按原字节数计算;
HZSIZE=w/8*h; //计算将要读取多少位字模;
}
if((xPosition%8)>4) xPosition=xPosition/8+1;
else xPosition/=8;
if(Alignflag == -1)
if(((w/8)%2)!=0) xPosition -= stringlen * ((w/8+1)/2);
else xPosition -= stringlen * (w/8/2);
ptr=(char far *)0xa0000000l+80*yPosition+xPosition;
while(*HZPTR!=NULL)
{
if((*HZPTR&128)!=0) //判断其是否为汉字;
{
qh=HZPTR[0]-0xa0;// /
wh=HZPTR[1]-0xa0;// |
offset=(long)(94*(qh)+(wh))*HZSIZE;// >--- 取字模;
lseek(HZKFILE,offset,SEEK_SET);// |
read(HZKFILE,HZKMAT,HZSIZE);// /
for(s=1; s<=(w/8*h); s++)
{
t = *ptr; //将背景色暂存,稍候送入位屏蔽寄存器用来过滤;
outportb(0x3ce,0x05); //将方式寄存器口地址送索引寄存器;
outportb(0x3cf,0x02); //将方式寄存器的第二位置'1';
outportb(0x3ce,0x08); //将位屏蔽寄存器口地址送索引寄存器;
outportb(0x3cf,HZKMAT[s-1]); //将字模信息送入位屏蔽寄存器;
*ptr = color; //将前景色写入对应地址;
t = *ptr; //将背景色暂存,稍候送入位屏蔽寄存器用来过滤;
outportb(0x3ce,0x08); //将位屏蔽寄存器口地址送索引寄存器;
outportb(0x3cf,t); //将背景色送入位屏蔽寄存器;
outportb(0x3ce,0x0005); //恢复为缺省的写方式 0 ;
outportb(0x3ce,0x8ff08); //位屏蔽寄存器不再屏蔽;
if(s%(w/8) != 0) //判断汉字写屏时在同一线上还是在下一线上;
ptr ++; //在同一线上时,只用加上一字节,即可移到下一字节;
else ptr = ptr + 81 - w / 8; //当前线显示结束,移到下一线上;
}
HZPTR += 2; //字符串中的汉字指针向后移两个字字;
if(isHorizontal==0)ptr=ptr-h*80+w/8+space;//判断是否为竖向显示;
else ptr = ptr + 80 + 80 * space; //竖向显示时的处理方法;
}
else
{
offset=(HZPTR[0]-0x21);//取字库文件中ASCII码开始处的偏移;
if(HZPTR[0] == ' ')
offset = 92*94 + HZSIZE;
lseek(HZKFILE,offset*HZSIZE*1L,SEEK_SET); //移文件指针到字库文
read(HZKFILE,HZKMAT,HZSIZE); //件ASCII码开始处;
for(s=1; s<=(w/8*h); s++)
{
t = *ptr; //将背景色暂存,稍候送入位屏蔽寄存器用来过滤;
// asm mov dl,0x05;
// asm mov 0x3ce,dl;
// asm in al,dl;
outportb(0x3ce,0x05); //将方式寄存器口地址送索引寄存器;
outportb(0x3cf,0x02); //将方式寄存器的第二位置'1';
outportb(0x3ce,0x08); //将位屏蔽寄存器口地址送索引寄存器;
outportb(0x3cf,HZKMAT[s-1]); //将字模信息送入位屏蔽寄存器;
*ptr = color; //将前景色写入对应地址;
t = *ptr; //将背景色暂存,稍候送入位屏蔽寄存器用来过滤;
outportb(0x3ce,0x08); //将位屏蔽寄存器口地址送索引寄存器;
outportb(0x3cf,t); //将背景色送入位屏蔽寄存器;
outportb(0x3ce,0x0005); //恢复为缺省的写方式 0 ;
outportb(0x3ce,0x8ff08); //位屏蔽寄存器不再屏蔽;
if(s%(w/8) != 0) //判断ASC写屏时在同一线上还是在下一线上;
ptr ++; //在同一线上时,只用加上一字节,即可移到下一字节;
else ptr = ptr + 81 - w / 8; //当前线显示结束,移到下一线上;
}
HZPTR ++; //ASC只占一个字节,所以只用加加;
if(isHorizontal==0) //判断,如果是横向显示则'字间距'如下处理;
{
if((w/8%2)==0) ptr = ptr - h * 80 + w / 8 / 2 + space;
else ptr = ptr - h * 80 + (w / 8 + 1) / 2 + space;
}
else ptr=ptr+80+80*space; //竖向显示时对字间距的处理方法;
}
}
close(HZKFILE); //关闭汉字库文件;
}
///
//------------------|| 汉 字 显 示 程 序 结 束 ||--------------------//
///
void main(void)
{
int i,j;
InitialGraphics();
// Button(0,0,getmaxx(),getmaxy(),1);
puthz(16,16,4,1,0,1,10,10, "abc ABC`1234567890-=[]//;',./黄~!@#$%^&*()_+{}|:/"<>?礼进",15);
puthz(16,16,4,1,0,0,10,30,"abcdefghijklmnopqrstuvwxyz 我左边有一个空格哦",2);
puthz(24,24,1,1,0,0,10,50,"ABCDEFGHIJKLMNOPQRSTUVWXYZ 我左边有一个空格哦!",2);
puthz(32,32,3,1,0,0,10,70,"长江之水天上来!",2);
puthz(40,40,3,1,0,0,10,90,"这是我的一个测试程序.",2);
/*
puthz(40,40,1,1,0,0,122,232,"长江之水天上来",4);
puthz(40,40,2,2,1,0,52,15, "长江之水天上来",0);
puthz(40,40,2,2,1,0,42,9, "长江之水天上来",14);
puthz(40,40,2,2,1,0,560,15, "奔流到海不复回",0);
puthz(40,40,2,2,1,0,550,9, "奔流到海不复回",14);
puthz(48,48,4,0,0,0,122,282,"长江之水天上来",2);
puthz(48,48,4,0,0,2,122,342,"长江之水天上来",0);
puthz(48,48,4,0,0,0,122,342,"长江之水天上来",14);
puthz(80,80,2,0,0,0,15,390, "长江之水天上来",2);
getch();
setfillstyle(1,1);
Clrscr(0,0,getmaxx(),getmaxy(),0);
Ltan(0,0,getmaxx(),getmaxy(),1);
*/
disphz(16,16,4,0,0,1,80,18, "长江abc ABC 123 !@# 之水天上来",10);
disphz(16,16,4,0,0,1,80,35, "长江之水天上来",2);
// disphz(24,24,1,0,0,1,80,55, "长江之水天上来abcdefg",4);
disphz(32,32,2,0,0,1,80,80, "长江之水天上来",4);
disphz(40,40,3,0,0,1,80,114,"长江之水天上来",5);
disphz(48,48,4,0,0,1,80,158,"长江之水天上来",11);
/* disphz(56,56,1,0,0,1,80,203,"长江之水天上来",10);
disphz(64,64,3,0,0,1,80,261,"长江之水天上来",9);
disphz(80,80,2,0,0,1,80,340,"长江之水天上来",14);
disphz(32,32,4,0,0,1,80,423,"ABCDEFGHIJKLMNOPQRSTUVWXYZ",12);
disphz(24,24,4,0,0,1,80,455,"abcdefghijklmnopqrstuvwxyz",2);
disphz(64,64,2,0,1,1,18, 17,"长江之水天上来",0);
disphz(64,64,2,0,1,1,2, 10, "长江之水天上来",14);
*/
puthz(16,16,4,0,0,0,0,50,"黄 礼进abcABC`123~!@#$%^&*()_+-=[]/{}|;':/",./<>?黄我的字体测试程序!",10);
puthz(32,32,3,0,0,0,0,300,"黄 我的字体测试程序!",10);
// GetZMToFile(16,16,4,"dispinformation3","[<是否禁用详细信息>]","dispinfo.c");
// Puthz(16,16,200,200,"错误:请先运行UCDOS7.0的特显功能!",&noruntx[0][0],10);
// ChangeFile("c://borlandc//bin//mkhzk2.c","t_tt.c");
puthz(16,16,4,1,0,0,10,400, "abc ABC`1234567890-=[]//;',./黄~!@#$%^&*()_+{}|:/"<>?礼进",15);
getch();
Clrscr(0,0,getmaxx(),getmaxy(),0);
closegraph();
}
要想看的远,必须站的高,要站的高,必须基础牢!要不然摔下来就惨了......
[1 楼] | Posted: 2005-12-28 18:37
enjoyo
级别: 管理员
精华: 2
发帖: 1010
威望: 764 点
金钱: 4227 RMB
贡献值: 0 点
在线时间:326(小时)
注册时间:2005-08-29
最后登录:2006-05-12
1、 什么叫字模,以及怎样得到字模。
(1)、每个汉字在屏幕上显示的时候,都会占用屏幕上的一个小矩形区域,这个矩形区域是由屏幕上集中在一个矩形区的像素点组成的,在DOS环境里,这组像素点有的为黑色,有的为白色,所有黑色点(或白色的点)的路径描绘出汉字的轮廓,让我们得以识别,而这组点阵信息在机内是怎样表示的呢,根据字体的大小,比如一个宽度为16像素,高度也为16像素的“大”字,就要占用16×16=256个像素点,这256个像素点呈矩形排列,横向16个点,纵向16个点,而16个点,刚好用两2个字节完全表示,而亮的点用二进制里的“1”表示,暗的点用二进制里的“0”表示,即会出现下面的矩阵:
高八位 低八位
□□□□□□■■□□□□□□□□
□□□□□□■■□□□□□□□□
□□□□□□■■□□□□□□□□
□□□□□□■■□□□□□■□□
■■■■■■■■■■■■■■■□
□□□□□□■■□□□□□□□□
□□□□□□■■□□□□□□□□
□□□□□□■■□□□□□□□□
□□□□□□■■□□□□□□□□
□□□□□□■■■□□□□□□□
□□□□□■■□□■□□□□□□
□□□□■■□□□□■□□□□□
□□□■■□□□□□□■□□□□
□□□■□□□□□□□■■□□□
□□■□□□□□□□□□■■■□
■■□□□□□□□□□□□■□□
高八位 低八位 高字节编号 字模 低字节编号 字模
0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0x03 1 0x00
0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 2 0x03 3 0x00
0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 4 0x03 5 0x00
0 0 0 0 0 0 1 1 0 0 0 0 0 1 0 0 6 0x03 7 0x04
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 8 0xFF 9 0xFE
0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 10 0x03 11 0x00
0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 12 0x03 13 0x00
0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 14 0x03 15 0x00
0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 16 0x03 17 0x00
0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 18 0x03 19 0x00
0 0 0 0 0 1 1 0 0 1 0 0 0 0 0 0 20 0x06 21 0x40
0 0 0 0 1 1 0 0 0 0 1 0 0 0 0 0 22 0x0C 23 0x20
0 0 0 1 1 0 0 0 0 0 0 1 0 0 0 0 24 0x18 25 0x10
0 0 0 1 0 0 0 0 0 0 0 1 1 0 0 0 26 0x10 27 0x18
0 0 1 0 0 0 0 0 0 0 0 0 1 1 1 0 28 0x20 29 0x0E
1 1 0 0 0 0 0 0 0 0 0 0 0 1 0 0 30 0xC0 31 0x04
从上图中可以看出,一个16×16点阵的汉字,需要32个字节存放字模。看到这里,应该能明白什么叫字模了。你可以把它理解为是用于显示汉字用的一组模子,它由二进制的“0”或“1”标记屏幕上的白点或黑点,以此勾画汉字的轮廓。
(2)、怎以得到字模,形成汉字库呢。在DOS下,当屏幕初始化为640×480像素16色模式时,屏幕上共有480(0-479)行,每一行有640(0-639)个点,而640个像素点,每个点用一个二进制位表示,则刚好可以用640/8=80个字节来表示。现在假设显示模式为单色模式,暂别管16色,即屏幕上只能显示黑点或者白点,那么满屏的图像就有(80×480)=(38400)个字节,这38400个字节的值怎么得到呢,很简单,因为显存的起始地址是0xa0000000,38400个字节,只用依次往上加就可以了。如下图:
屏幕第000行地址:0xA0000000 0xA0000001 0xA0000002 …… 0xA000004F
屏幕第001行地址:0xA0000050 0xA0000051 0xA0000052 …… 0xA000009F
………………
屏幕第439行地址:0xA00088E0 0xA00088E1 0xA00088E2 …… 0xA00095FF
现在假设有一个16×16的汉字显示在屏幕的0行0列,那么它的字模地址就会分布在:
屏幕第000行地址:0xA0000000 0xA0000001
屏幕第001行地址:0xA0000050 0xA0000051
………………
屏幕第015行地址:0xA0000460 0xA0000461
于是只用将这些地址对应的值依次存起来就形成了这个汉字的字模,因此,如果将94个区94个位的所有汉字在屏幕上的0行0列显示一遍,并且每显示一个汉字就存下这个汉字的字模到文件。这样就形成了一个16×16点阵字的字库。
同样原理,如果要制做一个24×24点阵的字库,依次将所有汉字显示到0行0列,每显示一个就存储下面地址对应的值就可以了:
屏幕第000行地址:0xA0000000 0xA0000001 0xA0000002
屏幕第001行地址:0xA0000050 0xA0000051 0xA0000052
………………
屏幕第023行地址:0xA0000730 0xA0000731 0xA0000732
在16色模式下,因为每个点可以显示16种颜色,而16种颜色刚好可以用4位二进制数完全表示,因此屏幕上每个像素点实际上需要4个二进制位来表示,即满屏图像信息需要占用(480×80×4)=(38400×4)=153600个字节。这个你不必担心,因为每机器把153600个字节分成了4个独立的单色位面,每个单色位面占38400个字节,4个单色位面叠加起来就复合成了彩色的。(你可以把它想象成一个[640×480×4]的三维矩阵,从正面看,它就是一个[640×480]的位面,从左侧看,是一个[4×480]的矩阵,表示的是屏幕最左边一列的颜色信息,每一个像素点占4个二进制位,第0位面为高位,第3位面为低位,这个4位二进制数的值就是对应着从正面看时,该点的颜色号),第个位面的起始地址还是0xA0000000,只不过在16色模式下,需要选位面存字模。
怎么样选位面呢。可以通过EGA/VGA适配器上的图形控制器的两个寄存器,即方式寄存器和位屏蔽寄存器,图形控制器上共有9个寄存器,编号如下:
索引号 寄存器名称
00 设置/清除寄存器
01 设置/清除允许寄存器
02 颜色比较寄存器
03 数据旋转移动与功能选择寄存器
04 读颜色位面选择寄存器
05 方式寄存器
06 混合寄存器
07 颜色无关寄存器
08 位屏蔽寄存器
为了减少I/O口地址数量,它们共用一个口地址,这个口地址可以重复使用,另外还有一个索引寄存器(其口地址为0x3CE),如上表,9个内部寄存器分别有自己的索引号,它们共用一个口地址0x3CF,当给索引寄存器写入一个索引号后,其后对0x3CF口地址的读或写操作,就是对由索引号指定那个寄存器进行的操作,这样就实现了口地址的复用,总之,索引寄存器的口地址为0x3CE,9个寄存器的口地址为共用的口地址0x3CF。
在上面表中可以看出“位面选择寄存器”的索引号是4,因此先将索引号“4”送入索引寄存器,表示接下来要选位面,再将位面号(0-3)送入共用的口地址0x3CF,即选择所需要的位面,但选择时要保证选择的是有颜色信息的位面。因为4个位面的值根据当前的颜色会有所不同,为了防止选中无颜色的位面,最好的方法是在显示汉字的时间用白色,这样所有位面上的值都是一样的了。随便选一个也会得到字模。但要注意,最后读完字模后,一定要关掉0x3CF口的当前寄存器号,即向0x3CF口地址送一个“0”就可以了。