1 /**
2  * @file lv_font_manager.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 
10 #include "lv_font_manager.h"
11 
12 #if LV_USE_FONT_MANAGER
13 
14 #include "lv_font_manager_recycle.h"
15 #include "lv_font_manager_utils.h"
16 #include "../../lvgl.h"
17 
18 /*********************
19  *      DEFINES
20  *********************/
21 
22 #define IS_FONT_FAMILY_NAME(name) (lv_strchr((name), ',') != NULL)
23 #define IS_FONT_HAS_FALLBACK(font) ((font)->fallback != NULL)
24 
25 /**********************
26  *      TYPEDEFS
27  **********************/
28 
29 /* font manager object */
30 struct _lv_font_manager_t {
31     lv_ll_t refer_ll; /* freetype font record list */
32     lv_ll_t rec_ll; /* lvgl font record list */
33     lv_ll_t path_ll; /* font path record list */
34     lv_font_manager_recycle_t * recycle_manager;
35 };
36 
37 /* freetype font reference node */
38 typedef struct _lv_font_refer_node_t {
39     lv_font_t * font_p; /* lv_freetype gen font */
40     lv_freetype_info_t ft_info; /* freetype font info */
41     char name[LV_FONT_MANAGER_NAME_MAX_LEN]; /* name buffer */
42     int ref_cnt; /* reference count */
43 } lv_font_refer_node_t;
44 
45 /* lvgl font record node */
46 typedef struct _lv_font_rec_node_t {
47     lv_font_t font; /* lvgl font info */
48     const lv_font_refer_node_t * refer_node_p; /* referenced freetype resource */
49 } lv_font_rec_node_t;
50 
51 typedef struct _lv_font_path_t {
52     char * name;
53     char * path;
54     bool is_static;
55 } lv_font_path_t;
56 
57 /**********************
58  *  STATIC PROTOTYPES
59  **********************/
60 
61 static const char * lv_font_manager_get_path(lv_font_manager_t * manager, const char * name);
62 
63 static bool lv_font_manager_check_resource(lv_font_manager_t * manager);
64 static lv_font_rec_node_t * lv_font_manager_search_rec_node(lv_font_manager_t * manager, lv_font_t * font);
65 
66 static const lv_font_refer_node_t * lv_font_manager_get_freetype_font(lv_font_manager_t * manager,
67                                                                       const lv_freetype_info_t * ft_info);
68 static bool lv_font_manager_reset_freetype_font(lv_font_manager_t * manager, const lv_font_refer_node_t * node);
69 
70 static lv_font_t * lv_font_manager_create_font_single(lv_font_manager_t * manager, const lv_freetype_info_t * ft_info);
71 static bool lv_font_manager_delete_font_single(lv_font_manager_t * manager, lv_font_t * font);
72 
73 static lv_font_t * lv_font_manager_create_font_family(lv_font_manager_t * manager, const lv_freetype_info_t * ft_info);
74 static void lv_font_manager_delete_font_family(lv_font_manager_t * manager, lv_font_t * font);
75 
76 static void lv_font_manager_add_path_core(lv_font_manager_t * manager, const char * name, const char * path,
77                                           bool is_static);
78 
79 /**********************
80  *  STATIC VARIABLES
81  **********************/
82 
83 /**********************
84  *   GLOBAL FUNCTIONS
85  **********************/
86 
lv_font_manager_create(uint32_t recycle_cache_size)87 lv_font_manager_t * lv_font_manager_create(uint32_t recycle_cache_size)
88 {
89     LV_ASSERT_MSG(recycle_cache_size > 0, "recycle_cache_size should be greater than 0");
90     lv_font_manager_t * manager = lv_malloc_zeroed(sizeof(lv_font_manager_t));
91     LV_ASSERT_MALLOC(manager);
92     if(!manager) {
93         LV_LOG_ERROR("malloc failed for lv_font_manager_t");
94         return NULL;
95     }
96 
97     lv_ll_init(&manager->refer_ll, sizeof(lv_font_refer_node_t));
98     lv_ll_init(&manager->rec_ll, sizeof(lv_font_rec_node_t));
99     lv_ll_init(&manager->path_ll, sizeof(lv_font_path_t));
100 
101     manager->recycle_manager = lv_font_manager_recycle_create(recycle_cache_size);
102 
103     LV_LOG_INFO("success");
104     return manager;
105 }
106 
lv_font_manager_delete(lv_font_manager_t * manager)107 bool lv_font_manager_delete(lv_font_manager_t * manager)
108 {
109     LV_ASSERT_NULL(manager);
110 
111     /* Resource leak check */
112     if(lv_font_manager_check_resource(manager)) {
113         LV_LOG_ERROR("unfreed resource detected, delete failed!");
114         return false;
115     }
116 
117     /* clean recycle_manager */
118     lv_font_manager_recycle_delete(manager->recycle_manager);
119 
120     /* clean path map */
121     lv_font_path_t * font_path;
122     LV_LL_READ(&manager->path_ll, font_path) {
123         if(!font_path->is_static) {
124             lv_free(font_path->name);
125             lv_free(font_path->path);
126         }
127 
128         font_path->name = NULL;
129         font_path->path = NULL;
130     }
131     lv_ll_clear(&manager->path_ll);
132 
133     lv_free(manager);
134 
135     LV_LOG_INFO("success");
136     return true;
137 }
138 
lv_font_manager_add_path(lv_font_manager_t * manager,const char * name,const char * path)139 void lv_font_manager_add_path(lv_font_manager_t * manager, const char * name, const char * path)
140 {
141     lv_font_manager_add_path_core(manager, name, path, false);
142 }
143 
lv_font_manager_add_path_static(lv_font_manager_t * manager,const char * name,const char * path)144 void lv_font_manager_add_path_static(lv_font_manager_t * manager, const char * name, const char * path)
145 {
146     lv_font_manager_add_path_core(manager, name, path, true);
147 }
148 
lv_font_manager_remove_path(lv_font_manager_t * manager,const char * name)149 bool lv_font_manager_remove_path(lv_font_manager_t * manager, const char * name)
150 {
151     lv_font_path_t * font_path;
152     LV_LL_READ(&manager->path_ll, font_path) {
153         if(lv_strcmp(name, font_path->name) == 0) {
154             break;
155         }
156     }
157 
158     if(!font_path) {
159         LV_LOG_WARN("name: %s not found", name);
160         return false;
161     }
162 
163     lv_ll_remove(&manager->path_ll, font_path);
164 
165     if(!font_path->is_static) {
166         lv_free(font_path->name);
167         lv_free(font_path->path);
168     }
169 
170     font_path->name = NULL;
171     font_path->path = NULL;
172 
173     lv_free(font_path);
174 
175     LV_LOG_WARN("name: %s remove success", name);
176     return true;
177 }
178 
lv_font_manager_create_font(lv_font_manager_t * manager,const char * font_family,uint16_t render_mode,uint32_t size,uint16_t style)179 lv_font_t * lv_font_manager_create_font(lv_font_manager_t * manager, const char * font_family, uint16_t render_mode,
180                                         uint32_t size, uint16_t style)
181 {
182 
183     LV_ASSERT_NULL(manager);
184     LV_ASSERT_NULL(font_family);
185 
186     lv_freetype_info_t ft_info;
187     lv_memzero(&ft_info, sizeof(ft_info));
188     ft_info.name = font_family;
189     ft_info.render_mode = render_mode;
190     ft_info.size = size;
191     ft_info.style = style;
192 
193     lv_font_t * ret_font;
194 
195     if(IS_FONT_FAMILY_NAME(ft_info.name)) {
196         ret_font = lv_font_manager_create_font_family(manager, &ft_info);
197     }
198     else {
199         ret_font = lv_font_manager_create_font_single(manager, &ft_info);
200     }
201 
202     /* Append fallback font to make LV_SYMBOL displayable */
203     lv_font_t * cur_font = ret_font;
204     while(cur_font) {
205         if(cur_font->fallback == NULL) {
206             cur_font->fallback = LV_FONT_DEFAULT;
207             break;
208         }
209         cur_font = (lv_font_t *)cur_font->fallback;
210     }
211 
212     return ret_font;
213 }
214 
lv_font_manager_delete_font(lv_font_manager_t * manager,lv_font_t * font)215 void lv_font_manager_delete_font(lv_font_manager_t * manager, lv_font_t * font)
216 {
217     LV_ASSERT_NULL(manager);
218     LV_ASSERT_NULL(font);
219 
220     if(IS_FONT_HAS_FALLBACK(font)) {
221         lv_font_manager_delete_font_family(manager, font);
222         return;
223     }
224 
225     lv_font_manager_delete_font_single(manager, font);
226 }
227 
228 /**********************
229  *   STATIC FUNCTIONS
230  **********************/
231 
lv_font_manager_create_font_single(lv_font_manager_t * manager,const lv_freetype_info_t * ft_info)232 static lv_font_t * lv_font_manager_create_font_single(lv_font_manager_t * manager, const lv_freetype_info_t * ft_info)
233 {
234     LV_ASSERT_NULL(manager);
235     LV_ASSERT_NULL(ft_info);
236 
237     /* get freetype font */
238     const lv_font_refer_node_t * refer_node = lv_font_manager_get_freetype_font(manager, ft_info);
239     if(!refer_node) {
240         return NULL;
241     }
242 
243     /* add font record node */
244     lv_font_rec_node_t * rec_node = lv_ll_ins_head(&manager->rec_ll);
245     LV_ASSERT_MALLOC(rec_node);
246     lv_memzero(rec_node, sizeof(lv_font_rec_node_t));
247 
248     /* copy freetype_font data */
249     rec_node->font = *refer_node->font_p;
250 
251     /* record reference freetype_font */
252     rec_node->refer_node_p = refer_node;
253 
254     LV_LOG_INFO("success");
255     return &rec_node->font;
256 }
257 
lv_font_manager_delete_font_single(lv_font_manager_t * manager,lv_font_t * font)258 static bool lv_font_manager_delete_font_single(lv_font_manager_t * manager, lv_font_t * font)
259 {
260     LV_ASSERT_NULL(manager);
261     LV_ASSERT_NULL(font);
262 
263     if(font == LV_FONT_DEFAULT) {
264         LV_LOG_INFO("LV_FONT_DEFAULT can not be deleted");
265         return false;
266     }
267 
268     /* check font is created by font manager */
269     lv_font_rec_node_t * rec_node = lv_font_manager_search_rec_node(manager, font);
270     if(!rec_node) {
271         LV_LOG_WARN("NO record found for font: %p(%d),"
272                     " it was not created by font manager",
273                     (void *)font, (int)font->line_height);
274         return false;
275     }
276 
277     /* reset freetype font resource */
278     bool retval = lv_font_manager_reset_freetype_font(manager, rec_node->refer_node_p);
279     LV_ASSERT(retval);
280 
281     /* free rec_node */
282     lv_ll_remove(&manager->rec_ll, rec_node);
283     lv_free(rec_node);
284 
285     LV_LOG_INFO("success");
286     return retval;
287 }
288 
strncpy_until(char * dest,const char * src,size_t n,char c)289 static const char * strncpy_until(char * dest, const char * src, size_t n, char c)
290 {
291     LV_ASSERT_NULL(dest);
292     LV_ASSERT_NULL(src);
293 
294     size_t i = 0;
295     while(i < n && *src != '\0' && *src != c) {
296         *dest++ = *src++;
297         i++;
298     }
299 
300     if(i < n) {
301         *dest = '\0';
302     }
303 
304     return src;
305 }
306 
lv_font_manager_create_font_family(lv_font_manager_t * manager,const lv_freetype_info_t * ft_info)307 static lv_font_t * lv_font_manager_create_font_family(lv_font_manager_t * manager, const lv_freetype_info_t * ft_info)
308 {
309     LV_ASSERT_NULL(manager);
310     LV_ASSERT_NULL(ft_info);
311 
312     lv_font_t * first_font = NULL;
313     lv_font_t * pre_font = NULL;
314 
315     const char * family_str = ft_info->name;
316     LV_LOG_INFO("font-family: %s", family_str);
317 
318     char tmp_name[LV_FONT_MANAGER_NAME_MAX_LEN] = { 0 };
319     lv_freetype_info_t tmp_ft_info = *ft_info;
320     tmp_ft_info.name = tmp_name;
321 
322     while(1) {
323         family_str = strncpy_until(tmp_name, family_str, sizeof(tmp_name) - 1, ',');
324 
325         lv_font_t * cur_font = lv_font_manager_create_font_single(manager, &tmp_ft_info);
326 
327         if(cur_font) {
328             /* save first font pointer */
329             if(!first_font) {
330                 first_font = cur_font;
331             }
332 
333             /* append font fallback */
334             if(pre_font) {
335                 pre_font->fallback = cur_font;
336             }
337 
338             pre_font = cur_font;
339         }
340 
341         /* stop */
342         if(*family_str == '\0') {
343             break;
344         }
345 
346         if(*family_str != ',') {
347             LV_LOG_ERROR("font name buffer is too small, please increase FONT_NAME_MAX");
348             break;
349         }
350 
351         /* skip ',' */
352         family_str++;
353     }
354 
355     return first_font;
356 }
357 
lv_font_manager_delete_font_family(lv_font_manager_t * manager,lv_font_t * font)358 static void lv_font_manager_delete_font_family(lv_font_manager_t * manager, lv_font_t * font)
359 {
360     LV_ASSERT_NULL(manager);
361     LV_ASSERT_NULL(font);
362 
363     lv_font_t * f = font;
364     while(f) {
365         lv_font_t * fallback = (lv_font_t *)f->fallback;
366         lv_font_manager_delete_font_single(manager, f);
367         f = fallback;
368     }
369 }
370 
lv_font_manager_add_path_core(lv_font_manager_t * manager,const char * name,const char * path,bool is_static)371 static void lv_font_manager_add_path_core(lv_font_manager_t * manager, const char * name, const char * path,
372                                           bool is_static)
373 {
374     LV_ASSERT_NULL(manager);
375     LV_ASSERT_NULL(name);
376     LV_ASSERT_NULL(path);
377 
378     const char * old_path = lv_font_manager_get_path(manager, name);
379     if(old_path) {
380         LV_LOG_WARN("name: %s, path: %s already exists", name, old_path);
381         return;
382     }
383 
384     lv_font_path_t * font_path = lv_ll_ins_tail(&manager->path_ll);
385     LV_ASSERT_MALLOC(font_path);
386     font_path->is_static = is_static;
387 
388     if(is_static) {
389         font_path->name = (char *)name;
390         font_path->path = (char *)path;
391     }
392     else {
393         uint32_t name_len = lv_strlen(name) + 1;
394         font_path->name = lv_malloc(name_len);
395         LV_ASSERT_MALLOC(font_path->name);
396         lv_memcpy(font_path->name, name, name_len);
397 
398         uint32_t path_len = lv_strlen(path) + 1;
399         font_path->path = lv_malloc(path_len);
400         LV_ASSERT_MALLOC(font_path->path);
401         lv_memcpy(font_path->path, path, path_len);
402     }
403 
404     LV_LOG_INFO("name: %s, path: %s add success", name, path);
405 }
406 
lv_font_manager_get_path(lv_font_manager_t * manager,const char * name)407 static const char * lv_font_manager_get_path(lv_font_manager_t * manager, const char * name)
408 {
409     lv_font_path_t * font_path;
410     LV_LL_READ(&manager->path_ll, font_path) {
411         if(lv_strcmp(name, font_path->name) == 0) {
412             return font_path->path;
413         }
414     }
415 
416     return NULL;
417 }
418 
lv_font_manager_check_resource(lv_font_manager_t * manager)419 static bool lv_font_manager_check_resource(lv_font_manager_t * manager)
420 {
421     LV_ASSERT_NULL(manager);
422 
423     /* Check the recorded font */
424     lv_ll_t * rec_ll = &manager->rec_ll;
425     uint32_t rec_ll_len = lv_ll_get_len(rec_ll);
426     if(rec_ll_len) {
427         LV_LOG_WARN("lvgl font resource[%" LV_PRIu32 "]:", rec_ll_len);
428 
429         lv_font_rec_node_t * node;
430         LV_LL_READ(rec_ll, node) {
431             LV_LOG_WARN("font: %p(%d) -> ref: %s(%d)",
432                         (void *)node,
433                         (int)node->font.line_height,
434                         node->refer_node_p->ft_info.name,
435                         node->refer_node_p->ft_info.size);
436         }
437     }
438 
439     /* Check the recorded font resources created by freetype */
440     lv_ll_t * refer_ll = &manager->refer_ll;
441     uint32_t refer_ll_len = lv_ll_get_len(refer_ll);
442     if(refer_ll_len) {
443         LV_LOG_WARN("freetype font resource[%" LV_PRIu32 "]:", refer_ll_len);
444 
445         lv_font_refer_node_t * node;
446         LV_LL_READ(refer_ll, node) {
447             LV_LOG_WARN("font: %s(%d), ref_cnt = %d",
448                         node->ft_info.name,
449                         node->ft_info.size,
450                         node->ref_cnt);
451         }
452     }
453 
454     /* Check resource leak */
455     bool has_resource = (rec_ll_len || refer_ll_len);
456 
457     return has_resource;
458 }
459 
lv_font_manager_search_rec_node(lv_font_manager_t * manager,lv_font_t * font)460 static lv_font_rec_node_t * lv_font_manager_search_rec_node(lv_font_manager_t * manager, lv_font_t * font)
461 {
462     LV_ASSERT_NULL(manager);
463     LV_ASSERT_NULL(font);
464 
465     lv_font_rec_node_t * rec_node;
466     LV_LL_READ(&manager->rec_ll, rec_node) {
467         if(font == &rec_node->font) {
468             LV_LOG_INFO("font: %p(%d) matched", (void *)font, (int)font->line_height);
469             return rec_node;
470         }
471     }
472 
473     return NULL;
474 }
475 
lv_font_manager_search_refer_node(lv_font_manager_t * manager,const lv_freetype_info_t * ft_info)476 static lv_font_refer_node_t * lv_font_manager_search_refer_node(lv_font_manager_t * manager,
477                                                                 const lv_freetype_info_t * ft_info)
478 {
479     LV_ASSERT_NULL(manager);
480     LV_ASSERT_NULL(ft_info);
481 
482     lv_font_refer_node_t * refer_node;
483     LV_LL_READ(&manager->refer_ll, refer_node) {
484         if(lv_freetype_info_is_equal(ft_info, &refer_node->ft_info)) {
485             LV_LOG_INFO("font: %s(%d) matched", ft_info->name, ft_info->size);
486             return refer_node;
487         }
488     }
489 
490     return NULL;
491 }
492 
lv_font_manager_create_font_warpper(lv_font_manager_t * manager,const lv_freetype_info_t * ft_info)493 static lv_font_t * lv_font_manager_create_font_warpper(lv_font_manager_t * manager, const lv_freetype_info_t * ft_info)
494 {
495     LV_ASSERT_NULL(manager);
496     LV_ASSERT_NULL(ft_info);
497 
498     lv_font_t * font;
499 
500     /* create freetype font */
501     font = lv_font_manager_recycle_get_reuse(manager->recycle_manager, ft_info);
502 
503     /* get reuse font from recycle */
504     if(font) {
505         return font;
506     }
507 
508     /* cache miss */
509 
510     /* generate full file path */
511     const char * path = lv_font_manager_get_path(manager, ft_info->name);
512     if(!path) {
513         LV_LOG_ERROR("name: %s not found path", ft_info->name);
514         return NULL;
515     }
516 
517     font = lv_freetype_font_create(path, ft_info->render_mode, ft_info->size, ft_info->style);
518     if(!font) {
519         LV_LOG_ERROR("Freetype font init failed, name: %s, render_mode: %d, size: %d, style: %d",
520                      ft_info->name, ft_info->render_mode, ft_info->size, ft_info->style);
521         return NULL;
522     }
523 
524     return font;
525 }
526 
lv_font_manager_delete_font_warpper(lv_font_manager_t * manager,lv_font_refer_node_t * refer_node)527 static void lv_font_manager_delete_font_warpper(lv_font_manager_t * manager, lv_font_refer_node_t * refer_node)
528 {
529     LV_ASSERT_NULL(manager);
530     LV_ASSERT_NULL(refer_node);
531     lv_font_manager_recycle_set_reuse(manager->recycle_manager, refer_node->font_p, &refer_node->ft_info);
532 }
533 
lv_font_manager_get_freetype_font(lv_font_manager_t * manager,const lv_freetype_info_t * ft_info)534 static const lv_font_refer_node_t * lv_font_manager_get_freetype_font(lv_font_manager_t * manager,
535                                                                       const lv_freetype_info_t * ft_info)
536 {
537     LV_ASSERT_NULL(manager);
538     LV_ASSERT_NULL(ft_info);
539 
540     /* check refer_node is existed */
541     lv_font_refer_node_t * refer_node = lv_font_manager_search_refer_node(manager, ft_info);
542     if(refer_node) {
543         refer_node->ref_cnt++;
544         LV_LOG_INFO("refer_node existed, ref_cnt++ = %d", refer_node->ref_cnt);
545         return refer_node;
546     }
547 
548     /* not found refer_node, start to create font */
549 
550     lv_font_t * font = lv_font_manager_create_font_warpper(manager, ft_info);
551 
552     if(!font) {
553         return NULL;
554     }
555 
556     /* add refer_node to refer_ll */
557     refer_node = lv_ll_ins_head(&manager->refer_ll);
558     LV_ASSERT_MALLOC(refer_node);
559     lv_memzero(refer_node, sizeof(lv_font_refer_node_t));
560 
561     lv_strncpy(refer_node->name, ft_info->name, sizeof(refer_node->name) - 1);
562 
563     /* copy font data */
564     refer_node->font_p = font;
565     refer_node->ft_info = *ft_info;
566     refer_node->ft_info.name = refer_node->name;
567     refer_node->ref_cnt = 1;
568 
569     LV_LOG_INFO("success");
570     return refer_node;
571 }
572 
lv_font_manager_reset_freetype_font(lv_font_manager_t * manager,const lv_font_refer_node_t * node)573 static bool lv_font_manager_reset_freetype_font(lv_font_manager_t * manager, const lv_font_refer_node_t * node)
574 {
575     LV_ASSERT_NULL(manager);
576     LV_ASSERT_NULL(node);
577 
578     /* Check refer_node is existed */
579     lv_font_refer_node_t * refer_node = lv_font_manager_search_refer_node(manager, &node->ft_info);
580     if(!refer_node) {
581         LV_LOG_WARN("NO record found for font: %s(%d),"
582                     " it was not created by font manager",
583                     node->ft_info.name, node->ft_info.size);
584         return false;
585     }
586 
587     refer_node->ref_cnt--;
588 
589     /* If ref_cnt is > 0, no need to delete font */
590     if(refer_node->ref_cnt > 0) {
591         LV_LOG_INFO("refer_node existed, ref_cnt-- = %d", refer_node->ref_cnt);
592         return true;
593     }
594 
595     /* if ref_cnt is about to be 0, free font resource */
596     lv_font_manager_delete_font_warpper(manager, refer_node);
597 
598     /* free refer_node */
599     lv_ll_remove(&manager->refer_ll, refer_node);
600     lv_free(refer_node);
601 
602     LV_LOG_INFO("success");
603     return true;
604 }
605 
606 #endif /* LV_USE_FONT_MANAGER */
607