使用目前最新的 Releases 版本 LVGL v8.2.0 ,移植好 freetype2 后测试,发现每次启动程序时会高概率出现屏幕显示全白色背景,无任何内容的问题。

打开日志开关,出现问题时会有如下打印:

lv_font_get_glyph_bitmap: Asserted at expression: font_p != NULL (NULL pointer)        (in lv_font.c line #51)

Lvgl 检测到了 font_p 参数为空指针,根据日志信息指引,出现问题的代码块如下:

const uint8_t * lv_font_get_glyph_bitmap(const lv_font_t * font_p, uint32_t letter)
{
    LV_ASSERT_NULL(font_p);  // 报错的地方
    return font_p->get_glyph_bitmap(font_p, letter);
}
#if LV_USE_ASSERT_NULL
#   define LV_ASSERT_NULL(p) LV_ASSERT_MSG(p != NULL, "NULL pointer");
#else
#   define LV_ASSERT_NULL(p)
#endif

回溯分析发现是 lv_draw_sw_letter() 内部通过 lv_font_get_glyph_dsc() 获取:

void lv_draw_sw_letter(lv_draw_ctx_t * draw_ctx, const lv_draw_label_dsc_t * dsc,  const lv_point_t * pos_p,
                       uint32_t letter)
{
    lv_font_glyph_dsc_t g;
    bool g_ret = lv_font_get_glyph_dsc(dsc->font, &g, letter, '\0');
    if(g_ret == false)  {
        /*Add warning if the dsc is not found
         *but do not print warning for non printable ASCII chars (e.g. '\n')*/
        if(letter >= 0x20 &&
           letter != 0xf8ff && /*LV_SYMBOL_DUMMY*/
           letter != 0x200c) { /*ZERO WIDTH NON-JOINER*/
            LV_LOG_WARN("lv_draw_letter: glyph dsc. not found for U+%X", (unsigned int)letter);
        }
        return;
    }

    /*Don't draw anything if the character is empty. E.g. space*/
    if((g.box_h == 0) || (g.box_w == 0)) return;

    lv_point_t gpos;
    gpos.x = pos_p->x + g.ofs_x;
    gpos.y = pos_p->y + (dsc->font->line_height - dsc->font->base_line) - g.box_h - g.ofs_y;

    /*If the letter is completely out of mask don't draw it*/
    if(gpos.x + g.box_w < draw_ctx->clip_area->x1 ||
       gpos.x > draw_ctx->clip_area->x2 ||
       gpos.y + g.box_h < draw_ctx->clip_area->y1 ||
       gpos.y > draw_ctx->clip_area->y2)  {
        return;
    }
    // 出现问题的时候, g.resolved_font 为空指针
    const uint8_t * map_p = lv_font_get_glyph_bitmap(g.resolved_font, letter);
    if(map_p == NULL) {
        LV_LOG_WARN("lv_draw_letter: character's bitmap not found");
        return;
    }

    if(g.resolved_font->subpx) {
#if LV_DRAW_COMPLEX && LV_USE_FONT_SUBPX
        draw_letter_subpx(draw_ctx, dsc, &gpos, &g, map_p);
#else
        LV_LOG_WARN("Can't draw sub-pixel rendered letter because LV_USE_FONT_SUBPX == 0 in lv_conf.h");
#endif
    }
    else {
        draw_letter_normal(draw_ctx, dsc, &gpos, &g, map_p);
    }
}

在 lv_font_get_glyph_dsc() 内部会预先设置为空指针,然后再去寻找对应的描述符:

bool lv_font_get_glyph_dsc(const lv_font_t * font_p, lv_font_glyph_dsc_t * dsc_out, uint32_t letter,
                           uint32_t letter_next)
{
    LV_ASSERT_NULL(font_p);
    LV_ASSERT_NULL(dsc_out);
    dsc_out->resolved_font = NULL;   // 这里直接设置为空指针
    const lv_font_t * f = font_p;
    bool found = false;
    while(f) {
        found = f->get_glyph_dsc(f, dsc_out, letter, letter_next);
        // 只有找到描述符并且不是占位符的情况下才会重新赋值
        if(found && !dsc_out->is_placeholder) {
            dsc_out->resolved_font = f;
            break;
        }
        f = f->fallback;
    }
    return found;
}

问题就出现在寻找字形描述符时,如果没有找到或者找到但是是占位符的情况,那么被设置为空指针的地方并未正确赋值。

可以修改不赋值空指针,而是仍保留原指针的方式来修复此问题:

bool lv_font_get_glyph_dsc(const lv_font_t * font_p, lv_font_glyph_dsc_t * dsc_out, uint32_t letter,
                           uint32_t letter_next)
{
    LV_ASSERT_NULL(font_p);
    LV_ASSERT_NULL(dsc_out);
    dsc_out->resolved_font = font_p;   // 这里保留原有的指针
    const lv_font_t * f = font_p;
    bool found = false;
    while(f) {
        found = f->get_glyph_dsc(f, dsc_out, letter, letter_next);
        // 只有找到描述符并且不是占位符的情况下才会重新赋值
        if(found && !dsc_out->is_placeholder) {
            dsc_out->resolved_font = f;
            break;
        }
        f = f->fallback;
    }
    return found;
}
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐