InfiniTime
InfiniTime copied to clipboard
backport lvgl fallback font to enable support for more language (via external flash font loading)
Verification
- [X] I searched for similar feature request and found none was relevant.
Pitch us your idea!
Let's backport font fallback to enable more languages and emoji
Description
Due to the small size of os storage, we can only fit a very small number of glyphs in the default font.
To support other non-English language, numerous forks have been created to support different language by adding glyphs to jetbrains_mono_bold_20 such as this one
Loading custom font from external storage is another idea that have been proposed several times. (e.g. https://github.com/InfiniTimeOrg/InfiniTime/issues/212 ) There are also watchfaces in current release that already use external flash to store fonts.
However, if a LV_LABEL is set to use a certain font. (as stated here)
lv_obj_set_style_local_text_font(label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, font);
It can only use the font specified here and cannot fallback to system font (e.g. jetbrains_mono_bold_20) if a glyph is not in this font. This means the custom font will have to duplicate everything in jetbrains_mono_bold_20 including ascii alnum, which is not very efficient.
Starting from LVGL 8.1, a fallback font can be selected. This way, only the extended glyphs (non-english alphabet, emoji ...) needs to be put in the custom font. Unfortunately, InfiniTime is currently using LVGL 7.
I think backporting this function should be considered since previous attempt to upgrade to LVGL 8 is stalled.
If I didn't miss anything, the following patch should be enough.
diff --git a/src/lv_draw/lv_draw_label.c b/src/lv_draw/lv_draw_label.c
index 970791ff..7cbf099b 100644
--- a/src/lv_draw/lv_draw_label.c
+++ b/src/lv_draw/lv_draw_label.c
@@ -433,6 +433,10 @@ LV_ATTRIBUTE_FAST_MEM static void lv_draw_letter(const lv_point_t * pos_p, const
return;
}
+ if (g.resolved_font) {
+ font_p = g.resolved_font;
+ }
+
const uint8_t * map_p = lv_font_get_glyph_bitmap(font_p, letter);
if(map_p == NULL) {
LV_LOG_WARN("lv_draw_letter: character's bitmap not found");
diff --git a/src/lv_font/lv_font.c b/src/lv_font/lv_font.c
index 9e3ec220..49bffcbc 100644
--- a/src/lv_font/lv_font.c
+++ b/src/lv_font/lv_font.c
@@ -61,7 +61,18 @@ const uint8_t * lv_font_get_glyph_bitmap(const lv_font_t * font_p, uint32_t lett
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)
{
- return font_p->get_glyph_dsc(font_p, dsc_out, letter, letter_next);
+ 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->resolved_font = f;
+ break;
+ }
+ f = f->fallback;
+ }
+ return found;
}
/**
diff --git a/src/lv_font/lv_font.h b/src/lv_font/lv_font.h
index 26cc653b..b4353c17 100644
--- a/src/lv_font/lv_font.h
+++ b/src/lv_font/lv_font.h
@@ -34,7 +34,9 @@ extern "C" {
*-----------------*/
/** Describes the properties of a glyph. */
+struct _lv_font_struct;
typedef struct {
+ const struct _lv_font_struct *resolved_font; /**< Pointer to a font where the gylph was actually found after handling fallbacks*/
uint16_t adv_w; /**< The glyph needs this space. Draw the next glyph after this width. */
uint16_t box_w; /**< Width of the glyph's bounding box*/
uint16_t box_h; /**< Height of the glyph's bounding box*/
@@ -70,6 +72,7 @@ typedef struct _lv_font_struct {
int8_t underline_thickness; /**< Thickness of the underline*/
void * dsc; /**< Store implementation specific or run_time data or caching here*/
+ const struct _lv_font_struct * fallback; /**< Fallback font for missing glyph. Resolved recursively */
#if LV_USE_USER_DATA
lv_font_user_data_t user_data; /**< Custom user data for font. */
#endif