1
0
mirror of https://github.com/lvgl/lvgl.git synced 2025-01-28 07:03:00 +08:00

Adding support to dynamic font loading.

This commit is contained in:
Fabio Guerra 2020-08-03 19:33:52 -03:00
parent c85295b5ea
commit f58d97d990
10 changed files with 909 additions and 0 deletions

View File

@ -1,5 +1,6 @@
CSRCS += lv_font.c CSRCS += lv_font.c
CSRCS += lv_font_fmt_txt.c CSRCS += lv_font_fmt_txt.c
CSRCS += lv_font_loader.c
CSRCS += lv_font_montserrat_12.c CSRCS += lv_font_montserrat_12.c
CSRCS += lv_font_montserrat_14.c CSRCS += lv_font_montserrat_14.c
CSRCS += lv_font_montserrat_16.c CSRCS += lv_font_montserrat_16.c

View File

@ -0,0 +1,589 @@
/**
* @file lv_font_loader.c
*
*/
/*********************
* INCLUDES
*********************/
#if LV_USE_FILESYSTEM
#include <stdint.h>
#include <stdbool.h>
#include "../lvgl.h"
#include "../lv_misc/lv_fs.h"
#include "lv_font_loader.h"
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_fs_file_t * fp;
int8_t bit_pos;
uint8_t byte_value;
} bit_iterator_t;
typedef struct font_header_bin {
uint32_t version;
uint16_t tables_count;
uint16_t font_size;
uint16_t ascent;
int16_t descent;
uint16_t typo_ascent;
int16_t typo_descent;
uint16_t typo_line_gap;
int16_t min_y;
int16_t max_y;
uint16_t default_advance_width;
uint16_t kerning_scale;
uint8_t index_to_loc_format;
uint8_t glyph_id_format;
uint8_t advance_width_format;
uint8_t bits_per_pixel;
uint8_t xy_bits;
uint8_t wh_bits;
uint8_t advance_width_bits;
uint8_t compression_id;
uint8_t subpixels_mode;
uint8_t padding;
} font_header_bin_t;
typedef struct cmap_table_bin
{
uint32_t cmaps_subtable_length;
uint32_t range_start;
uint16_t range_length;
uint16_t glyph_id_start;
uint16_t data_entries_count;
uint8_t format_type;
uint8_t padding;
} cmap_table_bin_t;
/**********************
* STATIC PROTOTYPES
**********************/
static bit_iterator_t init_bit_iterator(lv_fs_file_t * fp);
static bool lvgl_load_font(lv_fs_file_t * fp, lv_font_t * font);
static int read_bits_signed(bit_iterator_t *it, int n_bits, lv_fs_res_t * res);
static int read_bits(bit_iterator_t *it, int n_bits, lv_fs_res_t * res);
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_font_t * lv_font_load(const char * font_name)
{
bool success = false;
lv_font_t * font = lv_mem_alloc(sizeof(lv_font_t));
memset(font, 0, sizeof(lv_font_t));
lv_fs_file_t file;
lv_fs_res_t res = lv_fs_open(&file, font_name, LV_FS_MODE_RD);
if(res == LV_FS_RES_OK) {
success = lvgl_load_font(&file, font);
}
if (!success)
{
LV_LOG_WARN("Error opening font file: %s\n", font_name);
lv_font_free(font);
font = NULL;
}
lv_fs_close(&file);
return font;
}
void lv_font_free(lv_font_t * font)
{
if(NULL != font) {
lv_font_fmt_txt_dsc_t * dsc = (lv_font_fmt_txt_dsc_t *) font->dsc;
if(NULL != dsc) {
lv_font_fmt_txt_kern_classes_t * kern_dsc =
(lv_font_fmt_txt_kern_classes_t *) dsc->kern_dsc;
if(NULL != kern_dsc) {
if(kern_dsc->class_pair_values)
lv_mem_free((void *) kern_dsc->class_pair_values);
if(kern_dsc->left_class_mapping)
lv_mem_free((void *) kern_dsc->left_class_mapping);
if(kern_dsc->right_class_mapping)
lv_mem_free((void *) kern_dsc->right_class_mapping);
lv_mem_free((void *) kern_dsc);
}
lv_font_fmt_txt_cmap_t * cmaps =
(lv_font_fmt_txt_cmap_t *) dsc->cmaps;
if(NULL != cmaps) {
for(int i = 0; i < dsc->cmap_num; ++i) {
if(NULL != cmaps[i].glyph_id_ofs_list)
lv_mem_free((void *) cmaps[i].glyph_id_ofs_list);
if(NULL != cmaps[i].unicode_list)
lv_mem_free((void *) cmaps[i].unicode_list);
}
lv_mem_free(cmaps);
}
if(NULL != dsc->glyph_bitmap) {
lv_mem_free((void *) dsc->glyph_bitmap);
}
if(NULL != dsc->glyph_dsc) {
lv_mem_free((void *) dsc->glyph_dsc);
}
lv_mem_free(dsc);
}
lv_mem_free(font);
}
}
/**********************
* STATIC FUNCTIONS
**********************/
static bit_iterator_t init_bit_iterator(lv_fs_file_t * fp)
{
bit_iterator_t it;
it.fp = fp;
it.bit_pos = -1;
it.byte_value = 0;
return it;
}
static int read_bits(bit_iterator_t *it, int n_bits, lv_fs_res_t * res)
{
int value = 0;
while(n_bits--) {
it->byte_value = it->byte_value << 1;
it->bit_pos--;
if(it->bit_pos < 0) {
it->bit_pos = 7;
*res = lv_fs_read(it->fp, &(it->byte_value), 1, NULL);
if (*res != LV_FS_RES_OK) {
return -1;
}
}
int8_t bit = (it->byte_value & 0x80) ? 1 : 0;
value |= (bit << n_bits);
}
*res = LV_FS_RES_OK;
return value;
}
static int read_bits_signed(bit_iterator_t *it, int n_bits, lv_fs_res_t * res)
{
int value = read_bits(it, n_bits, res);
if (value & (1 << (n_bits-1))) {
for (int bit = n_bits; bit < 8; ++bit) {
value |= (1 << bit);
}
}
return value;
}
static int read_label(lv_fs_file_t *fp, int start, const char *label)
{
lv_fs_seek(fp, start);
uint32_t length;
char buf[4];
if(lv_fs_read(fp, &length, 4, NULL) != LV_FS_RES_OK
|| lv_fs_read(fp, buf, 4, NULL) != LV_FS_RES_OK
|| memcmp(label, buf, 4) != 0) {
LV_LOG_WARN("Error reading '%s'.", label);
return -1;
}
return length;
}
static bool lvgl_load_font(lv_fs_file_t * fp, lv_font_t * font)
{
lv_font_fmt_txt_dsc_t * font_dsc = (lv_font_fmt_txt_dsc_t *)
lv_mem_alloc(sizeof(lv_font_fmt_txt_dsc_t));
memset(font_dsc, 0, sizeof(lv_font_fmt_txt_dsc_t));
font->dsc = font_dsc;
// header
int32_t header_length = read_label(fp, 0, "head");
if (header_length < 0) {
return false;
}
font_header_bin_t font_header;
if(lv_fs_read(fp, &font_header, sizeof(font_header_bin_t), NULL) != LV_FS_RES_OK) {
return false;
}
font->base_line = -font_header.descent;
font->line_height = font_header.ascent - font_header.descent;
font->get_glyph_dsc = lv_font_get_glyph_dsc_fmt_txt;
font->get_glyph_bitmap = lv_font_get_bitmap_fmt_txt;
#if !(LVGL_VERSION_MAJOR == 6 && LVGL_VERSION_MINOR == 0)
font->subpx = LV_FONT_SUBPX_NONE;
#endif
font_dsc->bpp = font_header.bits_per_pixel;
font_dsc->kern_scale = font_header.kerning_scale;
font_dsc->bitmap_format = font_header.compression_id;
// cmaps
uint32_t cmaps_start = header_length;
int32_t cmaps_length = read_label(fp, cmaps_start, "cmap");
if (cmaps_length < 0) {
return false;
}
uint32_t cmaps_subtables_count;
if(lv_fs_read(fp, &cmaps_subtables_count, sizeof(uint32_t), NULL) != LV_FS_RES_OK) {
return false;
}
lv_font_fmt_txt_cmap_t * cmaps =
lv_mem_alloc(cmaps_subtables_count * sizeof(lv_font_fmt_txt_cmap_t));
memset(cmaps, 0, cmaps_subtables_count * sizeof(lv_font_fmt_txt_cmap_t));
font_dsc->cmaps = cmaps;
font_dsc->cmap_num = cmaps_subtables_count;
for(unsigned int i = 0; i < font_dsc->cmap_num; ++i) {
lv_font_fmt_txt_cmap_t * cmap = &cmaps[i];
cmap_table_bin_t cmap_table;
if(lv_fs_read(fp, &cmap_table, sizeof(cmap_table_bin_t), NULL) != LV_FS_RES_OK) {
return false;
}
cmap->range_start = cmap_table.range_start;
cmap->range_length = cmap_table.range_length;
cmap->glyph_id_start = cmap_table.glyph_id_start;
switch(cmap_table.format_type) {
case 0:
{
uint8_t ids_size = sizeof(uint8_t) * cmap->range_length;
uint8_t * glyph_id_ofs_list = lv_mem_alloc(ids_size);
if(lv_fs_read(fp, glyph_id_ofs_list, ids_size, NULL) != LV_FS_RES_OK) {
return false;
}
cmap->type = LV_FONT_FMT_TXT_CMAP_FORMAT0_FULL;
cmap->unicode_list = NULL;
cmap->glyph_id_ofs_list = glyph_id_ofs_list;
cmap->list_length = 0;
break;
}
case 1:
LV_LOG_WARN("cmap format: 1 not yet supported.");
return false;
case 2:
cmap->type = LV_FONT_FMT_TXT_CMAP_FORMAT0_TINY;
cmap->unicode_list = NULL;
cmap->glyph_id_ofs_list = NULL;
cmap->list_length = 0;
break;
case 3:
{
uint8_t list_size = sizeof(uint16_t) * cmap_table.data_entries_count;
uint16_t * unicode_list = (uint16_t *) lv_mem_alloc(list_size);
if(lv_fs_read(fp, unicode_list, list_size, NULL) != LV_FS_RES_OK) {
return false;
}
cmap->type = LV_FONT_FMT_TXT_CMAP_SPARSE_TINY;
cmap->unicode_list = unicode_list;
cmap->glyph_id_ofs_list = NULL;
cmap->list_length = cmap_table.data_entries_count;
break;
}
default:
LV_LOG_WARN("Unknown cmaps format type %d.", cmap_table.format_type);
return false;
}
}
// loca
uint32_t loca_start = cmaps_start + cmaps_length;
int32_t loca_length = read_label(fp, loca_start, "loca");
if (loca_length < 0) {
return false;
}
uint32_t loca_count;
if(lv_fs_read(fp, &loca_count, sizeof(uint32_t), NULL) != LV_FS_RES_OK) {
return false;
}
bool failed = false;
uint32_t *glyph_offset = lv_mem_alloc(sizeof(uint32_t) * (loca_count + 1));
for(unsigned int i = 0; i < loca_count; ++i) {
if(font_header.index_to_loc_format == 0) {
uint16_t offset;
if(lv_fs_read(fp, &offset, sizeof(uint16_t), NULL) != LV_FS_RES_OK) {
failed = true;
break;
}
glyph_offset[i] = offset;
}
else if(font_header.index_to_loc_format == 1) {
uint32_t offset;
if(lv_fs_read(fp, &offset, sizeof(uint32_t), NULL) != LV_FS_RES_OK) {
failed = true;
break;
}
glyph_offset[i] = offset;
}
else {
LV_LOG_WARN("Unknown index_to_loc_format: %d.", font_header.index_to_loc_format);
failed = true;
break;
}
}
if (failed) {
lv_mem_free(glyph_offset);
return false;
}
// glyph
uint32_t glyph_start = loca_start + loca_length;
int32_t glyph_length = read_label(fp, glyph_start, "glyf");
if (glyph_length < 0) {
lv_mem_free(glyph_offset);
return false;
}
lv_font_fmt_txt_glyph_dsc_t * glyph_dsc = (lv_font_fmt_txt_glyph_dsc_t *)
lv_mem_alloc((loca_count + 1) * sizeof(lv_font_fmt_txt_glyph_dsc_t));
memset(glyph_dsc, 0,
(loca_count + 1) * sizeof(lv_font_fmt_txt_glyph_dsc_t));
font_dsc->glyph_dsc = glyph_dsc;
int cur_bmp_size = 0;
for(unsigned int i = 0; i < loca_count; ++i) {
lv_font_fmt_txt_glyph_dsc_t * gdsc = &glyph_dsc[i];
lv_fs_res_t res = lv_fs_seek(fp, glyph_start + glyph_offset[i]);
if(res != LV_FS_RES_OK) {
failed = true;
break;
}
bit_iterator_t bit_it = init_bit_iterator(fp);
if(font_header.advance_width_bits == 0) {
gdsc->adv_w = font_header.default_advance_width;
}
else {
gdsc->adv_w = read_bits(&bit_it, font_header.advance_width_bits, &res);
if(res != LV_FS_RES_OK) {
failed = true;
break;
}
// TODO: understand how to interpret advance_width_format
if(font_header.advance_width_format == 0) { // uint
}
else if(font_header.advance_width_format == 1) { // unsigned with 4 bits fractional part
}
else {
LV_LOG_WARN("error unknown advance_width_format");
failed = true;
break;
}
}
gdsc->ofs_x = read_bits_signed(&bit_it, font_header.xy_bits, &res);
if(res != LV_FS_RES_OK) {
failed = true;
break;
}
gdsc->ofs_y = read_bits_signed(&bit_it, font_header.xy_bits, &res);
if(res != LV_FS_RES_OK) {
failed = true;
break;
}
gdsc->box_w = read_bits(&bit_it, font_header.wh_bits, &res);
if(res != LV_FS_RES_OK) {
failed = true;
break;
}
gdsc->box_h = read_bits(&bit_it, font_header.wh_bits, &res);
if(res != LV_FS_RES_OK) {
failed = true;
break;
}
int nbits = font_header.advance_width_bits + 2 * font_header.xy_bits + 2 * font_header.wh_bits;
int next_offset = (i < loca_count - 1) ? glyph_offset[i + 1] : (uint32_t) glyph_length;
int bmp_size = next_offset - glyph_offset[i] - nbits / 8;
if(i == 0) {
gdsc->adv_w = 0;
gdsc->box_w = 0;
gdsc->box_h = 0;
gdsc->ofs_x = 0;
gdsc->ofs_y = 0;
}
gdsc->bitmap_index = cur_bmp_size;
if(gdsc->box_w * gdsc->box_h != 0) {
cur_bmp_size += bmp_size;
}
}
if (failed) {
lv_mem_free(glyph_offset);
return false;
}
uint8_t * glyph_bmp = (uint8_t *) lv_mem_alloc(sizeof(uint8_t) * cur_bmp_size);
font_dsc->glyph_bitmap = glyph_bmp;
cur_bmp_size = 0;
for(unsigned int i = 1; i < loca_count; ++i) {
lv_fs_res_t res = lv_fs_seek(fp, glyph_start + glyph_offset[i]);
if(res != LV_FS_RES_OK) {
failed = true;
break;
}
bit_iterator_t bit_it = init_bit_iterator(fp);
int nbits = font_header.advance_width_bits + 2 * font_header.xy_bits + 2 * font_header.wh_bits;
read_bits(&bit_it, nbits, &res);
if(res != LV_FS_RES_OK) {
failed = true;
break;
}
if(glyph_dsc[i].box_w * glyph_dsc[i].box_h == 0) {
continue;
}
int next_offset = (i < loca_count - 1) ? glyph_offset[i + 1] : (uint32_t) glyph_length;
int bmp_size = next_offset - glyph_offset[i] - nbits / 8;
for(int k = 0; k < bmp_size; ++k) {
glyph_bmp[cur_bmp_size + k] = read_bits(&bit_it, 8, &res);
if(res != LV_FS_RES_OK) {
failed = true;
break;
}
}
if (failed) {
break;
}
cur_bmp_size += bmp_size;
}
lv_mem_free(glyph_offset);
if (failed) {
return false;
}
if(lv_fs_seek(fp, glyph_start + glyph_length) != LV_FS_RES_OK) {
return false;
}
uint32_t kern_start = glyph_start + glyph_length;
int32_t kern_length = read_label(fp, kern_start, "kern");
if (kern_length < 0) {
return false;
}
uint8_t kern_format_type;
int32_t padding;
if (lv_fs_read(fp, &kern_format_type, sizeof(uint8_t), NULL) != LV_FS_RES_OK ||
lv_fs_read(fp, &padding, 3 * sizeof(uint8_t), NULL) != LV_FS_RES_OK) {
return false;
}
lv_font_fmt_txt_kern_classes_t * kern_classes =
lv_mem_alloc(sizeof(lv_font_fmt_txt_kern_classes_t));
memset(kern_classes, 0, sizeof(lv_font_fmt_txt_kern_classes_t));
font_dsc->kern_dsc = kern_classes; // TODO: review
font_dsc->kern_classes = 1; // TODO: review this
if(0 == kern_format_type) { // sorted pairs
LV_LOG_WARN("kern_format_type 0 not supported yet!");
return false;
}
else if(3 == kern_format_type) { // array M*N of classes
uint16_t kern_class_mapping_length;
uint8_t kern_table_rows;
uint8_t kern_table_cols;
if(lv_fs_read(fp, &kern_class_mapping_length, sizeof(uint16_t), NULL) != LV_FS_RES_OK ||
lv_fs_read(fp, &kern_table_rows, sizeof(uint8_t), NULL) != LV_FS_RES_OK ||
lv_fs_read(fp, &kern_table_cols, sizeof(uint8_t), NULL) != LV_FS_RES_OK) {
return false;
}
kern_classes->left_class_cnt = kern_table_rows;
kern_classes->right_class_cnt = kern_table_cols;
uint8_t * kern_left = lv_mem_alloc(kern_class_mapping_length);
uint8_t * kern_right = lv_mem_alloc(kern_class_mapping_length);
kern_classes->left_class_mapping = kern_left;
kern_classes->right_class_mapping = kern_right;
int kern_values_length = sizeof(int8_t) * kern_table_rows * kern_table_cols;
int8_t * kern_values = (int8_t *) lv_mem_alloc(kern_values_length);
kern_classes->class_pair_values = kern_values;
if(lv_fs_read(fp, kern_left, kern_class_mapping_length, NULL) != LV_FS_RES_OK ||
lv_fs_read(fp, kern_right, kern_class_mapping_length, NULL) != LV_FS_RES_OK ||
lv_fs_read(fp, kern_values, kern_values_length, NULL) != LV_FS_RES_OK) {
return false;
}
}
else {
LV_LOG_WARN("kern_format_type %d not supported yet!", kern_format_type);
return false;
}
return true;
}
#endif /*LV_USE_FILESYSTEM*/

View File

@ -0,0 +1,44 @@
/**
* @file lv_font_loader.h
*
*/
#ifndef LV_FONT_LOADER_H
#define LV_FONT_LOADER_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
#if LV_USE_FILESYSTEM
lv_font_t * lv_font_load(const char * fontName);
void lv_font_free(lv_font_t * font);
#endif
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_FONT_LOADER_H*/

View File

@ -30,6 +30,7 @@ CSRCS += lv_test_assert.c
CSRCS += lv_test_core/lv_test_core.c CSRCS += lv_test_core/lv_test_core.c
CSRCS += lv_test_core/lv_test_obj.c CSRCS += lv_test_core/lv_test_obj.c
CSRCS += lv_test_core/lv_test_style.c CSRCS += lv_test_core/lv_test_style.c
CSRCS += lv_test_core/lv_test_font_loader.c
OBJEXT ?= .o OBJEXT ?= .o

View File

@ -97,6 +97,15 @@ void lv_test_error(const char * s, ...)
exit(1); exit(1);
} }
void lv_test_assert_true(int32_t expression, const char * s)
{
if(!expression) {
lv_test_error(" FAIL: %s. (Expected: not zero)", s, expression);
} else {
lv_test_print(" PASS: %s. (Expected: not zero)", s, expression);
}
}
void lv_test_assert_int_eq(int32_t n_ref, int32_t n_act, const char * s) void lv_test_assert_int_eq(int32_t n_ref, int32_t n_act, const char * s)
{ {
if(n_ref != n_act) { if(n_ref != n_act) {

View File

@ -32,6 +32,7 @@ extern "C" {
void lv_test_print(const char * s, ...); void lv_test_print(const char * s, ...);
void lv_test_exit(const char * s, ...); void lv_test_exit(const char * s, ...);
void lv_test_error(const char * s, ...); void lv_test_error(const char * s, ...);
void lv_test_assert_true(int32_t expression, const char * s);
void lv_test_assert_int_eq(int32_t n1, int32_t n2, const char * s); void lv_test_assert_int_eq(int32_t n1, int32_t n2, const char * s);
void lv_test_assert_int_gt(int32_t n_ref, int32_t n_act, const char * s); void lv_test_assert_int_gt(int32_t n_ref, int32_t n_act, const char * s);
void lv_test_assert_int_lt(int32_t n_ref, int32_t n_act, const char * s); void lv_test_assert_int_lt(int32_t n_ref, int32_t n_act, const char * s);

View File

@ -12,6 +12,7 @@
#include "lv_test_core.h" #include "lv_test_core.h"
#include "lv_test_obj.h" #include "lv_test_obj.h"
#include "lv_test_style.h" #include "lv_test_style.h"
#include "lv_test_font_loader.h"
/********************* /*********************
* DEFINES * DEFINES
@ -46,6 +47,7 @@ void lv_test_core(void)
lv_test_obj(); lv_test_obj();
lv_test_style(); lv_test_style();
lv_test_font_loader();
} }

View File

@ -0,0 +1,154 @@
/**
* @file lv_test_font_loader.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../lv_test_assert.h"
#if LV_BUILD_TEST
#include "../lvgl.h"
#include "../src/lv_font/lv_font_fmt_txt.h"
#include "../src/lv_font/lv_font.h"
#include "../src/lv_font/lv_font_loader.h"
#include "lv_test_font_loader.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static int compare_fonts(lv_font_t * f1, lv_font_t * f2);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_test_font_loader(void)
{
lv_font_t * font = lv_font_load("f:lv_font_montserrat_12.bin");
compare_fonts(&lv_font_montserrat_12, font);
}
static int compare_fonts(lv_font_t * f1, lv_font_t * f2)
{
lv_test_assert_true(f1 != NULL && f2 != NULL, "error loading font");
lv_test_assert_ptr_eq(f1->get_glyph_dsc, f2->get_glyph_dsc, "glyph_dsc");
lv_test_assert_ptr_eq(f1->get_glyph_bitmap, f2->get_glyph_bitmap, "glyph_bitmap");
lv_test_assert_int_eq(f1->line_height, f2->line_height, "line_height");
lv_test_assert_int_eq(f1->base_line, f2->base_line, "base_line");
#if !(LVGL_VERSION_MAJOR == 6 && LVGL_VERSION_MINOR == 0)
lv_test_assert_int_eq(f1->subpx, f2->subpx, "subpx");
#endif
lv_font_fmt_txt_dsc_t * dsc1 = (lv_font_fmt_txt_dsc_t *) f1->dsc;
lv_font_fmt_txt_dsc_t * dsc2 = (lv_font_fmt_txt_dsc_t *) f2->dsc;
lv_test_assert_int_eq(dsc1->kern_scale, dsc2->kern_scale, "kern_scale");
lv_test_assert_int_eq(dsc1->cmap_num, dsc2->cmap_num, "cmap_num");
lv_test_assert_int_eq(dsc1->bpp, dsc2->bpp, "bpp");
lv_test_assert_int_eq(dsc1->kern_classes, dsc2->kern_classes, "kern_classes");
lv_test_assert_int_eq(dsc1->bitmap_format, dsc2->bitmap_format, "bitmap_format");
// cmaps
int total_glyphs = 0;
for(int i = 0; i < dsc1->cmap_num; ++i) {
lv_font_fmt_txt_cmap_t * cmaps1 = (lv_font_fmt_txt_cmap_t *) &dsc1->cmaps[i];
lv_font_fmt_txt_cmap_t * cmaps2 = (lv_font_fmt_txt_cmap_t *) &dsc2->cmaps[i];
lv_test_assert_int_eq(cmaps1->range_start, cmaps2->range_start, "range_start");
lv_test_assert_int_eq(cmaps1->range_length, cmaps2->range_length, "range_length");
lv_test_assert_int_eq(cmaps1->glyph_id_start, cmaps2->glyph_id_start, "glyph_id_start");
lv_test_assert_int_eq(cmaps1->type, cmaps2->type, "type");
lv_test_assert_int_eq(cmaps1->list_length, cmaps2->list_length, "list_length");
if(cmaps1->unicode_list != NULL && cmaps2->unicode_list != NULL) {
lv_test_assert_true(cmaps1->unicode_list && cmaps2->unicode_list, "unicode_list");
for(int k = 0; k < cmaps1->list_length; ++k) {
lv_test_assert_int_eq(cmaps1->unicode_list[k], cmaps2->unicode_list[k], "unicode_list");
}
total_glyphs += cmaps1->list_length;
}
else {
total_glyphs += cmaps1->range_length;
lv_test_assert_ptr_eq(cmaps1->unicode_list, cmaps2->unicode_list, "unicode_list");
}
if(cmaps1->glyph_id_ofs_list != NULL && cmaps2->glyph_id_ofs_list != NULL) {
uint8_t * ids1 = (uint8_t *) cmaps1->glyph_id_ofs_list;
uint8_t * ids2 = (uint8_t *) cmaps2->glyph_id_ofs_list;
for(int j = 0; j < cmaps1->range_length; j++) {
lv_test_assert_int_eq(ids1[j], ids2[j], "glyph_id_ofs_list");
}
}
else {
lv_test_assert_ptr_eq(cmaps1->glyph_id_ofs_list, cmaps2->glyph_id_ofs_list, "glyph_id_ofs_list");
}
}
// kern_dsc
lv_font_fmt_txt_kern_classes_t * kern1 = (lv_font_fmt_txt_kern_classes_t *) dsc1->kern_dsc;
lv_font_fmt_txt_kern_classes_t * kern2 = (lv_font_fmt_txt_kern_classes_t *) dsc2->kern_dsc;
lv_test_assert_int_eq(kern1->right_class_cnt, kern2->right_class_cnt, "right_class_cnt");
lv_test_assert_int_eq(kern1->left_class_cnt, kern2->left_class_cnt, "left_class_cnt");
for(int i = 0; i < kern1->left_class_cnt; ++i) {
lv_test_assert_int_eq(kern1->left_class_mapping[i],
kern2->left_class_mapping[i], "left_class_mapping");
}
for(int i = 0; i < kern1->right_class_cnt; ++i) {
lv_test_assert_int_eq(kern1->right_class_mapping[i],
kern2->right_class_mapping[i], "right_class_mapping");
}
for(int i = 0; i < kern1->right_class_cnt * kern1->left_class_cnt; ++i) {
lv_test_assert_int_eq(kern1->class_pair_values[i],
kern2->class_pair_values[i], "class_pair_values");
}
// TODO: glyph_bitmap
lv_font_fmt_txt_glyph_dsc_t * glyph_dsc1 = (lv_font_fmt_txt_glyph_dsc_t *) dsc1->glyph_dsc;
lv_font_fmt_txt_glyph_dsc_t * glyph_dsc2 = (lv_font_fmt_txt_glyph_dsc_t *) dsc2->glyph_dsc;
for(int i = 0; i < total_glyphs; ++i) {
//lv_test_assert_int_eq(glyph_dsc1[i].bitmap_index, glyph_dsc2[i].bitmap_index, "bitmap_index");
lv_test_assert_int_eq(glyph_dsc1[i].adv_w, glyph_dsc2[i].adv_w, "adv_w");
lv_test_assert_int_eq(glyph_dsc1[i].box_w, glyph_dsc2[i].box_w, "box_w");
lv_test_assert_int_eq(glyph_dsc1[i].box_h, glyph_dsc2[i].box_h, "box_h");
lv_test_assert_int_eq(glyph_dsc1[i].ofs_x, glyph_dsc2[i].ofs_x, "ofs_x");
lv_test_assert_int_eq(glyph_dsc1[i].ofs_y, glyph_dsc2[i].ofs_y, "ofs_y");
}
LV_LOG_INFO("No differences found!");
return 0;
}
/**********************
* STATIC FUNCTIONS
**********************/
#endif // LV_BUILD_TEST

View File

@ -0,0 +1,39 @@
/**
* @file lv_font_loader.h
*
*/
#ifndef LV_TEST_FONT_LOADER_H
#define LV_TEST_FONT_LOADER_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
void lv_test_font_loader(void);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_TEST_FONT_LOADER_H*/

View File

@ -23,6 +23,61 @@ int main(void)
} }
static lv_fs_res_t open_cb(struct _lv_fs_drv_t * drv, void * file_p, const char * path, lv_fs_mode_t mode)
{
(void) drv;
(void) mode;
FILE * fp = fopen(path, "rb"); // only reading is supported
*((FILE **)file_p) = fp;
return NULL == fp ? LV_FS_RES_UNKNOWN : LV_FS_RES_OK;
}
static lv_fs_res_t close_cb(struct _lv_fs_drv_t * drv, void * file_p)
{
(void) drv;
FILE * fp = *((FILE **) file_p);
fclose(fp);
return LV_FS_RES_OK;
}
static lv_fs_res_t read_cb(struct _lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br)
{
(void) drv;
FILE * fp = *((FILE **) file_p);
*br = fread(buf, 1, btr, fp);
return (*br <= 0) ? LV_FS_RES_UNKNOWN : LV_FS_RES_OK;
}
static lv_fs_res_t seek_cb(struct _lv_fs_drv_t * drv, void * file_p, uint32_t pos)
{
(void) drv;
FILE * fp = *((FILE **) file_p);
fseek (fp, pos, SEEK_SET);
return LV_FS_RES_OK;
}
static lv_fs_res_t tell_cb(struct _lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p)
{
(void) drv;
FILE * fp = *((FILE **) file_p);
*pos_p = ftell(fp);
return LV_FS_RES_OK;
}
static bool ready_cb(struct _lv_fs_drv_t * drv)
{
(void) drv;
return true;
}
static void hal_init(void) static void hal_init(void)
{ {
static lv_disp_buf_t disp_buf; static lv_disp_buf_t disp_buf;
@ -35,6 +90,20 @@ static void hal_init(void)
disp_drv.buffer = &disp_buf; disp_drv.buffer = &disp_buf;
disp_drv.flush_cb = dummy_flush_cb; disp_drv.flush_cb = dummy_flush_cb;
lv_disp_drv_register(&disp_drv); lv_disp_drv_register(&disp_drv);
lv_fs_drv_t drv;
lv_fs_drv_init(&drv); /*Basic initialization*/
drv.letter = 'f'; /*An uppercase letter to identify the drive */
drv.file_size = sizeof(FILE *); /*Size required to store a file object*/
drv.ready_cb = ready_cb; /*Callback to tell if the drive is ready to use */
drv.open_cb = open_cb; /*Callback to open a file */
drv.close_cb = close_cb; /*Callback to close a file */
drv.read_cb = read_cb; /*Callback to read a file */
drv.seek_cb = seek_cb; /*Callback to seek in a file (Move cursor) */
drv.tell_cb = tell_cb; /*Callback to tell the cursor position */
lv_fs_drv_register(&drv); /*Finally register the drive*/
} }