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_loader.c (21365B)

      1 /**
      2  * @file lv_font_loader.c
      3  *
      4  */
      5 
      6 /*********************
      7  *      INCLUDES
      8  *********************/
      9 
     10 #include <stdint.h>
     11 #include <stdbool.h>
     12 
     13 #include "../lvgl.h"
     14 #include "../misc/lv_fs.h"
     15 #include "lv_font_loader.h"
     16 
     17 /**********************
     18  *      TYPEDEFS
     19  **********************/
     20 typedef struct {
     21     lv_fs_file_t * fp;
     22     int8_t bit_pos;
     23     uint8_t byte_value;
     24 } bit_iterator_t;
     25 
     26 typedef struct font_header_bin {
     27     uint32_t version;
     28     uint16_t tables_count;
     29     uint16_t font_size;
     30     uint16_t ascent;
     31     int16_t descent;
     32     uint16_t typo_ascent;
     33     int16_t typo_descent;
     34     uint16_t typo_line_gap;
     35     int16_t min_y;
     36     int16_t max_y;
     37     uint16_t default_advance_width;
     38     uint16_t kerning_scale;
     39     uint8_t index_to_loc_format;
     40     uint8_t glyph_id_format;
     41     uint8_t advance_width_format;
     42     uint8_t bits_per_pixel;
     43     uint8_t xy_bits;
     44     uint8_t wh_bits;
     45     uint8_t advance_width_bits;
     46     uint8_t compression_id;
     47     uint8_t subpixels_mode;
     48     uint8_t padding;
     49     int16_t underline_position;
     50     uint16_t underline_thickness;
     51 } font_header_bin_t;
     52 
     53 typedef struct cmap_table_bin {
     54     uint32_t data_offset;
     55     uint32_t range_start;
     56     uint16_t range_length;
     57     uint16_t glyph_id_start;
     58     uint16_t data_entries_count;
     59     uint8_t format_type;
     60     uint8_t padding;
     61 } cmap_table_bin_t;
     62 
     63 /**********************
     64  *  STATIC PROTOTYPES
     65  **********************/
     66 static bit_iterator_t init_bit_iterator(lv_fs_file_t * fp);
     67 static bool lvgl_load_font(lv_fs_file_t * fp, lv_font_t * font);
     68 int32_t load_kern(lv_fs_file_t * fp, lv_font_fmt_txt_dsc_t * font_dsc, uint8_t format, uint32_t start);
     69 
     70 static int read_bits_signed(bit_iterator_t * it, int n_bits, lv_fs_res_t * res);
     71 static unsigned int read_bits(bit_iterator_t * it, int n_bits, lv_fs_res_t * res);
     72 
     73 /**********************
     74  *      MACROS
     75  **********************/
     76 
     77 /**********************
     78  *   GLOBAL FUNCTIONS
     79  **********************/
     80 
     81 /**
     82  * Loads a `lv_font_t` object from a binary font file
     83  * @param font_name filename where the font file is located
     84  * @return a pointer to the font or NULL in case of error
     85  */
     86 lv_font_t * lv_font_load(const char * font_name)
     87 {
     88     lv_fs_file_t file;
     89     lv_fs_res_t res = lv_fs_open(&file, font_name, LV_FS_MODE_RD);
     90     if(res != LV_FS_RES_OK)
     91         return NULL;
     92 
     93     lv_font_t * font = lv_mem_alloc(sizeof(lv_font_t));
     94     if(font) {
     95         memset(font, 0, sizeof(lv_font_t));
     96         if(!lvgl_load_font(&file, font)) {
     97             LV_LOG_WARN("Error loading font file: %s\n", font_name);
     98             /*
     99             * When `lvgl_load_font` fails it can leak some pointers.
    100             * All non-null pointers can be assumed as allocated and
    101             * `lv_font_free` should free them correctly.
    102             */
    103             lv_font_free(font);
    104             font = NULL;
    105         }
    106     }
    107 
    108     lv_fs_close(&file);
    109 
    110     return font;
    111 }
    112 
    113 /**
    114  * Frees the memory allocated by the `lv_font_load()` function
    115  * @param font lv_font_t object created by the lv_font_load function
    116  */
    117 void lv_font_free(lv_font_t * font)
    118 {
    119     if(NULL != font) {
    120         lv_font_fmt_txt_dsc_t * dsc = (lv_font_fmt_txt_dsc_t *)font->dsc;
    121 
    122         if(NULL != dsc) {
    123 
    124             if(dsc->kern_classes == 0) {
    125                 lv_font_fmt_txt_kern_pair_t * kern_dsc =
    126                     (lv_font_fmt_txt_kern_pair_t *)dsc->kern_dsc;
    127 
    128                 if(NULL != kern_dsc) {
    129                     if(kern_dsc->glyph_ids)
    130                         lv_mem_free((void *)kern_dsc->glyph_ids);
    131 
    132                     if(kern_dsc->values)
    133                         lv_mem_free((void *)kern_dsc->values);
    134 
    135                     lv_mem_free((void *)kern_dsc);
    136                 }
    137             }
    138             else {
    139                 lv_font_fmt_txt_kern_classes_t * kern_dsc =
    140                     (lv_font_fmt_txt_kern_classes_t *)dsc->kern_dsc;
    141 
    142                 if(NULL != kern_dsc) {
    143                     if(kern_dsc->class_pair_values)
    144                         lv_mem_free((void *)kern_dsc->class_pair_values);
    145 
    146                     if(kern_dsc->left_class_mapping)
    147                         lv_mem_free((void *)kern_dsc->left_class_mapping);
    148 
    149                     if(kern_dsc->right_class_mapping)
    150                         lv_mem_free((void *)kern_dsc->right_class_mapping);
    151 
    152                     lv_mem_free((void *)kern_dsc);
    153                 }
    154             }
    155 
    156             lv_font_fmt_txt_cmap_t * cmaps =
    157                 (lv_font_fmt_txt_cmap_t *)dsc->cmaps;
    158 
    159             if(NULL != cmaps) {
    160                 for(int i = 0; i < dsc->cmap_num; ++i) {
    161                     if(NULL != cmaps[i].glyph_id_ofs_list)
    162                         lv_mem_free((void *)cmaps[i].glyph_id_ofs_list);
    163                     if(NULL != cmaps[i].unicode_list)
    164                         lv_mem_free((void *)cmaps[i].unicode_list);
    165                 }
    166                 lv_mem_free(cmaps);
    167             }
    168 
    169             if(NULL != dsc->glyph_bitmap) {
    170                 lv_mem_free((void *)dsc->glyph_bitmap);
    171             }
    172             if(NULL != dsc->glyph_dsc) {
    173                 lv_mem_free((void *)dsc->glyph_dsc);
    174             }
    175             lv_mem_free(dsc);
    176         }
    177         lv_mem_free(font);
    178     }
    179 }
    180 
    181 /**********************
    182  *   STATIC FUNCTIONS
    183  **********************/
    184 
    185 static bit_iterator_t init_bit_iterator(lv_fs_file_t * fp)
    186 {
    187     bit_iterator_t it;
    188     it.fp = fp;
    189     it.bit_pos = -1;
    190     it.byte_value = 0;
    191     return it;
    192 }
    193 
    194 static unsigned int read_bits(bit_iterator_t * it, int n_bits, lv_fs_res_t * res)
    195 {
    196     unsigned int value = 0;
    197     while(n_bits--) {
    198         it->byte_value = it->byte_value << 1;
    199         it->bit_pos--;
    200 
    201         if(it->bit_pos < 0) {
    202             it->bit_pos = 7;
    203             *res = lv_fs_read(it->fp, &(it->byte_value), 1, NULL);
    204             if(*res != LV_FS_RES_OK) {
    205                 return 0;
    206             }
    207         }
    208         int8_t bit = (it->byte_value & 0x80) ? 1 : 0;
    209 
    210         value |= (bit << n_bits);
    211     }
    212     *res = LV_FS_RES_OK;
    213     return value;
    214 }
    215 
    216 static int read_bits_signed(bit_iterator_t * it, int n_bits, lv_fs_res_t * res)
    217 {
    218     unsigned int value = read_bits(it, n_bits, res);
    219     if(value & (1 << (n_bits - 1))) {
    220         value |= ~0u << n_bits;
    221     }
    222     return value;
    223 }
    224 
    225 static int read_label(lv_fs_file_t * fp, int start, const char * label)
    226 {
    227     lv_fs_seek(fp, start, LV_FS_SEEK_SET);
    228 
    229     uint32_t length;
    230     char buf[4];
    231 
    232     if(lv_fs_read(fp, &length, 4, NULL) != LV_FS_RES_OK
    233        || lv_fs_read(fp, buf, 4, NULL) != LV_FS_RES_OK
    234        || memcmp(label, buf, 4) != 0) {
    235         LV_LOG_WARN("Error reading '%s' label.", label);
    236         return -1;
    237     }
    238 
    239     return length;
    240 }
    241 
    242 static bool load_cmaps_tables(lv_fs_file_t * fp, lv_font_fmt_txt_dsc_t * font_dsc,
    243                               uint32_t cmaps_start, cmap_table_bin_t * cmap_table)
    244 {
    245     if(lv_fs_read(fp, cmap_table, font_dsc->cmap_num * sizeof(cmap_table_bin_t), NULL) != LV_FS_RES_OK) {
    246         return false;
    247     }
    248 
    249     for(unsigned int i = 0; i < font_dsc->cmap_num; ++i) {
    250         lv_fs_res_t res = lv_fs_seek(fp, cmaps_start + cmap_table[i].data_offset, LV_FS_SEEK_SET);
    251         if(res != LV_FS_RES_OK) {
    252             return false;
    253         }
    254 
    255         lv_font_fmt_txt_cmap_t * cmap = (lv_font_fmt_txt_cmap_t *) & (font_dsc->cmaps[i]);
    256 
    257         cmap->range_start = cmap_table[i].range_start;
    258         cmap->range_length = cmap_table[i].range_length;
    259         cmap->glyph_id_start = cmap_table[i].glyph_id_start;
    260         cmap->type = cmap_table[i].format_type;
    261 
    262         switch(cmap_table[i].format_type) {
    263             case LV_FONT_FMT_TXT_CMAP_FORMAT0_FULL: {
    264                     uint8_t ids_size = sizeof(uint8_t) * cmap_table[i].data_entries_count;
    265                     uint8_t * glyph_id_ofs_list = lv_mem_alloc(ids_size);
    266 
    267                     cmap->glyph_id_ofs_list = glyph_id_ofs_list;
    268 
    269                     if(lv_fs_read(fp, glyph_id_ofs_list, ids_size, NULL) != LV_FS_RES_OK) {
    270                         return false;
    271                     }
    272 
    273                     cmap->list_length = cmap->range_length;
    274                     break;
    275                 }
    276             case LV_FONT_FMT_TXT_CMAP_FORMAT0_TINY:
    277                 break;
    278             case LV_FONT_FMT_TXT_CMAP_SPARSE_FULL:
    279             case LV_FONT_FMT_TXT_CMAP_SPARSE_TINY: {
    280                     uint32_t list_size = sizeof(uint16_t) * cmap_table[i].data_entries_count;
    281                     uint16_t * unicode_list = (uint16_t *)lv_mem_alloc(list_size);
    282 
    283                     cmap->unicode_list = unicode_list;
    284                     cmap->list_length = cmap_table[i].data_entries_count;
    285 
    286                     if(lv_fs_read(fp, unicode_list, list_size, NULL) != LV_FS_RES_OK) {
    287                         return false;
    288                     }
    289 
    290                     if(cmap_table[i].format_type == LV_FONT_FMT_TXT_CMAP_SPARSE_FULL) {
    291                         uint16_t * buf = lv_mem_alloc(sizeof(uint16_t) * cmap->list_length);
    292 
    293                         cmap->glyph_id_ofs_list = buf;
    294 
    295                         if(lv_fs_read(fp, buf, sizeof(uint16_t) * cmap->list_length, NULL) != LV_FS_RES_OK) {
    296                             return false;
    297                         }
    298                     }
    299                     break;
    300                 }
    301             default:
    302                 LV_LOG_WARN("Unknown cmaps format type %d.", cmap_table[i].format_type);
    303                 return false;
    304         }
    305     }
    306     return true;
    307 }
    308 
    309 static int32_t load_cmaps(lv_fs_file_t * fp, lv_font_fmt_txt_dsc_t * font_dsc, uint32_t cmaps_start)
    310 {
    311     int32_t cmaps_length = read_label(fp, cmaps_start, "cmap");
    312     if(cmaps_length < 0) {
    313         return -1;
    314     }
    315 
    316     uint32_t cmaps_subtables_count;
    317     if(lv_fs_read(fp, &cmaps_subtables_count, sizeof(uint32_t), NULL) != LV_FS_RES_OK) {
    318         return -1;
    319     }
    320 
    321     lv_font_fmt_txt_cmap_t * cmaps =
    322         lv_mem_alloc(cmaps_subtables_count * sizeof(lv_font_fmt_txt_cmap_t));
    323 
    324     memset(cmaps, 0, cmaps_subtables_count * sizeof(lv_font_fmt_txt_cmap_t));
    325 
    326     font_dsc->cmaps = cmaps;
    327     font_dsc->cmap_num = cmaps_subtables_count;
    328 
    329     cmap_table_bin_t * cmaps_tables = lv_mem_alloc(sizeof(cmap_table_bin_t) * font_dsc->cmap_num);
    330 
    331     bool success = load_cmaps_tables(fp, font_dsc, cmaps_start, cmaps_tables);
    332 
    333     lv_mem_free(cmaps_tables);
    334 
    335     return success ? cmaps_length : -1;
    336 }
    337 
    338 static int32_t load_glyph(lv_fs_file_t * fp, lv_font_fmt_txt_dsc_t * font_dsc,
    339                           uint32_t start, uint32_t * glyph_offset, uint32_t loca_count, font_header_bin_t * header)
    340 {
    341     int32_t glyph_length = read_label(fp, start, "glyf");
    342     if(glyph_length < 0) {
    343         return -1;
    344     }
    345 
    346     lv_font_fmt_txt_glyph_dsc_t * glyph_dsc = (lv_font_fmt_txt_glyph_dsc_t *)
    347                                               lv_mem_alloc(loca_count * sizeof(lv_font_fmt_txt_glyph_dsc_t));
    348 
    349     memset(glyph_dsc, 0, loca_count * sizeof(lv_font_fmt_txt_glyph_dsc_t));
    350 
    351     font_dsc->glyph_dsc = glyph_dsc;
    352 
    353     int cur_bmp_size = 0;
    354 
    355     for(unsigned int i = 0; i < loca_count; ++i) {
    356         lv_font_fmt_txt_glyph_dsc_t * gdsc = &glyph_dsc[i];
    357 
    358         lv_fs_res_t res = lv_fs_seek(fp, start + glyph_offset[i], LV_FS_SEEK_SET);
    359         if(res != LV_FS_RES_OK) {
    360             return -1;
    361         }
    362 
    363         bit_iterator_t bit_it = init_bit_iterator(fp);
    364 
    365         if(header->advance_width_bits == 0) {
    366             gdsc->adv_w = header->default_advance_width;
    367         }
    368         else {
    369             gdsc->adv_w = read_bits(&bit_it, header->advance_width_bits, &res);
    370             if(res != LV_FS_RES_OK) {
    371                 return -1;
    372             }
    373         }
    374 
    375         if(header->advance_width_format == 0) {
    376             gdsc->adv_w *= 16;
    377         }
    378 
    379         gdsc->ofs_x = read_bits_signed(&bit_it, header->xy_bits, &res);
    380         if(res != LV_FS_RES_OK) {
    381             return -1;
    382         }
    383 
    384         gdsc->ofs_y = read_bits_signed(&bit_it, header->xy_bits, &res);
    385         if(res != LV_FS_RES_OK) {
    386             return -1;
    387         }
    388 
    389         gdsc->box_w = read_bits(&bit_it, header->wh_bits, &res);
    390         if(res != LV_FS_RES_OK) {
    391             return -1;
    392         }
    393 
    394         gdsc->box_h = read_bits(&bit_it, header->wh_bits, &res);
    395         if(res != LV_FS_RES_OK) {
    396             return -1;
    397         }
    398 
    399         int nbits = header->advance_width_bits + 2 * header->xy_bits + 2 * header->wh_bits;
    400         int next_offset = (i < loca_count - 1) ? glyph_offset[i + 1] : (uint32_t)glyph_length;
    401         int bmp_size = next_offset - glyph_offset[i] - nbits / 8;
    402 
    403         if(i == 0) {
    404             gdsc->adv_w = 0;
    405             gdsc->box_w = 0;
    406             gdsc->box_h = 0;
    407             gdsc->ofs_x = 0;
    408             gdsc->ofs_y = 0;
    409         }
    410 
    411         gdsc->bitmap_index = cur_bmp_size;
    412         if(gdsc->box_w * gdsc->box_h != 0) {
    413             cur_bmp_size += bmp_size;
    414         }
    415     }
    416 
    417     uint8_t * glyph_bmp = (uint8_t *)lv_mem_alloc(sizeof(uint8_t) * cur_bmp_size);
    418 
    419     font_dsc->glyph_bitmap = glyph_bmp;
    420 
    421     cur_bmp_size = 0;
    422 
    423     for(unsigned int i = 1; i < loca_count; ++i) {
    424         lv_fs_res_t res = lv_fs_seek(fp, start + glyph_offset[i], LV_FS_SEEK_SET);
    425         if(res != LV_FS_RES_OK) {
    426             return -1;
    427         }
    428         bit_iterator_t bit_it = init_bit_iterator(fp);
    429 
    430         int nbits = header->advance_width_bits + 2 * header->xy_bits + 2 * header->wh_bits;
    431 
    432         read_bits(&bit_it, nbits, &res);
    433         if(res != LV_FS_RES_OK) {
    434             return -1;
    435         }
    436 
    437         if(glyph_dsc[i].box_w * glyph_dsc[i].box_h == 0) {
    438             continue;
    439         }
    440 
    441         int next_offset = (i < loca_count - 1) ? glyph_offset[i + 1] : (uint32_t)glyph_length;
    442         int bmp_size = next_offset - glyph_offset[i] - nbits / 8;
    443 
    444         if(nbits % 8 == 0) {  /*Fast path*/
    445             if(lv_fs_read(fp, &glyph_bmp[cur_bmp_size], bmp_size, NULL) != LV_FS_RES_OK) {
    446                 return -1;
    447             }
    448         }
    449         else {
    450             for(int k = 0; k < bmp_size - 1; ++k) {
    451                 glyph_bmp[cur_bmp_size + k] = read_bits(&bit_it, 8, &res);
    452                 if(res != LV_FS_RES_OK) {
    453                     return -1;
    454                 }
    455             }
    456             glyph_bmp[cur_bmp_size + bmp_size - 1] = read_bits(&bit_it, 8 - nbits % 8, &res);
    457             if(res != LV_FS_RES_OK) {
    458                 return -1;
    459             }
    460 
    461             /*The last fragment should be on the MSB but read_bits() will place it to the LSB*/
    462             glyph_bmp[cur_bmp_size + bmp_size - 1] = glyph_bmp[cur_bmp_size + bmp_size - 1] << (nbits % 8);
    463 
    464         }
    465 
    466         cur_bmp_size += bmp_size;
    467     }
    468     return glyph_length;
    469 }
    470 
    471 /*
    472  * Loads a `lv_font_t` from a binary file, given a `lv_fs_file_t`.
    473  *
    474  * Memory allocations on `lvgl_load_font` should be immediately zeroed and
    475  * the pointer should be set on the `lv_font_t` data before any possible return.
    476  *
    477  * When something fails, it returns `false` and the memory on the `lv_font_t`
    478  * still needs to be freed using `lv_font_free`.
    479  *
    480  * `lv_font_free` will assume that all non-null pointers are allocated and
    481  * should be freed.
    482  */
    483 static bool lvgl_load_font(lv_fs_file_t * fp, lv_font_t * font)
    484 {
    485     lv_font_fmt_txt_dsc_t * font_dsc = (lv_font_fmt_txt_dsc_t *)
    486                                        lv_mem_alloc(sizeof(lv_font_fmt_txt_dsc_t));
    487 
    488     memset(font_dsc, 0, sizeof(lv_font_fmt_txt_dsc_t));
    489 
    490     font->dsc = font_dsc;
    491 
    492     /*header*/
    493     int32_t header_length = read_label(fp, 0, "head");
    494     if(header_length < 0) {
    495         return false;
    496     }
    497 
    498     font_header_bin_t font_header;
    499     if(lv_fs_read(fp, &font_header, sizeof(font_header_bin_t), NULL) != LV_FS_RES_OK) {
    500         return false;
    501     }
    502 
    503     font->base_line = -font_header.descent;
    504     font->line_height = font_header.ascent - font_header.descent;
    505     font->get_glyph_dsc = lv_font_get_glyph_dsc_fmt_txt;
    506     font->get_glyph_bitmap = lv_font_get_bitmap_fmt_txt;
    507     font->subpx = font_header.subpixels_mode;
    508     font->underline_position = font_header.underline_position;
    509     font->underline_thickness = font_header.underline_thickness;
    510 
    511     font_dsc->bpp = font_header.bits_per_pixel;
    512     font_dsc->kern_scale = font_header.kerning_scale;
    513     font_dsc->bitmap_format = font_header.compression_id;
    514 
    515     /*cmaps*/
    516     uint32_t cmaps_start = header_length;
    517     int32_t cmaps_length = load_cmaps(fp, font_dsc, cmaps_start);
    518     if(cmaps_length < 0) {
    519         return false;
    520     }
    521 
    522     /*loca*/
    523     uint32_t loca_start = cmaps_start + cmaps_length;
    524     int32_t loca_length = read_label(fp, loca_start, "loca");
    525     if(loca_length < 0) {
    526         return false;
    527     }
    528 
    529     uint32_t loca_count;
    530     if(lv_fs_read(fp, &loca_count, sizeof(uint32_t), NULL) != LV_FS_RES_OK) {
    531         return false;
    532     }
    533 
    534     bool failed = false;
    535     uint32_t * glyph_offset = lv_mem_alloc(sizeof(uint32_t) * (loca_count + 1));
    536 
    537     if(font_header.index_to_loc_format == 0) {
    538         for(unsigned int i = 0; i < loca_count; ++i) {
    539             uint16_t offset;
    540             if(lv_fs_read(fp, &offset, sizeof(uint16_t), NULL) != LV_FS_RES_OK) {
    541                 failed = true;
    542                 break;
    543             }
    544             glyph_offset[i] = offset;
    545         }
    546     }
    547     else if(font_header.index_to_loc_format == 1) {
    548         if(lv_fs_read(fp, glyph_offset, loca_count * sizeof(uint32_t), NULL) != LV_FS_RES_OK) {
    549             failed = true;
    550         }
    551     }
    552     else {
    553         LV_LOG_WARN("Unknown index_to_loc_format: %d.", font_header.index_to_loc_format);
    554         failed = true;
    555     }
    556 
    557     if(failed) {
    558         lv_mem_free(glyph_offset);
    559         return false;
    560     }
    561 
    562     /*glyph*/
    563     uint32_t glyph_start = loca_start + loca_length;
    564     int32_t glyph_length = load_glyph(
    565                                fp, font_dsc, glyph_start, glyph_offset, loca_count, &font_header);
    566 
    567     lv_mem_free(glyph_offset);
    568 
    569     if(glyph_length < 0) {
    570         return false;
    571     }
    572 
    573     if(font_header.tables_count < 4) {
    574         font_dsc->kern_dsc = NULL;
    575         font_dsc->kern_classes = 0;
    576         font_dsc->kern_scale = 0;
    577         return true;
    578     }
    579 
    580     uint32_t kern_start = glyph_start + glyph_length;
    581 
    582     int32_t kern_length = load_kern(fp, font_dsc, font_header.glyph_id_format, kern_start);
    583 
    584     return kern_length >= 0;
    585 }
    586 
    587 int32_t load_kern(lv_fs_file_t * fp, lv_font_fmt_txt_dsc_t * font_dsc, uint8_t format, uint32_t start)
    588 {
    589     int32_t kern_length = read_label(fp, start, "kern");
    590     if(kern_length < 0) {
    591         return -1;
    592     }
    593 
    594     uint8_t kern_format_type;
    595     int32_t padding;
    596     if(lv_fs_read(fp, &kern_format_type, sizeof(uint8_t), NULL) != LV_FS_RES_OK ||
    597        lv_fs_read(fp, &padding, 3 * sizeof(uint8_t), NULL) != LV_FS_RES_OK) {
    598         return -1;
    599     }
    600 
    601     if(0 == kern_format_type) { /*sorted pairs*/
    602         lv_font_fmt_txt_kern_pair_t * kern_pair = lv_mem_alloc(sizeof(lv_font_fmt_txt_kern_pair_t));
    603 
    604         memset(kern_pair, 0, sizeof(lv_font_fmt_txt_kern_pair_t));
    605 
    606         font_dsc->kern_dsc = kern_pair;
    607         font_dsc->kern_classes = 0;
    608 
    609         uint32_t glyph_entries;
    610         if(lv_fs_read(fp, &glyph_entries, sizeof(uint32_t), NULL) != LV_FS_RES_OK) {
    611             return -1;
    612         }
    613 
    614         int ids_size;
    615         if(format == 0) {
    616             ids_size = sizeof(int8_t) * 2 * glyph_entries;
    617         }
    618         else {
    619             ids_size = sizeof(int16_t) * 2 * glyph_entries;
    620         }
    621 
    622         uint8_t * glyph_ids = lv_mem_alloc(ids_size);
    623         int8_t * values = lv_mem_alloc(glyph_entries);
    624 
    625         kern_pair->glyph_ids_size = format;
    626         kern_pair->pair_cnt = glyph_entries;
    627         kern_pair->glyph_ids = glyph_ids;
    628         kern_pair->values = values;
    629 
    630         if(lv_fs_read(fp, glyph_ids, ids_size, NULL) != LV_FS_RES_OK) {
    631             return -1;
    632         }
    633 
    634         if(lv_fs_read(fp, values, glyph_entries, NULL) != LV_FS_RES_OK) {
    635             return -1;
    636         }
    637     }
    638     else if(3 == kern_format_type) { /*array M*N of classes*/
    639 
    640         lv_font_fmt_txt_kern_classes_t * kern_classes = lv_mem_alloc(sizeof(lv_font_fmt_txt_kern_classes_t));
    641 
    642         memset(kern_classes, 0, sizeof(lv_font_fmt_txt_kern_classes_t));
    643 
    644         font_dsc->kern_dsc = kern_classes;
    645         font_dsc->kern_classes = 1;
    646 
    647         uint16_t kern_class_mapping_length;
    648         uint8_t kern_table_rows;
    649         uint8_t kern_table_cols;
    650 
    651         if(lv_fs_read(fp, &kern_class_mapping_length, sizeof(uint16_t), NULL) != LV_FS_RES_OK ||
    652            lv_fs_read(fp, &kern_table_rows, sizeof(uint8_t), NULL) != LV_FS_RES_OK ||
    653            lv_fs_read(fp, &kern_table_cols, sizeof(uint8_t), NULL) != LV_FS_RES_OK) {
    654             return -1;
    655         }
    656 
    657         int kern_values_length = sizeof(int8_t) * kern_table_rows * kern_table_cols;
    658 
    659         uint8_t * kern_left = lv_mem_alloc(kern_class_mapping_length);
    660         uint8_t * kern_right = lv_mem_alloc(kern_class_mapping_length);
    661         int8_t * kern_values = lv_mem_alloc(kern_values_length);
    662 
    663         kern_classes->left_class_mapping  = kern_left;
    664         kern_classes->right_class_mapping = kern_right;
    665         kern_classes->left_class_cnt = kern_table_rows;
    666         kern_classes->right_class_cnt = kern_table_cols;
    667         kern_classes->class_pair_values = kern_values;
    668 
    669         if(lv_fs_read(fp, kern_left, kern_class_mapping_length, NULL) != LV_FS_RES_OK ||
    670            lv_fs_read(fp, kern_right, kern_class_mapping_length, NULL) != LV_FS_RES_OK ||
    671            lv_fs_read(fp, kern_values, kern_values_length, NULL) != LV_FS_RES_OK) {
    672             return -1;
    673         }
    674     }
    675     else {
    676         LV_LOG_WARN("Unknown kern_format_type: %d", kern_format_type);
    677         return -1;
    678     }
    679 
    680     return kern_length;
    681 }