Chinaunix首页 | 论坛 | 博客
  • 博客访问: 645245
  • 博文数量: 108
  • 博客积分: 3236
  • 博客等级: 中校
  • 技术积分: 906
  • 用 户 组: 普通用户
  • 注册时间: 2010-05-04 21:23
文章分类

全部博文(108)

文章存档

2011年(33)

2010年(75)

我的朋友

分类: LINUX

2010-07-01 14:58:29

 计算机汉字显示原理(点阵字)转
  
  国家标准汉字字符集GB2312-80共收集了共7445个汉字和图形符号,其中汉字6763个,分为二级,
  一级汉字3755个,二级汉字3008个。汉字图形符号根据其位置将其分为94个“区”,
  每个区包含94个汉字字符,每个汉字字符又称为一个“位”。区的序号和位的序号都是从01到94,
  
  UCDOS软件中的文件HZK16和文件ASC16分别为16×16的国标汉字点阵文件和8×16的ASCII码点阵文件,
  以二进制格式存储。在文件HZK16中,按汉字区位码从小到大依次存有国标区位码表中的所有汉字,
  每个汉字占用32个字节,每个区为94个汉字。在文件ASC16中按ASCII码从小到大依次存有
  8×16的ASCII码点阵,每个ASCII码占用16个字节。
  
  
  国家标准汉字字符集GB2312-80共收集了共7445个汉字和图形符号,其中汉字6763个,分为二级,
  一级汉字3755个,二级汉字3008个。汉字图形符号根据其位置将其分为94个“区”,
  每个区包含94个汉字字符,每个汉字字符又称为一个“位”。区的序号和位的序号都是从01到94,
  
  UCDOS软件中的文件HZK16和文件ASC16分别为16×16的国标汉字点阵文件和8×16的ASCII码点阵文件,
  以二进制格式存储。在文件HZK16中,按汉字区位码从小到大依次存有国标区位码表中的所有汉字,
  每个汉字占用32个字节,每个区为94个汉字。在文件ASC16中按ASCII码从小到大依次存有
  8×16的ASCII码点阵,每个ASCII码占用16个字节。
  
  
  在PC机的文本文件中,汉字是以机内码的形式存储的,每个汉字占用两个字节:第一个字节为区码,
  为了与ASCII码区别,范围从十六进制的0A1H开始(小于80H的为ASCII码字符),
  对应区位码中区码的第一区;第二个字节为位码,范围也是从0A1H开始,对应某区中的第一个位码。
  这样,将汉字机内码减去0A0AH就得该汉字的区位码。
  
  
  例如汉字“房”的机内码为十六进制的“B7BF”,其中“B7”表示区码,“BF”表示位码。
  所以“房”的区位码为0B7BFH-0A0A0H=171FH。将区码和位码分别转换为十进制得汉字“房”的
  区位码为“2331”,即“房”的点阵位于第23区的第31个字的位置,相当于在文件HZK16中的
  位置为第32×[(23-1) ×94+(31-1)]=67136B以后的32个字节为“房”的显示点阵。
  
  
  ASCII码的显示与汉字的显示原理相同,在ASC16文件中不存在机内码的问题,
  其显示点阵直接按ASCII码从小到大依次排列,不过每个ASCII码在文本中只占1个字节并且小于80h,
  每个ASCII码为8X16点阵,即在ASCII16文件中,每个ASCII码的点阵也只占16个字节。
  
  
  以下是读取hzk16的C部分语言代码(仅供参考):
  
  void dis_cc(unsigned int x,unsigned int y,unsigned int color,unsigned int codeh,
  unsigned int codel,unsigned int circle,unsigned int kind,unsigned int mode)
  {
  char word[32];
  int area,bit;
  long count,offset;
  
  unsigned long count,offset;
  unsigned int i,j;
  
  area = (codeh&0x00ff)-0xa0-1; //根据机内码取得区码
  bit = (codel&0x00ff)-0xa0-1; //根据机内码取得位码
  
  offset = area*94+bit; //取得偏移量
  count = 32l*(long)offset;
  lseek(HZK16,count,SEEK_SET);//在HZK16文件中找到点阵起始位置
  j=_read(HZK16,&word[0],32);//读取32字节的点阵信息
  
  在电脑中,所有的数据都是以0和1保存的,按不同的数据操作,可以得到不同的结果。对于显示英文操作,由于英文字母种类很少,只需要8位(一字节)即可。而对于中文,常用却有6000以上,于是我们的DOS前辈想了一个办法,就是将 ASCII表的高128个很少用到的数值以两个为一组来表示汉字,即汉字的内码。而剩下的低128位则留给英文字符使用,即英文的内码。 汉字字模
    得到了汉字的内码后,还仅是一组数字,那又如何在屏幕上去显示呢?这就涉及到文字的字模,字模虽然也是一组数字,但它的意义却与数字的意义有了根本的变化,它是用数字的各位信息来记载英文或汉字的形状,如英文的'A'在字模中是这样记载的:
  汉字内码
    在电脑中,所有的数据都是以0和1保存的,按不同的数据操作,可以得到不同的结果。对于显示英文操作,由于英文字母种类很少,只需要8位(一字节)即可。而对于中文,常用却有6000以上,于是我们的DOS前辈想了一个办法,就是将 ASCII表的高128个很少用到的数值以两个为一组来表示汉字,即汉字的内码。而剩下的低128位则留给英文字符使用,即英文的内码。 汉字字模
    得到了汉字的内码后,还仅是一组数字,那又如何在屏幕上去显示呢?这就涉及到文字的字模,字模虽然也是一组数字,但它的意义却与数字的意义有了根本的变化,它是用数字的各位信息来记载英文或汉字的形状,如英文的'A'在字模中是这样记载的:
  而中文的“你”在字模中却是这样记载的:
  
  
    在硬件系统内,英文的字模信息一般固化在ROM里,中文的字模信息一般记录在汉字库文件HZK16里。
  
  采用如何在点阵LED或LCD中显示汉字?
  
  假设你采用16x16点阵汉字,水平扫描。
  
  显示汉字为“模”,用产生如下字模
  
  unsigned char mo[]={
  0x00,0x00, 0x10,0x90, 0x10,0x94, 0x13,0xFE,
  0x7C,0x90, 0x11,0xFC, 0x19,0x04, 0x35,0xFC,
  0x35,0x04, 0x51,0xFC, 0x50,0x40, 0x17,0xFE,
  0x10,0x90, 0x11,0x08, 0x16,0x06, 0x00,0x00
  };
  
  代码如下:
  
  unsigned char cmp_w[8]={128,64,32,16,8,4,2,1};
  
  void FontDisplay(int x, int y, unsigned char * FontModule)
  {
  for(int row=0;row<16;row++)
  {
  for(int c=0;c<8;c++)
  if((FontModule[row*2]&cmp_w[c])!=0)
  putpixel(c+x,row+y,15);
  for(c=0;c<8;c++)
  if((FontModule[row*2+1]&cmp_w[c])!=0)
  putpixel(c+8+x,row+y,15);
  }
  
  main(){
  
  FontDisplay(5,10,mo);
  
  }
  
  24x24点阵代码示例(水平扫描):
  
  unsigned char mo[]={
  0x00,0x00,0x00, 0x00,0x00,0x00, 0x07,0x0C,0xC0, 0x06,0x0C,0xC0,
  0x06,0x0C,0xDC, 0x06,0x7F,0xF0, 0x06,0xCC,0xC0, 0x1F,0xF0,0x70,
  0x06,0x3F,0xF0, 0x07,0x30,0x70, 0x0F,0xBF,0xF0, 0x0E,0xF0,0x70,
  0x1E,0xF0,0x70, 0x1E,0x3F,0xF0, 0x36,0x06,0x18, 0x06,0xFF,0xFC,
  0x06,0x07,0x00, 0x06,0x0F,0x80, 0x06,0x0C,0xE0, 0x06,0x38,0x7C,
  0x06,0xE0,0x18, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00
  };
  unsigned char cmp_w[8]={128,64,32,16,8,4,2,1};
  
  void FontDisplay(int x, int y, unsigned char * FontModule)
  {
  for(int row=0;row<24;row++)
  {
  for(int c=0;c<8;c++)
  if((FontModule[row*3]&cmp_w[c])!=0)
  putpixel(c+x,row+y,15);
  for(c=0;c<8;c++)
  if((FontModule[row*3+1]&cmp_w[c])!=0)
  putpixel(c+8+x,row+y,15);
  for(c=0;c<8;c++)
  if((FontModule[row*3+2]&cmp_w[c])!=0)
  putpixel(c+16+x,row+y,15);
  }
  }
  
  main(){
  
  FontDisplay(5,10,mo);
  
  }
  
  putpixel函数:
  
  putpixel(int x,int y,int color)
  
  x,y为坐标,color为颜色值.该函数在(x,y)点设定象素的颜色.由于硬件的不同,也许提供的函数不同,用户可把此函数作为参考,必要时用硬件提供的函数取而代之.
  
  点阵字库
  
  一般我们使用16*16的点阵宋体字库,所谓16*16,是每一个汉字在纵、横各16点的区域内显示的。
  不过后来又有了HZK12、HZK24,HZK32和HZK48字库及黑体、楷体和隶书字库。
  虽然汉字库种类繁多,但都是按照区位的顺序排列的。前一个字节为该汉字的区号,后一个字节为该字的位号。
  每一个区记录94个汉字,位号则为该字在该区中的位置。
  因此,汉字在汉字库中的具体位置计算公式为:94*(区号-1)+位号-1。
  减1是因为数组是以0为开始而区号位号是以1为开始的。
  这仅为以汉字为单位该汉字在汉字库中的位置,那么,如何得到以字节为单位得到该汉字在汉字库中的位置呢?
  只需乘上一个汉字字模占用的字节数即可,
  即:(94*(区号-1)+位号-1)*一个汉字字模占用字节数,而按每种汉字库的汉字大小不同又会得到不同的结果。
  以16*16点阵字库为例,计算公式则为:(94*(区号-1)+(位号-1))*32。
  汉字库文该从该位置起的32字节信息即记录了该字的字模信息。
  
  了解点阵汉字及汉字库的构成原理后,显示汉字就变得简单。以16*16点阵字库为例,
  通常的方法是:将文件工作指针移到需要的汉字字模处、将汉字库文件读入一2*16数组再用for循环一位位地显示。
  
  #include "graphics.h"
  #include "stdio.h"
  main()
  { int i=VGA,j=VGAHI,k;
  unsigned char mat[16][2],chinease[3]="我";
  FILE *HZK;
  if((HZK=fopen("hzk16","rb"))==NULL)exit(0);
  initgraph(&i,&j,"");
  i=chinease[0]-0xa0;j=chinease[1]-0xa0; /*获得区码与位码*/
  fseek(HZK,(94*(i-1)+(j-1))*32l,SEEK_SET);
  fread(mat,32,1,HZK);
  for(j=0;j<16;j++)
  for(i=0;i<2;i++)
  for(k=0;k<8;k++)
  if(mat[j][i]&(0x80>>k)) /*测试为1的位则显示*/
  putpixel(i*8+k,j,WHITE);
  
  getch();
  closegraph();
  fclose(HZK);
阅读(4794) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~