1
0
mirror of https://github.com/lvgl/lvgl.git synced 2025-01-14 06:42:58 +08:00

feat(font) add fallback support and mem. font load option to FreeType (#2796)

* adding font type check

* using theme specified font
supports freetype drawing

* adding font type check

* using theme specified font
supports freetype drawing

* freetype fallback font support

* improved fallback font

* updated fallback font modifier

* docs(events) LV_EVENT_APPLY was removed (#2791)

* reverted to default font logic

* removed unused function

* improved font fallback

* font fallback for default lv_draw_letter as well

* added back masked drawing support

* fallback support for freetype uncached

* updated description

* fixed constructor initialization for ISO C

* reverted unneeded changes

* using loop instead of recursion to resolve glyph info

* simplified glyph dec resolving

* removed unused enum value

* improved lv_font_fmt_ft_dsc_t field naming

* supports pointer as freetype font source

* Updated docs for font fallback

Co-authored-by: Vincent Hamp <higaski@users.noreply.github.com>
Co-authored-by: Gabor Kiss-Vamosi <kisvegabor@gmail.com>
This commit is contained in:
Mariotaku 2021-11-22 18:43:58 +09:00 committed by GitHub
parent 57cde348fa
commit 3ea4d66411
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 94 additions and 176 deletions

View File

@ -251,3 +251,19 @@ const uint8_t * my_get_glyph_bitmap_cb(const lv_font_t * font, uint32_t unicode_
return bitmap; /*Or NULL if not found*/
}
```
## Use font fallback
You can specify `fallback` in `lv_font_t` to provide fallback to the font. When the font
fails to find glyph to a letter, it will try to let font from `fallback` to handle.
`fallback` can be chained, so it will try to solve until there is no `fallback` set.
```c
/* Roboto font doesn't have support for CJK glyphs */
lv_font_t *roboto = my_font_load_function();
/* Droid Sans Fallback has more glyphs but its typeface doesn't look good as Roboto */
lv_font_t *droid_sans_fallback = my_font_load_function();
/* So now we can display Roboto for supported characters while having wider characters set support */
roboto->fallback = droid_sans_fallback;
```

View File

@ -449,6 +449,9 @@ LV_ATTRIBUTE_FAST_MEM void lv_draw_letter(const lv_point_t * pos_p, const lv_are
return;
}
if (g.resolved_font) {
font_p = g.resolved_font;
}
const uint8_t * map_p = lv_font_get_glyph_bitmap(font_p, letter);
if(map_p == NULL) {
LV_LOG_WARN("lv_draw_letter: character's bitmap not found");

View File

@ -23,6 +23,8 @@
* TYPEDEFS
**********************/
typedef struct {
const void * mem;
long size;
char * name;
} lv_face_info_t;
@ -49,8 +51,6 @@ static FT_Error font_face_requester(FTC_FaceID face_id,
FT_Library library_is, FT_Pointer req_data, FT_Face * aface);
static bool lv_ft_font_init_cache(lv_ft_info_t * info);
static void lv_ft_font_destroy_cache(lv_font_t * font);
static bool lv_ft_font_init_cache(lv_ft_info_t * info);
static void lv_ft_font_destroy_cache(lv_font_t * font);
#else
static FT_Face face_find_in_list(lv_ft_info_t * info);
static void face_add_to_list(FT_Face face);
@ -59,7 +59,6 @@ static void face_generic_finalizer(void * object);
static bool lv_ft_font_init_nocache(lv_ft_info_t * info);
static void lv_ft_font_destroy_nocache(lv_font_t * font);
#endif
/**********************
* STATIC VARIABLES
**********************/
@ -163,7 +162,12 @@ static FT_Error font_face_requester(FTC_FaceID face_id,
LV_UNUSED(req_data);
lv_face_info_t * info = (lv_face_info_t *)face_id;
FT_Error error = FT_New_Face(library, info->name, 0, aface);
FT_Error error;
if (info->mem) {
error = FT_New_Memory_Face(library, info->mem, info->size, 0, aface);
} else {
error = FT_New_Face(library, info->name, 0, aface);
}
if(error) {
LV_LOG_ERROR("FT_New_Face error:%d\n", error);
return error;
@ -209,6 +213,7 @@ static bool get_glyph_dsc_cb_cache(const lv_font_t * font,
dsc_out->ofs_x = sbit->left; /*X offset of the bitmap in [pf]*/
dsc_out->ofs_y = sbit->top - sbit->height; /*Y offset of the bitmap measured from the as line*/
dsc_out->bpp = 8; /*Bit per pixel: 1/2/4/8*/
dsc_out->is_placeholder = glyph_index == 0;
return true;
}
@ -230,12 +235,14 @@ static bool lv_ft_font_init_cache(lv_ft_info_t * info)
lv_mem_free(dsc);
return false;
}
lv_memset_00(dsc->font, sizeof(lv_font_t));
lv_face_info_t * face_info = NULL;
face_info = lv_mem_alloc(sizeof(lv_face_info_t) + strlen(info->name) + 1);
if(face_info == NULL) {
goto Fail;
}
face_info->mem = info->mem;
face_info->size = info->mem_size;
face_info->name = ((char *)face_info) + sizeof(lv_face_info_t);
strcpy(face_info->name, info->name);
@ -294,7 +301,6 @@ void lv_ft_font_destroy_cache(lv_font_t * font)
lv_mem_free(dsc);
}
}
#else/* LV_FREETYPE_CACHE_SIZE */
static FT_Face face_find_in_list(lv_ft_info_t * info)
@ -366,6 +372,7 @@ static bool get_glyph_dsc_cb_nocache(const lv_font_t * font,
if(face->size != dsc->size) {
FT_Activate_Size(dsc->size);
}
dsc_out->is_placeholder = glyph_index == 0;
error = FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT);
if(error) {
@ -406,6 +413,7 @@ static bool lv_ft_font_init_nocache(lv_ft_info_t * info)
lv_mem_free(dsc);
return false;
}
lv_memset_00(dsc->font, sizeof(lv_font_t));
lv_face_info_t * face_info = NULL;
FT_Face face = face_find_in_list(info);
@ -414,7 +422,12 @@ static bool lv_ft_font_init_nocache(lv_ft_info_t * info)
if(face_info == NULL) {
goto Fail;
}
FT_Error error = FT_New_Face(library, info->name, 0, &face);
FT_Error error;
if (info->mem) {
error = FT_New_Memory_Face(library, info->mem, (FT_Long) info->mem_size, 0, &face);
} else {
error = FT_New_Face(library, info->name, 0, &face);
}
if(error) {
lv_mem_free(face_info);
LV_LOG_WARN("create face error(%d)", error);
@ -422,6 +435,8 @@ static bool lv_ft_font_init_nocache(lv_ft_info_t * info)
}
/* link face and face info */
face_info->mem = info->mem;
face_info->size = (long) info->mem_size;
face_info->name = ((char *)face_info) + sizeof(lv_face_info_t);
strcpy(face_info->name, info->name);
face->generic.data = face_info;

View File

@ -30,6 +30,8 @@ typedef enum {
typedef struct {
const char * name; /* The name of the font file */
const void * mem; /* The pointer of the font file */
size_t mem_size; /* The size of the memory */
lv_font_t * font; /* point to lvgl font */
uint16_t weight; /* font size */
uint16_t style; /* font style */

View File

@ -65,7 +65,19 @@ bool lv_font_get_glyph_dsc(const lv_font_t * font_p, lv_font_glyph_dsc_t * dsc_o
uint32_t letter_next)
{
LV_ASSERT_NULL(font_p);
return font_p->get_glyph_dsc(font_p, dsc_out, letter, letter_next);
LV_ASSERT_NULL(dsc_out);
dsc_out->resolved_font = NULL;
const lv_font_t * f = font_p;
bool found = false;
while(f) {
found = f->get_glyph_dsc(f, dsc_out, letter, letter_next);
if (found && !dsc_out->is_placeholder) {
dsc_out->resolved_font = f;
break;
}
f = f->fallback;
}
return found;
}
/**

View File

@ -33,14 +33,17 @@ extern "C" {
* General types
*-----------------*/
struct _lv_font_t;
/** Describes the properties of a glyph.*/
typedef struct {
const struct _lv_font_t *resolved_font; /**< Pointer to a font where the gylph was actually found after handling fallbacks*/
uint16_t adv_w; /**< The glyph needs this space. Draw the next glyph after this width.*/
uint16_t box_w; /**< Width of the glyph's bounding box*/
uint16_t box_h; /**< Height of the glyph's bounding box*/
int16_t ofs_x; /**< x offset of the bounding box*/
int16_t ofs_y; /**< y offset of the bounding box*/
uint8_t bpp; /**< Bit-per-pixel: 1, 2, 4, 8*/
uint8_t bpp:4; /**< Bit-per-pixel: 1, 2, 4, 8*/
uint8_t is_placeholder:1; /** Glyph is missing. But placeholder will still be displayed */
} lv_font_glyph_dsc_t;
/** The bitmaps might be upscaled by 3 to achieve subpixel rendering.*/
@ -70,6 +73,7 @@ typedef struct _lv_font_t {
int8_t underline_thickness; /**< Thickness of the underline*/
const void * dsc; /**< Store implementation specific or run_time data or caching here*/
const struct _lv_font_t * fallback; /**< Fallback font for missing glyph. Resolved recursively */
#if LV_USE_USER_DATA
void * user_data; /**< Custom user data for font.*/
#endif

View File

@ -182,6 +182,7 @@ bool lv_font_get_glyph_dsc_fmt_txt(const lv_font_t * font, lv_font_glyph_dsc_t *
dsc_out->ofs_x = gdsc->ofs_x;
dsc_out->ofs_y = gdsc->ofs_y;
dsc_out->bpp = (uint8_t)fdsc->bpp;
dsc_out->is_placeholder = false;
if(is_tab) dsc_out->box_w = dsc_out->box_w * 2;

View File

@ -30,15 +30,11 @@
* TYPEDEFS
**********************/
typedef struct lv_sdl_font_atlas_t {
SDL_Rect * pos;
} lv_sdl_font_atlas_t;
typedef struct {
lv_gpu_cache_key_magic_t magic;
const lv_font_t * font_p;
uint32_t cmap_index;
} lv_font_key_t;
uint32_t letter;
} lv_font_glyph_key_t;
/**********************
* STATIC PROTOTYPES
@ -47,17 +43,8 @@ typedef struct {
static void draw_letter_masked(SDL_Renderer * renderer, SDL_Texture * atlas, SDL_Rect * src, SDL_Rect * dst,
SDL_Rect * clip, lv_color_t color, lv_opa_t opa);
static void font_atlas_free(lv_sdl_font_atlas_t * atlas);
static lv_font_glyph_key_t font_key_glyph_create(const lv_font_t * font_p, uint32_t letter);
static SDL_Texture * font_atlas_bake(SDL_Renderer * renderer, const lv_font_t * font_p, uint32_t cmap_idx,
lv_sdl_font_atlas_t * atlas);
static int32_t unicode_list_compare(const void * ref, const void * element);
static bool font_cmap_find_index(const lv_font_fmt_txt_dsc_t * dsc, uint32_t letter, uint32_t * cmap_index,
uint32_t * char_index);
static lv_font_key_t font_key_create(const lv_font_t * font_p, uint32_t cmap_index);
/**********************
* STATIC VARIABLES
@ -109,33 +96,38 @@ void lv_draw_letter(const lv_point_t * pos_p, const lv_area_t * clip_area,
pos_y > clip_area->y2) {
return;
}
lv_area_t dst = {pos_x, pos_y, pos_x + g.box_w - 1, pos_y + g.box_h - 1};
uint32_t atlas_index;
uint32_t cmap_index;
if(!font_cmap_find_index(font_p->dsc, letter, &cmap_index, &atlas_index)) {
return;
}
lv_font_key_t key = font_key_create(font_p, cmap_index);
lv_sdl_font_atlas_t * atlas = NULL;
bool found = false;
SDL_Texture * texture = lv_gpu_draw_cache_get_with_userdata(&key, sizeof(key), &found, (void **) &atlas);
lv_disp_t * disp = _lv_refr_get_disp_refreshing();
SDL_Renderer * renderer = (SDL_Renderer *) disp->driver->user_data;
if(!found) {
atlas = SDL_malloc(sizeof(lv_sdl_font_atlas_t));
texture = font_atlas_bake(renderer, font_p, cmap_index, atlas);
lv_gpu_draw_cache_put_advanced(&key, sizeof(key), texture, atlas, (lv_lru_free_t *) font_atlas_free, 0);
lv_font_glyph_key_t glyph_key = font_key_glyph_create(font_p, letter);
bool glyph_found = false;
SDL_Texture *texture = lv_gpu_draw_cache_get(&glyph_key, sizeof(glyph_key), &glyph_found);
if (!glyph_found) {
if (g.resolved_font) {
font_p = g.resolved_font;
}
const uint8_t *bmp = lv_font_get_glyph_bitmap(font_p, letter);
uint8_t *buf = lv_mem_alloc(g.box_w * g.box_h);
lv_sdl_to_8bpp(buf, bmp, g.box_w, g.box_h, g.box_w, g.bpp);
SDL_Surface * mask = lv_sdl_create_mask_surface(buf, g.box_w, g.box_h, g.box_w);
texture = SDL_CreateTextureFromSurface(renderer, mask);
SDL_FreeSurface(mask);
lv_mem_free(buf);
lv_gpu_draw_cache_put(&glyph_key, sizeof(glyph_key), texture);
}
if(texture == NULL) return;
SDL_Rect dstrect = {.x = pos_x, .y = pos_y, .w = g.box_w, .h = g.box_h};
if (!texture) {
return;
}
lv_area_t dst = {pos_x, pos_y, pos_x + g.box_w - 1, pos_y + g.box_h - 1};
SDL_Rect dstrect;
lv_area_to_sdl_rect(&dst, &dstrect);
SDL_Rect clip_area_rect;
lv_area_to_sdl_rect(clip_area, &clip_area_rect);
if(lv_draw_mask_is_any(&dst)) {
draw_letter_masked(renderer, texture, &atlas->pos[atlas_index], &dstrect, &clip_area_rect, color, opa);
draw_letter_masked(renderer, texture, NULL, &dstrect, &clip_area_rect, color, opa);
return;
}
@ -143,10 +135,9 @@ void lv_draw_letter(const lv_point_t * pos_p, const lv_area_t * clip_area,
SDL_SetTextureAlphaMod(texture, opa);
SDL_SetTextureColorMod(texture, color.ch.red, color.ch.green, color.ch.blue);
SDL_RenderSetClipRect(renderer, &clip_area_rect);
SDL_RenderCopy(renderer, texture, &atlas->pos[atlas_index], &dstrect);
SDL_RenderCopy(renderer, texture, NULL, &dstrect);
}
/**********************
* STATIC FUNCTIONS
**********************/
@ -192,140 +183,14 @@ static void draw_letter_masked(SDL_Renderer * renderer, SDL_Texture * atlas, SDL
SDL_DestroyTexture(mask);
}
SDL_Texture * font_atlas_bake(SDL_Renderer * renderer, const lv_font_t * font_p, uint32_t cmap_idx,
lv_sdl_font_atlas_t * atlas)
static lv_font_glyph_key_t font_key_glyph_create(const lv_font_t * font_p, uint32_t letter)
{
/* Clear atlas struct */
SDL_memset(atlas, 0, sizeof(lv_sdl_font_atlas_t));
const lv_font_fmt_txt_dsc_t * dsc = (lv_font_fmt_txt_dsc_t *) font_p->dsc;
if(dsc->bitmap_format != LV_FONT_FMT_TXT_PLAIN) {
/* we don't support compressed font at this moment */
return NULL;
}
const lv_font_fmt_txt_cmap_t * cmap = &dsc->cmaps[cmap_idx];
int glyph_count = cmap->type == LV_FONT_FMT_TXT_CMAP_FORMAT0_TINY ? cmap->range_length : cmap->list_length;
int atlas_size = 0;
while(atlas_size * atlas_size < glyph_count) {
atlas_size++;
}
int atlas_w = font_p->line_height * atlas_size;
int atlas_h = font_p->line_height * (glyph_count / atlas_size + 1);
if(atlas_w > 2048 || atlas_h > 2048) {
/*This atlas texture will be too large to load*/
return NULL;
}
lv_opa_t * s1 = lv_mem_buf_get(atlas_w * atlas_h * sizeof(lv_opa_t));
atlas->pos = SDL_malloc(sizeof(SDL_Rect) * glyph_count);
int atlas_y = 0;
int atlas_x = 0;
for(int i = 0; i < glyph_count; i++) {
int glyph_idx = cmap->glyph_id_start + i;
switch(cmap->type) {
case LV_FONT_FMT_TXT_CMAP_FORMAT0_FULL: {
const uint8_t * gid_ofs_8 = cmap->glyph_id_ofs_list;
glyph_idx = gid_ofs_8[i];
break;
}
case LV_FONT_FMT_TXT_CMAP_SPARSE_FULL: {
const uint16_t * gid_ofs_16 = cmap->glyph_id_ofs_list;
glyph_idx = gid_ofs_16[i];
break;
}
}
const lv_font_fmt_txt_glyph_dsc_t * gd = &dsc->glyph_dsc[glyph_idx];
if(atlas_x + gd->box_w >= atlas_w) {
atlas_x = 0;
atlas_y += font_p->line_height;
}
SDL_Rect * rect = &atlas->pos[i];
rect->x = atlas_x;
rect->y = atlas_y;
rect->w = gd->box_w;
rect->h = gd->box_h;
if(gd->box_w <= 0 || gd->box_h <= 0) {
continue;
}
lv_sdl_to_8bpp(&s1[rect->y * atlas_w + rect->x], &dsc->glyph_bitmap[gd->bitmap_index], rect->w,
rect->h, atlas_w, dsc->bpp);
atlas_x += gd->box_w;
}
SDL_Surface * mask = lv_sdl_create_mask_surface(s1, atlas_w, atlas_h, atlas_w);
SDL_Texture * result = SDL_CreateTextureFromSurface(renderer, mask);
SDL_FreeSurface(mask);
lv_mem_buf_release(s1);
if(!result) {
if(atlas->pos) {
SDL_free(atlas->pos);
}
SDL_memset(atlas, 0, sizeof(lv_sdl_font_atlas_t));
}
return result;
}
static void font_atlas_free(lv_sdl_font_atlas_t * atlas)
{
if(atlas->pos) {
SDL_free(atlas->pos);
}
SDL_free(atlas);
}
static bool font_cmap_find_index(const lv_font_fmt_txt_dsc_t * dsc, uint32_t letter, uint32_t * cmap_index,
uint32_t * char_index)
{
for(int i = 0; i < dsc->cmap_num; i++) {
const lv_font_fmt_txt_cmap_t * cmap = &dsc->cmaps[i];
/*Relative code point*/
uint32_t rcp = letter - cmap->range_start;
if(rcp > cmap->range_length) continue;
if(cmap->type == LV_FONT_FMT_TXT_CMAP_FORMAT0_TINY) {
*cmap_index = i;
*char_index = rcp;
return true;
}
else if(cmap->type == LV_FONT_FMT_TXT_CMAP_FORMAT0_FULL) {
*cmap_index = i;
*char_index = rcp;
return true;
}
else if(cmap->type == LV_FONT_FMT_TXT_CMAP_SPARSE_TINY) {
uint16_t key = rcp;
uint16_t * p = _lv_utils_bsearch(&key, cmap->unicode_list, cmap->list_length,
sizeof(cmap->unicode_list[0]), unicode_list_compare);
if(!p) continue;
*cmap_index = i;
*char_index = p - cmap->unicode_list;
return true;
}
else if(cmap->type == LV_FONT_FMT_TXT_CMAP_SPARSE_FULL) {
uint16_t key = rcp;
uint16_t * p = _lv_utils_bsearch(&key, cmap->unicode_list, cmap->list_length,
sizeof(cmap->unicode_list[0]), unicode_list_compare);
if(!p) continue;
*cmap_index = i;
*char_index = p - cmap->unicode_list;
return true;
}
}
return 0;
}
static int32_t unicode_list_compare(const void * ref, const void * element)
{
return ((int32_t)(*(uint16_t *) ref)) - ((int32_t)(*(uint16_t *) element));
}
static lv_font_key_t font_key_create(const lv_font_t * font_p, uint32_t cmap_index)
{
lv_font_key_t key;
lv_font_glyph_key_t key;
/* VERY IMPORTANT! Padding between members is uninitialized, so we have to wipe them manually */
SDL_memset(&key, 0, sizeof(key));
key.magic = LV_GPU_CACHE_KEY_MAGIC_FONT;
key.magic = LV_GPU_CACHE_KEY_MAGIC_FONT_GLYPH;
key.font_p = font_p;
key.cmap_index = cmap_index;
key.letter = letter;
return key;
}

View File

@ -43,7 +43,7 @@ typedef enum {
LV_GPU_CACHE_KEY_MAGIC_RECT_BG = 0x31,
LV_GPU_CACHE_KEY_MAGIC_RECT_SHADOW = 0x32,
LV_GPU_CACHE_KEY_MAGIC_RECT_BORDER = 0x33,
LV_GPU_CACHE_KEY_MAGIC_FONT = 0x41,
LV_GPU_CACHE_KEY_MAGIC_FONT_GLYPH = 0x41,
LV_GPU_CACHE_KEY_TEMP = 0xFF,
} lv_gpu_cache_key_magic_t;

View File

@ -163,7 +163,7 @@ void lv_sdl_to_8bpp(uint8_t * dest, const uint8_t * src, int width, int height,
while(cur < src_len) {
curbit = 8 - bpp;
uint8_t src_byte = src[cur * bpp / 8];
while(curbit >= 0) {
while(curbit >= 0 && cur < src_len) {
uint8_t src_bits = opa_mask & (src_byte >> curbit);
dest[(cur / width * stride) + (cur % width)] = opa_table[src_bits];
curbit -= bpp;