Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

backport lvgl fallback font to enable support for more language (via external flash font loading) #1473

Open
1 task done
Boteium opened this issue Dec 5, 2022 · 5 comments
Labels
enhancement Enhancement to an existing app/feature

Comments

@Boteium
Copy link

Boteium commented Dec 5, 2022

Verification

  • 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. #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
@JF002
Copy link
Collaborator

JF002 commented Dec 7, 2022

Thanks @Boteium, this feature from LVGL 8.1 looks really interesting!

We have not (yet) switched to lvgl8, mostly by lack of time to review the PR and fix remaining issues...

I think this would be really interesting to test this feature backported in LVGL7 and see how well it performs! Just keep in mind that loading a font from external flash is quite slow, especially with bigger fonts. Also, the whole font will be stored in RAM, which means that we won't be able to have a huuuuge font that contains all characters from all languages, emojis,... because we are limited by RAM capacity.

@Boteium
Copy link
Author

Boteium commented Dec 8, 2022

Oh, I didn't know that. Skimming through lvgl's source, It seems that lv_fs_open() does load the entire file into RAM just like you said. So, huge font (larger than ~20kb) can only be store in the precious system storage for now.

@Boteium
Copy link
Author

Boteium commented Dec 9, 2022

I think this feature can still be very useful. For example, a huge fallback font is still possible if someone write a font engine that loads the bitmap on the fly from external flash in the future. Or, custom font stored in system storage can still save some space.

@Riksu9000
Copy link
Contributor

We're preparing to use our own fork of LVGL. If you're interested in working on these ideas, feel free to open a PR in https://github.com/InfiniTimeOrg/lvgl

@Riksu9000 Riksu9000 added enhancement Enhancement to an existing app/feature and removed feature request labels Mar 10, 2023
@xz-dev
Copy link

xz-dev commented Jun 2, 2024

Oh, I didn't know that. Skimming through lvgl's source, It seems that lv_fs_open() does load the entire file into RAM just like you said. So, huge font (larger than ~20kb) can only be store in the precious system storage for now.

Please check: lvgl/lvgl#4462
Maybe we can upgrade to lvgl 8.1 to use all of them

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement Enhancement to an existing app/feature
Projects
None yet
Development

No branches or pull requests

4 participants