littlevGL:字体与汉字

使用各种嵌入式GUI时,总会遇到“汉字显示”“字体”这些关卡。

阅读本文前,最好已经了解Uincode,UTF-8,UTF-16,GBK,GB2312相关知识,不懂最好网络搜索相关知识。

1.内置字体

littlevGL内置了好几种字体。在lv_conf.h中开关相关字体

/*==================
 *    FONT USAGE
 *===================*/

/* More info about fonts: https://littlevgl.com/basics#fonts
 * To enable a built-in font use 1,2,4 or 8 values
 * which will determine the bit-per-pixel */
#define USE_LV_FONT_DEJAVU_10              0
#define USE_LV_FONT_DEJAVU_10_LATIN_SUP    0
#define USE_LV_FONT_DEJAVU_10_CYRILLIC     0
#define USE_LV_FONT_SYMBOL_10              0

#define USE_LV_FONT_DEJAVU_20              4
#define USE_LV_FONT_DEJAVU_20_LATIN_SUP    0
#define USE_LV_FONT_DEJAVU_20_CYRILLIC     0
#define USE_LV_FONT_SYMBOL_20              4

#define USE_LV_FONT_DEJAVU_30              0
#define USE_LV_FONT_DEJAVU_30_LATIN_SUP    0
#define USE_LV_FONT_DEJAVU_30_CYRILLIC     0
#define USE_LV_FONT_SYMBOL_30              0

#define USE_LV_FONT_DEJAVU_40              0
#define USE_LV_FONT_DEJAVU_40_LATIN_SUP    0
#define USE_LV_FONT_DEJAVU_40_CYRILLIC     0
#define USE_LV_FONT_SYMBOL_40              0

#define USE_LV_FONT_MONOSPACE_8            0

其中0代表不使用,1,2,4,8使能并设置抗锯齿值。

字体文件在lv_fonts文件夹下。

littlevGL支持UTF-8。在lv_conf.h中开启UTF8

/*Text settings*/
#define LV_TXT_UTF8             1                /*Enable UTF-8 coded Unicode character usage */

2.自定义字体

littlevGL支持自定义字体。

官方字体生成网站:https://littlevgl.com/ttf-font-to-c-array

不知为何作者没有发布离线字体生成工具。我打算过段时间有空就自己用C#写一个。

生成的字体文件类似于lv_fonts文件夹下面的各种字体文件,使用方式也一样。

使用之前需要把源文件转为UTF8格式。

//myfont是自定义的字体
void lv_tutorial_fonts(void)
{
    static lv_style_t style1;
    char *str="我的祖国\nis 好好的!";

    /*Create a style and use the new font*/
    lv_style_copy(&style1, &lv_style_plain);
    style1.text.font = &myfont; 

    /*Create a label and set new text*/
    lv_obj_t * label = lv_label_create(lv_scr_act(), NULL);
    lv_obj_set_pos(label, 100, 10);
    lv_label_set_style(label, &style1);
    lv_label_set_text(label, str ); 
}

3.外置字体

上面的方法只适合字体文件比较小的情况,要是字体太大,放不进MCU,那就需要更进一步。

对源文件的显示字体源码分析后,发现关键在于字体结构

typedef struct _lv_font_struct
{
    uint32_t unicode_first;
    uint32_t unicode_last;
    const uint8_t * glyph_bitmap;
    const lv_font_glyph_dsc_t * glyph_dsc;
    const uint32_t * unicode_list;
    const uint8_t * (*get_bitmap)(const struct _lv_font_struct *,uint32_t);     /*Get a glyph's  bitmap from a font*/
    int16_t (*get_width)(const struct _lv_font_struct *,uint32_t);        /*Get a glyph's with with a given font*/
    struct _lv_font_struct * next_page;    /*Pointer to a font extension*/
    uint32_t h_px       :8;
    uint32_t bpp        :4;                /*Bit per pixel: 1, 2 or 4*/
    uint32_t monospace  :8;                /*Fix width (0: normal width)*/
    uint16_t glyph_cnt;                    /*Number of glyphs (letters) in the font*/
} lv_font_t;

打开一个字体文件,其中字体定义

lv_font_t lv_font_dejavu_20 = {
    .unicode_first = 32,    /*First Unicode letter in this font*/
    .unicode_last = 126,    /*Last Unicode letter in this font*/
    .h_px = 20,             /*Font height in pixels*/
    .glyph_bitmap = lv_font_dejavu_20_glyph_bitmap, /*Bitmap of glyphs*/
    .glyph_dsc = lv_font_dejavu_20_glyph_dsc,       /*Description of glyphs*/
    .glyph_cnt = 95,            /*Number of glyphs in the font*/
    .unicode_list = NULL,   /*Every character in the font from 'unicode_first' to 'unicode_last'*/
    .get_bitmap = lv_font_get_bitmap_continuous,    /*Function pointer to get glyph's bitmap*/
    .get_width = lv_font_get_width_continuous,  /*Function pointer to get glyph's width*/
#if USE_LV_FONT_DEJAVU_20 == 1
    .bpp = 1,               /*Bit per pixel*/
#elif USE_LV_FONT_DEJAVU_20 == 2
    .bpp = 2,               /*Bit per pixel*/
#elif USE_LV_FONT_DEJAVU_20 == 4
    .bpp = 4,               /*Bit per pixel*/
#elif USE_LV_FONT_DEJAVU_20 == 8
    .bpp = 8,               /*Bit per pixel*/
#endif
    .monospace = 0,
    .next_page = NULL,      /*Pointer to a font extension*/
};

上面代码,lv_font_get_bitmap_continuous()函数为字体点阵获取函数,返回的是对应字符在点阵数组的位置。

另外还有些字体使用的是lv_font_get_bitmap_sparse();这两个函数功能是一样的。

由此,我们可以仿照此函数,想办法从外部存储器获得某个字符的点阵数据,并且保存在一个静态数组里,最后返回此数组首地址即可。

要完成这些,前提是1.把字符的点阵数组转化为BIN文件(去原子的论坛搜索下载C2B 1.1版本,2.0版本有BUG);2.把此文件弄到外置存储器。

对于第2点。有很多方法。比如通过写一个PC串口助手把文件发送烧写进SPI flash(W25QXX之类);或者直接使用专门的烧录工具写进SPI flash;或者偷懒,直接放在SD卡里让MCU调用。

这里我使用偷懒的方法,把字体文件放到SD卡里,以文件的形式读取数据。必须要说明,此方法频繁地进行文件操作,速度不咋地(最好把堆栈调大些,否则可能溢出)。

内置的字体点阵获取函数

/**
 * Generic bitmap get function used in 'font->get_bitmap' when the font NOT contains all characters in the range (sparse)
 * @param font pointer to font
 * @param unicode_letter an unicode letter which bitmap should be get
 * @return pointer to the bitmap or NULL if not found
 */
const uint8_t * lv_font_get_bitmap_sparse(const lv_font_t * font, uint32_t unicode_letter)
{
    /*Check the range*/
    if(unicode_letter < font->unicode_first || unicode_letter > font->unicode_last) return NULL;

    uint32_t i;
    for(i = 0; font->unicode_list[i] != 0; i++) {
        if(font->unicode_list[i] == unicode_letter) {
            return &font->glyph_bitmap[font->glyph_dsc[i].glyph_index];
        }
    }

    return NULL;
}

仿照上面,自定义的函数

//look above
const uint8_t * ex_lv_font_get_bitmap_sparse(const lv_font_t * font, uint32_t unicode_letter)
{
    uint8_t * pval=NULL; 
    uint32_t i;
    /*Check the range*/
    if(unicode_letter < font->unicode_first || unicode_letter > font->unicode_last){ return NULL;}

    for(i = 0; font->unicode_list[i] != 0; i++) 
    {
        if(font->unicode_list[i] == unicode_letter) 
        {
            FIL Binfile;
            FRESULT res;
            uint32_t br,fsize; 
            i=font->glyph_dsc[i].glyph_index;
            res=f_open(&Binfile, (const TCHAR*)font->glyph_bitmap ,FA_OPEN_EXISTING|FA_READ);
            if(res != FR_OK) { return NULL;}
            fsize = Binfile.fsize ;
            if(i+LetterSIZE <= fsize)
            {
                f_lseek(&Binfile,i );  
                res = f_read(&Binfile, letterBuff ,LetterSIZE ,&br);
                if( res == FR_OK && ( br == LetterSIZE || br == LetterSIZE/2 ) || br==0 )
                {
                    pval=letterBuff;
                }
            }
            f_close(&Binfile);
            
            return pval;
        }
    }
    return NULL;
}

然后要把字体的点阵数组删除,在字体定义里修改.glyph_bitmap和.glyph_bitmap成员。如下

lv_font_t myfont = 
{
    .unicode_first = 32,    /*First Unicode letter in this font*/
    .unicode_last = 40664,    /*First Unicode letter in this font*/
    .h_px = 37,                /*Font height in pixels*/
    .glyph_bitmap = "0:/FONT/HZ1A.bin",    /*Bitmap of glyphs*/
    .glyph_dsc = glyph_dsc,        /*Description of glyphs*/
    .unicode_list = unicode_list,    /*List of unicode characters*/
    .get_bitmap = ex_lv_font_get_bitmap_sparse,    /*Function pointer to get glyph's bitmap*/
    .get_width = lv_font_get_width_sparse,    /*Function pointer to get glyph's width*/
    .bpp = 1,                /*Bit per pixel*/
    .next_page = NULL,        /*Pointer to a font extension*/
};

"0:/FONT/HZ1A.bin"是字体文件路径。

再次说明,此方法不应该在实际项目中使用,效率不好。建议把字体文件按地址烧录进SPI flash,需要的时候再按照地址直接读取。上面的".glyph_bitmap"可赋值为该地址。原理都是一样的。

littlevGL:字体与汉字_第1张图片

 

你可能感兴趣的:(GUI)