奥索科技公司 董磊 周金明 杨磊

摘 要 论述VxWorks上WindML对多字节码的显示支持,着重论述其存储结构和意义,并给出汉字显示的实现方法。

关键词 VxWorks操作系统 WindML组件 汉字点阵 国标编码

一. VxWorks操作系统图形显示方案

VxWorks作为嵌入式实时操作系统的龙头老大,在国内应用已非常广泛,其中包括工业控制、医疗设备、家庭视听、车载电子等许多需要本地显示的行业。作为国内的显示应用,汉字显示是必不可少的,而vxWorks原厂商没有直接提供完备的汉字显示解决方案,本文就这个热点论题,通过对VxWorks显示组件的分析,具体给出一种汉字显示的方案。

VxWorks的汉字显示首先需要一种图形解决方案,只有工作在图形模式下才可能显示汉字。vxWorks有三种显示的方案可供选择:

1. WindML + Zinc:

WindML是wind river mutli-media library 的简称,其中包含UGL(universal graphics library)。该组件直接控制显示硬件,主要提供显示模式设置、标准输入输出控制、点线面作图等函数,编程接口很类似于Torbo C,Borland C里的图形库。Zinc基于WindML,提供了类Windows风格的接口,有各种控件被封装于内,编程接口很类似于windows编程。

2.WindML + Jworks:

Jworks 提供Java的支持,在VxWorks上启动Java 虚拟机来解释Java语言,图形开发编程接口是Java语言,Java虚拟机的图形实现基于WindML。

3.web server + WindML + Jworks + browser:

web server 使显示编程接口成为编写网页,可以通过远程的browser来访问而使设备具有远程显示接口,也可以在本机做browser使设备具有本机显示接口,该browser的实现需要WindML或Jworks的支持。

上述方案内,纯web server的方案不需要本地汉字显示,只要在网页上放国标码或unicode码等,由远程的browser去实现显示;其它方案基本上都需要WindML的支持,这是因为VxWorks系统里所有底层的显示操作都是通过WindML实现。

二.汉字显示方案

实现本地汉字显示的方案一定会涉及到WindML,只是不同的方案对WindML的依赖程度有所不同,一般有如下几种:

1.利用WindML对双字节编码的支持,实现对汉字的点阵存储、点阵获取、点阵显示的全过程,并使用WindML的双字节显示函数实现汉字码到汉字显示。这种方法使用了系统机制,是最根本的解决方法,并使使用WindML的其它上层组件很方便的实现汉字显示。

2.自己开发点阵存储、点阵获取、点阵显示,改造WindML的双字节或单字节显示函数,使其能判断汉字码,一旦判断出汉字码,则使用自己开发的点阵获取、点阵显示等把汉字显示出来。该方法对点阵的操作更加灵活,适合非标准的点阵算法,或者当开发者已有成熟的点阵操作方法时,把该方法绑接到WindML上。

3.完全自己开发一套点阵存储、点阵获取、点阵显示、汉字显示函数,使用者使用特定的汉字显示函数把汉字显示出来。该方法具有最大的灵活性,甚至不理睬WindML的任何机制,直接在上层组件里实现,但这种方法使程序的中英文混合显示变得复杂,程序可移植性也比较差。

本文将就第一种显示方案和第一种汉字显示方案详细论述WindML的双字节编码机制,并利用该机制构建WindML汉字显示框架,并论述Zinc如何使用该框架。这些机制和思路其实是任何方案都需要考虑的,对绕开WindML的方案也具有指导和借鉴意义。

三. WindML的点阵参数

先来讨论一下WindML点阵的各种参数:

1.每个字模都有一个占据的空间,该空间对于点阵字库里的每个字是一样大的,所有字符点阵的大小都不应该超过该空间,如果超过,显示时超出部分将被截掉。相关参数有:

maxAdvance:最大宽度(横向)大小,点为单位。

maxAscent,maxDescent:maxAscent + maxDescend 是最大长度(即纵向)大小,两数的交界决定了一个baseline,maxAscent是baseline以上的长度,maxDescent是baseline以下的部分。Baseline对于定位具体字符点阵的打点起始位置非常重要,另外如果有一行来自同一个字符集的字符串,则这些字符的baseline是在一条线上的。maxAscent + maxDescent 有时被称为字符表高度height,注意要与下面所述的字符高度区别开来。

2.每个字符点阵也有一个范围大小,该大小与每个字符有关,该大小一般不会填满整个字模空间。相关参数有:

width:宽度。

height:高度。

ascent:点阵打点开始位置在baseline之上的偏移,如果是负数,则点阵在baseline之下开始打点。

ascent和height决定了点阵从字模的多少行开始打点,要打多少行。而列的打点起始位置,固定是0,即字模的最左边,所以只有width描述打点的宽度。

3. 字符横向和纵向都要有额外空间,以防止字符粘接重叠。相关参数有:

leading:行间距。

4. 对字符的一些变换选项,字符读取的标志等。相关参数有:

pixelSize:平均行、列大小。

weight:行、列加粗属性。

italic:斜体属性。

spacing:字符行间距属性。

charSet:字符集标志。

faceName:字符集名称。

familyName:字符集的家族名称。

scalable:字符放大缩小比例。

WindML显示字符时,以maxAscent + maxDescend作为字符高度(不加leading),以width作为字符宽度(不以maxAdvance,不判断spacing),根据字符ascent和字符height取出字符点阵数据,进行weight、italic、scalable等运算(很多运算需要用户开发),然后输出到屏幕。

四.WindML的点阵存储结构和操作方法

WindML双字节编码显示的参数和实现思路集中体现在点阵存储文件的结构上,该文件的框架如下(这些文件在WIND_BASE/target/src/ugl/fonts/bmf目录下):

UGL_LOCAL const unsigned char UGL_FAR_DATA page0Data[]= 
{  …
/* 0x0023 ('#') */
0,  /* page*/
0x23, /*index*/
0, /*size (MSB)*/
20, /*size (LSB)*/
8, /*width*/
16, /*height*/
14, /*ascent*/
0x00,0x00,0x00,0x00,0x44,0x44,0x44,0xfe,0x44,0x44,0x44,0xfe,0x44,0x44,0x44,0x00, /*data*/
/*0x0024 and other characters*/
  …
  …
  /*end*/
  0,0,0,0
 }/*西文扩展为双字节编码的点阵表*/
 /*上表解析:
 page + index 就是该字符的ASCII编码,不过扩为了双字节;
 size (MSB) + size (LSB) 是该字符的点阵信息大小,注意实际描述体的长度是2(2字节page,index)+ 该大小 + 1 (从0开始编大小,所以从1数要加1),而ascent之后的真正点阵数据的大小是 该大小减4 ;
 width + height 是该点阵资料的宽度和长度;
 ascent是点阵处在baseline以上的偏移位置,baseline的位置要看整个字符表描述结构的定义;
 data 是按行扫描得到的点阵资料,是列递增把行显示效果排成一个连续空间后,按字节来描述每位的打点状态;如果行宽不是8位的整数倍,不会把位补0去凑8位整数倍。
   字符点阵描述数据依次往下排列,直到以4个0标示结束。
 */

UGL_LOCAL const unsigned char UGL_FAR_DATA page1Data[]=
{ 0x81, /*page*/
0x40, /*index*/
0, /*size*/
36, /*size*/
16, /*width*/
16, /*height*/
14, /*ascent*/
0x00, 0x08, 0xff, 0xfc, 0x04, 0x00, 0x04, 0x00, 0x08, 0x00, 0x08, 0x10, 0x1f, 0xf8, 0x00, 0x10, 
0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x02, 0x20, 0x01, 0x40, 0x00, 0x80,
/*other characters*/
… …
/*end*/
0,0,0,0 
 }/*国标码对应的点阵表*/
 /*上表解析:
 数据含义与西文表类似,注意字码是双字节国标码
 */
UGL_LOCAL const unsigned char * const pageArray[] =
    {
     page0Data,
     page1Data,
     UGL_NULL
    };/*总的点阵资料表*/
 /*上表解析:
 把分类的点阵资料表汇集成一张表,总表以UGL_NULL结束,注意各个分表是以0,0,0,0结束;
 可以把汉字表按GB2312编码的page分类,构建多个汉字分表,然后把分表指针填入该表,当然也可以做成一个汉字大表,把指针放在该表,甚至也可以把中西文点阵做成中西文混合大表,然后把该表指针放在该数组里。
 */
const UGL_BMF_FONT_DESC uglBMFFont_Song_16 =
    {
    /* UGL_FONT_DESC structure */
{      { 16, 16},                                     /* pixelSize */
     {UGL_FONT_BOLD_OFF, UGL_FONT_BOLD_OFF }, /* weight */
     UGL_FONT_UPRIGHT,                               /* italic */
     UGL_FONT_PROPORTIONAL,                          /* spacing */
     UGL_FONT_UNICODE,                           /* char set */
     "Song 16x16 Dot",                         /* face name */
     "Song"                                      /* family name */
    },/*点阵名称等点阵属性表*/
    /* UGL_BMF_FONT_DESC structure */
 /*点阵表的属性和位置*/
       2,                     /* leading */
       14,                     /* maxAscent */
       2,                     /* maxDescent */
       14,                     /* maxAdvance */
     pageArray                /* glyph pages */
  
};/*点阵描述结构*/
/*上表解析:
 首先是定义属性表,然后是定义字模和间距,并定义总的点阵描述表指针。
*/

最后定义的const UGL_BMF_FONT_DESC uglBMFFont_Song_16 包含了该点阵的所有信息。只要能定位住该结构,则任何能在该点阵表内匹配出字码的字都可以显示出来。WindML选择不同的点阵就是选择不同的该结构去做点阵寻址,其实现的大体过程如下:

1.

WIND_BASE/target/src/ugl/config/uglBmfCfg.c文件描述了整个系统能使用的字符集,如:

extern const UGL_BMF_FONT_DESC uglBMFFont_Courier_12;
extern const uglBMFFont_Song_16; /*就是上面的例子*/
const UGL_BMF_FONT_DESC * uglBMFFontData[] =
    {
    &uglBMFFont_Courier_12,
    &uglBMFFont_Song_16, 
    NULL
    };
2.

通过如uglDriverFind (UGL_FONT_ENGINE_TYPE, 0, (UGL_UINT32 *)&fontDrvId);的调用定位点阵驱动,即控制对点阵表访问、打点等操作的函数;

通过如uglFontFindString(fontDrvId, "familyName=Song; pixelSize = 16", &systemFontDef);的调用定位点阵描述结构;

通过如fontSystem = uglFontCreate(fontDrvId, &systemFontDef));的调用绑接点阵和点阵驱动,并标识为fontSystem;

如此重复创建多个字符集的点阵标识。

3.

通过如uglFontSet(gc, fontSystem);的调用设置当前字符集;

通过如uglTextDrawW(gc, iX, iY, iLength, caString);的调用在iX, iY的位置显示caString里的字码。

可见,只要开发者根据上述的存储结构开发出对应的字符点阵,然后把点阵描述结构加入到uglBMFFontData,便可以使用系统的函数使用这些点阵了。在Dos/Windows、linux系统下有一些应用程序,可以把操作系统的点阵导出为C文件或数组,把这些导出的文件加以调整,就可以得到vxWorks系统支持的格式。

五.Zinc对汉字的支持

只要WindML的汉字支持做成功,则可以按如下方法使Zinc支持汉字:

WIND_BASE/target/src/zinc/generic/i_ugldsp.cpp 文件是WindML与Zinc的接口文件,在ZafScreenDisplay::ZafScreenDisplay 函数里使用上述的函数定义了Zinc要使用的字符集,把这些字符集改成汉字字符集,并且把Zinc配置成支持UNICODE的方式重新编译,则Zinc就自然支持汉字显示了。

六.注意事项和总结

在使用过程中,还要注意几个问题:

  • Tornado对汉字的编译,如果把汉字定义为如short ca[ ]={ '董' ,0x00} (把单个汉字定义在单引号),则编译生成的目标码是正序的国标码;如果定义为如”董”(双引号定义出的汉字字符串),则编译生成的目标码是反序的国标码。这样,就必须约定一种定义,以根据该约定产生的国标码来构建字符点阵表(需要改变表里面的字码顺序)。
  • 如果使用双引号定义汉字字符串,要注意该串数据是以一个字节0结束,使用uglTextDrawW要求是字为单位,所以,需要在该字符串结尾补一个0(”\000”即可)。
  • 有些字库表的字码是UNICODE的编码,尤其是使用一些操作系统的点阵导出程序生成的字库表,UNICODE编码与国标码有一一对应关系,可以把这样的字库表转换过来或者在程序里适当位置把要显示的内容转为UNICODE再显示。

本文就嵌入式操作系统上使用汉字这一热门论题,论述了VxWorks上的图形实现方案、汉字开发方案,图形组件WindML对多字节码的支持特性,并在论述特性的同时,列举了宋体16点阵的框架,帮助汉字开发者理解点阵实现的细节;在论述WindML汉字操作函数之后,提出了在Zinc层实现汉字显示的方法和编程时的注意事项。这套方案已经成功的实现,并在上海大众的导航产品、南京电力的电力控制产品等项目上获得成功的应用。

参考文献

  1. WindRiver System, Inc . VxWorks Programmer’s Guide.
  2. WindRiver System, Inc . WindML Programmer’s Guide.
  3. WindRiver System, Inc . Zinc Programmer’s Guide.
  4. 《常用计算机汉字系统及汉字输入方法》,殷光复,中国水利水电出版社,1998