Get font name - At runtime

Fonts do not have their name stored in LittlevGL. In fact, there is hardly any identification information available in the font at all.

The only portable way I can think of right now to identify a font would be to hash all of its data, which would be a very expensive operation on a microcontroller.

Hmm… pewp. I thought that looking at the code.

I wonder…

To solve this problem (And to make runtime fonts a little easier), add an option to lv_conf.h to have a font registry?

Then simply store in the font itself a hash of some sort that identifies it. My thinking here is the cost would be low:
CPU - Hashing the font (This could be a simple hash of the name/size, or whatever)
Mem - Addition of a hash to the font itself

Ill code something up in a bit, and ifdef it so its an optional component. Ill send a PR, including memory usage.

1 Like

Hi,

lv_font_t has a user_data field. What about manually setting its name there?

Ok, that would definitely work for serialization, I could get the font name.

The issue would be deserialization.

I have a font name, how would I marry it up to a loaded font?

I suppose it would be used for the GUI editor.
Can you write your goal/approach step by step?

Sure. I will write up more later, but my thinking is this.

Have it ifdef guarded so it’s optional.
Modify register font to also have essentially a map of name to font pointer, and store the name as user data in the font. In the case of name collisions add a counter.

Then add a function to query the map for a name, and return the font pointer or null if none match

I’m guessing the additional code would be trivial, and the memory would be the name * 2+pointer per font

Sorry, I couldn’t imagine what you exactly did. Can you show a small example?

Honestly I havent done anything.

But here is what I would do (Pseudocode):
Modify LV_FONT_DECLARE to do 2 additional things:

  1. set the user_data of the font to the font name and pt size, so it would be somewhat unique. So like roboto16 or whatever
  2. Store the font name and a pointer to the fonts in a global location so it can be accessed. I have only made a cursory glance at the code to find a proper location of this, but maybe in lv_font.c

So the use case is:
Saving/Deserializing an object that references a font:

char *font=get_font_name(&pointer_to_font_struct)

That would essentially get the user_data for the font name

Serializing or loading an object:

lv_font_t *font=get_font(char *fontName)

This would do a lookup of the font table and get return a pointer to the font (or a reference, dealer’s choice), or if it is not there, return the default font.

This keeps objects separate from fonts as the architecture is currently, but provides a linkage if needed.

So here is my thoughts on mem/cpu usage:
Mem - Each font would have its name+pt size attached to it, either as user_data or as part of lv_font_t (I actually like the latter better, as end users might need user_data and could stomp on it).

This should be < 20 bytes per font, assuming a name < 18 bytes and a 2 byte pt size

We would also have the same memory duplicated in the map.

This would be a hashmap or a linked list that we walk on deserialization (Not a big hit, since there will be few fonts, and this is only done once per object)

So we would have a struct essentially of:

struct lv_font_map
{
   char *fontName;
   lv_font_t *font;
}
```c

Got it.

One part is still unclear: the fonts need to be linked to each other to make them iterable. Will this list be created by the editor from the used fonts?

So the above font map would be in lv. Here is my thought (And this is outside the scope of my editor, just a general programming issue)

I write a UI that used ROHMERFANTASTICFONT, which the user doesnt have. Here is my code path:

lv_font_t *myFontPointer = lv_font_get("ROHMERFANTASTICFONT");
// or maybe
lv_font_t *myFontPointer = lv_font_get("ROHMERFANTASTICFONT", 12); // name and point size

So in my example lets say roboto_12 is the default font, the variable myFontPointer would be set to roboto_12

Now for the second option, we could have some more complex fall back logic, if we wanted. We could fall back to the fall back “family” and keep the point size, or any number of other things.

We need to figure out how that would work. Do we register the fonts in lv_init?

void lv_init(void)
{
    // do other initialization ...
#if LV_FONT_ROBOTO_12
    lv_font_map_register(&lv_font_roboto_12, "roboto_12");
#endif
#if LV_FONT_ROBOTO_16
    lv_font_map_register(&lv_font_roboto_16, "roboto_16");
#endif
    // do more initialization ...
}

That could/would be a good place.

Also, it would potentially allow for runtime loading of fonts.

Curious, why couldn’t the mapping be added to the lv_font_declare macro for ease

The main issue with that is LV_FONT_DECLARE can be in header files to make the font globally accessible (declare a global variable). Therefore the call to lv_font_map_register could be out of the functions.

Why cant lv_font_map_register be in the header file?

This feature would consume ~30-40 byte RAM font. Finally can be a few hundred extra RAM usage. So what do you think, should we make this feature optional?

Oh it definitely should be optional, I would suggest it be on by default in the conf, but optional

1 Like

Maybe I missed something because you can’t call a function in a header file out of any functions.

E.g.:

/**
 * @file lv_label.h
 *
 */

#ifndef LV_LABEL_H
#define LV_LABEL_H

#include <stdarg.h>
#include "../lv_core/lv_obj.h"
#include "../lv_font/lv_font.h"

/*When this will be called? There is no "parent" function for it.
 Besides it'd be called every time the header file is included.*/
lv_font_map_register(&lv_font_robot_16, "roboto_16");


No, I mean define the function in the lv_font.h header.

Not just the signature.