高通字库
版本 V0.1 · 更新于 2024-10-15

三、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_tlv_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, &gt30l24a3w_montserrat_16, 0);

将字体对象(如 gt30l24a3w_montserrat_16)传入 lv_obj_set_style_text_font() 即可为控件设置自定义字体。


版本兼容性说明

特性LVGL V8.4LVGL V9.3
字体变量声明#if LV_VERSION_CHECK(8,0,0) 条件编译统一 const lv_font_t
get_glyph_dsc 位深字段dsc_out->bppdsc_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) 条件默认支持