三、LVGL 使用字库(续)
2. 实现 get_glyph_dsc 函数
get_glyph_dsc 函数用于获取字符的描述信息(宽度、高度、偏移量、位深等)。
2.1 点阵字库 — LVGL V8.4
芯片:GT30L24A3W,16 号字体,ASCII 字符宽 8px,非 ASCII 字符宽 16px,位深 1
static bool _get_gt_font_glyph_dsc_16(const lv_font_t * font,
lv_font_glyph_dsc_t * dsc_out,
uint32_t unicode_letter,
uint32_t unicode_letter_next)
{
lv_font_fmt_txt_dsc_t * fdsc = (lv_font_fmt_txt_dsc_t *)font->dsc;
if (unicode_letter >= 0x20 && unicode_letter < 0x80) {
dsc_out->adv_w = 8; // 文字的实际宽度
dsc_out->box_w = 8; // 文字的显示宽度
dsc_out->ofs_y = -2; // 文字的显示位置偏移
} else {
dsc_out->adv_w = 16; // 文字的实际宽度
dsc_out->box_w = 16; // 文字的显示宽度
dsc_out->ofs_y = 0; // 文字的显示位置偏移
}
dsc_out->box_h = 16; // 文字的显示高度
dsc_out->ofs_x = 0; // 文字的显示位置偏移
dsc_out->bpp = 1; // 文字的位深
dsc_out->is_placeholder = false;
return true;
}
2.2 点阵字库 — LVGL V9.3
LVGL V9.3 中 bpp 改为 format,并增加了 gid.index 字段。
static bool _get_gt_font_glyph_dsc_16(const lv_font_t * font,
lv_font_glyph_dsc_t * dsc_out,
uint32_t unicode_letter,
uint32_t unicode_letter_next)
{
lv_font_fmt_txt_dsc_t * fdsc = (lv_font_fmt_txt_dsc_t *)font->dsc;
if (unicode_letter >= 0x20 && unicode_letter < 0x80) {
dsc_out->adv_w = 8;
dsc_out->box_w = 8;
dsc_out->ofs_y = -2;
} else {
dsc_out->adv_w = 16;
dsc_out->box_w = 16;
dsc_out->ofs_y = 0;
}
dsc_out->box_h = 16;
dsc_out->ofs_x = 0;
dsc_out->format = 1; // LVGL V9 使用 format 代替 bpp
dsc_out->is_placeholder = false;
dsc_out->gid.index = unicode_letter;
return true;
}
2.3 灰度字库 — LVGL V8.4
芯片:GT5HL24A2W,位深 4。灰度字库 ASCII 字符为不等宽,需从数据中获取实际宽度(前两字节存储宽度信息)。
static bool _get_gt_font_glyph_dsc_16(const lv_font_t * font,
lv_font_glyph_dsc_t * dsc_out,
uint32_t unicode_letter,
uint32_t unicode_letter_next)
{
lv_font_fmt_txt_dsc_t * fdsc = (lv_font_fmt_txt_dsc_t *)font->dsc;
if (unicode_letter >= 0x20 && unicode_letter < 0x80) {
// 不等宽的字符,获取的数据前两个字节表示文字的宽度
GT_GetASCII(unicode_letter, GT_OPT_ASCII_16_ZK_4bit, &fdsc->glyph_bitmap[0]);
dsc_out->adv_w = fdsc->glyph_bitmap[1]; // 文字的实际宽度
dsc_out->box_w = 16; // 文字的显示宽度
dsc_out->ofs_y = -2;
} else {
// 等宽的字符不需要获取宽度,直接使用 16
dsc_out->adv_w = 16;
dsc_out->box_w = 16;
dsc_out->ofs_y = 0;
}
dsc_out->box_h = 16;
dsc_out->ofs_x = 0;
dsc_out->bpp = 4; // 灰度字库位深 = 4
dsc_out->is_placeholder = false;
return true;
}
2.4 灰度字库 — LVGL V9.3
static bool _get_gt_font_glyph_dsc_16(const lv_font_t * font,
lv_font_glyph_dsc_t * dsc_out,
uint32_t unicode_letter,
uint32_t unicode_letter_next)
{
lv_font_fmt_txt_dsc_t * fdsc = (lv_font_fmt_txt_dsc_t *)font->dsc;
if (unicode_letter >= 0x20 && unicode_letter < 0x80) {
// 不等宽的字符,获取的数据前两个字节表示文字的宽度
GT_GetASCII(unicode_letter, GT_OPT_ASCII_16_ZK_4bit, &fdsc->glyph_bitmap[0]);
dsc_out->adv_w = fdsc->glyph_bitmap[1];
dsc_out->box_w = 16;
dsc_out->ofs_y = -2;
} else {
dsc_out->adv_w = 16;
dsc_out->box_w = 16;
dsc_out->ofs_y = 0;
}
dsc_out->box_h = 16;
dsc_out->ofs_x = 0;
dsc_out->format = 4; // LVGL V9 使用 format
dsc_out->is_placeholder = false;
dsc_out->gid.index = unicode_letter;
return true;
}
2.5 矢量字库 — LVGL V8.4
芯片:GT5SLAD3BFA,24 号字体,等宽。bpp = 1,矢量数据最终以点阵位图输出。
static bool _get_gt_font_glyph_dsc_24(const lv_font_t * font,
lv_font_glyph_dsc_t * dsc_out,
uint32_t unicode_letter,
uint32_t unicode_letter_next)
{
// 判断编码范围
if (unicode_letter >= 0x20 && unicode_letter < 0x80) {
dsc_out->adv_w = 24; // 文字的实际宽度
dsc_out->box_w = 24; // 文字的显示宽度
dsc_out->ofs_y = -2; // 文字的显示位置偏移
} else {
dsc_out->adv_w = 24;
dsc_out->box_w = 24;
dsc_out->ofs_y = 0;
}
dsc_out->box_h = 24;
dsc_out->ofs_x = 0;
dsc_out->bpp = 1;
dsc_out->is_placeholder = false;
return true;
}
3. 实现 get_glyph_bitmap 函数
get_glyph_bitmap 函数用于获取字符的实际位图数据。
注意:LVGL V8.4 与 V9.3 的函数签名完全不同。V8.4 直接接收字体指针和 Unicode 编码,返回位图数据指针;V9.3 接收
lv_font_glyph_dsc_t和lv_draw_buf_t,将渲染结果写入绘制缓冲区并返回。
3.1 点阵字库 — LVGL V8.4
static const uint8_t * _get_gt_font_bitmap_fmt_txt_16(const lv_font_t * font,
uint32_t unicode_letter)
{
lv_font_fmt_txt_dsc_t * fdsc = (lv_font_fmt_txt_dsc_t *)font->dsc;
if (fdsc->bitmap_format == LV_FONT_FMT_TXT_PLAIN) {
if (unicode_letter >= 0x20 && unicode_letter < 0x80) {
// 读取 ASCII 字符
ASCII_GetData(unicode_letter, ASCII_8X16, &fdsc->glyph_bitmap[0]);
return &fdsc->glyph_bitmap[0];
} else {
#if 01
// Unicode 转换为 GBK 编码
uint32_t gbk = U2G(unicode_letter);
if (0 == gbk) {
return NULL;
}
// 读取 GBK 字符
gt_16_GetData((gbk >> 8) & 0xFF, gbk & 0xFF, &fdsc->glyph_bitmap[0]);
#else
U2G_GetData_16X16(unicode_letter, &fdsc->glyph_bitmap[0]);
#endif
return &fdsc->glyph_bitmap[0];
}
}
return NULL;
}
3.2 点阵字库 — LVGL V9.3
LVGL V9.3 的 get_glyph_bitmap 使用全新的 API,需要自行按位展开点阵数据到绘制缓冲区。
const void * _get_gt_font_bitmap_fmt_txt_16(lv_font_glyph_dsc_t * g_dsc,
lv_draw_buf_t * draw_buf)
{
const lv_font_t * font = g_dsc->resolved_font;
uint8_t * bitmap_out = draw_buf->data;
lv_font_fmt_txt_dsc_t * fdsc = (lv_font_fmt_txt_dsc_t *)font->dsc;
uint32_t unicode_letter = g_dsc->gid.index;
bool byte_aligned = fdsc->bitmap_format == LV_FONT_FMT_PLAIN_ALIGNED;
if (fdsc->bitmap_format == LV_FONT_FMT_TXT_PLAIN) {
// 读取数据
if (unicode_letter >= 0x20 && unicode_letter < 0x80) {
// 读取 ASCII 字符
ASCII_GetData(unicode_letter, ASCII_8X16, &fdsc->glyph_bitmap[0]);
} else {
#if 01
// Unicode 转换为 GBK 编码
uint32_t gbk = U2G(unicode_letter);
if (0 == gbk) {
return NULL;
}
// 读取 GBK 字符
gt_16_GetData((gbk >> 8) & 0xFF, gbk & 0xFF, &fdsc->glyph_bitmap[0]);
#else
U2G_GetData_16X16(unicode_letter, &fdsc->glyph_bitmap[0]);
#endif
}
const uint8_t * bitmap_in = &fdsc->glyph_bitmap[0];
uint8_t * bitmap_out_tmp = bitmap_out;
int32_t i = 0;
int32_t x, y;
uint32_t stride = lv_draw_buf_width_to_stride(g_dsc->box_w, LV_COLOR_FORMAT_A8);
if (fdsc->bpp == 1) {
for (y = 0; y < g_dsc->box_h; y++) {
for (x = 0; x < g_dsc->box_w; x++, i++) {
i = i & 0x7;
if (i == 0) bitmap_out_tmp[x] = (*bitmap_in) & 0x80 ? 0xff : 0x00;
else if (i == 1) bitmap_out_tmp[x] = (*bitmap_in) & 0x40 ? 0xff : 0x00;
else if (i == 2) bitmap_out_tmp[x] = (*bitmap_in) & 0x20 ? 0xff : 0x00;
else if (i == 3) bitmap_out_tmp[x] = (*bitmap_in) & 0x10 ? 0xff : 0x00;
else if (i == 4) bitmap_out_tmp[x] = (*bitmap_in) & 0x08 ? 0xff : 0x00;
else if (i == 5) bitmap_out_tmp[x] = (*bitmap_in) & 0x04 ? 0xff : 0x00;
else if (i == 6) bitmap_out_tmp[x] = (*bitmap_in) & 0x02 ? 0xff : 0x00;
else if (i == 7) {
bitmap_out_tmp[x] = (*bitmap_in) & 0x01 ? 0xff : 0x00;
bitmap_in++;
}
}
// 如果在字节中间停止且下一行是字节对齐的,跳到下一个字节
if (byte_aligned && i != 0) {
i = 0;
bitmap_in++;
}
bitmap_out_tmp += stride;
}
}
return draw_buf;
}
return NULL;
}
3.3 灰度字库 — LVGL V8.4
灰度字库 bpp = 4,ASCII 数据前 2 字节为宽度信息,位图数据从第 3 字节(索引 2)开始。
static const uint8_t * _get_gt_font_bitmap_fmt_txt_16(const lv_font_t * font,
uint32_t unicode_letter)
{
lv_font_fmt_txt_dsc_t * fdsc = (lv_font_fmt_txt_dsc_t *)font->dsc;
if (fdsc->bitmap_format == LV_FONT_FMT_TXT_PLAIN) {
if (unicode_letter >= 0x20 && unicode_letter < 0x80) {
// 读取 ASCII 字符(前 2 字节为宽度,数据从索引 2 开始)
GT_GetASCII(unicode_letter, GT_OPT_ASCII_16_ZK_4bit, &fdsc->glyph_bitmap[0]);
return &fdsc->glyph_bitmap[2];
} else {
// Unicode 转换为 GBK 编码
uint32_t gbk = UnicodeToGBK(unicode_letter);
if (0 == gbk) {
return NULL;
}
// 读取 GBK 字符
get_offset_GBK(gbk, GT_OPT_GBK_16_HT_4bit, &fdsc->glyph_bitmap[0]);
return &fdsc->glyph_bitmap[0];
}
}
return NULL;
}
3.4 灰度字库 — LVGL V9.3
灰度 4 bit 需要查表转换为 8 bit 灰度值(opa4_table),并按 nibble 展开。
static const uint8_t opa4_table[16] = {
0, 17, 34, 51,
68, 85, 102, 119,
136, 153, 170, 187,
204, 221, 238, 255
};
const void * _get_gt_font_bitmap_fmt_txt_16(lv_font_glyph_dsc_t * g_dsc,
lv_draw_buf_t * draw_buf)
{
const lv_font_t * font = g_dsc->resolved_font;
uint8_t * bitmap_out = draw_buf->data;
lv_font_fmt_txt_dsc_t * fdsc = (lv_font_fmt_txt_dsc_t *)font->dsc;
uint32_t unicode_letter = g_dsc->gid.index;
bool byte_aligned = fdsc->bitmap_format == LV_FONT_FMT_PLAIN_ALIGNED;
if (fdsc->bitmap_format == LV_FONT_FMT_TXT_PLAIN) {
const uint8_t * bitmap_in = NULL;
// 读取数据
if (unicode_letter >= 0x20 && unicode_letter < 0x80) {
// 读取 ASCII 字符
GT_GetASCII(unicode_letter, GT_OPT_ASCII_16_ZK_4bit, &fdsc->glyph_bitmap[0]);
bitmap_in = &fdsc->glyph_bitmap[2];
} else {
// Unicode 转换为 GBK 编码
uint32_t gbk = UnicodeToGBK(unicode_letter);
if (0 == gbk) {
return NULL;
}
// 读取 GBK 字符
get_offset_GBK(gbk, GT_OPT_GBK_16_HT_4bit, &fdsc->glyph_bitmap[0]);
bitmap_in = &fdsc->glyph_bitmap[0];
}
if (bitmap_in == NULL) {
return NULL;
}
uint8_t * bitmap_out_tmp = bitmap_out;
int32_t i = 0;
int32_t x, y;
uint32_t stride = lv_draw_buf_width_to_stride(g_dsc->box_w, LV_COLOR_FORMAT_A8);
if (fdsc->bpp == 1) {
for (y = 0; y < g_dsc->box_h; y++) {
for (x = 0; x < g_dsc->box_w; x++, i++) {
i = i & 0x7;
if (i == 0) bitmap_out_tmp[x] = (*bitmap_in) & 0x80 ? 0xff : 0x00;
else if (i == 1) bitmap_out_tmp[x] = (*bitmap_in) & 0x40 ? 0xff : 0x00;
else if (i == 2) bitmap_out_tmp[x] = (*bitmap_in) & 0x20 ? 0xff : 0x00;
else if (i == 3) bitmap_out_tmp[x] = (*bitmap_in) & 0x10 ? 0xff : 0x00;
else if (i == 4) bitmap_out_tmp[x] = (*bitmap_in) & 0x08 ? 0xff : 0x00;
else if (i == 5) bitmap_out_tmp[x] = (*bitmap_in) & 0x04 ? 0xff : 0x00;
else if (i == 6) bitmap_out_tmp[x] = (*bitmap_in) & 0x02 ? 0xff : 0x00;
else if (i == 7) {
bitmap_out_tmp[x] = (*bitmap_in) & 0x01 ? 0xff : 0x00;
bitmap_in++;
}
}
if (byte_aligned && i != 0) {
i = 0;
bitmap_in++;
}
bitmap_out_tmp += stride;
}
} else if (fdsc->bpp == 4) {
for (y = 0; y < g_dsc->box_h; y++) {
for (x = 0; x < g_dsc->box_w; x++, i++) {
i = i & 0x1;
if (i == 0) {
bitmap_out_tmp[x] = opa4_table[(*bitmap_in) >> 4];
} else if (i == 1) {
bitmap_out_tmp[x] = opa4_table[(*bitmap_in) & 0xF];
bitmap_in++;
}
}
if (byte_aligned && i != 0) {
i = 0;
bitmap_in++;
}
bitmap_out_tmp += stride;
}
}
return draw_buf;
}
return NULL;
}
3.5 矢量字库 — LVGL V8.4
矢量字库调用 get_font() 获取渲染后的位图数据。
static const uint8_t * _get_gt_font_bitmap_fmt_txt_24(const lv_font_t * font,
uint32_t unicode_letter)
{
lv_font_fmt_txt_dsc_t * fdsc = (lv_font_fmt_txt_dsc_t *)font->dsc;
// 判断位图的存储格式
if (fdsc->bitmap_format == LV_FONT_FMT_TXT_PLAIN) {
// 判断编码范围
if (unicode_letter >= 0x20 && unicode_letter < 0x80) {
// 读取 ASCII 字符
get_font(&fdsc->glyph_bitmap[0], VEC_FT_ASCII_STY, unicode_letter, 24, 24, 24);
return &fdsc->glyph_bitmap[0]; // 返回存储数组的首地址
} else {
// Unicode 转换为 GBK 编码
uint32_t gbk = U2G(unicode_letter);
if (0 == gbk) {
return NULL;
}
// 读取 GBK 字符
get_font(&fdsc->glyph_bitmap[0], VEC_SONG_STY, unicode_letter, 24, 24, 24);
return &fdsc->glyph_bitmap[0];
}
}
return NULL;
}
4. 使用
定义好字体变量并实现回调函数后,即可通过 LVGL 标准 API 使用高通字库:
lv_obj_set_style_text_font(label, >30l24a3w_montserrat_16, 0);
将字体对象(如 gt30l24a3w_montserrat_16)传入 lv_obj_set_style_text_font() 即可为控件设置自定义字体。
版本兼容性说明
| 特性 | LVGL V8.4 | LVGL V9.3 |
|---|---|---|
| 字体变量声明 | #if LV_VERSION_CHECK(8,0,0) 条件编译 | 统一 const lv_font_t |
| get_glyph_dsc 位深字段 | dsc_out->bpp | dsc_out->format |
| get_glyph_dsc 字符标识 | 无 | dsc_out->gid.index = unicode_letter |
| get_glyph_bitmap 签名 | (const lv_font_t*, uint32_t) → const uint8_t* | (lv_font_glyph_dsc_t*, lv_draw_buf_t*) → const void* |
| get_glyph_bitmap 返回 | 直接返回位图数据指针 | 写入 draw_buf->data 后返回 draw_buf |
| 缓存 | lv_font_fmt_txt_glyph_cache_t cache | 无需手动管理 |
| 下划线 | #if LV_VERSION_CHECK(7,4,0) 条件 | 默认支持 |

