基于哈希查找的字库设计与实现及其自动生成工具的实现
—— 一种嵌入式字库设计的解决方案
一、 基于哈希查找的字库设计与实现
1、 基本思想:
设计一个哈希链表,哈希码(哈希函数的值)采用字符内码的平方取中法获得,并采用类链表的方式处理冲突。
2、 哈希函数和哈希码:
根据上述基本思想可以构造如下的哈希函数:
HashCode = ( (CharCode)2 & 0x3ff00 ) >> 8
可以看出,取的是字符内码(CharCode)平方的值的中间10位作为哈希码(HashCode)的,因此哈希表是含1024个元素的数组。
3、 冲突处理:
将所有具有相同哈希码的字符信息保存在一个数组中,并在以哈希码为下标的哈希表中(HashTable[HashCode]的形式)保存相应的信息(包括:具有相同哈希码的字符的个数, 数组的地址)。
4、 数据结构的设计:
1)、字模结构:一个一维数组,数组的大小视字体不同的宽度和高度而定。设字体的宽度为w象素,高度为h象素,则数组的大小为(w*h/8)字节。因此可以看出,数组中每一位表示一个象素,用1表示字符笔划经过该象素(在显示的时候将被填上画笔的颜色),0表示不经过(在显示的时候不填色,即显示背景色)。因此对一个“大”(16×16)字,有如下结构(图1):
<shapetype id="_x0000_t75" stroked="f" filled="f" path="m@4@5l@4@11@9@11@9@5xe" o:preferrelative="t" o:spt="75" coordsize="21600,21600"><stroke joinstyle="miter"></stroke><formulas><f eqn="if lineDrawn pixelLineWidth 0"></f><f eqn="sum @0 1 0"></f><f eqn="sum 0 0 @1"></f><f eqn="prod @2 1 2"></f><f eqn="prod @3 21600 pixelWidth"></f><f eqn="prod @3 21600 pixelHeight"></f><f eqn="sum @0 0 1"></f><f eqn="prod @6 1 2"></f><f eqn="prod @7 21600 pixelWidth"></f><f eqn="sum @8 21600 0"></f><f eqn="prod @7 21600 pixelHeight"></f><f eqn="sum @10 21600 0"></f></formulas><path o:connecttype="rect" gradientshapeok="t" o:extrusionok="f"></path><lock aspectratio="t" v:ext="edit"></lock></shapetype><shape id="_x0000_i1025" style="WIDTH: 312pt; HEIGHT: 196.5pt" type="#_x0000_t75"><imagedata o:title="" src="file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtml1/01/clip_image001.png"></imagedata></shape>
其中:_ 表示0,X表示1。(字模来源:由ucGUI的字体生成工具生成)
2)、字符信息结构类型(sGUI_CHAR_INFO)定义:字符信息包含字符的宽度和字模所在地址,只包含字符的宽度而不包含高度,是假设同一字体的所有字符都是等高的。因此,有如下的字符信息结构(图2):
<shape id="_x0000_i1026" style="WIDTH: 120.75pt; HEIGHT: 47.25pt" type="#_x0000_t75"><imagedata o:title="" src="file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtml1/01/clip_image003.png"></imagedata></shape>
其中,XSize表示字符的宽度(象素);pCharData表示字模的地址。如“大”字的字符信息结构为(图3):
说明,“大”字的字符宽度为16, 字模地址为字模的数组名acFontHZ16x16_b<chmetcnv w:st="on" unitname="F" sourcevalue="4" hasspace="False" negative="False" numbertype="1" tcsc="0">4f</chmetcnv>3。
3)、前面提到,把具有相同哈希码的字符信息保存在一个数组中,但是除了字符信息外还不够,为了根据字符内码找到对应的字符信息还必须将字符内码一起放到这个数组中,因此数组是由具有如下结构的元素组成的,称其为字符节点(图4):
<shape id="_x0000_i1027" style="WIDTH: 173.25pt; HEIGHT: 48pt" type="#_x0000_t75"><imagedata o:title="" src="file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtml1/01/clip_image007.png"></imagedata></shape>
其中,CharCode是字符的内码;pCharInfo是字符信息的地址。如“大”字的字符节点结构为(图5):
<shape id="_x0000_i1029" style="WIDTH: 249pt; HEIGHT: 53.25pt" type="#_x0000_t75"><imagedata o:title="" src="file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtml1/01/clip_image009.png"></imagedata></shape>
说明,“大”字的内码为0xb<chmetcnv w:st="on" unitname="F" sourcevalue="4" hasspace="False" negative="False" numbertype="1" tcsc="0">4f</chmetcnv>3,字符信息地址为&charInfo_0xb<chmetcnv w:st="on" unitname="F" sourcevalue="4" hasspace="False" negative="False" numbertype="1" tcsc="0">4f</chmetcnv>3。
这个数组的形式如下(图6):
说明这个数组里的字符都是有相同的哈希码670(从数组名看出)。
4)、前面提到,哈希表的元素包括具有相同哈希码的字符的个数、数组的地址。因此哈希表的元素的结构如下定义(图7):
<shape id="_x0000_i1030" style="WIDTH: 186.75pt; HEIGHT: 44.25pt" type="#_x0000_t75"><imagedata o:title="" src="file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtml1/01/clip_image013.png"></imagedata></shape>
其中,len表示的是具有相同哈希码的字符的个数;ppNodeList这个指针的指针就是一个数组(注:具有相同哈希码的字符信息保存在一个数组)的地址,因数组中的元素也是指针,所以它应该是双重指针。
假设哈希表定义如下:
THEAD CharTable[1024];
则有:
CharTable[670].len = 10;
CharTable[670].ppNodeList = Nodes_Hash_670;
表示:在这个字库中,哈希码是670的字符共有10个,它们的信息存放在Nodes_Hash_670数组中。
5、 图示(图8)
6、 字符的查找
给定一字符内码(a_CharCode),根据哈希函数得到其哈希码(a_HashCode),用哈希码作为下标查找哈希表得到具有同样哈希码的字符个数和字符节点数组信息(CharTable[a_HashCode]),在字符节点数组中查找和给定字符内码相同的字符信息,查到返回字符信息,否则说明无此字符信息。字符信息包含字符宽度和字模信息,用这两个信息就可以画出字符了。
7、有了上述准备,就可以定义真正的字体结构了,字体结构包含字符的高度信息(规定同一字体的字符的高度是相同的)和哈希表的地址。另外为了节省空间(每个哈希表的大小是1024*sizeof(THEAD) == 8192字节),所有的字体公用一个哈希表,因此要在设置字体的时候动态设置哈希表的表项,把这个过程叫做安装字库,因此必须提供安装字库的接口函数,这个函数是每个字库都不同的,因此必须定义成回调函数(函数指针),在字库定义的时候指定。由此可定义字体结构如下(图9):
<shape id="_x0000_i1033" style="WIDTH: 256.5pt; HEIGHT: 80.25pt" type="#_x0000_t75"><imagedata o:title="" src="file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtml1/01/clip_image016.png"></imagedata></shape>
其中,YSize表示字符的高度;pCharTable是哈希表的地址;pInstalFun是字库安装函数的指针。
字体定义实例(图10):
<shape id="_x0000_i1034" style="WIDTH: 239.25pt; HEIGHT: 72.75pt" type="#_x0000_t75"><imagedata o:title="" src="file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtml1/01/clip_image018.png"></imagedata></shape>
表示字符的高度是16象素,字符哈希表CharTable,字库安装函数sGUI_InstalFont_sGUI_FONT_hzFX_16。
关于安装函数的具体内容,就是把不同字库的字符的节点数组地址填到哈希表的相应元素,如下形式(图11):
<shape id="_x0000_i1035" style="WIDTH: 240.75pt; HEIGHT: 79.5pt" type="#_x0000_t75"><imagedata o:title="" src="file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtml1/01/clip_image020.png"></imagedata></shape>
二、 自动生成工具的实现
未完,待续......
原创 By Nathan 与 2007年5月14日