引言
随着显示技术日新月异的发展,各种性能的显示器越来越广泛地运用于嵌入式产品中。用户界面友好已经成为一个产品能否获得成功至关重要的因素。在Windows 操作系统中,系统本身为用户提供了各种丰富的矢量字体,同时用户也可以通过简单地安装相应的字体文件,然后在编辑软件中点击相应的按键就可以使用各种矢量字体。因为PC系统中,可以非常方便和快捷地获取各种矢量字体,所以在嵌入式系统开发设计中,我们会思考如何利用Windows系统所提供的丰富的矢量字体来获取所需要的点阵数据。本文利用Windows 提供的API函数,设计实现了矢量字体字模提取的工具软件。
1 矢量字体
在纸上写字时,我们需要在纸上把字符的所有的点都画出来,这样就完成了字符的书写。计算机显示字符也是通过把字符轮廓中相应的点阵数据显示在显示器上,从而实现计算机“书写”功能。计算机所显示的字符点阵数据是通过一定的方式预先储存在计算机系统的文件中,当操作系统需要显示字符时,通过相应程序去查找对应的点阵数据,然后达到显示字符的效果。
矢量字体(vector font)中每一个字形是通过数学曲线来描述的,它包含了字形边界上的关键点,连线的导数信息等,字体的渲染引擎通过读取这些数学矢量,然后进行一定的数学运算来进行渲染。这类字体的优点是,字体实际尺寸可以任意缩放而不变形、变色。矢量字体主要包括Type1、 TrueType、OpenType等几类,这些格式都是与平台无关的。由于矢量字体具有以上各种优势,所以在嵌入式产品的人机界面设计中有着广泛的应用。
2 提取原理
在Windows 系统当中提取矢量字体的字模,一般有两种方法。
一种方法,通过截取字体在当前Windows系统DC设备当中的位图,然后根据位图的像素元素提取相应的点阵数据。当字符数目比较多的时候,这种方法的缺点是提取速度慢,需要通过程序处理大量的图片像素数据来获取字符点阵。这种方法无论是从时间、空间还是内存空间来看,对系统的消耗都是非常大的。同时,如果用户输入的字符数量非常大时,软件界面需要提供比较大的输入区以方便用户输入,所以这种提取方式对于程序输入界面有比较高的要求,且不易支持直接从文本文件直接输入字符。
另一种方法是利用Windows系统的API函数GetGlyphOutline,该函数可以方便快捷地提取矢量字体字符点阵数据,并且可以很好地支持从文本文件中读取字符。面对大量字符数据输入时,获取点阵数据所需要的时间量也很少。GetGlyphOutline函数声明如下:
DWORD GetGlyphOutline(
HDC hdc,// handler to DC
UINT uChar,// character to query
UINT uFormat,// data format
LPGLYPHMETRICS lpgm, // glyph metrics
DWORD cbBuffer,// size of data buffer
LPVOID lpvBuffer,// data buffer
CONST MAT2 *lpmat2 // transformation matrix
);
GetGlyphOutline函数是Windows系统的API函数,在使用VC++开发时,这个函数被封装在DC类中,是DC类的一个成员函数。
3 字符点阵数据提取的实现
当应用程序调用GetGlyphOutline 函数时,该函数可以通过LPGLYPHMETRIC结构体指针返回所需的字符点阵数据所占的矩形区域信息。该函数所得到的点阵数据是gmBlackBoxX与gmBlackBoxY所组成的最小矩形区域的点阵数据,如图1所示。实际应用中,所需要显示的字符点阵数据却是gmCellIncX与gmCellIncY所组成的大矩形区域内的点阵数据,所以调用GetGlyphOutline所得到的字符的点阵数据时,还需要把最小矩形以外的边框区域加上,这需要通过相应的矩阵变换把最小矩形的点阵数据区平移到以gmCellIncX与gmCellIncY所组成的大矩形区域的中间位置。
图1
在Windows 操作系统当中实践表明,GetGlyphOutlinep这个函数返回的结构体LPGLYPHMETRICS中的gmCellIncY这个数值返回是0,这是操作系统版本本身的原因,因此需要通过另外的方法来获取。我们采用GetTextExtent(CString,int)和GetTextMetrics(TEXTMETRIC *tm),通过以上两个函数可以获取字符的宽度与高度信息,然后通过相应矩阵变换的变换,就可以得到所需的字符字模点阵数据。GetGlyphOutline 函数获取的字符点阵数据的宽度是4字节对齐,所以要做4字节对齐处理。对于宽度不是以8位对齐的字符数据,应该在补足8位后,再做4字节对齐处理。获取字符点阵数据的程序如下:
CString str ( “华”);//字符
CDC dc;//CDC 类,有GetGlyphOutline方法
dc.CreateDC(_T("DISPLAY"), NULL, NULL, NULL);
CFont *poldfont=dc.SelectObject(&m_font);//字体设置
TEXTMETRIC tm;//这个结构体包含了字体的信息
GLYPHMETRICS pGL;//这个结构体包含了一个基本字符单元的位置与方向的信息
MAT2 mat2 = {{0, 1},{0, 0},{0, 0},{0, 1}};//转换矩阵
dc.GetTextMetrics(&tm);//获取当前选择字体宽度与高度
int bitWidth =tm.tmAveCharWidth;//字符宽度的平均值
int bitHeigh = tm.tmHeight;//字符高度
int ch = str.GetAt(0);
int len =dc.GetGlyphOutline(ch, GGO_BITMAP, &pGL, 0, NULL, &mat2);//所得到数据缓存区的大小
CSize cs = pDC->GetTextExtent(str,1);//重新获得字符的宽度,修正值
int widthEx = cs.cx;
bitWidth = widthEx;
if(bitWidth %8 ==0){
bitWidth = bitWidth /8; //字符宽度8位对齐,不足补齐8位
}
else{
bitWidth = bitWidth /8+1;
}
int boxXByteWidth = ALIGN(pGL.gmBlackBoxX, 4);//最小矩形宽度,4字节对齐
int FontOffY = tm.tmAscent - pGL.gmptGlyphOrigin.y;//获取Y方向偏移
int FontOffX = pGL.gmptGlyphOrigin.x < 0 ? 0 : pGL.gmptGlyphOrigin.x;//获取X方向偏移
int bufSize = bitWidth * bitHeigh;//字符点阵数据大小
unsigned char *pBuf = new unsigned char[bufSize];//databuf
if(pBuf != NULL){
memset(pBuf, 0, bufSize);
if(len > 0){
unsigned char *pSrc = new unsigned char [len];
unsigned char *pDest = (unsigned char *)pBuf;
dc.GetGlyphOutline(ch, GGO_BITMAP, &pGL, len, pSrc, &mat2);//得到点阵数据
for(int i = 0; i < len / boxXByteWidth; i++){//copy databuf to pDest
memcpy(pDest + i * (bitWidth /8), pSrc + i * boxXByteWidth, boxXByteWidth);
}//转换矩阵,把GetGlyphOutline得到的点阵转换成含有边框的点阵数据
MartixCovert(pDest,bitWidth, bitHeigh ,FontOffX, FontOffY);
}
}
dc.SelectObject(poldfont);
dc.DeleteDC();
delete []pSrc;
delete []pDest;
4 软件总体设计
通过上文分析已经得到单个字符的点阵数据,然而在实际应用当中,获取单个字符的点阵数据是没有实用性的,所以在上文的基础上,利用VC++开发工具,开发了实用的字模点阵数据提取软件。软件支持两种字符输入方式,一是通过VC++中的CEdit类来接收用户直接输入的字符,二是利用CStdioFile类来实现文本文件的输入;利用CString类对通过以上两种方式输入获取字符进行串化处理(用CString保存),同时利用CFont类实现对用户对字体格式修改操作,然后对所得到的字符串做空格过滤处理;最后根据上文介绍的方法获取字符串中每一个字符的点阵数据。对于获取到的点阵数据作输出格式化处理,为其加上相应的数据信息头,并用“{}”框起,结尾处加上“;”,以实现符合C语言中变量声明方式,这样在实际应用当中只需修改文本文件的后缀名,在文件中加上相关的变量声明就可以在直接在实现工程中应用。
为了把软件开发成为通用性强的应用软件,本软件在提取字符点阵数据的基础上增加了提取图像像素数点阵的功能。这个功能是以图像文件操作为基础,可以支持PNG、BMP、JPEG等图像文件输入,提供用户创建图像文件的功能,同时支持用户的绘图操作。软件总体运行流程如图2所示,软件运行界面如图3所示。
图2
图3
5 验证与测试
为了测试软件功能,在开发过程加入了测试验证环节。启动软件后,点击字体设置,设置字符格式为华文行楷,字符大小为72磅,在字符输入区输入“华文行楷”4个汉字,点击生成字模,在输出区得到了这4个汉字的点阵字模数据。然后点击生成文件,把所得到的字模点阵数据另存为文本文件。打开所得到的文本文件,在文件中加上变量的声明,修改文本文件名的后缀名(.txt)为C语言的头文件格式(.h)。在集成开发环境加添加所得到的变量头文件,编译通过后下载到硬件平台。在测试中采用的硬件平台是天嵌科技公司推出的TQ2440ARM9开发板,通过其提供的测试程序加以修改,把软件提取得到的字符点阵数据按行扫描显示,测试程序主要代码实现如下:
void PointDataTest(
unsigned int x_p,//字符显示的x坐标
unsigned int y_p, //字符显示的y坐标
unsigned int width,//字符的宽度
unsigned int hight, //字符的高度
unsigned char *pData){//指向点阵数据区的指针
int x = 0;
int y = 0;
int i = 0;
for(y = y_p; y<y_p+hight; y++){
for(x = x_p; x<x_p+width/8; x++){
for(i=0; i<8; i++){
if(pData[(y-y_p)*(width/8)+x-x_p] & (0x80>>i)){
PutPixel(x_p+(x-x_p)*8+i, y, 0x00ff00);//画点函数
}
}
}
}
}
测试结果如图4所示。通过测试结果显示的字符与开发软件的字符输入区输入的字符对比,可以看出软件功能满足设计要求。
图4
结语
利用VC++IDE工具开发了在Windows 系统下,提取矢量字体字符的点阵数据的软件。软件界面美观大方,运行流畅稳定,操作直观简单,为嵌入式开发人员提供了强有力的设计工具,为开发人员节省开发周期,提高开发效率。实践结果表明,本软件非常适合嵌入式开发设计,是一款功能强大的工具软件。