APP下载

Arduino平台BDF字库像素数据压缩算法及显示程序设计研究

2021-11-22

物联网技术 2021年11期
关键词:字符字节字体

彭 伟

(武汉城市职业学院,湖北 武汉 430064)

0 引 言

物联网嵌入式系统节点微控制器,通常直接提取使用未经压缩的字符像素数据用于驱动显示,导致嵌入式系统资源被大量占用,而U8G2图形驱动库则非常好地解决了这一问题。本文将提取U8G2的像素数据并剖析研究行程编码压缩算法;在Proteus环境下设计Arduino+OLED电路[1-4]进行显示仿真测试,为物联网嵌入式系统类似设计提供重要参考。

1 U8G2驱动与BDF字库简介

1.1 U8G2显示驱动

U8G2是单色LCD、OLED及eInk显示驱动库,以页面缓冲模式下的显示程序为例,针对OLED有语句U8G2_SSD1306_128X64_NONAME_1_4W_SW_SPI u8g2(U8G2_R0,/*clock=*/13,/*data=*/11,/*cs=*/10,/*dc=*/9,/*reset=*/8),可构造面向 OLED(SSD1306)的 U8G2对象,同时完成SPI接口引脚配置。U8G2显示字符通过enableUTF8Print()使其能支持Unicode编码,通过setFont设置字体,通过drawStr输出字符串。C程序中的字体像素数据须符合u8g2fontformat要求,其最前面一段为编码个数及比特位0和1的行程编码宽度等,其后是各字符编码、压缩编码长度及行程编码字节等。

1.2 BDF字库结构

BDF是Adobe字形位图格式,使用ASCII编码,以字体全局信息开始,其后是各字形位图数据。以unifont.bdf字库文件为例,其开头部分全局信息如下:

STARTFONT 2.1 /*字体文件版本*/

FONT XXX /*表示字体名*/

SIZE 16 75 75 /*字符大小及在XY方向分辨率

FONTBOUNDINGBOX 16 16 0 -2 /*X方向宽度与Y方向高度及XY方向偏移*/

STARTPROPERTIES 24 /*24为所列属性个数*/

/*这里是大量公共属性,具体内容此略*/

ENDPROPERTIES /*全局属性结束*/

CHARS 57086 /*字库文件字符总个数*/

以下是各字符具体结构信息,以"1"为例:

STARTCHAR U+0031 /*"1"的编码*/

BBX 8 16 0 -2 /*字符边框8*16,偏移0/-2*/

/*限于篇幅,这里略去了其他属性项*/

BITMAP /*字符位图点阵编码起始标识*/

/*以字符"1"的位图像素为例,每1字节占一行,表示8像素,为缩减篇幅以下将16字节列于一行*/

00 00 00 00 08 18 28 08 08 08 08 08 08 3E 00 00

ENDCHAR /*字符结束标志*/

/*限于篇幅,这里略去大量其他字符像素数据*/

ENDFONT /*字体文件结束标志*/

2 U8G2字符提取压缩与显示程序设计

2.1 U8G2对BDF字库的像素数据提取

将指定字符集各字符像素数据从BDF文件提取到C文件,可使用U8G2提供的bdfconv.exe,它根据指定映射文件(如myFONT.map)给出的待提取字符集编码从BDF字库文件提取字符像素数据并进行RLE压缩[5];然后以字符串形式存入C文件。假定myFONT.map内有:$0031、$0032、$0041、$0042、$554a、$4e2d、$56fd,它们分别是字符“12AB 啊中国”的Unicode编码,BDF字库文件为unifont.bdf,转换命令行及输出示例如下:

bdfconv.exe -v ../bdf/unifont.bdf -b 0 -f 1 -M ../build/ myFONT.map-n MyU8g2fonts -o MyU8g2fonts.c

const uint8_t MyU8g2fonts[173] U8G2_FONT_SECTION("MyU8g2fonts") =

"732453261620376123761637631666113245,252LJ302>"

"1526216246*232!1130564323302j:14A15246**-j11305a20353B16246"

"*212A1121530322:1613N-3513153343431326134K207307P14305P14305"

"px10…../*限于篇幅,这里截去部分内容*/

上述用C字符串表示的字符像素数据,每行32项,凡值小于32的数据(这些数据编码对应字符为不可打印显示字符)以及单引号、双引号、数字字符“0~9”对应编码数据,外加127,均以“”加8进制形式输出;凡编码小于128的数据则直接以字符形式输出,包括标点符号与大小写字母。

2.2 像素数据RLE压缩算法分析

为便于解析字符像素数据整体压缩[5]过程,图1给出了其关键部分的算法流程(含示意图)。

图1 字符像素数据RLE压缩流程及示意图

图1中有关RLE压缩算法的关键部分剖析如下:

(1)连续的0序列与连续的1序列个数构成的个数对[a,b]的生成(其中a为连续出现的0的个数,b为连续出现的1的个数)。仍以字符“1”为例,其位图[6-7]像素对应字节序列为:00 00 00 00 08 18 28 08 08 08 08 08 08 3E 00 00,共16字节,它们构成了一个8×16的矩形位图区域,字符像素实际所占最大矩形区域为5×10。逐行循环扫描像素位,可获取该区域连续相同像素位(0与1)的个数对[a,b]。例如5×10区域最前面两行像素位为00100 011…,其最前面2个0和1个1可表示为[2,1],再接着是2个0加下一行1个0共3个0,随后是2个1可表示为[3,2]。依此类推,共计有11对 [a,b]值,即 [2,1][3,2][2,1][1,1][4,1][4,1][4,1][4,1][4,1][4,1][2,5],这11对数据描述了字符“1”的5×10像素区域所有连续0、连续1的行程变化情况(即个数变化情况)。

(2)将N个[a,b]对,即连续0的个数、连续1的个数、下一组连续0的个数和连续1的个数,依此类推,交替写入位流缓冲;同时约定每当出现连续相同的[a,b]对时,均直接插入1个比特1来代替,否则先插入1个比特0,再向位流缓冲写入新的[a,b]对。现假定用3个比特位存放0的个数,接着的3个比特位存放1的个数,则[a,b]对中a、b的最大值均不能超过7。假定连续有5个0,然后是4个1,即[5,4],要写入以字节为单位的位流缓冲,则写入序列为00100101,其中最低的0~2(共3位)所放置101(5)表示5个0,接着的3~5位(共3位)放置100(4)表示4个1;再假定接下来要写入01序列对[3,2],由于[3,2]≠[5,4],故先在未被占用的“00100101”第6位写0,然后再写[3,2],3(011)最低位(即1)被写入这一字节余下的最高位(即第7位),多出的2位(即01)将分配到缓冲空间下一字节的最低两位(即第0、1位),新字节的第2~4位将放置010(2),代表2个0,于是连续两字节当前序列分别为:10100101 0001001,依此类推。

(3)要确定位流缓冲中用于存放连续0和连续1的个数的最佳比特位数[8](即位宽),也就是针对一系列[a,b]对,统一固定分配多少比特位存放a、多少比特位存放b,能够实现最佳压缩[9]。算法流程图中使用双重循环控制变量rle_0和rle_1,它们分别为0和1的连续个数(对应位宽值),循环取值均为2~7(表示行程范围为22-1~27-1,即3~127;显然,超过行程最大值的需切割到下一行程范围)。通过共计36次循环(6×6双循环),每趟循环跟踪当前选取的rle_0和rle_1及按此行程宽度存储后的最终压缩位数,找到最终压缩位数值最小者,即找到了最佳位宽取值,本文示例中最终最佳取值best_rle_0、best_rle_1分别为3、2。

(4)以最佳行程宽度位数完成最终压缩。以best_rle_0、best_rle_1取值3、2为基准,参照图1右下角分析表格,假定位流最前面保存“1”的Unicode编码及BBX等数据后D[4]=0x0A,则在存入 [2,1][3,2][2,1][1,1][4,1][4,1][4,1][4,1][4,1][4,1][2,5]共11对[a,b]值以后,D[4]~D[10]将分别为:AA、4C、4A、C2、3E、0D、02。注意:写最后的[2,5]时,因5至少需3个比特位才能保存,但rle_1=2表示存放连续1的个数的比特位宽为2(最大行程=22-1=3<5),无法表示5,故RLE算法[10]中将[2,5]切割为[2,3]和[0,2],表示先缓存2个0与3个1,然后再缓存0个0(即没有0)并补充缓存2个1(共计仍为5个连续1,行程=5)。

查看前述bdfconv生成的MyU8g2fonts序列,其中有“6113245,252LJ302>152”,该 8进制 +字符混合表示的序列以16进制形式表示为:31 0B A5 2C AA 4C 4A C2 3E 0D 02,其中“61=0x31”为“1”的16进制编码,“13=0x0B=11D”表示该字符点阵压缩后共占11字节。显然跳过这一长度即进入下一字符压缩数据区(依此规则可快速从第一个字符编码位置向后跨越寻址,形成了简易的链式存储结构),其后A5~02则为流程图中按rle_0、rle_1当前最佳取值3、2逐一写位流后生成的压缩流对应的字节序列,详细分析过程可参考图1右下角位流压缩缓冲表格。

另外,在MyU8g2fonts字符串中还有“6216”,“62”表示数字字符“2”(62=0x32,即“2”的编码),它是“2”的编码数据起点,总长为16=14字节;其后出现的A15表示“A”的压缩编码共15=13字节,B16表示“B”的压缩编码共16=14字节;对于N-35,其中“N-”对应0x4E、0x2D,它们是“中”的编码4E2D,“35=29”则表示其编码共29字节,依此类推。

对于MyU8g2fonts最前面的一段,根据U8G2的字体格式u8g2fontformat要求可知,其最前面的“732...”分别表示当前字符总个数为7(12AB啊中国),Box Mode为0,确定的bit0、bit1行程编码最佳宽度为3、2(即best_rle_0=3,best_rle_1=2)。限于篇幅,其余解析此处省略。

2.3 TTF/OTF向BDF的转换

对于TTF/OTF字库字符,可使用U8G2的工具将其转换为BDF,然后按前述同样方法输出C文件。使用转换工具otf2bdf.exe的命令行示例为:otf2bdf.exe simkai.ttf -p 12 -o my_bdf.bdf,其中simkai.ttf为某TTF字体文件,-p 12为输出字体大小,-o后是待输出BDF文件名。otf2bdf编译时需引入FreeType2库。

2.4 U8G2显示程序设计与仿真测试

以Arduino程序为例,setup函数通过begin()启动u8g2对象,通过enableUTF8Print()使其能支持UTF8,loop主循环主要语句如下:

u8g2.firstPage(); //首页初始化

do { u8g2.setFont(MyU8g2fonts); //选择字体

u8g2.setCursor(0,40); u8g2.print("12AB 啊中国 ");

/*字符串还可直接以Unicode编码形式给出*/

/*限于篇幅,这里截去了其他部分代码*/

} while ( u8g2.nextPage())

图2给出了用Proteus绘制的Arduino+UG-2864(SSD1306 OLED)仿真电路及仿真运行效果。

图2 Arduino+OLED字符显示仿真

3 结 语

本文研究了U8G2驱动环境下的字符像素数据提取与RLE压缩算法,分析了RLE压缩的关键细节;在Proteus平台设计了Arduino+OLED屏仿真电路;针对RLE压缩后生成的数据进行显示测试,为类似设计提供了参考。

猜你喜欢

字符字节字体
No.8 字节跳动将推出独立出口电商APP
字体的产生
字符代表几
一种USB接口字符液晶控制器设计
No.10 “字节跳动手机”要来了?
消失的殖民村庄和神秘字符
简谈MC7字节码
组合字体
人类进入“泽它时代”
字体安装步步通