diff --git a/docs/overview/font.md b/docs/overview/font.md index 6315c45bb..e178573ee 100644 --- a/docs/overview/font.md +++ b/docs/overview/font.md @@ -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; +``` \ No newline at end of file diff --git a/src/draw/lv_draw_label.c b/src/draw/lv_draw_label.c index 0bbf0c4e0..0a92e57a3 100644 --- a/src/draw/lv_draw_label.c +++ b/src/draw/lv_draw_label.c @@ -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"); diff --git a/src/extra/libs/freetype/lv_freetype.c b/src/extra/libs/freetype/lv_freetype.c index 96f68af21..579976c00 100644 --- a/src/extra/libs/freetype/lv_freetype.c +++ b/src/extra/libs/freetype/lv_freetype.c @@ -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; diff --git a/src/extra/libs/freetype/lv_freetype.h b/src/extra/libs/freetype/lv_freetype.h index 3f1fe7076..247a7fb7f 100644 --- a/src/extra/libs/freetype/lv_freetype.h +++ b/src/extra/libs/freetype/lv_freetype.h @@ -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 */ diff --git a/src/font/lv_font.c b/src/font/lv_font.c index 8aab8d4d7..72a4188c4 100644 --- a/src/font/lv_font.c +++ b/src/font/lv_font.c @@ -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; } /** diff --git a/src/font/lv_font.h b/src/font/lv_font.h index 6ff40acb7..5ed204baf 100644 --- a/src/font/lv_font.h +++ b/src/font/lv_font.h @@ -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 diff --git a/src/font/lv_font_fmt_txt.c b/src/font/lv_font_fmt_txt.c index 991b23e6d..452cbe912 100644 --- a/src/font/lv_font_fmt_txt.c +++ b/src/font/lv_font_fmt_txt.c @@ -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; diff --git a/src/gpu/sdl/lv_gpu_sdl_draw_label.c b/src/gpu/sdl/lv_gpu_sdl_draw_label.c index e87773e53..30d5cff94 100644 --- a/src/gpu/sdl/lv_gpu_sdl_draw_label.c +++ b/src/gpu/sdl/lv_gpu_sdl_draw_label.c @@ -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; } diff --git a/src/gpu/sdl/lv_gpu_sdl_texture_cache.h b/src/gpu/sdl/lv_gpu_sdl_texture_cache.h index 5a20d542d..afecc5a4f 100644 --- a/src/gpu/sdl/lv_gpu_sdl_texture_cache.h +++ b/src/gpu/sdl/lv_gpu_sdl_texture_cache.h @@ -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; diff --git a/src/gpu/sdl/lv_gpu_sdl_utils.c b/src/gpu/sdl/lv_gpu_sdl_utils.c index d6486014a..b78b813bb 100644 --- a/src/gpu/sdl/lv_gpu_sdl_utils.c +++ b/src/gpu/sdl/lv_gpu_sdl_utils.c @@ -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;