Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2603466
  • 博文数量: 877
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 5920
  • 用 户 组: 普通用户
  • 注册时间: 2013-12-05 12:25
个人简介

技术的乐趣在于分享,欢迎多多交流,多多沟通。

文章分类

全部博文(877)

文章存档

2021年(2)

2016年(20)

2015年(471)

2014年(358)

2013年(26)

分类: 嵌入式

2014-08-31 20:58:55

http://blog.csdn.net/l_yangliu/article/details/7261759
比如汉字“中”的内码是0xd6d0,而他的区位码是5448.这个是怎么转换的呢?区位码不是内码减去0xa0a0吗?怎么算的呢?

问题补充:

找到答案了:
汉字机内码、国标码和区位码三者之间的关系为:

    区位码(十进制)的两个字节分别转换为十六进制后加20H得到对应的国标码;

    机内码是汉字交换码(国标码)两个字节的最高位分别加1,即汉字交换码(国标码)的两个字节分别加80H得到对应的机内码;

    区位码(十进制)的两个字节分别转换为十六进制后加A0H得到对应的机内码。

    即:

区位码(十进制)—— +2020H(8224)——>国标码(十六进制)—— +8080H(32896)——>机内码(十六进制)

 

所有的汉字或者英文都是下面的原理,
由左至右,每8个点占用一个字节,最后不足8个字节的占用一个字节,而且从最高位向最低位排列。
生成的字库说明:(以12×12例子)


一个汉字占用字节数:12÷8=1····4也就是占用了2×12=24个字节。
编码排序A0A0→A0FE A1A0→A2FE依次排列。
以12×12字库的“我”为例:“我”的编码为CED2,所以在汉字排在CEH-AOH=2EH区的D2H-A0H=32H个。所以在12×12字库的起始位置就是[{FE-A0}*2EH+32H]*24=104976开始的24个字节就是我的点阵模。
其他的类推即可。
英文点阵也是如此推理。

在DOS程序中使用点阵字库的方法

回到顶部

    首先需要理解的是点阵字库是一个数据文件,在这个数据文件里面保存了所有文字的点阵数据.至于什么是点阵,我想我不讲大家都知道 的,使用过"文曲星"之类的辞典吧,那个的上面显示的汉子就能够明显的看出"点阵"的痕迹.在 PC 机上也是如此,文字也是由点阵来组成了,不同的是,PC机的显示分辨率更高,高到了我们肉眼无法区分的地步,因此"点阵"的痕迹也就不那么明显了.

    点阵、矩阵、位图这三个概念在本质上是有联系的,从某种程度上来讲,这三个就是同义词.点阵从本质上讲就是单色位图,他使用一个比特来表示一个点,如果这 个比特为0,表示某个位置没有点,如果为1表示某个位置有点.矩阵和位图有着密不可分的联系,矩阵其实是位图的数学抽象,是一个二维的阵列.位图就是这种 二维的阵列,这个阵列中的 (x,y) 位置上的数据代表的就是对原始图形进行采样量化后的颜色值.但是,另一方面,我们要面对的问题是,计算机中数据的存放都是一维的,线性的.因此,我们需要 将二维的性化到一维里面去.通常的做法就是将二维数据按行顺序的存放,这样就线性化到了一维.

    那么点阵字的数据存放细节到底是怎么样的呢.其实也十分的简单,举个例子最能说明问题.比如说 16*16 的点阵,也就是说每一行有16个点,由于一个点使用一个比特来表示,如果这个比特的值为1,则表示这个位置有点,如果这个比特的值为0,则表示这个位置没 有点,那么一行也就需要16个比特,而8个比特就是一个字节,也就是说,这个点阵中,一行的数据需要两个字节来存放.第一行的前八个点的数据存放在点阵数 据的第一个字节里面,第一行的后面八个点的数据存放在点阵数据的第二个字节里面,第二行的前八个点的数据存放在点阵数据的第三个字节里面,…,然后后 面的就以此类推了.这样我们可以计算出存放一个点阵总共需要32个字节.看看下面这个图形化的例子:

    | |1| | | | | | | | | | |1| | | |

    | | |1|1| |1|1|1|1|1|1|1|1|1| | |

    | | | |1| | | | | | | | |1| | | |

    |1| | | | | |1| | | | | |1| | | |

    | |1|1| | | |1| | | | | |1| | | |

    | | |1| | | |1| | | | |1| | | | |

    | | | | |1| | |1| | | |1| | | | |

    | | | |1| | | |1| | |1| | | | | |

    | | |1| | | | | |1| |1| | | | | |

    |1|1|1| | | | | | |1| | | | | | |

    | | |1| | | | | |1| |1| | | | | |

    | | |1| | | | |1| | | |1| | | | |

    | | |1| | | |1| | | | | |1| | | |

    | | |1| | |1| | | | | | |1|1|1| |

    | | | | |1| | | | | | | | |1| | |

    | | | | | | | | | | | | | | | | |

    可以看出这是一个"汉"字的点阵,当然文本的方式效果不是很好.根据上面的原则,我们可以写出这个点阵的点阵数 据:0x40,0x08,0x37,0xfc,0x10,0x08,…, 当然写这个确实很麻烦所以我不再继续下去.我这样做,也只是为了向你说明,在点阵字库中,每一个点阵的数据就是按照这种方式存放的.

    当然也存在着不规则的点阵,这里说的不规则,指的是点阵的宽度不是8的倍数,比如 12*12 的点阵,那么这样的点阵数据又是如何存放的呢?其实也很简单,每一行的前面8个点存放在一个字节里面,每一行的剩下的4点就使用一个字节来存放,也就是说 剩下的4个点将占用一个字节的高4位,而这个字节的低4位没有使用,全部都默认的为零.这样做当然显得有点浪费,不过却能够便于我们进行存放和寻址.对于 其他不规则的点阵,也是按照这个原则进行处理的.这样我们可以得出一个 m*n 的点阵所占用的字节数为 (m+7)/8*n.

    在明白了以上所讲的以后,我们可以写出一个显示一个任意大小的点阵字模的函数,这个函数的功能是输出一个宽度为w,高度为h的字模到屏幕的 (x,y) 坐标出,文字的颜色为 color,文字的点阵数据为 pdata 所指:

    /*输出字模的函数*/

    void _draw_model(char *pdata, int w, int h, int x, int y, int color)

    {

    int     i;    /* 控制行 */

    int     j;    /* 控制一行中的8个点 */

    int     k;    /* 一行中的第几个"8个点"了 */

    int     nc;   /* 到点阵数据的第几个字节了 */

    int     cols; /* 控制列 */

    BYTE    static mask[8]={128, 64, 32, 16, 8, 4, 2, 1}; /* 位屏蔽字 */

    w = (w + 7) / 8 * 8; /* 重新计算w */

    nc = 0;

    for (i=0; i

    {

    cols = 0;

    for (k=0; k

    {

    for (j=0; j<8; j++)

    {

    if (pdata[nc]&mask[j])

    putpixel(x+cols, y+i, color);

    cols++;

    }

    nc++;

    }

    }

    }

    代码很简单,不用怎么讲解就能看懂,代码可能不是最优化的,但是应该是最易读懂的.其中的 putpixel 函数,使用的是TC提供的 Graphics 中的画点函数.使用这个函数就可以完成点阵任意大小的点阵字模的输出.

    接下来的问题就是如何在汉子库中寻址某个汉子的点阵数据了.要解决这个问题,首先需要了解汉字在计算机中是如何表示的.在计算机中英文可以使用 ASCII 码来表示,而汉字使用的是扩展 ASCII 码,并且使用两个扩展 ASCII 码来表示一个汉字.一个 ASCII 码使用一个字节表示,所谓扩展 ASCII 码,也就是 ASCII 码的最高位是1的 ASCII 码,简单的说就是码值大于等于 128 的 ASCII 码.一个汉字由两个扩展 ASCII 码组成,第一个扩展 ASCII 码用来存放区码,第二个扩展 ASCII 码用来存放位码.在 GB2312-80 标准中,将所有的汉字分为94个区,每个区有94个位可以存放94个汉字,形成了人们常说的区位码,这样总共就有 94*94=8836 个汉字.在点阵字库中,汉字点阵数据就是按照这个区位的顺序来存放的,也就是最先存放的是第一个区的汉字点阵数据,在每一个区中有是按照位的顺序来存放 的.在汉字的内码中,汉字区位码的存放实在扩展 ASCII 基础上存放的,并且将区码和位码都加上了32,然后存放在两个扩展 ASCII 码中.具体的说就是:

    第一个扩展ASCII码 = 128+32 + 汉字区码

    第二个扩展ASCII吗 = 128+32 + 汉字位码

    如果用char hz[2]来表示一个汉字,那么我可以计算出这个汉字的区位码为:

    区码 = hz[0] - 128 - 32 = hz[0] - 160

    位码 = hz[1] - 128 - 32 = hz[1] - 160.

    这样,我们可以根据区位码在文件中进行殉职了,寻址公式如下:

    汉字点阵数据在字库文件中的偏移 = ((区码-1) * 94 + 位码) * 一个点阵字模占用的字节数

    在寻址以后,即可读取汉字的点阵数据到缓冲区进行显示了.以下是实现代码:

    /* 输出一个汉字的函数 */

    void _draw_hz(char hz[2], FILE *fp, int x, int y, int w, int h, int color)

    {

    char ftbuf[128];   /* 足够大的缓冲区,也可以动态分配 */

    int ch0 = (BYTE)hz[0]-0xA0; /* 区码 */

    int ch1 = (BYTE)hz[1]-0xA0; /* 位码 */

    /* 计算偏移 */

    long offset = (long)pf->_hz_buf_size * ((ch0 - 1) * 94 + ch1 - 1);

    fseek(fp, offset, SEEK_SET);              /* 进行寻址 */

    fread(fontbuf, 1, (w + 7) / 8 * h, fp);   /* 读入点阵数据 */

    _draw_model(fontbuf, w, h, x, y, color); /* 绘制字模 */

    }

    以上介绍完了中文点阵字库的原理,当然还有英文点阵字库了.英文点阵字库中单个点阵字模数据的存放方式与中文是一模一样的,也就是对我们所写的 _draw_model 函数同样可以使用到英文字库中.唯一不同的是对点阵字库的寻址上.英文使用的就是 ASCII 码,其码值是0到127,寻址公式为:

    英文点阵数据在英文点阵字库中的偏移 = 英文的ASCII码 * 一个英文字模占用的字节数

    可以看到,区分中英文的关键就是,一个字符是 ASCII 码还是扩展 ASCII 码,如果是 ASCII 码,其范围是0到127,这样是使用的英文字库,如果是扩展 ASCII 码,则与其后的另一个扩展 ASCII 码组成汉字内码,使用中文字库进行显示.只要正确区分 ASCII 码的类型并进行分别的处理,也就能实现中英文字符串的混合输出了.

点阵字库和矢量字库的差别

回到顶部

    我们都只知道,各种字符在屏幕上都是以一些点来表示的,因此也叫点阵.最早的字库就是直接把这些点存储起来,就是点阵字库.常见的汉字点阵字库有 16x16, 24x24 等.点阵字库也有很多种,主要区别在于其中存储编码的方式不同.点阵字库的最大缺点就是它是固定分辨率的,也就是每种字库都有固定的大小尺寸,在原始尺寸下使用,效果很好,但如果将其放大或缩小使用,效果就很糟糕了,就会出现我们通常说的锯齿现象.因为需要的字体大小组合有无数种,我们也不可能为每种大小都定义一个点阵字库.于是就出现了矢量字库.

    矢量字库

    矢量字库是把每个字符的笔划分解成各种直线和曲线,然后记下这些直线和曲线的参数,在显示的时候,再根据具体的尺寸大小,画出这些线条,就还原了原来的字符.它的好处就是可以随意放大缩小而不失真.而且所需存储量和字符大小无关.矢量字库有很多种,区别在于他们采用的不同数学模型来描述组成字符的线条.常见的矢量字库有 Type1字库和Truetype字库.

    在点阵字库中,每个字符由一个位图表示(如图2.5所示),并把它用一个称为字符掩膜的矩阵来表示,其中的每个元素都是一位二进制数,如果该位为1表示字符的笔画经过此位,该像素置为字符颜色;如果该位为0,表示字符的笔画不经过此位,该像素置为背景颜色.点阵字符的显示分为两步:首先从字库中将它的位图检索出来,然后将检索到的位图写到帧中.

    在实际应用中,同一个字符有多种字体(如宋体、楷体等),每种字体又有多种大小型号,因此字库的存储空间十分庞大.为了减少存储空间,一般采用压缩技术.

    矢量字符记录字符的笔画信息而不是整个位图,具有存储空间小,美观、变换方便等优点.例如:在AutoCAD中使用图形实体-形(Shape)-来定义矢量字符,其中,采用了直线和圆弧作为基本的笔画来对矢量字符进行描述. 对于字符的旋转、放大、缩小等几何变换,点阵字符需要对其位图中的每个象素进行变换,而矢量字符则只需要对其几何图素进行变换就可以了,例如:对直线笔画的两个端点进行变换,对圆弧的起点、终点、半径和圆心进行变换等等.

    矢量字符的显示也分为两步.首先从字库中将它的字符信息.然后取出端点坐标,对其进行适当的几何变换,再根据各端点的标志显示出字符.

    轮廓字形法是当今国际上最流行的一种字符表示方法,其压缩比大,且能保证字符质量.轮廓字形法采用直线、B样条/Bezier曲线的集合来描述一个字符的轮廓线.轮廓线构成一个或若干个封闭的平面区域.轮廓线定义加上一些指示横宽、竖宽、基点、基线等等控制信息就构成了字符的压缩数据.

如何使用Windows的系统字库生成点阵字库?

回到顶部

    我的程序现在只能预览一个汉字的不同字体的点阵表达.

    界面很简单:   一个输出点阵大小的选择列表(8x8,16x16,24x24等),一个系统中已有的字体名称列表,一个预览按钮,一块画图显示区域.

    得到字体列表的方法:(作者称这一段是用来取回系统的字体,然后添加到下拉框中)

    //取字体名称列表的回调函数,使用前要声明一下该方法

    int   CALLBACK   MyEnumFtProc(ENUMLOGFONTEX*   lpelf,NEWTEXTMETRICEX*   lpntm,DWORD   nFontType,long   lParam)

    {

    CFontPeekerDlg*   pWnd=(CFontPeekerDlg*)   lParam;

    if(pWnd)

    {

    if(   pWnd->m_combo_sfont.Findring(0,   lpelf->elfLogFont.lfFaceName)   <0   )

    pWnd->m_combo_sfont.AddString(lpelf->elfLogFont.lfFaceName);

    return   1;

    }

    return   0;

    }

    //说明:CFontPeekerDlg   是我的dialog的类名,   m_combo_sfont是列表名称下拉combobox关联的control变量

    //调用的地方     (******问题1:下面那个&lf怎么得到呢……)

    {

    ::EnumFontFamiliesEx((HDC)   dc,&lf,   (FONTENUMPROC)MyEnumFontProc,(LPARAM)   this,0);

    m_combo_sfont.SetCurSel(0);

    }

    字体预览:

    如果点阵大小选择16,显示的时候就画出16x16个方格.自定义一个类CMyStatic继承自CStatic,用来画图.在CMyStatic的OnPaint()函数中计算并显示.

    取得字体:

    常用的方法:用CreateFont创建字体,把字TextOut再用GetPixel()取点存入数组.   缺点:必须把字TextOut出来,能在屏幕上看见,不爽.

    我的方法,用这个函数:GetGlyphOutline(),可以得到一个字的轮廓矢量或者位图.可以不用textout到屏幕,直接取得字模信息

    函数原型如下:

    DWORD   GetGlyphOutline(

    HDC   hdc,                     //画图设备句柄

    UINT   uChar,                 //将要读取的字符/汉字

    UINT   uFormat,             //返回数据的格式(字的外形轮廓还是字的位图)

    LPGLYPHMETR   lpgm,     //   GLYPHMETRICS结构地址,输出参数

    DWORD   cbBuffer,       //输出数据缓冲区的大小

    LPVOID   lpvBuffer,     //输出数据缓冲区的地址

    COT   MAT2   *lpmat2   //转置矩阵的地址

    );

    说明:

    uChar字符需要判断是否是汉字还是英文字符.中文占2个字节长度.

    lpgm是输出函数,调用GetGlyphOutline()是无须给lpgm   赋值.

    lpmat2如果不需要转置,将   eM11.value=1;   eM22.value=1;   即可.

    cbBuffer缓冲区的大小,可以先通过调用GetGlyphOutline(……lpgm,   0,   NULL,   mat);   来取得,然后动态分配lpvBuffer,再一次调用GetGlyphOutline,将信息存到lpvBuffer.   使用完毕后再释放lpvBuffer.

    程序示例:(***问题2:用这段程序,我获取的字符点阵总都是一样的,不管什么字……)

    ……前面部分省略……

    GLYPHMETRICS   glyph;

    MAT2   m2;

    memset(&m2,   0,   sizeof(MAT2));

    m2.eM11.value   =   1;

    m2.eM22.value   =   1;

    //取得buffer的大小

    DWORD   cbBuf   =   dc.GetGlyphOutline(   nChar,   GGO_BITMAP,   &glyph,   0L,   NULL,   &m2);

    BYTE*   pBuf=NULL;

    //返回GDI_ERROR表示失败.

    if(   cbBuf   !=   GDI_ERROR   )

    {

    pBuf   =   new   BYTE[cbBuf];

    //输出位图GGO_BITMAP   的信息.输出信息4字节(DWORD)对齐

    dc.GetGlyphOutline(   nChar,   GGO_BITMAP,   &glyph,   cbBuf,   pBuf,   &m2);

    }

    else

    {

    if(m_pFont!=NULL)

    delete   m_pFont;

    return;

    }

    编程中遇到问题:

    一开始,GetGlyphOutline总是返回-1,getLastError显示是"无法完成的功能",后来发现是因为调用之前没有给hdc设置Font.

    后来能取得pBuf信息后,又开始郁闷,因为不太明白bitmap的结果是按什么排列的.后来跟踪汉字"一"来调试(这个字简单),注意到了glyph.gmBlackBoxX   其实就是输出位图的宽度,glyph.gmBlackBoxY就是高度.如果gmBlackBoxX=15,glyph.gmBlackBoxY=2,表示输出的pBuf中有这些信息:位图有2行信息,每一行使用15   bit来存储信息.

    例如:我读取"一":glyph.gmBlackBoxX   =   0x0e,glyph.gmBlackBoxY=0x2;     pBuf长度cbBuf=8   字节

    pBuf信息:       00   08   00   00   ff   fc   00   00

    字符宽度   0x0e=14     则   第一行信息为:           0000   0000   0000   100       (只取到前14位)

    第二行根据4字节对齐的规则,从0xff开始         1111   1111   1111   110

    看出"一"字了吗?呵呵

    直到他的存储之后就可以动手解析输出的信息了.

    我定义了一个宏#define   BIT(n)     (1<<(n))     用来比较每一个位信息时使用

    后来又遇到了一个问题,就是小头和大头的问题了.在我的机器上是little   endian的形式,如果我用

    unsigned   long   *lptr   =   (unsigned   long*)pBuf;

    //j   from   0   to   15

    if(   *lptr   &   BIT(j)   )

    {

    //这时候如果想用j来表示写1的位数,就错了

    }

    因为从字节数组中转化成unsigned   long型的时候,数值已经经过转化了,像上例中,实际上是0x0800   在同BIT(j)比较.

    不多说了,比较之前转化一下就可以了if(   htonl(*lptr)   &   BIT(j)   )

Unicode中文点阵字库的生成与使用

回到顶部

    点阵字库包含两部分信息.首先是点阵字库文件头信息,它包含点阵字库文字的字号、多少位表示一个像素,英文字母与符号的size、起始和结束unicode编码、在文件中的起始偏移,汉字的size、起始和结束unicode编码、在文件中的起始偏移.然后是真实的点阵数据,即一段段二进制串,每一串表示一个字母、符号或汉字的点阵信息.

    要生成点阵字库必须有文字图形的来源,我的方法是使用ttf字体.ttf字体的显示采用的是SDL_ttf库,这是开源图形库SDL的一个扩展库,它使用的是libfreetype以读取和绘制ttf字体.

    它提供了一个函数,通过传入一个Unicode编码便能输出相应的文字的带有alpha通道的位图.那么我们可以扫描这个位图以得到相应文字的点阵信息.由于带有alpha通道,我们可以在点阵信息中也加入权值,使得点阵字库也有反走样效果.我采用两位来表示一个点,这样会有三级灰度(还有一个表示透明).

    点阵字库的显示首先需要将文件头信息读取出来,然后根据unicode编码判断在哪个区间内,然后用unicode编码减去此区间的起始unicode编码,算出相对偏移,并加上此区间的文件起始偏移得到文件的绝对偏移,然后读出相应位数的数据,最后通过扫描这段二进制串,在屏幕的相应位置输出点阵字型.

    显示点阵字体需要频繁读取文件,因此最好做一个固定大小的缓存,采用LRU置换算法维护此缓存,以减少磁盘读取.

标准点阵字库芯片

回到顶部
      最近重新找了一下C语言的资料,深深的被c语言的底层操作特性迷恋~。在这方面,最经典的一本书莫过于清华大学出版社的《C高级实用程序设计》(王士元),在C语言高级应用领域里这是我见过的写的最好的一本书,非常可惜的这本书现在已经绝版了(可能是因为技术发展和更新的太快),在书店里网上都无法买到了。记得本科时期经常借同学的这本书来读,爱不释手,里面的知识极具魅力,即使今天看起来仍让我觉得不是过时,而是回味无穷。这里提到的字库文件和操作系统都已经属于古董级别了,现在可能也很难找到了。。。。这种应用在现在时代也很少有人研究了,但我想在单片机等嵌入式系统的点阵式汉字显示屏中仍然在使用。
      在这里我参考了一些资料中的用C语言显示汉字字库的资料。在计算机发展的早期,为了支持显示汉字,国内发明了相应的2个字节表示的汉字国标(GB)码,根据这个编码规则,汉字分为94个区,每区94个汉字,汉字在其所在区内的位置用位号表示,两个字节分别表示区号和位号,为了区分ASCII码,每个字节的首位都被置为1。在网络传输时还有特定的区分方法,这里不细述这些细节了。在国际编码中,中国汉字被分配到第16区(起始区号0x0F)。为了显示汉字,需要汉字字形文件(字库)的支持。在DOS时代,出现了USDOS系统,有HZK16(16*16字形),HZK24(24*24字形)等字库文件,本质上一个汉字字符是一个二值图像(即bpp=1),所以本质上是一种图形字体(非矢量的),例如HZK16,每个字是16*16像素,每个像素占1位(1/8byte),因此每个汉字在文件中占据了16*16/8=32 bytes/汉字。HZK24每个汉字占据24*24/8=72 bytes/汉字。由于采用了这些约定,所以文件中没有任何文件头等附加描述信息,而全部是紧密排列的像素字形点阵,文件从第一个字节开始第一个字符一直到最后一个字符结束,文件也没有后缀名。字形在文件中是紧密排列的,但是需要注意的2点重要问题是:
       (1)字形扫描顺序:
        HZK16是按行扫描,而HZK24是按列扫描。
        所以假设一个字形读取到一个byte[]。则字节的分布分为按行扫描和按列扫描。例如在HZK16中是按行一行一行扫描,即前byte[0],byte[1]是第一行,接着2个byte是第二行。。。而HZK24为了使使用它的打印机输出,采用的是按列扫描,因为打印机有一个纵向的24针,每一次可以逐列的打印一行汉字,因此为了配合打印机,HZK24采用了按列扫描。如下图所示:(图中的数字表示的该字节在byte[]中的索引。)
          
          下面这张图显示的是24*24像素的按列扫描的汉字字符,是如何用一个BYTE[]数组来描述的:图中用彩色填充的方式标示出了BYTE[0],BYTE[1],BYTE[2]:
           
        (2)字形在字库文件中的定位:
         请注意的是,在HZK16和HZK24中定位是不同的。这是由于汉字在文件中的起始区不同。HZK16的汉字起始区在第16个分区,而HZK24则直接从第一个分区开始。HZK16的前15个分区里面存储了一些特殊符号,字母等(见后面的截图)。因此汉字所在的分区是从15号分区开始的,而HZK24不包含前面的特殊字符,第一个区就是汉字区“啊”,从0号分区开始。另一点需要注意的是,两者的【bytes/汉字】数值不同。用offset表示从文件头开始计算的文件偏移地址,code[2]表示汉子码。
          则code是用内码表示的,如汉字表中的第一个汉字“啊”的内码是{0xb0,0xa1};从汉字码的第一个字节获取区号,从第二个字节获取位号。将他们减去0xA1就转换为我们需要的区号和位号。即换算成区号是{0x0f,0x00},表示“啊”字位于第15区,位号是0。
          对于HZK16来说:94是每个区的汉字数。
          unsigned long offset=( (code[0]-0xA1) *94 + (code[1]-0xA1) )*32L;    //32是每个汉字占据的字节数
          对于HZK24来说:
          unsigned long offset=( (code[0]-0xA1-15) *94 + (code[1]-0xA1) )*72L;//72是每个汉字占据的字节数
          注意上面的HZK24中,由于第一个分区就是汉字区,所以区号被减去了0x0F。
          
          这样,我们看显示一个字符的代码:(以下代码来自于《C高级应用程序设计》一书。)
HZK16显示汉字的代码
/*在屏幕的x0,y0位置显示一个汉字字符*/
void dishz(int x0,int y0,char code[],int color)
{
unsigned 
char mask[]={0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01};
/*屏蔽字模每行各位的数组*/
int i,j,x,y,pos;
char mat[32];
/*下面这个函数用于在文件中读取相应的字节到mat数组中*/
get_hz(code,mat);
y
=y0;
for (i=0;i<16;++i)
{
x
=x0;
pos
=2*i;
for(j=0;j<16;++j)
{
/*屏蔽出汉字字模的一个位,即一个点是1 则显示,否则不显示该点*/
if ((mask[j%8]&mat[pos+j/8])!=NULL)
putpixel(x,y,color);
++x;
}
++y;
}
}
      我们要注意的是,由于每个汉字占用了2个字节,因此我们必须每次是字符串的指针递增2。例如:
       char* s="这是汉字字符串";
       while(*s)
       {
            dishz(x0,y0,s,LIGHTBLUE);
            x0+=16;
            s+=2;   //notice: not s++
       }
      上面的字模数组的每个数字的某一位为1,这样用位与操作可以判断某个byte中的某一位为1还是0,如果为1表示这里应该显示汉字的前景颜色。上面的代码是用于HZK16的,即按行扫描的顺序显示的。按列显示的代码原理类似,不再列出。
      关于加载字模,主要采用上面的文件定位,读取一定数量字节即可,这里我们也不再列出。
      下面的截图是HZK16字库的前面一部分特殊字符(有象棋棋子,制表符,全角的字母数字等等):(每行32个字符,每个字符的左上角被我标记了一个红点)。
      
     上面的截图采用了在《用c语言显示BMP》一文中的TC截屏代码截屏的代码(我将截屏代码写到cpyscr.h文件中放到了include文件夹下,这样只要用#include "cpyscr.h"就可以直接使用截屏函数了),注意第一行中的数字序号表示的是该字符的序号(注意该序号不是ASCII码!!!),而不是文件地址!。
下图是用HZK24F(仿宋体)显示的汉字,上面是原样大小输出(24*24),较大的字是把字形边长扩大成2倍边长(48*48)的输出,由于是位图字体,所以扩大后会有位图放大的锯齿感:
      
      宋体汉字库:
      
 
注意国标码,区位码和内码的关系和转换关系
阅读(1791) | 评论(0) | 转发(0) |
0

上一篇:片外FLASH 读取函数

下一篇:WideCharToMultiByte

给主人留下些什么吧!~~