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