矢量字体
自4.23版本开始,TouchGFX支持使用矢量格式的字体。 以矢量格式存储的字体可潜在地减小字体数据的大小,因为字体数据在所有字体大小之间共享。 这与位图字体形成对比,在位图字体中,每个字体大小都包含该大小字形的专用位图。
矢量字体在用户界面上使用,类似使用TextArea或ButtonWithLabel等控件的普通位图字体。
要使用矢量字体格式,必须针对STM32CubeMX中的平台启用矢量字体功能。
只有16bpp、24bpp或32bpp帧缓冲区支持矢量字体。
什么是矢量字体
矢量字体是用曲线和直线描述单个字母的字体。 这些曲线和直线能按比例放大和缩小,以产生不同大小的字形。 我们在TouchGFX中使用的所有字体通常均为矢量字体(例如:TrueType或OpenType字体)。 TouchGFX设计器生成素材时,字体描述中的曲线和直线会由TouchGFX-字体转换器转换为许多小位图。
矢量字体到位图的转换以TouchGFX设计器中定义的字体排印为基础。 例如,一个应用程序可能使用两种字体,大号和小号。 大号可以30号的Verdana为基础,小号可以20号的Verdina为基础。 如果我们假设应用程序在此类字体排印中使用字形A-Z和a-z,则每种字体排印有52个字形,但应用程序将包含104个位图,因为每个字母有两个位图。 一个位图的大小为30,另一个位图大小为20。
矢量字体各不相同,因为我们保留了字形的矢量定义,而非创建字形的位图。 相反,此矢量定义只包含在目标应用程序中一次。 生成两种字体排印所需的唯一额外数据是两个不同的缩放因子。 一个用于将矢量定义缩放到大小30,另一个用于缩放到大小20。
因此,如果我们在示例应用程序中添加另一个字体排印大小,例如40号的Verdana,则闪存需求只会略有增加。
示例
举个例子,Truetype字体Verdana中“G”的矢量定义为170字节。
表示字形的位图大小取决于所使用的bpp和字形的大小。 下表显示了4bpp中各种字形尺寸中“G”的位图大小:
字体大小 | G字形尺寸/像素 | 位图大小/字节 |
---|---|---|
20 | 14 x 14 | 98 |
30 | 21 x 22 | 242 |
40 | 27 x 32 | 448 |
总计 | 788 |
我们看到,字体尺寸为20的位图大小小于“G”的矢量定义,但在尺寸为30的情况下,矢量定义已小于位图大小。 如果我们使用三种不同尺寸的Verdana,位图使用788字节,而矢量定义只使用170字节。 因此,我们节省了78%的闪存使用量。
这种节省存储空间的做法有一个缺点——性能。 当我们需要绘制一个字形时,必须将矢量定义转换为像素。 通过缩放和平移字形的矢量定义,然后将其渲染到帧缓存来完成。
Verdana中的单个“G”由20条Bezier曲线和6条直线组成。 这些均被转换为轮廓,最后使用选定的文本颜色对其进行着色。 对文本中的所有字母重复此过程,并在重新绘制文本时重复此过程。
上述过程在具有GPU2D(例如STM32U5G9)且性能良好的微控制器上进行硬件加速。 在STM32G0这样没有浮点硬件的慢速微控制器上,对执行此类操作的硬件要求非常高。
这种情况下,如果使用位图字体,渲染时间会更短。 因此,建议在需要节省闪存的情况下使用矢量字体,其他情况下使用位图字体。
如果文本已设置动画(滚动、移动或淡出),建议使用位图字体。
配置
矢量字体的使用需要额外的框架功能矢量渲染
和矢量字体
。 此类功能在STM32CubeMX中启用。 详细信息请参阅成器用户指南。
字体排印在TouchGFX设计器中进行配置。 您可以选择项目中字体排印应使用位图字体格式还是矢量字体格式。 默认位图字体格式。
使用矢量字体
矢量字体与TouchGFX中的位图字体完全相同。 您可以将文本创建为命名资源或一次性使用文本。 您可以在其中一个控件中显示文本,例如TextArea。 矢量字体和位图字体之间的区别隐藏在渲染代码中。
字体排印
当某个字体排印在TouchGFX设计器中基于矢量进行配置时,将生成矢量字体。 参见TouchGFX生成器用户指南。
如果多个字体排印使用相同的字体(例如Verdana),则字体排印将在项目中共享矢量定义,但使用不同的比例因子来生成不同大小的字形。
这也意味着字体排印属性回退字符
和省略号字符
对于字体排印必须相同。
字体排印中使用的字符(包括通配符)将被组合起来,并用于使用相同字体项目的所有字体排印。
架构
矢量字体的渲染基于一个新组件VectorFontRenderer
。 此组件使用VectorRenderer
组件来绘制字形。
在使用矢量字体的项目中,这两个组件必须可用。 如启用了STM32CubeMX,则会自动执行此操作。 如未使用STM32CubeMX,则必须手动执行此操作。
以下是STM32CubeMX生成的代码,其中突出显示了相关行:
static STM32TouchController tc;
static STM32DMA dma;
static TouchGFXDataReader dataReader;
static LCD16bppSerialFlash display(dataReader);
static VectorFontRendererImpl vectorFontRenderer;
static ApplicationFontProvider fontProvider;
static Texts texts;
static TouchGFXHAL hal(dma, display, tc, 240, 320);
void touchgfx_init()
{
Bitmap::registerBitmapDatabase(BitmapDatabase::getInstance(), BitmapDatabase::getInstanceSize());
TypedText::registerTexts(&texts);
Texts::setLanguage(0);
hal.setDataReader(&dataReader);
fontProvider.setFlashReader(&dataReader);
display.setVectorFontRenderer(&vectorFontRenderer);
...
必须提供VectorRenderer
组件(来自TouchGFXGeneratedHAL.cpp
):
namespace touchgfx
{
VectorRenderer* VectorRenderer::getInstance()
{
static CWRVectorRendererRGB565 renderer;
return &renderer;
}
} // namespace touchgfx
如果您在具有GPU2D加速器的平台上,则应使用CPU2DVectorRenderer
,而非CWRVectorRendererRGB565
。 从而实现硬件加速。
限制
缠绕规则
TouchGFX应用程序中使用的矢量字体排版需要遵循某些规则才能正确绘制字形。 具体来说,由于TouchGFX在绘制矢量字体时部署的缠绕规则,有时无法正确渲染轮廓重叠的字形。 要从自定义排版中删除重叠的轮廓,建议使用免费的FontForge工具。
使用FontForge
导航到FontForge安装文件夹(通常为c:\/Program Files(x86)/FontForgeBuilds
)启动.exe
:
导航到项目的assets/fonts/
文件夹,例如,c:\/TTouchGFXProjects/MyApplication/TTouchGF X/assets/fonts/
。 FontForge将检测应用程序中所有的TrueType字体。 在本例中,Cairo-Bold.ttf TrueType字体包含未正确绘制的重叠轮廓:
单击确定
将打开排版中所有字形的字体视图:
要从字形中删除重叠的轮廓,请使用 Ctrl+A在字体视图中选择所有字形。
. 接下来,在选择所有字形的情况下,使用快捷键 Ctrl+u,
然后 使用Ctrl+Shift+O
从选定的字形中删除重复的轮廓。 一个蓝色标记将出现在所有字形上方,表示其已更改。 双击各个字形可以看到效果。 下面显示了移除重叠轮廓之前和之后的$
字形示例:
请注意,字形轮廓不受影响,但重叠的轮廓将合并。 为了在TouchGFX应用程序中使用不重叠的排版,我们使用FontForge导出新字体。 在FontForge字体视图中,使用快捷键 Ctrl+Shift+G
导出字体。 接着将打开生成字体视图。 建议重新命名排版,例如使用“-无重叠”作为后缀,以避免在出现错误时覆盖原始字体。 选择TrueType
作为输出格式。 设置应如下所示:
按生成
导出字体。 如出现警告,请按是。 导出的排版将显示在项目的资源/字体
/文件夹中原始TrueType字体旁边。 原始字体可从该文件夹中删除并放置在磁盘上其他位置,以避免TouchGFX Designer中出现多个同名字体。 重启TouchGFX Designer,使新字体在字体选择器中可见。 如果您在修改字体之前已经在TouchGFX Designer中生成了代码,请确保再次生成代码之前删除生成的/字体/
文件夹。
存储
矢量字体数据必须存储在存储映射区域中。 可以是存储器映射模式下的内部flash、外部QSPI/OPI flash、RAM或其他类似存储器。
如果您的平台将其他字体数据存储在非存储器映射数据中,则必须更改链接器脚本以便将所有矢量字体数据移动到内部flash。 以下为使用ARM gcc执行此操作的方法:
/* Constant data into "FLASH" Rom type memory */
.FontFlashSection :
{
. = ALIGN(4);
*/Vector_*.o(FontFlashSection) /* Vector font data */
. = ALIGN(4);
} >FLASH
FontFlashSection :
{
*(FontFlashSection FontFlashSection.*)
*(.gnu.linkonce.r.*)
. = ALIGN(0x4);
} >SPI_FLASH
将来自匹配*/Vector_*.o/
文件的字体数据放入FLASH
区域,并将其他字体数据放入SPI_FLASH
区域。
矢量字体数据生成至文件中,如:generated/fonts/src/Vector_font_Verdana.cp
等。
泰语
众所周知,泰语无法使用矢量字体正确渲染。 解决办法是使用泰语位图字体。