王小亮,王勇,湯永科 ,秦磊
(1. 四川大學(xué) 電子信息學(xué)院, 成都 610044; 2. 四川大學(xué) 電氣信息學(xué)院)
?
Windows矢量字體點(diǎn)陣數(shù)據(jù)提取的實(shí)現(xiàn)方法
王小亮1,王勇1,湯永科1,秦磊2
(1. 四川大學(xué) 電子信息學(xué)院, 成都 610044; 2. 四川大學(xué) 電氣信息學(xué)院)
字符顯示在嵌入式系統(tǒng)有著廣泛的應(yīng)用,尤其是在沒有操作系統(tǒng)的嵌入式設(shè)計(jì)中,提取字符點(diǎn)陣數(shù)據(jù)已經(jīng)成為嵌入式設(shè)計(jì)中的關(guān)鍵一步。相比于一般通過位圖方式來獲取字符點(diǎn)陣數(shù)據(jù),本文介紹了如何利用Windows操作系統(tǒng)提供API函數(shù)提取矢量字體點(diǎn)陣數(shù)據(jù)的方法,程序可以方便快速提取Windows系統(tǒng)所安裝的所有矢量字體的點(diǎn)陣數(shù)據(jù)。程序界面簡(jiǎn)潔美觀,功能強(qiáng)大,實(shí)用性強(qiáng)。
嵌入式系統(tǒng);矢量字體;點(diǎn)陣;快速;API
隨著顯示技術(shù)日新月異的發(fā)展,各種性能的顯示器越來越廣泛地運(yùn)用于嵌入式產(chǎn)品中。用戶界面友好已經(jīng)成為一個(gè)產(chǎn)品能否獲得成功至關(guān)重要的因素。在Windows 操作系統(tǒng)中,系統(tǒng)本身為用戶提供了各種豐富的矢量字體,同時(shí)用戶也可以通過簡(jiǎn)單地安裝相應(yīng)的字體文件,然后在編輯軟件中點(diǎn)擊相應(yīng)的按鍵就可以使用各種矢量字體。因?yàn)镻C系統(tǒng)中,可以非常方便和快捷地獲取各種矢量字體,所以在嵌入式系統(tǒng)開發(fā)設(shè)計(jì)中,我們會(huì)思考如何利用Windows系統(tǒng)所提供的豐富的矢量字體來獲取所需要的點(diǎn)陣數(shù)據(jù)。本文利用Windows 提供的API函數(shù),設(shè)計(jì)實(shí)現(xiàn)了矢量字體字模提取的工具軟件。
在紙上寫字時(shí),我們需要在紙上把字符的所有的點(diǎn)都畫出來,這樣就完成了字符的書寫。計(jì)算機(jī)顯示字符也是通過把字符輪廓中相應(yīng)的點(diǎn)陣數(shù)據(jù)顯示在顯示器上,從而實(shí)現(xiàn)計(jì)算機(jī)“書寫”功能。計(jì)算機(jī)所顯示的字符點(diǎn)陣數(shù)據(jù)是通過一定的方式預(yù)先儲(chǔ)存在計(jì)算機(jī)系統(tǒng)的文件中,當(dāng)操作系統(tǒng)需要顯示字符時(shí),通過相應(yīng)程序去查找對(duì)應(yīng)的點(diǎn)陣數(shù)據(jù),然后達(dá)到顯示字符的效果。
矢量字體(vector font)中每一個(gè)字形是通過數(shù)學(xué)曲線來描述的,它包含了字形邊界上的關(guān)鍵點(diǎn),連線的導(dǎo)數(shù)信息等,字體的渲染引擎通過讀取這些數(shù)學(xué)矢量,然后進(jìn)行一定的數(shù)學(xué)運(yùn)算來進(jìn)行渲染。這類字體的優(yōu)點(diǎn)是,字體實(shí)際尺寸可以任意縮放而不變形、變色。矢量字體主要包括Type1、 TrueType、OpenType等幾類,這些格式都是與平臺(tái)無關(guān)的。由于矢量字體具有以上各種優(yōu)勢(shì),所以在嵌入式產(chǎn)品的人機(jī)界面設(shè)計(jì)中有著廣泛的應(yīng)用。
在Windows 系統(tǒng)當(dāng)中提取矢量字體的字模,一般有兩種方法。
一種方法,通過截取字體在當(dāng)前Windows系統(tǒng)DC設(shè)備當(dāng)中的位圖,然后根據(jù)位圖的像素元素提取相應(yīng)的點(diǎn)陣數(shù)據(jù)。當(dāng)字符數(shù)目比較多的時(shí)候,這種方法的缺點(diǎn)是提取速度慢,需要通過程序處理大量的圖片像素?cái)?shù)據(jù)來獲取字符點(diǎn)陣。這種方法無論是從時(shí)間、空間還是內(nèi)存空間來看,對(duì)系統(tǒng)的消耗都是非常大的。同時(shí),如果用戶輸入的字符數(shù)量非常大時(shí),軟件界面需要提供比較大的輸入?yún)^(qū)以方便用戶輸入,所以這種提取方式對(duì)于程序輸入界面有比較高的要求,且不易支持直接從文本文件直接輸入字符。
另一種方法是利用Windows系統(tǒng)的API函數(shù)GetGlyphOutline,該函數(shù)可以方便快捷地提取矢量字體字符點(diǎn)陣數(shù)據(jù),并且可以很好地支持從文本文件中讀取字符。面對(duì)大量字符數(shù)據(jù)輸入時(shí),獲取點(diǎn)陣數(shù)據(jù)所需要的時(shí)間量也很少。GetGlyphOutline函數(shù)聲明如下:
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函數(shù)是Windows系統(tǒng)的API函數(shù),在使用VC++開發(fā)時(shí),這個(gè)函數(shù)被封裝在DC類中,是DC類的一個(gè)成員函數(shù)。
當(dāng)應(yīng)用程序調(diào)用GetGlyphOutline 函數(shù)時(shí),該函數(shù)可以通過LPGLYPHMETRIC結(jié)構(gòu)體指針返回所需的字符點(diǎn)陣數(shù)據(jù)所占的矩形區(qū)域信息。該函數(shù)所得到的點(diǎn)陣數(shù)據(jù)是gmBlackBoxX與gmBlackBoxY所組成的最小矩形區(qū)域的點(diǎn)陣數(shù)據(jù),如圖1所示。實(shí)際應(yīng)用中,所需要顯示的字符點(diǎn)陣數(shù)據(jù)卻是gmCellIncX與gmCellIncY所組成的大矩形區(qū)域內(nèi)的點(diǎn)陣數(shù)據(jù),所以調(diào)用GetGlyphOutline所得到的字符的點(diǎn)陣數(shù)據(jù)時(shí),還需要把最小矩形以外的邊框區(qū)域加上,這需要通過相應(yīng)的矩陣變換把最小矩形的點(diǎn)陣數(shù)據(jù)區(qū)平移到以gmCellIncX與gmCellIncY所組成的大矩形區(qū)域的中間位置。

圖 1
在Windows 操作系統(tǒng)當(dāng)中實(shí)踐表明,GetGlyphOutlinep這個(gè)函數(shù)返回的結(jié)構(gòu)體LPGLYPHMETRICS中的gmCellIncY這個(gè)數(shù)值返回是0,這是操作系統(tǒng)版本本身的原因,因此需要通過另外的方法來獲取。我們采用GetTextExtent(CString,int)和GetTextMetrics(TEXTMETRIC *tm),通過以上兩個(gè)函數(shù)可以獲取字符的寬度與高度信息,然后通過相應(yīng)矩陣變換的變換,就可以得到所需的字符字模點(diǎn)陣數(shù)據(jù)。GetGlyphOutline 函數(shù)獲取的字符點(diǎn)陣數(shù)據(jù)的寬度是4字節(jié)對(duì)齊,所以要做4字節(jié)對(duì)齊處理。對(duì)于寬度不是以8位對(duì)齊的字符數(shù)據(jù),應(yīng)該在補(bǔ)足8位后,再做4字節(jié)對(duì)齊處理。獲取字符點(diǎn)陣數(shù)據(jù)的程序如下:
CString str ( “華”); //字符
CDC dc; //CDC 類,有GetGlyphOutline方法
dc.CreateDC(_T("DISPLAY"), NULL, NULL, NULL);
CFont *poldfont=dc.SelectObject(&m_font); //字體設(shè)置
TEXTMETRIC tm;
//這個(gè)結(jié)構(gòu)體包含了字體的信息
GLYPHMETRICS pGL;
//這個(gè)結(jié)構(gòu)體包含了一個(gè)基本字符單元的位置與方向的信息
MAT2 mat2 = {{0, 1},{0, 0},{0, 0},{0, 1}}; //轉(zhuǎn)換矩陣
dc.GetTextMetrics(&tm);
//獲取當(dāng)前選擇字體寬度與高度
int bitWidth =tm.tmAveCharWidth;
//字符寬度的平均值
int bitHeigh = tm.tmHeight; //字符高度
int ch = str.GetAt(0);
int len =dc.GetGlyphOutline(ch, GGO_BITMAP, &pGL, 0, NULL, &mat2);
//所得到數(shù)據(jù)緩存區(qū)的大小
CSize cs = pDC->GetTextExtent(str,1);
//重新獲得字符的寬度,修正值
int widthEx = cs.cx;
bitWidth = widthEx;
if(bitWidth %8 ==0){
bitWidth = bitWidth /8;
//字符寬度8位對(duì)齊,不足補(bǔ)齊8位
}
else{
bitWidth = bitWidth /8+1;
}
int boxXByteWidth = ALIGN(pGL.gmBlackBoxX, 4);
//最小矩形寬度,4字節(jié)對(duì)齊
int FontOffY = tm.tmAscent - pGL.gmptGlyphOrigin.y;
//獲取Y方向偏移
int FontOffX = pGL.gmptGlyphOrigin.x < 0 ? 0 : pGL.gmptGlyphOrigin.x; //獲取X方向偏移
int bufSize = bitWidth * bitHeigh; //字符點(diǎn)陣數(shù)據(jù)大小
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);
//得到點(diǎn)陣數(shù)據(jù)
for(int i = 0; i < len / boxXByteWidth; i++){
//copy databuf to pDest
memcpy(pDest + i * (bitWidth /8), pSrc + i * boxXByteWidth, boxXByteWidth);
}
//轉(zhuǎn)換矩陣,把GetGlyphOutline得到的點(diǎn)陣轉(zhuǎn)換成 //含有邊框的點(diǎn)陣數(shù)據(jù)
MartixCovert(pDest,bitWidth, bitHeigh ,FontOffX, FontOffY);
}
}
dc.SelectObject(poldfont);
dc.DeleteDC();
delete []pSrc;
delete []pDest;

圖 3
通過上文分析已經(jīng)得到單個(gè)字符的點(diǎn)陣數(shù)據(jù),然而在實(shí)際應(yīng)用當(dāng)中,獲取單個(gè)字符的點(diǎn)陣數(shù)據(jù)是沒有實(shí)用性的,所以在上文的基礎(chǔ)上,利用VC++開發(fā)工具,開發(fā)了實(shí)用的字模點(diǎn)陣數(shù)據(jù)提取軟件。軟件支持兩種字符輸入方式,一是通過VC++中的CEdit類來接收用戶直接輸入的字符,二是利用CStdioFile類來實(shí)現(xiàn)文本文件的輸入;利用CString類對(duì)通過以上兩種方式輸入獲取字符進(jìn)行串化處理(用CString保存),同時(shí)利用CFont類實(shí)現(xiàn)對(duì)用戶對(duì)字體格式修改操作,然后對(duì)所得到的字符串做空格過濾處理;最后根據(jù)上文介紹的方法獲取字符串中每一個(gè)字符的點(diǎn)陣數(shù)據(jù)。對(duì)于獲取到的點(diǎn)陣數(shù)據(jù)作輸出格式化處理,為其加上相應(yīng)的數(shù)據(jù)信息頭,并用“{}”框起,結(jié)尾處加上“;”,以實(shí)現(xiàn)符合C語言中變量聲明方式,這樣在實(shí)際應(yīng)用當(dāng)中只需修改文本文件的后綴名,在文件中加上相關(guān)的變量聲明就可以在直接在實(shí)現(xiàn)工程中應(yīng)用。
為了把軟件開發(fā)成為通用性強(qiáng)的應(yīng)用軟件,本軟件在提取字符點(diǎn)陣數(shù)據(jù)的基礎(chǔ)上增加了提取圖像像素?cái)?shù)點(diǎn)陣的功能。這個(gè)功能是以圖像文件操作為基礎(chǔ),可以支持PNG、BMP、JPEG等圖像文件輸入,提供用戶創(chuàng)建圖像文件的功能,同時(shí)支持用戶的繪圖操作。軟件總體運(yùn)行流程如圖2所示,軟件運(yùn)行界面如圖3所示。

圖 2
為了測(cè)試軟件功能,在開發(fā)過程加入了測(cè)試驗(yàn)證環(huán)節(jié)。啟動(dòng)軟件后,點(diǎn)擊字體設(shè)置,設(shè)置字符格式為華文行楷,字符大小為72磅,在字符輸入?yún)^(qū)輸入“華文行楷”4個(gè)漢字,點(diǎn)擊生成字模,在輸出區(qū)得到了這4個(gè)漢字的點(diǎn)陣字模數(shù)據(jù)。然后點(diǎn)擊生成文件,把所得到的字模點(diǎn)陣數(shù)據(jù)另存為文本文件。打開所得到的文本文件,在文件中加上變量的聲明,修改文本文件名的后綴名(.txt)為C語言的頭文件格式(.h)。在集成開發(fā)環(huán)境加添加所得到的變量頭文件,編譯通過后下載到硬件平臺(tái)。在測(cè)試中采用的硬件平臺(tái)是天嵌科技公司推出的TQ2440ARM9開發(fā)板,通過其提供的測(cè)試程序加以修改,把軟件提取得到的字符點(diǎn)陣數(shù)據(jù)按行掃描顯示,測(cè)試程序主要代碼實(shí)現(xiàn)如下:
void PointDataTest(
unsigned int x_p, //字符顯示的x坐標(biāo)
unsigned int y_p, //字符顯示的y坐標(biāo)
unsigned int width, //字符的寬度
unsigned int hight, //字符的高度
unsigned char *pData){ //指向點(diǎn)陣數(shù)據(jù)區(qū)的指針
int x = 0;
int y = 0;
int i = 0;
for(y = y_p; y for(x = x_p; 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); //畫點(diǎn)函數(shù) } } } } } 測(cè)試結(jié)果如圖4所示。通過測(cè)試結(jié)果顯示的字符與開發(fā)軟件的字符輸入?yún)^(qū)輸入的字符對(duì)比,可以看出軟件功能滿足設(shè)計(jì)要求。 圖 4 [1] Stephen Prata. C++ Primer Plus[M]. 北京:人民郵電出版社, 2012:564-623. [2] Petxold. Programming Windows[M]. 北京:清華大學(xué)出版社, 2010:769-838. [3] Jeff Prosise. MFC Windows 程序設(shè)計(jì)[M]. 北京:清華大學(xué)出版社, 1999:757-820. [4] 孫鑫. VC++深入詳解[M]. 北京:電子工業(yè)出版社, 2006:135-161. [5] 侯俊杰. 深入淺出MFC[M]. 武漢:華中科技大學(xué)出版社, 1996:339-460. [6] 廣州天嵌計(jì)算機(jī)科技有限公司. TQ2440開發(fā)板使用手冊(cè),2006. [7] 王赤. Windows矢量字體字模的提取[J]. 微型機(jī)與應(yīng)用, 2004(8). [8] 陳曉明. 利用VC++ 實(shí)現(xiàn)漢字與位圖點(diǎn)陣數(shù)據(jù)的提取[J]. 現(xiàn)代電子技術(shù), 2006(9). [9] 王保華. 利用VC++ 實(shí)現(xiàn)漢字字模的提取與小漢字庫的生成[J]. 單片機(jī)與嵌入式系統(tǒng)應(yīng)用, 2002(5). 王小亮,(碩士研究生),研究方向?yàn)橹悄芟到y(tǒng)與設(shè)計(jì);王勇,(副教授),研究方向?yàn)榧呻娐吩O(shè)計(jì)。 Withdrawing Lattice of Windows Vector Fonts Wang Xiaoliang1,Wang Yong1,Tang Yongke1,Qin Lei2 (1.College of Electronics and Information Engineering, Sichuan University, Chengdu 610044,China;2.College of Electrical Engineering and Information, Sichuan University) Character display is widely used in embedded systems, especially for the embedded systems design without OS, getting lattice of character has played a crucial role in embedded design. Comparing with the general technique to get lattice of vector font through bitmap, this paper introduces a new method that can easily and quickly get lattice of any vector font installed in Windows system by using API of Windows OS. The program interface is simple and beautiful, and the function of the program is powerful and practical. embedded systems; vector font; lattice; speed; API TP399 A 珍 2013-12-17)
結(jié) 語
