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),会依次处理每个字符:

  1. 获取字符编码(例如 '备' = 0x5907)。
  2. 调用字体的 get_glyph_dsc(这里为 lv_font_get_glyph_dsc_fmt_txt),该函数会:
    • 遍历 cmaps,找到匹配的 cmap 条目(根据 range)。
    • 在 unicode_list 中查找偏移,确定 glyph index。
    • 从 glyph_dsc 数组中取出对应的描述符。
    • 若未找到,则返回空(可能用后备字符)。
  3. 调用 get_glyph_bitmap(这里为 lv_font_get_bitmap_fmt_txt),传入 glyph index,根据描述符中的 bitmap_index 从 glyph_bitmap 中提取原始像素数据。
  4. 渲染到画布:
    • 根据 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 采用稀疏索引,减少内存占用。