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 }