1 /**
2 * @file lv_font.c
3 *
4 */
5
6 /*********************
7 * INCLUDES
8 *********************/
9 #include "lv_font.h"
10 #include "lv_font_fmt_txt.h"
11 #include "../lv_misc/lv_debug.h"
12 #include "../lv_draw/lv_draw.h"
13 #include "../lv_misc/lv_types.h"
14 #include "../lv_misc/lv_gc.h"
15 #include "../lv_misc/lv_log.h"
16 #include "../lv_misc/lv_utils.h"
17 #include "../lv_misc/lv_mem.h"
18
19 #if defined(LV_GC_INCLUDE)
20 #include LV_GC_INCLUDE
21 #endif /* LV_ENABLE_GC */
22
23 /*********************
24 * DEFINES
25 *********************/
26
27 /**********************
28 * TYPEDEFS
29 **********************/
30 typedef enum {
31 RLE_STATE_SINGLE = 0,
32 RLE_STATE_REPEATE,
33 RLE_STATE_COUNTER,
34 } rle_state_t;
35
36 /**********************
37 * STATIC PROTOTYPES
38 **********************/
39 static uint32_t get_glyph_dsc_id(const lv_font_t * font, uint32_t letter);
40 static int8_t get_kern_value(const lv_font_t * font, uint32_t gid_left, uint32_t gid_right);
41 static int32_t unicode_list_compare(const void * ref, const void * element);
42 static int32_t kern_pair_8_compare(const void * ref, const void * element);
43 static int32_t kern_pair_16_compare(const void * ref, const void * element);
44
45 #if LV_USE_FONT_COMPRESSED
46 static void decompress(const uint8_t * in, uint8_t * out, lv_coord_t w, lv_coord_t h, uint8_t bpp, bool prefilter);
47 static inline void decompress_line(uint8_t * out, lv_coord_t w);
48 static inline uint8_t get_bits(const uint8_t * in, uint32_t bit_pos, uint8_t len);
49 static inline void bits_write(uint8_t * out, uint32_t bit_pos, uint8_t val, uint8_t len);
50 static inline void rle_init(const uint8_t * in, uint8_t bpp);
51 static inline uint8_t rle_next(void);
52 #endif /* LV_USE_FONT_COMPRESSED */
53
54 /**********************
55 * STATIC VARIABLES
56 **********************/
57 #if LV_USE_FONT_COMPRESSED
58 static uint32_t rle_rdp;
59 static const uint8_t * rle_in;
60 static uint8_t rle_bpp;
61 static uint8_t rle_prev_v;
62 static uint8_t rle_cnt;
63 static rle_state_t rle_state;
64 #endif /* LV_USE_FONT_COMPRESSED */
65
66 /**********************
67 * GLOBAL PROTOTYPES
68 **********************/
69
70 /**********************
71 * MACROS
72 **********************/
73
74 /**********************
75 * GLOBAL FUNCTIONS
76 **********************/
77
78 /**
79 * Used as `get_glyph_bitmap` callback in LittelvGL's native font format if the font is uncompressed.
80 * @param font pointer to font
81 * @param unicode_letter an unicode letter which bitmap should be get
82 * @return pointer to the bitmap or NULL if not found
83 */
lv_font_get_bitmap_fmt_txt(const lv_font_t * font,uint32_t unicode_letter)84 const uint8_t * lv_font_get_bitmap_fmt_txt(const lv_font_t * font, uint32_t unicode_letter)
85 {
86 if(unicode_letter == '\t') unicode_letter = ' ';
87
88 lv_font_fmt_txt_dsc_t * fdsc = (lv_font_fmt_txt_dsc_t *) font->dsc;
89 uint32_t gid = get_glyph_dsc_id(font, unicode_letter);
90 if(!gid) return NULL;
91
92 const lv_font_fmt_txt_glyph_dsc_t * gdsc = &fdsc->glyph_dsc[gid];
93
94 if(fdsc->bitmap_format == LV_FONT_FMT_TXT_PLAIN) {
95 if(gdsc) return &fdsc->glyph_bitmap[gdsc->bitmap_index];
96 }
97 /*Handle compressed bitmap*/
98 else {
99 #if LV_USE_FONT_COMPRESSED
100 uint32_t gsize = gdsc->box_w * gdsc->box_h;
101 if(gsize == 0) return NULL;
102
103 uint32_t buf_size = gsize;
104 /*Compute memory size needed to hold decompressed glyph, rounding up*/
105 switch(fdsc->bpp) {
106 case 1:
107 buf_size = (gsize + 7) >> 3;
108 break;
109 case 2:
110 buf_size = (gsize + 3) >> 2;
111 break;
112 case 3:
113 buf_size = (gsize + 1) >> 1;
114 break;
115 case 4:
116 buf_size = (gsize + 1) >> 1;
117 break;
118 }
119
120 if(_lv_mem_get_size(LV_GC_ROOT(_lv_font_decompr_buf)) < buf_size) {
121 LV_GC_ROOT(_lv_font_decompr_buf) = lv_mem_realloc(LV_GC_ROOT(_lv_font_decompr_buf), buf_size);
122 LV_ASSERT_MEM(LV_GC_ROOT(_lv_font_decompr_buf));
123 if(LV_GC_ROOT(_lv_font_decompr_buf) == NULL) return NULL;
124 }
125
126 bool prefilter = fdsc->bitmap_format == LV_FONT_FMT_TXT_COMPRESSED ? true : false;
127 decompress(&fdsc->glyph_bitmap[gdsc->bitmap_index], LV_GC_ROOT(_lv_font_decompr_buf), gdsc->box_w, gdsc->box_h,
128 (uint8_t)fdsc->bpp,
129 prefilter);
130 return LV_GC_ROOT(_lv_font_decompr_buf);
131 #else /* !LV_USE_FONT_COMPRESSED */
132 return NULL;
133 #endif
134 }
135
136 /*If not returned earlier then the letter is not found in this font*/
137 return NULL;
138 }
139
140 /**
141 * Used as `get_glyph_dsc` callback in LittelvGL's native font format if the font is uncompressed.
142 * @param font_p pointer to font
143 * @param dsc_out store the result descriptor here
144 * @param letter an UNICODE letter code
145 * @return true: descriptor is successfully loaded into `dsc_out`.
146 * false: the letter was not found, no data is loaded to `dsc_out`
147 */
lv_font_get_glyph_dsc_fmt_txt(const lv_font_t * font,lv_font_glyph_dsc_t * dsc_out,uint32_t unicode_letter,uint32_t unicode_letter_next)148 bool lv_font_get_glyph_dsc_fmt_txt(const lv_font_t * font, lv_font_glyph_dsc_t * dsc_out, uint32_t unicode_letter,
149 uint32_t unicode_letter_next)
150 {
151 bool is_tab = false;
152 if(unicode_letter == '\t') {
153 unicode_letter = ' ';
154 is_tab = true;
155 }
156 lv_font_fmt_txt_dsc_t * fdsc = (lv_font_fmt_txt_dsc_t *) font->dsc;
157 uint32_t gid = get_glyph_dsc_id(font, unicode_letter);
158 if(!gid) return false;
159
160 int8_t kvalue = 0;
161 if(fdsc->kern_dsc) {
162 uint32_t gid_next = get_glyph_dsc_id(font, unicode_letter_next);
163 if(gid_next) {
164 kvalue = get_kern_value(font, gid, gid_next);
165 }
166 }
167
168 /*Put together a glyph dsc*/
169 const lv_font_fmt_txt_glyph_dsc_t * gdsc = &fdsc->glyph_dsc[gid];
170
171 int32_t kv = ((int32_t)((int32_t)kvalue * fdsc->kern_scale) >> 4);
172
173 uint32_t adv_w = gdsc->adv_w;
174 if(is_tab) adv_w *= 2;
175
176 adv_w += kv;
177 adv_w = (adv_w + (1 << 3)) >> 4;
178
179 dsc_out->adv_w = adv_w;
180 dsc_out->box_h = gdsc->box_h;
181 dsc_out->box_w = gdsc->box_w;
182 dsc_out->ofs_x = gdsc->ofs_x;
183 dsc_out->ofs_y = gdsc->ofs_y;
184 dsc_out->bpp = (uint8_t)fdsc->bpp;
185
186 if(is_tab) dsc_out->box_w = dsc_out->box_w * 2;
187
188 return true;
189 }
190
191 /**
192 * Free the allocated memories.
193 */
_lv_font_clean_up_fmt_txt(void)194 void _lv_font_clean_up_fmt_txt(void)
195 {
196 if(LV_GC_ROOT(_lv_font_decompr_buf)) {
197 lv_mem_free(LV_GC_ROOT(_lv_font_decompr_buf));
198 LV_GC_ROOT(_lv_font_decompr_buf) = NULL;
199 }
200 }
201
202
203 /**********************
204 * STATIC FUNCTIONS
205 **********************/
206
get_glyph_dsc_id(const lv_font_t * font,uint32_t letter)207 static uint32_t get_glyph_dsc_id(const lv_font_t * font, uint32_t letter)
208 {
209 if(letter == '\0') return 0;
210
211 lv_font_fmt_txt_dsc_t * fdsc = (lv_font_fmt_txt_dsc_t *) font->dsc;
212
213 /*Check the cache first*/
214 if(letter == fdsc->last_letter) return fdsc->last_glyph_id;
215
216 uint16_t i;
217 for(i = 0; i < fdsc->cmap_num; i++) {
218
219 /*Relative code point*/
220 uint32_t rcp = letter - fdsc->cmaps[i].range_start;
221 if(rcp > fdsc->cmaps[i].range_length) continue;
222 uint32_t glyph_id = 0;
223 if(fdsc->cmaps[i].type == LV_FONT_FMT_TXT_CMAP_FORMAT0_TINY) {
224 glyph_id = fdsc->cmaps[i].glyph_id_start + rcp;
225 }
226 else if(fdsc->cmaps[i].type == LV_FONT_FMT_TXT_CMAP_FORMAT0_FULL) {
227 const uint8_t * gid_ofs_8 = fdsc->cmaps[i].glyph_id_ofs_list;
228 glyph_id = fdsc->cmaps[i].glyph_id_start + gid_ofs_8[rcp];
229 }
230 else if(fdsc->cmaps[i].type == LV_FONT_FMT_TXT_CMAP_SPARSE_TINY) {
231 uint16_t key = rcp;
232 uint8_t * p = _lv_utils_bsearch(&key, fdsc->cmaps[i].unicode_list, fdsc->cmaps[i].list_length,
233 sizeof(fdsc->cmaps[i].unicode_list[0]), unicode_list_compare);
234
235 if(p) {
236 lv_uintptr_t ofs = (lv_uintptr_t)(p - (uint8_t *) fdsc->cmaps[i].unicode_list);
237 ofs = ofs >> 1; /*The list stores `uint16_t` so the get the index divide by 2*/
238 glyph_id = fdsc->cmaps[i].glyph_id_start + ofs;
239 }
240 }
241 else if(fdsc->cmaps[i].type == LV_FONT_FMT_TXT_CMAP_SPARSE_FULL) {
242 uint16_t key = rcp;
243 uint8_t * p = _lv_utils_bsearch(&key, fdsc->cmaps[i].unicode_list, fdsc->cmaps[i].list_length,
244 sizeof(fdsc->cmaps[i].unicode_list[0]), unicode_list_compare);
245
246 if(p) {
247 lv_uintptr_t ofs = (lv_uintptr_t)(p - (uint8_t *) fdsc->cmaps[i].unicode_list);
248 ofs = ofs >> 1; /*The list stores `uint16_t` so the get the index divide by 2*/
249 const uint8_t * gid_ofs_16 = fdsc->cmaps[i].glyph_id_ofs_list;
250 glyph_id = fdsc->cmaps[i].glyph_id_start + gid_ofs_16[ofs];
251 }
252 }
253
254 /*Update the cache*/
255 fdsc->last_letter = letter;
256 fdsc->last_glyph_id = glyph_id;
257 return glyph_id;
258 }
259
260 fdsc->last_letter = letter;
261 fdsc->last_glyph_id = 0;
262 return 0;
263
264 }
265
get_kern_value(const lv_font_t * font,uint32_t gid_left,uint32_t gid_right)266 static int8_t get_kern_value(const lv_font_t * font, uint32_t gid_left, uint32_t gid_right)
267 {
268 lv_font_fmt_txt_dsc_t * fdsc = (lv_font_fmt_txt_dsc_t *) font->dsc;
269
270 int8_t value = 0;
271
272 if(fdsc->kern_classes == 0) {
273 /*Kern pairs*/
274 const lv_font_fmt_txt_kern_pair_t * kdsc = fdsc->kern_dsc;
275 if(kdsc->glyph_ids_size == 0) {
276 /* Use binary search to find the kern value.
277 * The pairs are ordered left_id first, then right_id secondly. */
278 const uint8_t * g_ids = kdsc->glyph_ids;
279 uint16_t g_id_both = (gid_right << 8) + gid_left; /*Create one number from the ids*/
280 uint8_t * kid_p = _lv_utils_bsearch(&g_id_both, g_ids, kdsc->pair_cnt, 2, kern_pair_8_compare);
281
282 /*If the `g_id_both` were found get its index from the pointer*/
283 if(kid_p) {
284 lv_uintptr_t ofs = (lv_uintptr_t)(kid_p - g_ids);
285 ofs = ofs >> 1; /*ofs is for pair, divide by 2 to refer as a single value*/
286 value = kdsc->values[ofs];
287 }
288 }
289 else if(kdsc->glyph_ids_size == 1) {
290 /* Use binary search to find the kern value.
291 * The pairs are ordered left_id first, then right_id secondly. */
292 const uint16_t * g_ids = kdsc->glyph_ids;
293 lv_uintptr_t g_id_both = (uint32_t)((uint32_t)gid_right << 8) + gid_left; /*Create one number from the ids*/
294 uint8_t * kid_p = _lv_utils_bsearch(&g_id_both, g_ids, kdsc->pair_cnt, 4, kern_pair_16_compare);
295
296 /*If the `g_id_both` were found get its index from the pointer*/
297 if(kid_p) {
298 lv_uintptr_t ofs = (lv_uintptr_t)(kid_p - (const uint8_t *)g_ids);
299 ofs = ofs >> 4; /*ofs is 4 byte pairs, divide by 4 to refer as a single value*/
300 value = kdsc->values[ofs];
301 }
302
303 }
304 else {
305 /*Invalid value*/
306 }
307 }
308 else {
309 /*Kern classes*/
310 const lv_font_fmt_txt_kern_classes_t * kdsc = fdsc->kern_dsc;
311 uint8_t left_class = kdsc->left_class_mapping[gid_left];
312 uint8_t right_class = kdsc->right_class_mapping[gid_right];
313
314 /* If class = 0, kerning not exist for that glyph
315 * else got the value form `class_pair_values` 2D array*/
316 if(left_class > 0 && right_class > 0) {
317 value = kdsc->class_pair_values[(left_class - 1) * kdsc->right_class_cnt + (right_class - 1)];
318 }
319
320 }
321 return value;
322 }
323
kern_pair_8_compare(const void * ref,const void * element)324 static int32_t kern_pair_8_compare(const void * ref, const void * element)
325 {
326 const uint8_t * ref8_p = ref;
327 const uint8_t * element8_p = element;
328
329 /*If the MSB is different it will matter. If not return the diff. of the LSB*/
330 if(ref8_p[0] != element8_p[0]) return (int32_t)ref8_p[0] - element8_p[0];
331 else return (int32_t) ref8_p[1] - element8_p[1];
332
333 }
334
kern_pair_16_compare(const void * ref,const void * element)335 static int32_t kern_pair_16_compare(const void * ref, const void * element)
336 {
337 const uint16_t * ref16_p = ref;
338 const uint16_t * element16_p = element;
339
340 /*If the MSB is different it will matter. If not return the diff. of the LSB*/
341 if(ref16_p[0] != element16_p[0]) return (int32_t)ref16_p[0] - element16_p[0];
342 else return (int32_t) ref16_p[1] - element16_p[1];
343 }
344
345 #if LV_USE_FONT_COMPRESSED
346 /**
347 * The compress a glyph's bitmap
348 * @param in the compressed bitmap
349 * @param out buffer to store the result
350 * @param px_num number of pixels in the glyph (width * height)
351 * @param bpp bit per pixel (bpp = 3 will be converted to bpp = 4)
352 * @param prefilter true: the lines are XORed
353 */
decompress(const uint8_t * in,uint8_t * out,lv_coord_t w,lv_coord_t h,uint8_t bpp,bool prefilter)354 static void decompress(const uint8_t * in, uint8_t * out, lv_coord_t w, lv_coord_t h, uint8_t bpp, bool prefilter)
355 {
356 uint32_t wrp = 0;
357 uint8_t wr_size = bpp;
358 if(bpp == 3) wr_size = 4;
359
360 rle_init(in, bpp);
361
362 uint8_t * line_buf1 = _lv_mem_buf_get(w);
363
364 uint8_t * line_buf2 = NULL;
365
366 if(prefilter) {
367 line_buf2 = _lv_mem_buf_get(w);
368 }
369
370 decompress_line(line_buf1, w);
371
372 lv_coord_t y;
373 lv_coord_t x;
374
375 for(x = 0; x < w; x++) {
376 bits_write(out, wrp, line_buf1[x], bpp);
377 wrp += wr_size;
378 }
379
380 for(y = 1; y < h; y++) {
381 if(prefilter) {
382 decompress_line(line_buf2, w);
383
384 for(x = 0; x < w; x++) {
385 line_buf1[x] = line_buf2[x] ^ line_buf1[x];
386 bits_write(out, wrp, line_buf1[x], bpp);
387 wrp += wr_size;
388 }
389 }
390 else {
391 decompress_line(line_buf1, w);
392
393 for(x = 0; x < w; x++) {
394 bits_write(out, wrp, line_buf1[x], bpp);
395 wrp += wr_size;
396 }
397 }
398 }
399
400 _lv_mem_buf_release(line_buf1);
401 _lv_mem_buf_release(line_buf2);
402 }
403
404 /**
405 * Decompress one line. Store one pixel per byte
406 * @param out output buffer
407 * @param w width of the line in pixel count
408 */
decompress_line(uint8_t * out,lv_coord_t w)409 static inline void decompress_line(uint8_t * out, lv_coord_t w)
410 {
411 lv_coord_t i;
412 for(i = 0; i < w; i++) {
413 out[i] = rle_next();
414 }
415 }
416
417 /**
418 * Read bits from an input buffer. The read can cross byte boundary.
419 * @param in the input buffer to read from.
420 * @param bit_pos index of the first bit to read.
421 * @param len number of bits to read (must be <= 8).
422 * @return the read bits
423 */
get_bits(const uint8_t * in,uint32_t bit_pos,uint8_t len)424 static inline uint8_t get_bits(const uint8_t * in, uint32_t bit_pos, uint8_t len)
425 {
426 uint8_t bit_mask;
427 switch(len) {
428 case 1:
429 bit_mask = 0x1;
430 break;
431 case 2:
432 bit_mask = 0x3;
433 break;
434 case 3:
435 bit_mask = 0x7;
436 break;
437 case 4:
438 bit_mask = 0xF;
439 break;
440 case 8:
441 bit_mask = 0xFF;
442 break;
443 default:
444 bit_mask = (uint16_t)((uint16_t) 1 << len) - 1;
445 }
446
447 uint32_t byte_pos = bit_pos >> 3;
448 bit_pos = bit_pos & 0x7;
449
450 if(bit_pos + len >= 8) {
451 uint16_t in16 = (in[byte_pos] << 8) + in[byte_pos + 1];
452 return (in16 >> (16 - bit_pos - len)) & bit_mask;
453 }
454 else {
455 return (in[byte_pos] >> (8 - bit_pos - len)) & bit_mask;
456 }
457 }
458
459 /**
460 * Write `val` data to `bit_pos` position of `out`. The write can NOT cross byte boundary.
461 * @param out buffer where to write
462 * @param bit_pos bit index to write
463 * @param val value to write
464 * @param len length of bits to write from `val`. (Counted from the LSB).
465 * @note `len == 3` will be converted to `len = 4` and `val` will be upscaled too
466 */
bits_write(uint8_t * out,uint32_t bit_pos,uint8_t val,uint8_t len)467 static inline void bits_write(uint8_t * out, uint32_t bit_pos, uint8_t val, uint8_t len)
468 {
469 if(len == 3) {
470 len = 4;
471 switch(val) {
472 case 0:
473 val = 0;
474 break;
475 case 1:
476 val = 2;
477 break;
478 case 2:
479 val = 4;
480 break;
481 case 3:
482 val = 6;
483 break;
484 case 4:
485 val = 9;
486 break;
487 case 5:
488 val = 11;
489 break;
490 case 6:
491 val = 13;
492 break;
493 case 7:
494 val = 15;
495 break;
496 }
497 }
498
499 uint16_t byte_pos = bit_pos >> 3;
500 bit_pos = bit_pos & 0x7;
501 bit_pos = 8 - bit_pos - len;
502
503 uint8_t bit_mask = (uint16_t)((uint16_t) 1 << len) - 1;
504 out[byte_pos] &= ((~bit_mask) << bit_pos);
505 out[byte_pos] |= (val << bit_pos);
506 }
507
rle_init(const uint8_t * in,uint8_t bpp)508 static inline void rle_init(const uint8_t * in, uint8_t bpp)
509 {
510 rle_in = in;
511 rle_bpp = bpp;
512 rle_state = RLE_STATE_SINGLE;
513 rle_rdp = 0;
514 rle_prev_v = 0;
515 rle_cnt = 0;
516 }
517
rle_next(void)518 static inline uint8_t rle_next(void)
519 {
520 uint8_t v = 0;
521 uint8_t ret = 0;
522
523 if(rle_state == RLE_STATE_SINGLE) {
524 ret = get_bits(rle_in, rle_rdp, rle_bpp);
525 if(rle_rdp != 0 && rle_prev_v == ret) {
526 rle_cnt = 0;
527 rle_state = RLE_STATE_REPEATE;
528 }
529
530 rle_prev_v = ret;
531 rle_rdp += rle_bpp;
532 }
533 else if(rle_state == RLE_STATE_REPEATE) {
534 v = get_bits(rle_in, rle_rdp, 1);
535 rle_cnt++;
536 rle_rdp += 1;
537 if(v == 1) {
538 ret = rle_prev_v;
539 if(rle_cnt == 11) {
540 rle_cnt = get_bits(rle_in, rle_rdp, 6);
541 rle_rdp += 6;
542 if(rle_cnt != 0) {
543 rle_state = RLE_STATE_COUNTER;
544 }
545 else {
546 ret = get_bits(rle_in, rle_rdp, rle_bpp);
547 rle_prev_v = ret;
548 rle_rdp += rle_bpp;
549 rle_state = RLE_STATE_SINGLE;
550 }
551 }
552 }
553 else {
554 ret = get_bits(rle_in, rle_rdp, rle_bpp);
555 rle_prev_v = ret;
556 rle_rdp += rle_bpp;
557 rle_state = RLE_STATE_SINGLE;
558 }
559
560
561 }
562 else if(rle_state == RLE_STATE_COUNTER) {
563 ret = rle_prev_v;
564 rle_cnt--;
565 if(rle_cnt == 0) {
566 ret = get_bits(rle_in, rle_rdp, rle_bpp);
567 rle_prev_v = ret;
568 rle_rdp += rle_bpp;
569 rle_state = RLE_STATE_SINGLE;
570 }
571 }
572
573 return ret;
574 }
575 #endif /* LV_USE_FONT_COMPRESSED */
576
577 /** Code Comparator.
578 *
579 * Compares the value of both input arguments.
580 *
581 * @param[in] pRef Pointer to the reference.
582 * @param[in] pElement Pointer to the element to compare.
583 *
584 * @return Result of comparison.
585 * @retval < 0 Reference is greater than element.
586 * @retval = 0 Reference is equal to element.
587 * @retval > 0 Reference is less than element.
588 *
589 */
unicode_list_compare(const void * ref,const void * element)590 static int32_t unicode_list_compare(const void * ref, const void * element)
591 {
592 return ((int32_t)(*(uint16_t *)ref)) - ((int32_t)(*(uint16_t *)element));
593 }
594