位图字体压缩
TouchGFX从4.25版本开始支持位图字体压缩。
位图字体压缩功能支持对4 BPP位图字体进行 压缩。 其目标是在保持渲染性能的同时, 减小字体数据的大小。
在Designer中启用字体压缩非常简单。 只需在Designer中为字体排印选择“4 - compressed”作为“Bpp”即可。
两类应用程序可从中获益。 包含数千字符的应用程序(例如中文字符), 或使用超大字号的应用程序。 这类应用程序获益显著。 而仅使用20号 英文字母的应用程序获益有限,因其字体数据本身较小。
压缩率取决于字母大小与字体 大小。 小字体的压缩效果比大字体差。 预期压缩后的大小为原始大小的 30%至70%。
字母在绘制前会被解压到缓存缓冲区。 如果字母不在缓存中,那么在渲染之前就必须先进行解压缩操作。 所用压缩算法(游程编码类型)的解压过程非常简便,通常不会造成问题。 如果字母已在缓存中,渲染性能将略优于常规情况,因为从SRAM渲染比 从外部Flash更快。 缓存大小可在Designer中配置。 请参见下文。
如果多次绘制同一个字母,无论是因为该字母在文本中重复出现,还是因为在动画或滚动效果中重绘文本,在很多情况下,缓存都能省去解压缩的这一步。
Note
若缓存不够大,应用程序将停止工作。
请参见说明此处。
如果平台支持硬件加速绘制,该功能同样适用于压缩字体。
矢量字体可作为压缩字体的替代方案。 当使用多种大小时,矢量字体通常 比压缩字体更节省空间,但会对性能造成更大的影响。
有关矢量字体的相关信息,请单击此处。
性能
本部分我们将分析一个使用大量 汉字的应用程序,以及一个包含大量数字的应用程序。
第一个示例将使用 NotoSansCJKsc-Black.otf创建28号字体。 我们包含的字符范围为 0x4E00至0x5E00(另加文本中使用的若干额外字符), 总计 4165个字符。 当然,实际的汉字应用程序可能包含更多字符。
该应用程序显示包含24个汉字的文本:
第二个应用程序使用40号和140号Verdana字体:
我们将检查字体数据的大小以及绘制这些字符的性能。
字体数据大小
下表显示了字体数据大小(单位:字节):
字体 | 大小 | 字符 | 未压缩大小 | 压缩后大小 | 节省 | 压缩率 |
---|---|---|---|---|---|---|
NotoSansCJKsc-Black.otf | 28 | 4,116 | 1,568,599 | 917,170 | 651,429 | 1.7 |
Verdana.ttf | 40 | 95 | 27,369 | 12,789 | 14,580 | 2.1 |
Verdana.ttf | 140 | 11 | 40,408 | 8,211 | 32,197 | 4.9 |
可见汉字压缩节省了超过 0.5兆字节的数据。 该节省量足以减小外部Flash的容量需求,从而降低BOM成本。
尽管压缩率更高,但Verdana字体的压缩并未节省相近数量的 字节数。 主要原因是字符数量少了很多。
两种Verdana字体的数据总和低于22Kb。 这意味着字体数据能够存储在非常小的器件的内部Flash中。
如果我们将Noto字体生成为矢量字体,则字体数据的大小为1,509,236 字节。 这略小于未压缩字体的大小,但比压缩后的位图字体要大。 在使用多种字号时, 矢量字体在大小方面的优势就会显现出来,因为所有大小的字体都共享数据。
渲染
现在我们将检查渲染压缩字母的性能。 如我们将看到的,缓存大小至关重要。 如果应用程序重复绘制相同字母且有足够的缓存来存储,则性能接近未压缩字体,有时甚至更优。
我们将从使用汉字应用程序开始,缓存大小为10,000 字节。 这足以存储所有使用的字母。
我们测量重绘整个屏幕(包括背景图像)的渲染时间。
测试 | 缓存大小 | 渲染时间/ms |
---|---|---|
背景图像 | - | 5.28 ms |
未压缩的Noto | - | 7.57 ms |
压缩的Noto | 10000 | 7.55 ms |
压缩的Noto | 5000 | 9.41 ms |
压缩的Noto | 2000 | 9.42 ms |
存储显示的所有字母所需的缓存约为8000字节。 如果缓存大于该值,并且字符在第一帧中已经解压缩,那么在后续所有帧中进行渲染时就无需再次解压缩。 当缓存较小 (5000或2000字节)时,由于 缓存大小不足以存储所有字符,必须反复进行解压缩。 虽然性能会降低,但仍然可用。
现在我们来看第二个应用程序。 这次在每帧中仅重绘那个大数字。 每个数字的大小约为4000字节, 因此10,000字节的缓存足以存储两个数字。 例如,一个5000字节的缓存只能存储一个字母, 因此我们必须反复解压缩。
测试 | 缓存大小 | 渲染时间/ms |
---|---|---|
背景图像 | - | |
未压缩的Verdana | - | 2.46 ms |
压缩的Verdana | 10000 | |
压缩的Verdana | 5000 | 3.57 ms |
我们再次看到,如果缓存足够大,性能不会改变,否则性能就会受到影响。
作为最后一个示例,我们将使用一个更具模拟性质的应用程序, 仅显示大量“A”字符:
测试 | 缓存大小 | 渲染时间/ms |
---|---|---|
背景图像 | - | |
未压缩的Verdana | - | 16.51 ms |
压缩的Verdana | 10000 | 16.45 ms |
压缩的Verdana | 1000 | 16.46 ms |
所有配置的性能都相同。 由于我们仅 绘制单个字符“A”,因此只需非常小的缓存即可。
缓存必须足够大,以存储帧中绘制的所有独特字符。 在此情况下仅需存储“A”。
结论
总之,如果缓存能够存储 帧中绘制的字母,则压缩字体的性能与未压缩字体完全相同。 这意味着只要缓存足够大,压缩字体可应用于 动画和滚动文本场景。
如果缓存不够大,解压缩字符对性能的影响也不大。 这意味着压缩字体可以 搭配较小的缓存用于静态文本,而不会出现大的性能问题。
配置
如引言中所述,在Designer中通过对各个字体排印选择以启用字体压缩功能。 应用程序会自动包含必要的解压缩代码。
缓存大小
缓存大小可在Designer中配置。 在“Config”选项卡中选择“Text Configuration”:
如果缓存小于最大压缩字符所需的大小,TouchGFX Designer将在日志中报告错误:
将字体缓存更改为大于报告数值。
字体文件显示绘制该字体所有字符所需的最小缓存大小。 可以在TouchGFX/generated/fonts/src/Table_xxx.cpp文件中找到此信息:
从上面我们可以发现,140号Verdana字体需要至少4025字节的缓存来绘制所有字母。
缓存失败
如果缓存不够大,无法存储所绘制的字符,应用程序将会停止。 应用程序将调用函数 CompressedFontCache::unableToCache(const GlyphNode* glyphNode, int byteSize)(该函数无返回值)。
只有当手动修改生成的文件时,才会发生这种情况。
限制
无法同时在压缩的4 Bpp和未压缩的4 Bpp中使用相同的字体。