跳转到主要内容

位图字体压缩

TouchGFX从4.25版本开始支持位图字体压缩。

位图字体压缩功能支持对4 BPP位图字体进行 压缩。 其目标是在保持渲染性能的同时, 减小字体数据的大小。

在Designer中启用字体压缩非常简单。 只需在Designer中为字体排印选择“4 - compressed”作为“Bpp”即可。

选择压缩4 BPP

两类应用程序可从中获益。 包含数千字符的应用程序(例如中文字符), 或使用超大字号的应用程序。 这类应用程序获益显著。 而仅使用20号 英文字母的应用程序获益有限,因其字体数据本身较小。

压缩率取决于字母大小与字体 大小。 小字体的压缩效果比大字体差。 预期压缩后的大小为原始大小的 30%至70%。

字母在绘制前会被解压到缓存缓冲区。 如果字母不在缓存中,那么在渲染之前就必须先进行解压缩操作。 所用压缩算法(游程编码类型)的解压过程非常简便,通常不会造成问题。 如果字母已在缓存中,渲染性能将略优于常规情况,因为从SRAM渲染比 从外部Flash更快。 缓存大小可在Designer中配置。 请参见下文。

如果多次绘制同一个字母,无论是因为该字母在文本中重复出现,还是因为在动画或滚动效果中重绘文本,在很多情况下,缓存都能省去解压缩的这一步。

Note
缓存大小必须足够存储待绘制文本中最大的字母。

若缓存不够大,应用程序将停止工作。

请参见说明此处

如果平台支持硬件加速绘制,该功能同样适用于压缩字体。

矢量字体可作为压缩字体的替代方案。 当使用多种大小时,矢量字体通常 比压缩字体更节省空间,但会对性能造成更大的影响。

有关矢量字体的相关信息,请单击此处

性能

本部分我们将分析一个使用大量 汉字的应用程序,以及一个包含大量数字的应用程序。

第一个示例将使用 NotoSansCJKsc-Black.otf创建28号字体。 我们包含的字符范围为 0x4E00至0x5E00(另加文本中使用的若干额外字符), 总计 4165个字符。 当然,实际的汉字应用程序可能包含更多字符。

该应用程序显示包含24个汉字的文本:

28号汉字

第二个应用程序使用40号和140号Verdana字体:

40号和140号Verdana字体文本

我们将检查字体数据的大小以及绘制这些字符的性能。

字体数据大小

下表显示了字体数据大小(单位:字节):

字体大小字符未压缩大小压缩后大小节省压缩率
NotoSansCJKsc-Black.otf284,1161,568,599917,170651,4291.7
Verdana.ttf409527,36912,78914,5802.1
Verdana.ttf1401140,4088,21132,1974.9

可见汉字压缩节省了超过 0.5兆字节的数据。 该节省量足以减小外部Flash的容量需求,从而降低BOM成本。

尽管压缩率更高,但Verdana字体的压缩并未节省相近数量的 字节数。 主要原因是字符数量少了很多。
两种Verdana字体的数据总和低于22Kb。 这意味着字体数据能够存储在非常小的器件的内部Flash中。

如果我们将Noto字体生成为矢量字体,则字体数据的大小为1,509,236 字节。 这略小于未压缩字体的大小,但比压缩后的位图字体要大。 在使用多种字号时, 矢量字体在大小方面的优势就会显现出来,因为所有大小的字体都共享数据。

渲染

现在我们将检查渲染压缩字母的性能。 如我们将看到的,缓存大小至关重要。 如果应用程序重复绘制相同字母且有足够的缓存来存储,则性能接近未压缩字体,有时甚至更优。

我们将从使用汉字应用程序开始,缓存大小为10,000 字节。 这足以存储所有使用的字母。

我们测量重绘整个屏幕(包括背景图像)的渲染时间。

测试缓存大小渲染时间/ms
背景图像-5.28 ms
未压缩的Noto-7.57 ms
压缩的Noto100007.55 ms
压缩的Noto50009.41 ms
压缩的Noto20009.42 ms

存储显示的所有字母所需的缓存约为8000字节。 如果缓存大于该值,并且字符在第一帧中已经解压缩,那么在后续所有帧中进行渲染时就无需再次解压缩。 当缓存较小 (5000或2000字节)时,由于 缓存大小不足以存储所有字符,必须反复进行解压缩。 虽然性能会降低,但仍然可用。

现在我们来看第二个应用程序。 这次在每帧中仅重绘那个大数字。 每个数字的大小约为4000字节, 因此10,000字节的缓存足以存储两个数字。 例如,一个5000字节的缓存只能存储一个字母, 因此我们必须反复解压缩。

测试缓存大小渲染时间/ms
背景图像-
未压缩的Verdana-2.46 ms
压缩的Verdana10000
压缩的Verdana50003.57 ms

我们再次看到,如果缓存足够大,性能不会改变,否则性能就会受到影响。

作为最后一个示例,我们将使用一个更具模拟性质的应用程序, 仅显示大量“A”字符:

40字号的Verdana字体文本

测试缓存大小渲染时间/ms
背景图像-
未压缩的Verdana-16.51 ms
压缩的Verdana1000016.45 ms
压缩的Verdana100016.46 ms

所有配置的性能都相同。 由于我们仅 绘制单个字符“A”,因此只需非常小的缓存即可。

缓存必须足够大,以存储帧中绘制的所有独特字符。 在此情况下仅需存储“A”。

结论

总之,如果缓存能够存储 帧中绘制的字母,则压缩字体的性能与未压缩字体完全相同。 这意味着只要缓存足够大,压缩字体可应用于 动画和滚动文本场景。

如果缓存不够大,解压缩字符对性能的影响也不大。 这意味着压缩字体可以 搭配较小的缓存用于静态文本,而不会出现大的性能问题。

配置

如引言中所述,在Designer中通过对各个字体排印选择以启用字体压缩功能。 应用程序会自动包含必要的解压缩代码。

缓存大小

缓存大小可在Designer中配置。 在“Config”选项卡中选择“Text Configuration”:

123

配置缓存大小。

如果缓存小于最大压缩字符所需的大小,TouchGFX Designer将在日志中报告错误:

缓存不足时的错误输出。

将字体缓存更改为大于报告数值。

字体文件显示绘制该字体所有字符所需的最小缓存大小。 可以在TouchGFX/generated/fonts/src/Table_xxx.cpp文件中找到此信息:

140号Verdana字体的压缩大小和最小缓存大小

从上面我们可以发现,140号Verdana字体需要至少4025字节的缓存来绘制所有字母。

缓存失败

如果缓存不够大,无法存储所绘制的字符,应用程序将会停止。 应用程序将调用函数 CompressedFontCache::unableToCache(const GlyphNode* glyphNode, int byteSize)(该函数无返回值)。

只有当手动修改生成的文件时,才会发生这种情况。

限制

无法同时在压缩的4 Bpp和未压缩的4 Bpp中使用相同的字体。