lvgl字体文件一般这样子命名
lv_font_xxx_20.c
xxx : 字体名称
20 : 字体大小
内容解析
/*******************************************************************************
* Size: 20 px
* Bpp: 2
* Opts: --bpp 2 --size 20 --no-compress --stride 1 --align 1 --font simhei.ttf --symbols 设备 --format lvgl -o xxx_20.c
******************************************************************************/
#ifdef __has_include
#if __has_include("lvgl.h")
#ifndef LV_LVGL_H_INCLUDE_SIMPLE
#define LV_LVGL_H_INCLUDE_SIMPLE
#endif
#endif
#endif
#ifdef LV_LVGL_H_INCLUDE_SIMPLE
#include "lvgl.h"
#else
#include "lvgl/lvgl.h"
#endif
#ifndef XXX_20
#define XXX_20 1
#endif
#if XXX_20
/*-----------------
* BITMAPS
*----------------*/
/*Store the image of the glyphs*/
static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
/* U+5907 "备" */
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2c, 0x0,
0x0, 0x0, 0x0, 0xe0, 0x0, 0x0, 0x0, 0xf,
0xff, 0xff, 0x40, 0x0, 0xbc, 0x0, 0x38, 0x0,
0xb, 0x7c, 0x3, 0xc0, 0x0, 0x74, 0x3c, 0x3c,
0x0, 0x0, 0x0, 0x3f, 0x80, 0x0, 0x0, 0x6,
0xff, 0x90, 0x0, 0x5b, 0xfd, 0xb, 0xff, 0xe3,
0xfe, 0x40, 0x1, 0xab, 0x40, 0x3f, 0xff, 0xff,
0x80, 0x0, 0xe0, 0x34, 0xe, 0x0, 0x3, 0x80,
0xe0, 0x34, 0x0, 0xf, 0xff, 0xff, 0xd0, 0x0,
0x38, 0xd, 0x3, 0x40, 0x0, 0xe5, 0x79, 0x5d,
0x0, 0x3, 0xff, 0xff, 0xf8, 0x0, 0xe, 0x0,
0x0, 0xe0, 0x0,
/* U+8BBE "设" */
0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0x0, 0x15,
0x54, 0x0, 0x7, 0xc0, 0x7f, 0xfc, 0x0, 0x1,
0xe0, 0x70, 0x1c, 0x0, 0x0, 0x60, 0x70, 0x1c,
0x0, 0x0, 0x0, 0xb0, 0x1c, 0x0, 0x0, 0x0,
0xe0, 0x1d, 0x0, 0x3f, 0xc7, 0xc0, 0x1f, 0xf4,
0x1, 0xc2, 0x0, 0x0, 0x0, 0x1, 0xc3, 0xff,
0xff, 0x80, 0x1, 0xc1, 0xb5, 0x5b, 0x0, 0x1,
0xc0, 0x34, 0xf, 0x0, 0x1, 0xc0, 0x3c, 0x1d,
0x0, 0x1, 0xc8, 0xd, 0x38, 0x0, 0x1, 0xfd,
0xb, 0xf0, 0x0, 0x1, 0xf4, 0x3, 0xe0, 0x0,
0x3, 0xd0, 0x2f, 0xbd, 0x0, 0x1, 0x86, 0xf4,
0xb, 0xf8, 0x0, 0xa, 0x40, 0x0, 0x60
};
/*---------------------
* GLYPH DESCRIPTION
*--------------------*/
static const lv_font_fmt_txt_glyph_dsc_t glyph_dsc[] = {
{.bitmap_index = 0, .adv_w = 0, .box_w = 0, .box_h = 0, .ofs_x = 0, .ofs_y = 0} /* id = 0 reserved */,
{.bitmap_index = 0, .adv_w = 320, .box_w = 19, .box_h = 19, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 91, .adv_w = 320, .box_w = 20, .box_h = 19, .ofs_x = 0, .ofs_y = -2}
};
/*---------------------
* CHARACTER MAPPING
*--------------------*/
static const uint16_t unicode_list_0[] = {
0x0, 0x32b7
};
/*Collect the unicode lists and glyph_id offsets*/
static const lv_font_fmt_txt_cmap_t cmaps[] =
{
{
.range_start = 22791, .range_length = 12984, .glyph_id_start = 1,
.unicode_list = unicode_list_0, .glyph_id_ofs_list = NULL, .list_length = 2, .type = LV_FONT_FMT_TXT_CMAP_SPARSE_TINY
}
};
/*--------------------
* ALL CUSTOM DATA
*--------------------*/
#if LVGL_VERSION_MAJOR == 8
/*Store all the custom data of the font*/
static lv_font_fmt_txt_glyph_cache_t cache;
#endif
#if LVGL_VERSION_MAJOR >= 8
static const lv_font_fmt_txt_dsc_t font_dsc = {
#else
static lv_font_fmt_txt_dsc_t font_dsc = {
#endif
.glyph_bitmap = glyph_bitmap,
.glyph_dsc = glyph_dsc,
.cmaps = cmaps,
.kern_dsc = NULL,
.kern_scale = 0,
.cmap_num = 1,
.bpp = 2,
.kern_classes = 0,
.bitmap_format = 0,
#if LVGL_VERSION_MAJOR == 8
.cache = &cache
#endif
};
/*-----------------
* PUBLIC FONT
*----------------*/
/*Initialize a public general font descriptor*/
#if LVGL_VERSION_MAJOR >= 8
const lv_font_t xxx_20 = {
#else
lv_font_t xxx_20 = {
#endif
.get_glyph_dsc = lv_font_get_glyph_dsc_fmt_txt, /*Function pointer to get glyph's data*/
.get_glyph_bitmap = lv_font_get_bitmap_fmt_txt, /*Function pointer to get glyph's bitmap*/
.line_height = 19, /*The maximum line height required by the font*/
.base_line = 2, /*Baseline measured from the bottom of the line*/
#if !(LVGL_VERSION_MAJOR == 6 && LVGL_VERSION_MINOR == 0)
.subpx = LV_FONT_SUBPX_NONE,
#endif
#if LV_VERSION_CHECK(7, 4, 0) || LVGL_VERSION_MAJOR >= 8
.underline_position = -2,
.underline_thickness = 1,
#endif
.static_bitmap = 0,
.dsc = &font_dsc, /*The custom font data. Will be accessed by `get_glyph_bitmap/dsc` */
#if LV_VERSION_CHECK(8, 2, 0) || LVGL_VERSION_MAJOR >= 9
.fallback = NULL,
#endif
.user_data = NULL,
};
#endif /*#if XXX_20*/
1. 核心数据结构
lv_font_t – 字体对象
这是 LVGL 使用的字体描述符,包含:
get_glyph_dsc/get_glyph_bitmap:函数指针,用于获取字形描述符和位图数据。line_height/base_line:行高和基线偏移。dsc:指向具体的字体格式数据(lv_font_fmt_txt_dsc_t)。- 其他辅助字段(下划线、子像素等)。
在你的代码中:
c
const lv_font_t xxx_20 = {
.get_glyph_dsc = lv_font_get_glyph_dsc_fmt_txt,
.get_glyph_bitmap = lv_font_get_bitmap_fmt_txt,
.line_height = 19,
.base_line = 2,
.dsc = &font_dsc,
...
};
lv_font_fmt_txt_dsc_t – 格式描述
包含:
glyph_bitmap:所有字形像素数据的连续存储区。glyph_dsc:每个字形的度量数组(宽、高、偏移、在 bitmap 中的起始位置)。cmaps:字符映射表(cmap),负责将 Unicode 编码映射到字形索引。bpp:每个像素的比特数(此处为 2,即 4 级灰度)。bitmap_format:0 表示未压缩。
2. 字形描述数组 glyph_dsc[]
每个字形对应的描述符(lv_font_fmt_txt_glyph_dsc_t)包含:
bitmap_index:在glyph_bitmap中的起始偏移(字节)。box_w/box_h:字形实际占用的像素宽度和高度。ofs_x/ofs_y:相对于字符原点的偏移(用于垂直或水平对齐)。adv_w:推进宽度(即下一个字符的 x 坐标增量)。
你的代码中定义了两个字形(“备”和“设”):
c
static const lv_font_fmt_txt_glyph_dsc_t glyph_dsc[] = {
{.bitmap_index = 0, ...}, // 保留
{.bitmap_index = 0, .adv_w = 320, .box_w = 19, .box_h = 19, ...}, // "备"
{.bitmap_index = 91, .adv_w = 320, .box_w = 20, .box_h = 19, ...} // "设"
};
索引 1 对应“备”,索引 2 对应“设”。bitmap_index 指明每个字形的像素数据在 glyph_bitmap 中的起始位置(第一个从 0 开始,第二个从 91 字节开始)。
3. 字符映射表(cmap)
用于将 Unicode 码点转换成 glyph index。当前使用 LV_FONT_FMT_TXT_CMAP_SPARSE_TINY 类型,即稀疏的紧凑存储:
range_start= 22791(即0x5907,“备”的 Unicode)。range_length= 12984(覆盖到0x8BBE,恰好包含“设”)。glyph_id_start= 1(glyph 索引从 1 开始)。unicode_list显式列出所有需要映射的字符码点(这里是两个:0x0和0x32B7,但注意到0x0是保留,第二个0x32B7对应“设”?实际上需要计算:0x5907是“备”,0x8BBE是“设”。在 sparse 方式中,unicode_list列出相对于range_start的偏移:第一个偏移0x0对应0x5907,第二个偏移0x32B7(即 12983)对应0x5907+0x32B7 = 0x8BBE,符合“设”。所以映射如下:- 查找字符
0x5907:在 range 内,偏移为 0,对应列表第 0 项,glyph_id = 1。 - 查找字符
0x8BBE:偏移为 0x32B7,对应列表第 1 项,glyph_id = 2。
- 查找字符
4. 位图数据 glyph_bitmap
存储的是所有字形的像素数据,按字形顺序依次放置。每个像素使用 2 位(BPP=2),即 4 级灰度(0~3)。数据按行连续排列,每行字节数由 box_w 和 BPP 决定:row_bytes = (box_w * bpp + 7) / 8,但 LVGL 内部有专门的解码函数。
你的 “备” 字形大小为 19×19,位图数据从偏移 0 开始,共 (19*2+7)/8 * 19 ≈ 5*19=95 字节,但实际 glyph_bitmap 中“备”占用 91 字节(因为第二个起始是 91),可能由于对齐或压缩,但无需深究。
5. 渲染流程(运行时)
当 LVGL 需要绘制文本时(例如调用 lv_label_set_text),会依次处理每个字符:
- 获取字符编码(例如
'备'=0x5907)。 - 调用字体的
get_glyph_dsc(这里为lv_font_get_glyph_dsc_fmt_txt),该函数会:- 遍历
cmaps,找到匹配的 cmap 条目(根据 range)。 - 在
unicode_list中查找偏移,确定 glyph index。 - 从
glyph_dsc数组中取出对应的描述符。 - 若未找到,则返回空(可能用后备字符)。
- 遍历
- 调用
get_glyph_bitmap(这里为lv_font_get_bitmap_fmt_txt),传入 glyph index,根据描述符中的bitmap_index从glyph_bitmap中提取原始像素数据。 - 渲染到画布:
- 根据
box_w/box_h和ofs_x/ofs_y确定在缓冲区中的绘制位置。 - 逐像素读取位图数据(每像素 2 位),转化为颜色(通常将灰度映射到当前文本颜色透明度)。
- 绘制到当前渲染缓冲区(
lv_draw_ctx)。
- 根据
此外,adv_w 用于更新下一个字符的绘制 x 坐标。
6. 相关宏与兼容性
代码中包含了版本检测宏(LV_LVGL_H_INCLUDE_SIMPLE),确保无论在独立 LVGL 环境还是 Arduino 等环境中都能正确包含头文件。同时,#if XXX_20 允许条件编译,方便在多个字体间切换。
7. 字体生成工具
这类 C 文件通常由 lv_font_conv(或在线转换工具)生成。输入 TTF/OTF 字体文件,指定字符集、大小、BPP、压缩选项,输出包含上述所有静态数据的 .c 文件。开发者只需在项目中包含该文件,并声明 extern const lv_font_t xxx_20,即可像使用内置字体一样使用它。
总结
LVGL 自定义字体通过 静态数据 + 函数回调 实现了高效、灵活的字形渲染:
- 数据:位图、度量、映射表均在编译时生成,占用 ROM。
- 接口:标准化的
get_glyph_dsc和get_glyph_bitmap使得 LVGL 内核无需关心字形来源。 - 性能:BPP 可选,支持灰度抗锯齿;cmap 采用稀疏索引,减少内存占用。