docs(font_manager): add documentation and test cases (#7058)

Signed-off-by: FASTSHIFT <vifextech@foxmail.com>
Signed-off-by: pengyiqiang <pengyiqiang@xiaomi.com>
Co-authored-by: pengyiqiang <pengyiqiang@xiaomi.com>
This commit is contained in:
VIFEX
2024-10-16 00:34:45 +08:00
committed by GitHub
parent bbe5f032e6
commit 4f086111a1
10 changed files with 353 additions and 53 deletions

View File

@@ -0,0 +1,104 @@
.. _font_manager:
============
Font Manager
============
Font Manager is a secondary encapsulation based on :ref:`_freetype`, which facilitates upper-level applications to manage and use vector fonts.
Currently supported functions include:
- Font resource reference counting (reduces repeated creation of font resources).
- Font resource concatenation (font fallback).
- Font resource recycling mechanism (buffers recently deleted font resources to reduce the time overhead of repeated creation).
.. _font_manager_usage:
Usage
-----
Enable :c:macro:`LIB_FREETYPE` and `LV_USE_FONT_MANAGER` in ``lv_conf.h``.
Configure :c:macro:`LV_FONT_MANAGER_NAME_MAX_LEN` to set the maximum length of the font name.
Initialize Font Manager
~~~~~~~~~~~~~~~~~~~~~~~
Use :cpp:func:`lv_font_manager_create` to create a font manager, where the :cpp:func:`recycle_cache_size` parameter
is used to set the number of font recycling caches,which can improve font creation efficiency.
Use :cpp:func:`lv_font_manager_add_path_static` to add a mapping between the font file path and the custom font name,
so that the application can access the font resources more conveniently.
It should be noted that if the file path is not static (assigned from a local variable),
please use :cpp:func:`lv_font_manager_add_path` to add the path. This API will copy the path content to the internal management.
Use :cpp:func:`lv_font_manager_remove_path` to remove the font path mapping.
.. code-block:: c
static lv_font_manager_t * g_font_manager = NULL;
void font_manager_init(void)
{
/* Create font manager, with 8 fonts recycling buffers */
g_font_manager = lv_font_manager_create(8);
/* Add font path mapping to font manager */
lv_font_manager_add_path_static(g_font_manager, "Lato-Regular", "./lvgl/examples/libs/freetype/Lato-Regular.ttf");
lv_font_manager_add_path_static(g_font_manager, "MyFont", "./path/to/myfont.ttf");
}
Create Font from Font Manager
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Use :cpp:func:`lv_font_manager_create_font` to create a font. The parameters are basically the same as :cpp:func:`lv_freetype_font_create`.
The :cpp:func:`font_family` parameter can be filled with the names of multiple fonts (separated by :cpp:expr:`,`)
to achieve font concatenation (when the corresponding glyph is not found in a font file, it will automatically search from the next concatenated font).
.. code-block:: c
static lv_font_t * g_font = NULL;
/* Create font from font manager */
lv_font_t * g_font = lv_font_manager_create_font(g_font_manager,
"Lato-Regular,MyFont",
LV_FREETYPE_FONT_RENDER_MODE_BITMAP,
24,
LV_FREETYPE_FONT_STYLE_NORMAL);
/* Create label with the font */
lv_obj_t * label = lv_label_create(lv_screen_active());
lv_obj_set_style_text_font(label, g_font, 0);
lv_label_set_text(label, "Hello World!");
Delete Font
~~~~~~~~~~~
Use :cpp:func:`lv_font_manager_delete_font` to delete the font.
The font manager will mark the font resource as a recyclable font so that it can be quickly created next time.
Note that you need to delete the widget that references the font first, and then delete the font to avoid accessing wild pointers.
.. code-block:: c
/* Delete label and font */
lv_obj_delete(label);
lv_font_manager_delete_font(g_font_manager, g_font);
g_font = NULL;
Delete Font Manager
~~~~~~~~~~~~~~~~~~~
Use :cpp:func:`lv_font_manager_delete` to destroy the entire font manager. It should be noted that before destruction,
it is necessary to ensure that the application has deleted all fonts using :cpp:func:`lv_font_manager_delete_font`.
The font manager will check the reference status of all allocated fonts. If there are still fonts being referenced,
the font manager will fail to destroy and return false.
.. _font_manager_example:
Example
-------
.. include:: ../examples/others/font_manager/index.rst
.. _font_manager_api:
API
---

View File

@@ -0,0 +1,6 @@
Touchpad font manager example
-----------------------
.. lv_example:: others/monkey/lv_example_font_manager_1
:language: c

View File

@@ -0,0 +1,38 @@
/**
* @file lv_example_font_manager.h
*
*/
#ifndef LV_EXAMPLE_FONT_MANAGER_H
#define LV_EXAMPLE_FONT_MANAGER_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
void lv_example_font_manager_1(void);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_EXAMPLE_MONKEY_H*/

View File

@@ -0,0 +1,41 @@
#include "../../lv_examples.h"
#if LV_USE_FREETYPE && LV_USE_FONT_MANAGER && LV_BUILD_EXAMPLES
#if LV_FREETYPE_USE_LVGL_PORT
#define PATH_PREFIX "A:"
#else
#define PATH_PREFIX "./"
#endif
static lv_font_manager_t * g_font_manager = NULL;
void lv_example_font_manager_1(void)
{
/* Create font manager, with 8 fonts recycling buffers */
g_font_manager = lv_font_manager_create(8);
/* Add font path mapping to font manager */
lv_font_manager_add_path_static(g_font_manager,
"Lato-Regular",
PATH_PREFIX "lvgl/examples/libs/freetype/Lato-Regular.ttf");
/* Create font from font manager */
lv_font_t * font = lv_font_manager_create_font(g_font_manager,
"Lato-Regular",
LV_FREETYPE_FONT_RENDER_MODE_BITMAP,
24,
LV_FREETYPE_FONT_STYLE_NORMAL);
if(!font) {
LV_LOG_ERROR("Could not create font");
return;
}
/* Create label with the font */
lv_obj_t * label = lv_label_create(lv_screen_active());
lv_obj_set_style_text_font(label, font, 0);
lv_label_set_text(label, "Hello Font Manager!");
lv_obj_center(label);
}
#endif

View File

@@ -14,6 +14,7 @@ extern "C" {
* INCLUDES
*********************/
#include "file_explorer/lv_example_file_explorer.h"
#include "font_manager/lv_example_font_manager.h"
#include "fragment/lv_example_fragment.h"
#include "gridnav/lv_example_gridnav.h"
#include "ime/lv_example_ime_pinyin.h"

1
lvgl.h
View File

@@ -91,6 +91,7 @@ extern "C" {
#include "src/others/observer/lv_observer.h"
#include "src/others/ime/lv_ime_pinyin.h"
#include "src/others/file_explorer/lv_file_explorer.h"
#include "src/others/font_manager/lv_font_manager.h"
#include "src/libs/barcode/lv_barcode.h"
#include "src/libs/bin_decoder/lv_bin_decoder.h"

View File

@@ -199,9 +199,14 @@ lv_font_t * lv_font_manager_create_font(lv_font_manager_t * manager, const char
ret_font = lv_font_manager_create_font_single(manager, &ft_info);
}
/* make LV_SYMBOL displayable */
if(ret_font) {
ret_font->fallback = LV_FONT_DEFAULT;
/* Append fallback font to make LV_SYMBOL displayable */
lv_font_t * cur_font = ret_font;
while(cur_font) {
if(cur_font->fallback == NULL) {
cur_font->fallback = LV_FONT_DEFAULT;
break;
}
cur_font = (lv_font_t *)cur_font->fallback;
}
return ret_font;
@@ -265,7 +270,7 @@ static bool lv_font_manager_delete_font_single(lv_font_manager_t * manager, lv_f
if(!rec_node) {
LV_LOG_WARN("NO record found for font: %p(%d),"
" it was not created by font manager",
font, (int)font->line_height);
(void *)font, (int)font->line_height);
return false;
}
@@ -424,7 +429,7 @@ static bool lv_font_manager_check_resource(lv_font_manager_t * manager)
lv_font_rec_node_t * node;
LV_LL_READ(rec_ll, node) {
LV_LOG_WARN("font: %p(%d) -> ref: %s(%d)",
node,
(void *)node,
(int)node->font.line_height,
node->refer_node_p->ft_info.name,
node->refer_node_p->ft_info.size);
@@ -460,7 +465,7 @@ static lv_font_rec_node_t * lv_font_manager_search_rec_node(lv_font_manager_t *
lv_font_rec_node_t * rec_node;
LV_LL_READ(&manager->rec_ll, rec_node) {
if(font == &rec_node->font) {
LV_LOG_INFO("font: %p(%d) matched", font, (int)font->line_height);
LV_LOG_INFO("font: %p(%d) matched", (void *)font, (int)font->line_height);
return rec_node;
}
}

View File

@@ -101,7 +101,7 @@ lv_font_t * lv_font_manager_recycle_get_reuse(lv_font_manager_recycle_t * manage
/* match font */
if(lv_freetype_info_is_equal(ft_info, &recycle->ft_info)) {
lv_font_t * font = recycle->font;
LV_LOG_INFO("found font: %p", font);
LV_LOG_INFO("found font: %p", (void *)font);
/* remove reused font */
lv_ll_remove(recycle_ll, recycle);

View File

@@ -158,6 +158,8 @@
#define LV_FREETYPE_USE_LVGL_PORT 0
#define LV_FREETYPE_CACHE_FT_GLYPH_CNT 10
#define LV_USE_FONT_MANAGER 1
#define LV_USE_DRAW_SW_COMPLEX_GRADIENTS 1
#endif /* LV_TEST_CONF_FULL_H */

View File

@@ -4,7 +4,7 @@
#include "unity/unity.h"
#if LV_USE_FREETYPE
#if LV_USE_FREETYPE && LV_USE_FONT_MANAGER
#include "rnd_unicodes/lv_rnd_unicodes.h"
@@ -27,36 +27,36 @@ typedef struct {
int font_cnt;
int label_cnt;
int loop_cnt;
} lvx_font_stress_config_t;
} font_stress_config_t;
typedef struct {
struct _font_stress_ctx_t;
typedef lv_font_t * (*font_create_cb_t)(struct _font_stress_ctx_t * ctx,
const char * name,
lv_freetype_font_render_mode_t render_mode,
uint32_t size,
lv_freetype_font_style_t style);
typedef void (*font_delete_cb_t)(struct _font_stress_ctx_t * ctx, lv_font_t * font);
typedef struct _font_stress_ctx_t {
font_stress_config_t config;
lv_obj_t * par;
lv_obj_t ** label_arr;
lvx_font_stress_config_t config;
} stress_test_ctx_t;
lv_font_manager_t * font_manager;
font_create_cb_t font_create_cb;
font_delete_cb_t font_delete_cb;
} font_stress_ctx_t;
/**********************
* STATIC PROTOTYPES
**********************/
static void update_cb(void);
/**********************
* STATIC VARIABLES
**********************/
static stress_test_ctx_t g_ctx = { 0 };
static const char * font_name_arr[] = {
"./src/test_files/fonts/noto/NotoSansSC-Regular.ttf",
"../src/libs/freetype/arial.ttf",
"../demos/multilang/assets/fonts/Montserrat-Bold.ttf",
"UNKNOWN_FONT_NAME"
};
static const uint16_t font_style[] = {
LV_FREETYPE_FONT_STYLE_NORMAL,
LV_FREETYPE_FONT_STYLE_ITALIC,
LV_FREETYPE_FONT_STYLE_BOLD,
};
static font_stress_ctx_t g_ctx = { 0 };
/**********************
* MACROS
@@ -78,8 +78,15 @@ static const uint16_t font_style[] = {
* STATIC FUNCTIONS
**********************/
static lv_obj_t * label_create(const char * font_name, lv_obj_t * par, int size, int x, int y)
static lv_obj_t * font_stress_label_create(font_stress_ctx_t * ctx, const char * font_name, lv_obj_t * par, int size,
int x, int y)
{
static const uint16_t font_style[] = {
LV_FREETYPE_FONT_STYLE_NORMAL,
LV_FREETYPE_FONT_STYLE_ITALIC,
LV_FREETYPE_FONT_STYLE_BOLD,
};
uint32_t index = lv_rand(0, sizeof(font_style) / sizeof(uint16_t) - 1);
uint32_t r = lv_rand(0, 0xFF);
uint32_t g = lv_rand(0, 0xFF);
@@ -87,7 +94,7 @@ static lv_obj_t * label_create(const char * font_name, lv_obj_t * par, int size,
lv_opa_t opa = lv_rand(0, LV_OPA_COVER);
lv_color_t color = lv_color_make(r, g, b);
lv_font_t * font = lv_freetype_font_create(font_name, LV_FREETYPE_FONT_RENDER_MODE_BITMAP, size, font_style[index]);
lv_font_t * font = ctx->font_create_cb(ctx, font_name, LV_FREETYPE_FONT_RENDER_MODE_BITMAP, size, font_style[index]);
if(!font) {
return NULL;
}
@@ -106,26 +113,28 @@ static lv_obj_t * label_create(const char * font_name, lv_obj_t * par, int size,
lv_label_set_text(label, (char *)str);
return label;
}
static void label_delete(lv_obj_t * label)
static void font_stress_label_delete(font_stress_ctx_t * ctx, lv_obj_t * label)
{
const lv_font_t * font = lv_obj_get_style_text_font(label, 0);
LV_ASSERT_NULL(font);
lv_freetype_font_delete((lv_font_t *)font);
TEST_ASSERT_NOT_NULL(font);
lv_obj_del(label);
ctx->font_delete_cb(ctx, (lv_font_t *)font);
}
static void label_delete_all(stress_test_ctx_t * ctx)
static void font_stress_label_delete_all(font_stress_ctx_t * ctx)
{
for(int i = 0; i < ctx->config.label_cnt; i++) {
lv_obj_t * label = ctx->label_arr[i];
if(label) {
label_delete(label);
font_stress_label_delete(ctx, label);
ctx->label_arr[i] = NULL;
}
}
}
static void update_cb(void)
static void font_stress_update(font_stress_ctx_t * ctx)
{
stress_test_ctx_t * ctx = &g_ctx;
uint32_t label_index = lv_rand(0, ctx->config.label_cnt - 1);
uint32_t font_index = lv_rand(0, ctx->config.font_cnt - 1);
uint32_t font_size = lv_rand(0, MAX_FONT_SIZE);
@@ -134,13 +143,14 @@ static void update_cb(void)
lv_obj_t * label = ctx->label_arr[label_index];
if(label) {
label_delete(label);
font_stress_label_delete(ctx, label);
ctx->label_arr[label_index] = NULL;
}
else {
const char * pathname = ctx->config.font_name_arr[font_index];
LV_ASSERT_NULL(pathname);
label = label_create(
TEST_ASSERT_NOT_NULL(pathname);
label = font_stress_label_create(
ctx,
pathname,
ctx->par,
(int)font_size,
@@ -150,37 +160,74 @@ static void update_cb(void)
}
}
static lv_font_t * freetype_font_create_cb(font_stress_ctx_t * ctx,
const char * name,
lv_freetype_font_render_mode_t render_mode,
uint32_t size,
lv_freetype_font_style_t style)
{
LV_UNUSED(ctx);
return lv_freetype_font_create(name, render_mode, size, style);
}
static void freetype_font_delete_cb(font_stress_ctx_t * ctx, lv_font_t * font)
{
LV_UNUSED(ctx);
lv_freetype_font_delete(font);
}
static lv_font_t * font_manager_font_create_cb(font_stress_ctx_t * ctx,
const char * name,
lv_freetype_font_render_mode_t render_mode,
uint32_t size,
lv_freetype_font_style_t style)
{
TEST_ASSERT_NOT_NULL(ctx->font_manager);
return lv_font_manager_create_font(ctx->font_manager, name, render_mode, size, style);
}
static void font_manager_font_delete_cb(font_stress_ctx_t * ctx, lv_font_t * font)
{
TEST_ASSERT_NOT_NULL(ctx->font_manager);
lv_font_manager_delete_font(ctx->font_manager, font);
}
void setUp(void)
{
lv_freetype_init(LV_FREETYPE_CACHE_FT_GLYPH_CNT);
g_ctx.par = lv_scr_act();
g_ctx.config.loop_cnt = MAX_LOOP_CNT;
g_ctx.par = lv_screen_active();
g_ctx.config.label_cnt = MAX_LABEL_CNT;
g_ctx.config.font_name_arr = font_name_arr;
g_ctx.config.font_cnt = sizeof(font_name_arr) / sizeof(font_name_arr[0]);
size_t arr_size = sizeof(lv_obj_t *) * g_ctx.config.label_cnt;
g_ctx.label_arr = lv_malloc(arr_size);
LV_ASSERT_MALLOC(g_ctx.label_arr);
lv_memzero(g_ctx.label_arr, arr_size);
lv_rand_set_seed(RND_START_SEED);
g_ctx.label_arr = lv_malloc_zeroed(sizeof(lv_obj_t *) * g_ctx.config.label_cnt);
TEST_ASSERT_NOT_NULL(g_ctx.label_arr);
}
void tearDown(void)
{
label_delete_all(&g_ctx);
lv_freetype_uninit();
lv_free(g_ctx.label_arr);
g_ctx.label_arr = NULL;
}
void test_font_stress(void)
{
lv_rand_set_seed(RND_START_SEED);
static const char * font_name_arr[] = {
"./src/test_files/fonts/noto/NotoSansSC-Regular.ttf",
"../src/libs/freetype/arial.ttf",
"../demos/multilang/assets/fonts/Montserrat-Bold.ttf",
"UNKNOWN_FONT_NAME"
};
g_ctx.config.loop_cnt = MAX_LOOP_CNT;
g_ctx.config.font_name_arr = font_name_arr;
g_ctx.config.font_cnt = sizeof(font_name_arr) / sizeof(font_name_arr[0]);
g_ctx.font_create_cb = freetype_font_create_cb;
g_ctx.font_delete_cb = freetype_font_delete_cb;
for(uint32_t i = 0; g_ctx.config.loop_cnt > 0; g_ctx.config.loop_cnt--) {
update_cb();
font_stress_update(&g_ctx);
lv_refr_now(NULL);
if(g_ctx.config.loop_cnt % CAPTURE_SKIP_FRAMES == 0) {
@@ -189,6 +236,57 @@ void test_font_stress(void)
i++;
}
}
font_stress_label_delete_all(&g_ctx);
}
void test_font_manager_stress(void)
{
lv_rand_set_seed(RND_START_SEED);
g_ctx.font_manager = lv_font_manager_create(2);
TEST_ASSERT_NOT_NULL(g_ctx.font_manager);
lv_font_manager_add_path_static(g_ctx.font_manager, "NotoSansSC-Regular",
"./src/test_files/fonts/noto/NotoSansSC-Regular.ttf");
lv_font_manager_add_path_static(g_ctx.font_manager, "Arial", "../src/libs/freetype/arial.ttf");
lv_font_manager_add_path(g_ctx.font_manager, "Montserrat-Bold", "../demos/multilang/assets/fonts/Montserrat-Bold.ttf");
lv_font_manager_add_path(g_ctx.font_manager, "UNKNOWN", "UNKNOWN_FONT_PATH");
static const char * font_name_arr[] = {
"NotoSansSC-Regular,Arial",
"Arial",
"Montserrat-Bold",
"UNKNOWN"
};
g_ctx.config.loop_cnt = MAX_LOOP_CNT;
g_ctx.config.font_name_arr = font_name_arr;
g_ctx.config.font_cnt = sizeof(font_name_arr) / sizeof(font_name_arr[0]);
g_ctx.font_create_cb = font_manager_font_create_cb;
g_ctx.font_delete_cb = font_manager_font_delete_cb;
for(uint32_t i = 0; g_ctx.config.loop_cnt > 0; g_ctx.config.loop_cnt--) {
font_stress_update(&g_ctx);
lv_refr_now(NULL);
if(g_ctx.config.loop_cnt % CAPTURE_SKIP_FRAMES == 0) {
char buf[64];
TEST_FREETYPE_ASSERT_EQUAL_SCREENSHOT(i);
i++;
}
}
font_stress_label_delete_all(&g_ctx);
bool remove_ok = lv_font_manager_remove_path(g_ctx.font_manager, "Arial");
TEST_ASSERT_TRUE(remove_ok);
remove_ok = lv_font_manager_remove_path(g_ctx.font_manager, "UNKNOWN");
TEST_ASSERT_TRUE(remove_ok);
bool success = lv_font_manager_delete(g_ctx.font_manager);
TEST_ASSERT_TRUE(success);
g_ctx.font_manager = NULL;
}
#else
@@ -205,6 +303,10 @@ void test_font_stress(void)
{
}
void test_font_manager_stress(void)
{
}
#endif
#endif