acid-drop- Hacking the planet from a LilyGo T-Deck using custom firmware |
git clone git://git.acid.vegas/acid-drop.git |
Log | Files | Refs | Archive | README | LICENSE |
lv_font_fmt_txt.c (18278B)
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 */ 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 */ 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 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 */ 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 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 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 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 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 */ 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 */ 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 */ 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 */ 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 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 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 */ 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 }