硬汉嵌入式论坛

 找回密码
 立即注册
查看: 8994|回复: 4
收起左侧

[μCGUI] [ucgui原创]在UCGUI中增加汉字显示的说明(转自www.ucgui.com)

[复制链接]

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
107491
QQ
发表于 2012-11-3 14:49:33 | 显示全部楼层 |阅读模式
[paragraph]在UCGUI中增加汉字显示的说明.
作 者: ucgui
email: ucgui@163.com
home: http://www.ucgui.com
版 本: v1.0.0.1
UCGUI中本身只支持E,没有提供中文的字库的.C源码文件, 但是我们可以通过下面的方式来实现汉字的显示...
我们知道, 在DOS下经常利用点阵来显示汉字. 带汉字显示的程序,很多都会自己带上汉字库, 这个字库里放的就是每个汉字的点阵.
一. 汉字的显示原理之一 -----------------点阵汉字.
        简单的理解, 所谓一个字的点阵. 其实就是指这个汉字用多少个象素点来描述. 每个象素点显示为什么颜色, 通常情况下,

HZK16采用的是16*16点阵, 即256个象素点描述一个汉字.这些点的颜色分为两种, 一种是前景色, 一种是显示为背景色.
那么,关于那些点显示为前景色, 那些点显示为背景色, 是如何得知的呢??可以这样来考虑, 你在纸上比较正正方方的写
一个规则的楷字, 然后在这个字的从上到下,左到右, 分别画十七条直线,那么这个字就被放置于一个16*16的方格之内,

这样我们就可以很明显的看出, 16*16的方格内的具体哪些点有笔划经过,有笔划经过与没笔化经过的即就是应该被分别
填充上前景色与背景色的点.现在,找到了一个汉字的点阵, 那么还须要用数据来记录点阵的信息, 通常情况下, 我们会用32
个字节来表示16*16点阵的汉字, 即每一行用二个字节来记录十六个象素点的色色彩情况, 0表示背景色, 1表示前景色.

16行其须要32个字节.
        点阵汉字的原理同时也决定了它的缺点, 他不具务放大特性, 因为它的显示是基于被定死的点阵, 放大后, 会产生明
显的锯齿,非常的难看, 当然, 可以进行一些光滑处理, 但基本上没有多在的改观.但点阵汉字简易, 对于复杂汉字, 它比矢
量显示汉字法更快带.
       矢量显示是基于记录汉字的笔化的. 对于简单的汉字它比较占优势, 容易放大处理.

但对于复杂的汉字, 表示起来, 则笔化太多..复杂.
二. 关于字库的建立及其原理.
现在讲完了汉字点阵. 也说了一个汉字点阵的存放方式, 但具体的点阵如何存放, 读者也应该了解.
通常情况下, 一般的DOS下的程序都会提供一个汉字库, 这样在脱离汉字平台(如UCDOS)的支持下也可以进行汉字显示,

但是这样会存一个问题, 就是如果每个DOS下的程序员都这么做的话, 就会造成一定的磁盘空间浪费. 所以有的DOS下的
程序,针对自己所需要的汉字,就会定制自己的小型字库, 那么字库的制作到底应该如何进行呢? 下面我们将就这个问题
进行一些基本的讨论.
众所周知,一个ASCII字符占一个字节,它的数值从0到255, 那么汉字字符将如何与ASCII字符区别开来呢?
实际上,仔细观察ASCII字符表,从第161(即0xa1)个字符开始,后面的字符并不经常为E文所使用。充分利用这
一特性,将161-255之间的数值空间作为汉字的标识码。既然255-161 = 94不能满足汉字容量的要求,就将每两
个字符并在一块(即一个汉字占两个字节),显然,94*94 =8836基本上已经满足了常用汉字个数的要求。从以上
的讨论可以知道, 用二个字节来表示一个汉字, 其原因就是上面说的, 这个就是我们常说的汉字机内码, 一个汉字的
机内码是由值都大于0xa1的值组成的.
说完机内码, 有的朋友可能就会问题, 机内码与建立汉字字库有什么关系呢??
我们常见的标准的汉字字库HZX16(点阵16*16),HZK24(24*24)两种.由上面的讨论我们得知,一个汉字点阵须要256
个象素点阵来表示, 我们采用一个字节的8位来表示八个象素, 其须32个字节;字库中要存放的是所有常用的汉字的
二进制点阵数据, 它的存放是有序的, 下面我们说一下这个顺序:首先.对于"我"字来说, 它的机内码是0xce,0xd2; 机内
码每个字节均从0xa1开始, 那么我们已经采用的建立点阵字在库中的索引方法是:将整个字库里面的汉字是94*94的
二维数组, 要找任意一个汉字的点阵, 就须要知道这个汉字在这个二维数组当中的X维与Y维.


                                     x维 = (机内码字节1-0xa1) & 0x7f;
                                     y维 = (机内码字节2-0xa1) & 0x7f;


求汉字在X,Y维后, 那么按照每个汉字占用32个字节, 则可以得出汉字相对于字库头的偏移是 offset = (x*94 + y)*32;
其实,X与Y就是汉字的区位码, 汉字的区位码是从0-94的. 但实际上只用了16-87..其中一级汉字在16-55..二级汉字在
56-87.是按照一定的规则来确定区位码的.对于一级汉字.是按拼音首字母级笔划.二级汉字是按部首来的.我特意生了
一个汉字的区痊码,机内码.在字库中偏移的文件..大家可以下载来看一下. 可以知道:


                                   啊-------------区位码(x = 15, y = 0); offset=b040; 机内码0xb0,0xa1);



所以汉字的区内码,机内码,偏移的信息,请下载这个文件查看.
http://www.macro-tax.com/home/ucgui/HZK_info.rar
其中,区位码(x=0-14)与(88-94)都是没有对应汉字的.字库中实际的对应汉字点阵字数为94*72=6768个汉字.实际上,

一个字库中有前16*32个字节没有表示具体的汉字的, 在字库里被用来表示什么东西没有什么具体的要求, 如果说你
自己要做一个字库.那么这一段你可以自己发挥, 填充为一个中文的符号,笑脸,特别文字什么的.这些没有具体的要求.
同理.对于(88---94)*32, 你也可以自己发挥. 然后告知别人如何使用,因为这个没有标准, 所以一定要有特别的说明,别
人才可可以使用.在一般的HZK16当中, 最前16*32个节有表示两个大小的"A"及两个感叹号, 一个在圆内的"帅"字..大
家可以仔细看一下,其它几个没作特别使用.
三.应用程序中进行汉这显示的处理
那么, 在以上我们谈了汉字的显示原理, 汉字字库的存放原理, 其实都是为了更方便的让我们自由使用..在实际小,

一个应用程序未必须要显示所有的汉字, 可能他仅须要显示1000个常用的汉字, 那么就可制作一个1000个常用
小型汉字字库, 即所需要的汉字库从250K降到32K左右了, 大大的减少了资源占用,使用上非常的灵活.
四. 在UCGUI中如何加入汉字显示的支持.
UCGUI中没有汉字功能的支持, 但其实只要稍加改造, 我们就可以解决点阵汉字显示的问题.
UCGUI中, 对于E文的显示, 也同样采用的是点阵的方式, 而且有8*8,6*8, 16*8, 16*16等各种点阵, 这里,我们
可以实现在设置显示16*16的E文字体时, 加上我们的汉字显示, 因为是同样的点阵, 我们不用任何改造, 只要有
HZK16文件,就可以在此E文字体下显示汉字了.
全部的改造基本上集中在这个函数内部.
void GL_DispLine(const char GUI_FAR *s, int Len, const GUI_RECT *pRect);
这个函数在GUI\Core\GUIChar.c 文件内部
要支持汉字显示, 那么必须改成如下形式.
void GL_DispLine(const char GUI_FAR *s, int Len, const GUI_RECT *pRect) {
  /*
    Check if we have anything to do at all ...
    If the window manager has already set the clipping rect, it does not
    make sense to due this. So it makes sense only if
    a) The window manager is not used (-> Configuration)
      or
    b) The window manager is inactive (-> Memory device active)
  */
  if (GUI_Context.pClipRect_HL) {
    if (GUI_RectsIntersect(GUI_Context.pClipRect_HL, pRect) == 0)
      return;
  }
  if (GUI_Context.pAFont->pafEncode) {
    GUI_Context.pAFont->pafEncode->pfDispLine(s, Len);
    return;
  }
#if (GUI_SUPPORT_UNICODE)
  {
    U8 c0;
    char UCActive=0;
    while (--Len >=0) {
      c0=*(U8*)s++;
      if (UCActive) {
        if (c0 == GUI_UC_ENDCHAR)
          UCActive = 0;
        else {
          U8  c1 = *(U8*)s++;
          Len--;
          GL_DispChar (GUI_DB2UC(c0, c1));
        }
      } else { /* Unicode not active */
        if (c0 == GUI_UC_STARTCHAR)
          UCActive = 1;
        else
  {
    //增加汉字支持所加的...2005-6-13 0:14:09
    if (c0&0x80 && (*(U8*)s)&0x80){
   char hz[3];
   hz[0]=c0;
   hz[1]=*(U8*)s;
   hz[2]=0;
   WriteHZ(0,0,hz,0);
   s++;
    }
    else
   GL_DispChar(c0);
  }
      }
    }
  }
#else
  {
   U8 c0;
   while (--Len >=0) {  
    c0=*(U8*)s++;
    //增加汉字支持所加的...2005-6-13 0:14:09
    if (c0&0x80 && (*(U8*)s)&0x80){
     char hz[3];
     hz[0]=c0;
     hz[1]=*(U8*)s;
     hz[2]=0;
     WriteHZ(0,0,hz,0);
     s++;
    }
    else{
     GL_DispChar(c0);
    }
   }
  }
#endif
}


处理汉字显示:
int WriteHZ (int x, int y,const char *p,int color)
{
  U16 c1,c2,rec;
  long l;
  char pixeldata[32];
  int BytesPerLine;
  GUI_DRAWMODE DrawMode = GUI_Context.TextMode;
  GUI_DRAWMODE OldDrawMode;
  if (handle<0 ) return 0;
  if (p==NULL) return 0;

  c1=(p[0]-0xa1)&0x07f;
  c2=(p[1]-0xa1)&0x07f;
  rec=c1*94+c2;  //汉字库94*94的二维结构...
  l=rec*32L;  //求字库偏移...
  lseek(handle,l,SEEK_SET);
  read(handle,pixeldata,32);

  BytesPerLine = 2;
  OldDrawMode  = LCD_SetDrawMode(DrawMode);

//半汉字点阵以二色位图方式绘出, 前景色/背景色
  LCD_DrawBitmap (GUI_Context.DispPosX, GUI_Context.DispPosY,
    HZSIZEX,HZSIZEY,
    1, 1,
    1,            /*Bits per Pixel */
    BytesPerLine,
    (U8*)pixeldata,
//    NULL  /* no palette means default palette */
    &LCD_BKCOLORINDEX  //在csword的bc3.0版本中, 是用NULL, 但在此处要要修改, 表明此位图所用调色析             //为二色, 前景色与背景色...
    );

  LCD_SetDrawMode(OldDrawMode); /* Restore draw mode */
  GUI_Context.DispPosX += HZSIZEX;
  return 1;
}


另外, 除了以上所讲的, 我在网上发现如下的一篇文章非常适合大家加强对汉字处理的理解. 汉字处理在DOS时代是一个比较热门的技术,

但在现在的WIN时代.没有什么人关注了, 但是在嵌入式开发了, 它还有一定的用武之地, 理解它还是有一定的帮助的.
一篇介绍汉字处理的文章, 非常不错, 很基础,很明白, 其分四节来讲汉字的基本原理.
http://www.vcer.net/showTip.jsp?tipid=2291
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
107491
QQ
 楼主| 发表于 2012-11-3 14:51:05 | 显示全部楼层
我在vc下做的时候只要在Font目录下加入汉字库,GUI.h中增加extern const GUI_FONT GUI_FontHZ12;就可以显示汉字了,不知道为什么要修改函授GL_DispLine,请指教,谢谢
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
107491
QQ
 楼主| 发表于 2012-11-3 14:52:39 | 显示全部楼层
我在vc下做的时候只要在Font目录下加入汉字库,GUI.h中增加extern const GUI_FONT GUI_FontHZ12;
就可以显示汉字了,不知道为什么要修改函授GL_DispLine,请指教,谢谢
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
107491
QQ
 楼主| 发表于 2012-11-3 15:00:58 | 显示全部楼层
楼上的, 其实我们所用的显示汉字的途径是不同的. 是两种方法, 但都是以点阵来显示来处理汉字显示的,

不同点在于你的汉字点阵是与你的程序编译在一起的, 汉字点阵的数据包含在你程序当中了.你所说的方法,

其实用的是汉字库弄出的点阵数据的.C文件, 这是别人从汉字库中生成的. 这种方法与UCGUI中本身处理
文字显示的方法是相同的, 都提供了所须文字的点阵及显示文字的方法函数. 关于具体如何实现, 我将会更
详细的写一篇介绍文章.至于我的汉字显示, 说的是直接利用汉字库, 而不用什么.C的汉字库字体文件, 根本
不用UCGUI中本身的一套方法,但本质还是相同的, 以位图来显示点阵汉字, 为了显示汉字, 须要点阵汉字库,

及显示汉字的函数(WriteHZ()).而且还要修改UCGUI中的显示字符的函数, 来特别处理汉字的显示. 因为汉
字的机内码与ASCII中英文的有差别, 是从a1往上走的,所以很容易区分开来.关于你的. 你的是下载使用了中
文字库的.C文件, 这个在网上是有.如下.

Eric2013添加备注:下面的方法就是大部分同学所用的方法,现在已经能将字库存到外部FLASH或SD卡,支持任意字
体任意大小。详细的使用,后面我会做一个教程

一个优化过的完全版的ucGUI汉字库_hzk12使用方法:
将解压后的hzk12.c放到ucGUI的font路径下
在GUI.H文件内加一行:extern const GUI_FONT GUI_FontHZ12;
按如下形式调用即可显示中文了:
GUI_Init();
GUI_SetColor(GUI_RED);
GUI_SetFont(&GUI_FontHZ12);
GUI_SetTextAlign(GUI_TA_LEFT);
GUI_DispStringAt("汉字库显示测试",20,20);


ucGUI_hzk12.rar (188 KB, 下载次数: 487)
回复

使用道具 举报

5

主题

36

回帖

5

积分

新手上路

积分
5
发表于 2014-3-17 17:39:26 | 显示全部楼层

回 eric2013 的帖子

eric2013:     楼上的, 其实我们所用的显示汉字的途径是不同的. 是两种方法, 但都是以点阵来显示来处理汉字显示的,

不同点在于你的汉字点阵是与你的程序编译在一起的, 汉字点阵的数据包含在你程序当中了.你所说的方法,

其实用的是汉字库弄出的点阵数据的.C文件,  .. (2012-11-03 15:00) 
能不能把这个改造加到EMWIN里面???让单片机使用者也方便一下??
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|Archiver|手机版|硬汉嵌入式论坛

GMT+8, 2024-6-4 09:33 , Processed in 0.291136 second(s), 29 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

快速回复 返回顶部 返回列表