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