关于英文DOS下的汉字显示技术

一、汉字库结构及区位码

  国家标准的汉字字符集(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”就可以了。 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值