葛炳侖GE Bing-lun
(西交利物浦大學(xué),蘇州 215123)
隨著當(dāng)代計算機技術(shù)和漢語言文字處理技術(shù)的不斷發(fā)展和進步,人們對于漢字的輸入和顯示逐漸有了更多新的追求。例如,隨著屏幕制造技術(shù)的進步,嵌入式設(shè)備的屏幕尺寸和顯示性能已經(jīng)有了較為明顯的進步。傳統(tǒng)的單色點陣字體已經(jīng)無法滿足當(dāng)今時代的需求。同樣地,對于漢語言信息處理,傳統(tǒng)的GB-2312 字庫很難顯示一些人名和地名中的生僻字,如“趙孟”、“崗站”等。據(jù)不完全統(tǒng)計,全國有超過6 000 萬人的名字中包含了生僻字。[1]這就需要有相應(yīng)的Unicode 字庫的支持。目前市面上的絕大多數(shù)Unicode 字庫都采用了TrueType 或OpenType 矢量格式。雖然可以實現(xiàn)任意尺寸的抗鋸齒渲染,但占用空間大,所需的算力較高,不適用于存儲空間和內(nèi)存空間受局限的嵌入式設(shè)備。同樣地,相應(yīng)字體轉(zhuǎn)換來的位圖字體也需要大量的存儲空間,且需要為每個尺寸生成單獨的字體文件,占用了太多不必要的空間。因此,有必要引入動態(tài)組字技術(shù)實現(xiàn)更低的存儲空間占用及運算時間消耗。
位圖字體是歷史最為悠久的計算機字體,也被稱作光柵字體或點陣字體。它的結(jié)構(gòu)簡單、顯示快速,因此也是在嵌入式系統(tǒng)中所使用最多的字體格式。然而,它的缺陷也比較明顯。首先,對于不同尺寸的漢字,需要分別為其準(zhǔn)備字體文件。也就是說,將造成極大的空間浪費。其次,對于抗鋸齒字體,它需要更多的空間占用。例如,如果抗鋸齒需要16 個灰度,則其所占用的存儲空間是黑白字體的4 倍。但是對于其他的矢量格式,則不需要額外的存儲空間。
矢量字體又稱輪廓字體,這是目前應(yīng)用最廣的漢字顯示技術(shù),它存儲了每個漢字的圖像矢量輪廓。因此,它相較于位圖字體有更多的優(yōu)點。例如,矢量字體不需要為每個不同尺寸的字形單獨占用存儲空間,只需要儲存一次就可以繪制出不同尺寸的中文字形。此外,它也不需要更多的存儲空間就能實現(xiàn)抗鋸齒渲染。但是,盡管有一些針對于嵌入式設(shè)備的優(yōu)化[2],但矢量字體仍然需要大量的存儲空間占用。這是因為對于漢字,矢量字體需要冗余地存儲不必要的襯線等部分,這占用了大量的存儲空間。因此,矢量字體對于嵌入式漢字字形的顯示并不是最佳方案。
動態(tài)組字技術(shù)在中文信息處理發(fā)展史上有著很長的歷史。在1985 年,朱邦復(fù)等人就曾對此進行過研究[3]。朱邦復(fù)的動態(tài)組字方案基于倉頡輸入法,利用對漢字部件位圖的變換實現(xiàn)對位圖中文字體的動態(tài)生成。但是由于他的系統(tǒng)完全由8080 匯編編寫,移植性很差,并且只支持繁體中文的生成,目前已經(jīng)退出了市場。此外,在日本,大東文化大學(xué)的上地宏一等人開發(fā)的“影KAGE”漢字字體自動生成系統(tǒng)[4]、東京大學(xué)的田中哲朗等人開發(fā)的“和田研”部件合成的漢字骨架字體生成系統(tǒng)(見圖1)[5]等也對動態(tài)組字技術(shù)作出了嘗試。由于其使用了LISP 語言進行迭代生成,因此對于內(nèi)存空間的占用較大,并不十分適用于嵌入式系統(tǒng)。此外,這兩個系統(tǒng)也只能生成單一風(fēng)格的字體,局限性較高。近年來,隨著深度學(xué)習(xí)技術(shù)的發(fā)展,可以利用它生成高質(zhì)量的不同風(fēng)格的動態(tài)組字字體。但是由于其需要大量的算力,無法在嵌入式設(shè)備上運行。
1999 年9 月,Unicode 發(fā)布3.0 版本,定義表意文字描述序列(Ideographic Description Sequence,縮寫為IDS)和表意文字描述符(Ideographic Description Characters,縮寫為IDC)。IDC 共12 描述符(U+2FF0 至U+2FFB),如表1所示。IDS 的語法比較簡單,只要求三元IDC(U+2FF2 和U+2FF3)后面必須有3 個IDS,二元IDC(U+2FF2 和U+2FF3 以外的其他IDC)后面必須有2 個IDS,IDS 中除了IDC 以外必須是一元CJK 字符。IDS 還有2 個長度限制:序列長度不可超過16 個Unicode 編碼;如果沒有表意文字描述符作為間隔,構(gòu)成序列的部件或者偏旁不能超過6個。IDS 支持遞歸算法,應(yīng)注意遞歸深度和逆向掃描長度[6]。
表1 IDC 編碼表
本系統(tǒng)將字形存儲部分分為兩部分,分別是IDS 字形描述數(shù)據(jù)庫以及部首筆畫結(jié)構(gòu)數(shù)據(jù)庫。
由于本系統(tǒng)需要考慮在嵌入式設(shè)備上運行,因此需要優(yōu)化時間和空間效率。該數(shù)據(jù)庫是一系列二進制文件,分為索引頭和IDS 數(shù)據(jù)兩部分。
以中日韓統(tǒng)一表意文字?jǐn)U展區(qū)A 區(qū)的漢字為例,索引頭的結(jié)構(gòu)為:
<uint32:Index><uint8:Length>
如果要讀取“打匯”的IDS 數(shù)據(jù),則程序?qū)⒆x取uint32 ExtA['打匯'*5] 開始的uint8 ExtA['打匯'*5+1]個字節(jié)的數(shù)據(jù)。
類似地,系統(tǒng)以類似IDS 字形描述數(shù)據(jù)庫的結(jié)構(gòu)存儲部首筆畫結(jié)構(gòu)數(shù)據(jù)庫中的數(shù)據(jù)。為了節(jié)約空間占用,部首筆畫數(shù)據(jù)庫以二進制格式存儲。它的BNF 形式描述為:
radical::=<width><height>
<variantsNumber>{<variant>}
<componentsNumber>{<components>}
variant::=<position><codePoint>
components::=<type>(
<dataHanzi>|
<dataLine>|
<dataCurve>
)
dataHanzi::=<codePoint><xyInit><xyFinl>
dataLine::=<xyInit><xyFinl>
dataCurve::=<xyInit><xyMedi><xyFinl>
codePoint::=<uint8><uint8><uint8>
<width>::=<uint8>
<height>::=<uint8>
<variantsNumber>::=<uint8>
<componentsNumber>::=<uint8>
<position>:==<x><y>
<xyInit>:==<x><y>
<xyMedi>:==<x><y>
<xyFinl>:==<x><y>
<x>::=<uint8>
<y>::=<uint8>
<type>::=<uint16>
個漢字筆畫大致可分為兩部分,骨架和襯線。我們可以將每個漢字的骨架視為它的基本,而印刷字體比如宋體和仿宋體都有襯線附著于筆畫的骨架上以提高閱讀效率和可辨認(rèn)度。黑體可以視為一種只有骨架而沒有襯線的特殊字體。因此,本系統(tǒng)在繪制筆畫時將分別處理其筆畫和襯線。
漢字有四種基礎(chǔ)筆畫:橫、豎、撇、點。其中,這些基礎(chǔ)筆畫又可以分為主筆形和附筆形。折是一種特殊的筆畫類型,包含有很多種復(fù)合筆形。因此,在程序中主要處理這四種基礎(chǔ)筆畫及其復(fù)合筆形的繪制方法。
這是兩種最基礎(chǔ)的筆畫,可以視作是一條線段及其附屬襯線。系統(tǒng)將繪制一個矩形,并將襯線附加到矩形上。
我們可以應(yīng)用二次貝塞爾曲線來擬合這兩種筆畫的軌跡。
根據(jù)以下步驟,我們可以繪制出這兩種筆畫:
二次貝塞爾曲線的插值公式為:
其中:P0為起始點,P1為控制點,P2為終止點,t 為補償系數(shù),一般取0.2。
①利用該公式,我們可以計算出在該曲線上連續(xù)的點的水平座標(biāo)。
②接下來我們可以將每個點為(0,0),求它下一個點的極座標(biāo)(分別是第i 個點和第i+1 個點)。
③并在當(dāng)前點為中心,分別向x 軸的正方向和負(fù)方向平移Bold(i)的距離,得出點p1和p2。
④對于這兩個點,以當(dāng)前點為中心旋轉(zhuǎn)θ。
⑤將這兩個點的座標(biāo)放到輪廓數(shù)組中。
⑥以順時針順序分別連接輪廓數(shù)組中的點矢量,形成筆畫輪廓。
其中,Bold 函數(shù)決定了輪廓的粗細(xì)變化。我們可以利用一個Sigmoid 回歸函數(shù)來擬合具體的字體,如宋體或仿宋體。
在實際應(yīng)用中,對于字號較小的情況可以直接用直線連接這些點。如果在字號較大的情況下,可以采用B 樣條曲線以盡量使輪廓平滑。
如表2 所示,復(fù)合筆畫可以被看作是多個基本筆畫的結(jié)合。其中,我們可以發(fā)現(xiàn)這些基本筆畫相交的地方,襯線融合成了一個襯線。因此,在繪制過程中需要計算出筆畫交點,在刪除原有的襯線之后在該位置繪制一個新的襯線。
表2 現(xiàn)行漢字筆形表[7]
為了保證字形最后生成效果的美觀性,本系統(tǒng)在筆畫生成完畢后檢查每個筆畫周圍1/64 范圍內(nèi)的填充情況。如果周圍已經(jīng)有了筆畫被繪制,則將減小襯線大小或筆畫粗細(xì)。
由于這種優(yōu)化處理需要更多的計算量,因此它是可選的。
采用了ESP32-S3-N16R8 單片機運行該系統(tǒng)。這款單片機的ROM 容量為16MB,RAM 容量為8MB。
由于測試所使用的SimSun 字體大于本單片機ROM容量,因此我們將其存儲到了TF 卡上。這造成了顯著的拖慢。
圖2是本系統(tǒng)的渲染結(jié)果,其中紅色的部分是襯線。
圖2 本系統(tǒng)的渲染結(jié)果圖
圖3是在16*16 分辨率下優(yōu)化的顯示結(jié)果,可以發(fā)現(xiàn)它的橫和豎具有更高的辨識度。
圖3 優(yōu)化顯示結(jié)果圖
表3所示為本系統(tǒng)運行的系統(tǒng)資源占用情況對比。
表3 系統(tǒng)資源占用情況對比
分析可見,本系統(tǒng)在存儲空間方面表現(xiàn)良好,尤其適用于ROM 空間小的嵌入式系統(tǒng)。而矢量字體由于其體積原因,需要額外的TF 卡適配器來存儲,將提升制造成本。同時,這也將減慢其運行速度。對于位圖字體,每個尺寸的漢字都需要單獨的字體文件,這將大大浪費寶貴的存儲器空間。
在內(nèi)存占用方面,只需要128kB 以內(nèi)的內(nèi)存就能運行此系統(tǒng),同樣優(yōu)于TrueType 字體。值得注意的是,位圖字體由于其特性,使用時幾乎不需要內(nèi)存占用,速度也是最快的。也就是說,在極低性能的嵌入式場景下,本系統(tǒng)仍然無法取代位圖字體。