阅读并理解lv_demo_widgets()函数

阅读并理解lv_demo_widgets()函数

现在我就是一个小白,我们用左键点进去这个函数lv_demo_widgets(),在LVGL.Simulator.cpp文件107行中。当前任务就是理解这部分代码,它在干了些什么事情,为什么这样设置,能不能不这样设置?我们把代码分为3部分看:

  1. 设置屏幕大小
  2. 设置字体和样式,样式包括文本颜色,边框等等。
  3. 创建了lv_tabview,增加了3个tab,Profile、Analytics、Shop。

设置屏幕大小

把代码放上来:

if(LV_HOR_RES <= 320) disp_size = DISP_SMALL;
   else if(LV_HOR_RES < 720) disp_size = DISP_MEDIUM;
   else disp_size = DISP_LARGE;

理解起来还比较容易,判断当前屏幕横向大小,然后定义屏幕的size为小、中、大。
接下来我们理解 LV_HOR_RES 代表什么意思?继续左键点进去看。在lv_disp.h这个头文件中看到了定义:

#ifndef LV_HOR_RES
/**
 * The horizontal resolution of the currently active display.
 */
#define LV_HOR_RES lv_disp_get_hor_res(lv_disp_get_default())
#endif

实现是在lv_hal_disp.c文件中

/**
 * Get the horizontal resolution of a display
 * @param disp pointer to a display (NULL to use the default display)
 * @return the horizontal resolution of the display
 */
lv_coord_t lv_disp_get_hor_res(lv_disp_t * disp)
{
    if(disp == NULL) disp = lv_disp_get_default();

    if(disp == NULL) {
        return 0;
    }
    else {
        switch(disp->driver->rotated) {
            case LV_DISP_ROT_90:
            case LV_DISP_ROT_270:
                return disp->driver->ver_res;
            default:
                return disp->driver->hor_res;
        }
    }
}

用在线的翻译框翻译一下,
翻译
这时,可以知道,LV_HOR_RES 是可以直接获取默认屏幕的水平分辨率的,就是横向尺寸。我提个问题,分辨率是像素吗?百度百科中说,描述分辨率单位有很多,重要两个dpi(点每英寸),ppi(像素每英寸)。ppi是主要用在显示领域,dpi则是在打印领域。

  • dpi:点每英寸是描述在水平的和垂直的方向上,每英寸距离的图像包含多少个打印点
  • ppi:它是描述在水平的和垂直的方向上,每英寸距离的图像包含的像素(pixel)数目
    在这里应该是可以理解问像素的。

DISP_SALL 这3个呢?这里只是知道它们是一个disp_size_t的枚举,看不出来是啥?先跳过。

/**********************
 *      TYPEDEFS
 **********************/
typedef enum {
    DISP_SMALL,
    DISP_MEDIUM,
    DISP_LARGE,
}disp_size_t;

设置字体和样式

同样地,先上代码

// 文件内的静态定义
static const lv_font_t * font_large;
static const lv_font_t * font_normal;

// lv_demo_widgets 函数内容

    font_large = LV_FONT_DEFAULT;
    font_normal = LV_FONT_DEFAULT;

    lv_coord_t tab_h;
    if(disp_size == DISP_LARGE) {
        tab_h = 70;
#if LV_FONT_MONTSERRAT_24
        font_large     =  &lv_font_montserrat_24;
#else
        LV_LOG_WARN("LV_FONT_MONTSERRAT_24 is not enabled for the widgets demo. Using LV_FONT_DEFAULT instead.");
#endif
#if LV_FONT_MONTSERRAT_16
        font_normal    =  &lv_font_montserrat_16;
#else
        LV_LOG_WARN("LV_FONT_MONTSERRAT_16 is not enabled for the widgets demo. Using LV_FONT_DEFAULT instead.");
#endif
    } else if(disp_size == DISP_MEDIUM) {
        tab_h = 45;
#if LV_FONT_MONTSERRAT_20
        font_large     =  &lv_font_montserrat_20;
#else
        LV_LOG_WARN("LV_FONT_MONTSERRAT_20 is not enabled for the widgets demo. Using LV_FONT_DEFAULT instead.");
#endif
#if LV_FONT_MONTSERRAT_14
        font_normal    =  &lv_font_montserrat_14;
#else
        LV_LOG_WARN("LV_FONT_MONTSERRAT_14 is not enabled for the widgets demo. Using LV_FONT_DEFAULT instead.");
#endif
    } else { /* disp_size == DISP_SMALL */
        tab_h = 45;
#if LV_FONT_MONTSERRAT_18
        font_large     =  &lv_font_montserrat_18;
#else
    LV_LOG_WARN("LV_FONT_MONTSERRAT_18 is not enabled for the widgets demo. Using LV_FONT_DEFAULT instead.");
#endif
#if LV_FONT_MONTSERRAT_12
        font_normal    =  &lv_font_montserrat_12;
#else
    LV_LOG_WARN("LV_FONT_MONTSERRAT_12 is not enabled for the widgets demo. Using LV_FONT_DEFAULT instead.");
#endif
    }

#if LV_USE_THEME_DEFAULT
    lv_theme_default_init(NULL, lv_palette_main(LV_PALETTE_BLUE), lv_palette_main(LV_PALETTE_RED), LV_THEME_DEFAULT_DARK, font_normal);
#endif

    lv_style_init(&style_text_muted);
    lv_style_set_text_opa(&style_text_muted, LV_OPA_50);

    lv_style_init(&style_title);
    lv_style_set_text_font(&style_title, font_large);

    lv_style_init(&style_icon);
    lv_style_set_text_color(&style_icon, lv_theme_get_color_primary(NULL));
    lv_style_set_text_font(&style_icon, font_large);

    lv_style_init(&style_bullet);
    lv_style_set_border_width(&style_bullet, 0);
    lv_style_set_radius(&style_bullet, LV_RADIUS_CIRCLE);

从字面上理解,定义了两个lv_font_t * 的指针,都指向了LV_FONT_DEFAULT这个变量。根据屏幕水平分辨率去判断使用的字号,比如,大尺寸的时候,大字体是使用24号,正常的使用16号的,字体用的MONTSERRAT这款字体。分别设置文本、图标、按钮。(style_bullet,我这里理解问按钮样式了)。
中午接着补上内容,先搬砖。

lv_font_t

先看一下这个是什么东西?在lv_font.h里面有定义:

typedef struct _lv_font_t {
    /** Get a glyph's descriptor from a font*/
    bool (*get_glyph_dsc)(const struct _lv_font_t *, lv_font_glyph_dsc_t *, uint32_t letter, uint32_t letter_next);

    /** Get a glyph's bitmap from a font*/
    const uint8_t * (*get_glyph_bitmap)(const struct _lv_font_t *, uint32_t);

    /*Pointer to the font in a font pack (must have the same line height)*/
    lv_coord_t line_height;         /**< The real line height where any text fits*/
    lv_coord_t base_line;           /**< Base line measured from the top of the line_height*/
    uint8_t subpx  : 2;             /**< An element of `lv_font_subpx_t`*/

    int8_t underline_position;      /**< Distance between the top of the underline and base line (< 0 means below the base line)*/
    int8_t underline_thickness;     /**< Thickness of the underline*/

    const void * dsc;               /**< Store implementation specific or run_time data or caching here*/
    const struct _lv_font_t * fallback;   /**< Fallback font for missing glyph. Resolved recursively */
#if LV_USE_USER_DATA
    void * user_data;               /**< Custom user data for font.*/
#endif
} lv_font_t;

如果需要理解这个是什么意思?需要补一些关于字体的知识,后面要调字体也会用到freetype2,所以可以找这方面的知识,在csdn站内也有很多这些知识,可以搜来学习。还推荐一个苹果的字体手册fonts.
这里面说明一个truetype字体文件内容,其实可以这么理解,就是有一个字体数据库,库里面有各种字体的位图,他们放在不同的表里面。怎么查到这些字体内,一般根据unicode码去索引。再来看位图1 里面有什么信息:这个定义了字体的baseline,字体宽度,字体高度等信息。
图1:
图1中字位图
如果使用freetype的话,这里用了一张绿野耕夫博客的图,看到一个字体的信息有这么多信息
图2:
freetype
回到lv_font_t这个结构体重看,应该能看懂一些了,重要的如:

  • get_glyph_dsc : 或者字体描述的,如是什么字体等信息
  • const uint8_t * : 位图信息
  • line_height : 实际行高

其实用我们古代的活字印刷术更好理解,先把写好的字体放在文件中,你要用的时候就先找到里面的文字,最后根据设置的比例印到屏幕上。这样屏幕就能看到文字了。

查看lv_font_montserrat_24这个结构体

从上面代码看,当disp_size = DISP_LARGE 时, font_large = &lv_font_montserrat_24。我们点进去看这个结构体。
代码在lv_font_montserrat_24.c文件中,如下:

if LV_VERSION_CHECK(8, 0, 0)
const lv_font_t lv_font_montserrat_24 = {
#else
lv_font_t lv_font_montserrat_24 = {
#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 = 27,          /*The maximum line height required by the font*/
    .base_line = 5,             /*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)
    .underline_position = -2,
    .underline_thickness = 1,
#endif
    .dsc = &font_dsc           /*The custom font data. Will be accessed by `get_glyph_bitmap/dsc` */
};

这里只是简单地定义了一个结构体,接着看到两个重要的函数:

  • lv_font_get_glyph_dsc_fmt_txt
  • lv_font_get_bitmap_fmt_txt

好奇,继续看一下这两个函数。

lv_font_get_glyph_dsc_fmt_txt

这个函数,前面理解是获取到字体的字体描述,这里可以印证一下。先贴一段代码:

/**
 * Used as `get_glyph_dsc` callback in LittelvGL's native font format if the font is uncompressed.
 * @param font_p pointer to font
 * @param dsc_out store the result descriptor here
 * @param letter an UNICODE letter code
 * @return true: descriptor is successfully loaded into `dsc_out`.
 *         false: the letter was not found, no data is loaded to `dsc_out`
 */
bool lv_font_get_glyph_dsc_fmt_txt(const lv_font_t * font, lv_font_glyph_dsc_t * dsc_out, uint32_t unicode_letter,
                                   uint32_t unicode_letter_next)
{
    bool is_tab = false;
    if(unicode_letter == '\t') {
        unicode_letter = ' ';
        is_tab = true;
    }
    lv_font_fmt_txt_dsc_t * fdsc = (lv_font_fmt_txt_dsc_t *)font->dsc;
    uint32_t gid = get_glyph_dsc_id(font, unicode_letter);
    if(!gid) return false;

    int8_t kvalue = 0;
    if(fdsc->kern_dsc) {
        uint32_t gid_next = get_glyph_dsc_id(font, unicode_letter_next);
        if(gid_next) {
            kvalue = get_kern_value(font, gid, gid_next);
        }
    }

    /*Put together a glyph dsc*/
    const lv_font_fmt_txt_glyph_dsc_t * gdsc = &fdsc->glyph_dsc[gid];

    int32_t kv = ((int32_t)((int32_t)kvalue * fdsc->kern_scale) >> 4);

    uint32_t adv_w = gdsc->adv_w;
    if(is_tab) adv_w *= 2;

    adv_w += kv;
    adv_w  = (adv_w + (1 << 3)) >> 4;

    dsc_out->adv_w = adv_w;
    dsc_out->box_h = gdsc->box_h;
    dsc_out->box_w = gdsc->box_w;
    dsc_out->ofs_x = gdsc->ofs_x;
    dsc_out->ofs_y = gdsc->ofs_y;
    dsc_out->bpp   = (uint8_t)fdsc->bpp;

    if(is_tab) dsc_out->box_w = dsc_out->box_w * 2;

    return true;
}

从这里可以看到,并没有获取整体字体信息,我原本以为是能够获取到如微软雅黑这种整体的字体信息。从返回值看,反而是获取到某个字体的盒子信息,如:

  • adv_w :字体间距
  • box_h : 盒子高度

等等,可以配合上面图2的值看。这段代码,用当前的unicode字符和下一个unicode字符去获取当前的字体盒子信息。其中get_glyph_dsc_id() 函数是根据unicode字符获取位图id, get_kern_value()根据2个gid去获取kern 信息,即字符间距,最终的字符间距是要和缩放比例计算的。
只懂CRUD的我,这个函数可以通过字符索引到字符的id,并且通过id能拿到字符的高度间距等信息。接下来看另一个函数:

lv_font_get_bitmap_fmt_txt

看注释Function pointer to get glyph’s bitmap,是能够获取到字符的位图的函数的。点进去看代码:

const uint8_t * lv_font_get_bitmap_fmt_txt(const lv_font_t * font, uint32_t unicode_letter)
{
    if(unicode_letter == '\t') unicode_letter = ' ';

    lv_font_fmt_txt_dsc_t * fdsc = (lv_font_fmt_txt_dsc_t *)font->dsc;
    uint32_t gid = get_glyph_dsc_id(font, unicode_letter);
    if(!gid) return NULL;

    const lv_font_fmt_txt_glyph_dsc_t * gdsc = &fdsc->glyph_dsc[gid];

    if(fdsc->bitmap_format == LV_FONT_FMT_TXT_PLAIN) {
        return &fdsc->glyph_bitmap[gdsc->bitmap_index];
    }
    ..... //省略else部分
}

为什么省略呢,不占边幅,else是说明怎么从压缩字体中获取位图,可自行了解。这里只看if true部分,先获取到字体的描述函数,根据unicode获取字符的id,根据id查找到具体的字符位图信息,这里面包括了位图信息,盒子信息,就是lv_font_get_glyph_dsc_fmt_txt函数运行的结果,条件是先判断字符位图的格式 == LV_FONT_FMT_TXT_PLAIN。最后返回位图字节数组。

其实,这时候有个问题想问一下,这两个函数在什么地方调的呢?按照理解,应该是在渲染文字的时候会调用的。

字体这部分的总结

我们提供了一个结构体,这个结构体定义了2个重要的函数,这2个函数能够帮助我们根据字符去找到字符的描述信息,以及最终需要渲染的位图信息。这些位图信息也是我们提供的,这里看一下最终渲染的位图是啥样的?
在lv_font_montserrat_24.c文件拉到最开始的地方,这个uint8_t的字符数组
const uint8_t glyph_bitmap[],就是我们提供的位图信息:

static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
    /* U+0020 " " */

    /* U+0021 "!" */
    0x1f, 0xf8, 0x1f, 0xf8, 0xf, 0xf7, 0xf, 0xf7,
    0xf, 0xf6, 0xe, 0xf5, 0xe, 0xf5, 0xd, 0xf4,
    0xd, 0xf3, 0xc, 0xf3, 0xb, 0xf2, 0x7, 0xa1,
    0x0, 0x0, 0x0, 0x0, 0x1d, 0xf6, 0x4f, 0xfb,
    0xc, 0xe5,
 }

这些信息要怎么获取呢?lvgl提供了工具font工具可以从ttf,woff 等字体文件中提取,当然也可以从freetype提供的库直接从ttf文件中提取。这两者的区别是啥?提供静态字符的方式,每定义一个字号都需要一个文件,lv_font_montserrat_24、lv_font_montserrat_16、lv_font_montserrat_14,如果你的项目需要更多变化的字号,则需要更多的文件。所以这需要看你项目的取舍了,因为加载freetype库也需要占用内存的。这里想吐槽一个事,如果弄中文的是不现实的,用例子的方式是不现实的,因为全中文有2万7千个字符左右,常用的需要7000个才能覆盖,除非你能确定字符的使用边界。
接下来我们看样式部分,分在另外一篇写了,这里太长了,继续学习吧。

  • 7
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值