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