From e218e19ea3f47b69b985ce997fa5dba80ae78e9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 17 Oct 2019 17:43:40 +0200 Subject: [PATCH] font: load all fallbacks when instantiating a primary font The caching of fallback fonts didn't work. It "worked" because we didn't free the fonts... When we started doing that, the fallback fonts were no longer cached. Another solution would have been to keep the cached fallback fonts around until exit, and free them there. But that didn't seem very clean. So, for now, load *all* fallbacks when instantiating a primary (non-fallback) font. Note that this slows down initial startup time. --- font.c | 155 +++++++++++++++------------------------------------------ font.h | 4 +- 2 files changed, 44 insertions(+), 115 deletions(-) diff --git a/font.c b/font.c index dbd230e8..14d55061 100644 --- a/font.c +++ b/font.c @@ -20,12 +20,6 @@ static FT_Library ft_lib; static mtx_t ft_lock; -struct font_cache_entry { - uint64_t hash; - struct font *font; -}; -static tll(struct font_cache_entry) font_cache = tll_init(); - static const size_t cache_size = 512; static void __attribute__((constructor)) @@ -39,8 +33,6 @@ init(void) static void __attribute__((destructor)) fini(void) { - assert(tll_length(font_cache) == 0); - mtx_destroy(&ft_lock); FT_Done_FreeType(ft_lib); FcFini(); @@ -84,14 +76,11 @@ underline_strikeout_metrics(struct font *font) } static bool -from_font_set(FcPattern *pattern, FcFontSet *fonts, int start_idx, const font_list_t *fallbacks, - const char *attributes, struct font *font, bool is_fallback) +from_font_set(FcPattern *pattern, FcFontSet *fonts, int start_idx, + struct font *font, bool is_fallback) { memset(font, 0, sizeof(*font)); - size_t attr_len = attributes == NULL ? 0 : strlen(attributes); - bool have_attrs = attr_len > 0; - FcChar8 *face_file = NULL; FcPattern *final_pattern = NULL; int font_idx = -1; @@ -211,6 +200,7 @@ from_font_set(FcPattern *pattern, FcFontSet *fonts, int start_idx, const font_li case FC_LCD_LEGACY: font->lcd_filter = FT_LCD_FILTER_LEGACY; break; } + font->name = strdup((char *)face_file); FcPatternDestroy(final_pattern); mtx_init(&font->lock, mtx_plain); @@ -236,79 +226,13 @@ from_font_set(FcPattern *pattern, FcFontSet *fonts, int start_idx, const font_li font->glyph_cache = calloc(cache_size, sizeof(font->glyph_cache[0])); } - if (fallbacks != NULL) { - tll_foreach(*fallbacks, it) { - size_t len = strlen(it->item) + (have_attrs ? 1 : 0) + attr_len + 1; - char *fallback = malloc(len); - - strcpy(fallback, it->item); - if (have_attrs) { - strcat(fallback, ":"); - strcat(fallback, attributes); - } - - LOG_DBG("%s: adding fallback: %s", it->item, fallback); - tll_push_back(font->fallbacks, fallback); - } - } - underline_strikeout_metrics(font); return true; } -static uint64_t -hash_font(const char *base_name, const font_list_t *fallbacks, - const char *attributes, bool is_fallback) -{ -#define rot(h, n) (((h) << (n)) | ((h) >> (64 - (n)))) - - /* TODO: better string hash */ - uint64_t hash = 0; - - for (size_t i = 0; i < strlen(base_name); i++) - hash = rot(hash, 7) ^ base_name[i]; - - if (fallbacks != NULL) { - tll_foreach(*fallbacks, it) { - for (size_t i = 0; i < strlen(it->item); i++) - hash = rot(hash, 17) ^ it->item[i]; - } - } - - if (attributes != NULL) { - for (size_t i = 0; i < strlen(attributes); i++) - hash = rot(hash, 11) ^ attributes[i]; - } - - if (is_fallback) - hash = rot(hash, 27); - - return hash; -#undef rot -} - static struct font * -from_name(const char *base_name, const font_list_t *fallbacks, - const char *attributes, bool is_fallback) +from_name(const char *name, bool is_fallback) { - uint64_t hash = hash_font(base_name, fallbacks, attributes, is_fallback); - tll_foreach(font_cache, it) { - if (it->item.hash == hash) { - it->item.font->ref_counter++; - return it->item.font; - } - } - - size_t attr_len = attributes == NULL ? 0 : strlen(attributes); - bool have_attrs = attr_len > 0; - - char name[strlen(base_name) + (have_attrs ? 1 : 0) + attr_len + 1]; - strcpy(name, base_name); - if (have_attrs){ - strcat(name, ":"); - strcat(name, attributes); - } - LOG_DBG("instantiating %s", name); FcPattern *pattern = FcNameParse((const unsigned char *)name); @@ -335,7 +259,7 @@ from_name(const char *base_name, const font_list_t *fallbacks, struct font *font = malloc(sizeof(*font)); - if (!from_font_set(pattern, fonts, 0, fallbacks, attributes, font, is_fallback)) { + if (!from_font_set(pattern, fonts, 0, font, is_fallback)) { free(font); FcFontSetDestroy(fonts); FcPatternDestroy(pattern); @@ -347,7 +271,7 @@ from_name(const char *base_name, const font_list_t *fallbacks, FcPatternDestroy(pattern); } - tll_push_back(font_cache, ((struct font_cache_entry){.hash = hash, .font = font})); + LOG_DBG("instantiated: %s", font->name); return font; } @@ -357,21 +281,39 @@ font_from_name(font_list_t names, const char *attributes) if (tll_length(names) == 0) return false; - font_list_t fallbacks = tll_init(); - bool skip_first = true; + struct font *res = NULL; + + bool have_attrs = attributes != NULL && strlen(attributes) > 0; + size_t attr_len = have_attrs ? strlen(attributes) + 1 : 0; + + bool first = true; tll_foreach(names, it) { - if (skip_first) { - skip_first = false; + const char *base_name = it->item; + + char primary_name[strlen(base_name) + attr_len + 1]; + strcpy(primary_name, base_name); + if (have_attrs) { + strcat(primary_name, ":"); + strcat(primary_name, attributes); + } + + struct font *font = from_name(primary_name, !first); + if (font == NULL) { + if (first) + return NULL; continue; } - tll_push_back(fallbacks, it->item); + if (first) { + res = font; + first = false; + continue; + } + + tll_push_back(res->fallbacks, font); } - struct font *font = from_name(tll_front(names), &fallbacks, attributes, false); - - tll_free(fallbacks); - return font; + return res; } static size_t @@ -398,16 +340,10 @@ glyph_for_wchar(const struct font *font, wchar_t wc, struct glyph *glyph) if (idx == 0) { /* No glyph in this font, try fallback fonts */ - /* Try user configured fallback fonts */ tll_foreach(font->fallbacks, it) { - struct font *fallback = from_name(it->item, NULL, "", true); - if (fallback == NULL) - continue; - - if (glyph_for_wchar(fallback, wc, glyph)) { - LOG_DBG("%C: used fallback %s (fixup = %f)", - wc, it->item, fallback->pixel_size_fixup); - font_destroy(fallback); + if (glyph_for_wchar(it->item, wc, glyph)) { + LOG_DBG("%C: used fallback: %s (fixup = %f)", + wc, it->item->name, it->item->pixel_size_fixup); return true; } } @@ -426,8 +362,7 @@ glyph_for_wchar(const struct font *font, wchar_t wc, struct glyph *glyph) if (font->fc_loaded_fallbacks[i] == NULL) { /* Load font */ struct font *fallback = malloc(sizeof(*fallback)); - if (!from_font_set(font->fc_pattern, font->fc_fonts, i, NULL, - "", fallback, true)) + if (!from_font_set(font->fc_pattern, font->fc_fonts, i, fallback, true)) { LOG_WARN("failed to load fontconfig fallback font"); free(fallback); @@ -444,7 +379,8 @@ glyph_for_wchar(const struct font *font, wchar_t wc, struct glyph *glyph) assert(font->fc_loaded_fallbacks[i] != NULL); if (glyph_for_wchar(font->fc_loaded_fallbacks[i], wc, glyph)) { - LOG_DBG("%C: used fontconfig fallback", wc); + LOG_DBG("%C: used fontconfig fallback: %s", + wc, font->fc_loaded_fallbacks[i]->name); return true; } } @@ -661,7 +597,8 @@ font_destroy(struct font *font) if (--font->ref_counter > 0) return; - tll_free_and_free(font->fallbacks, free); + free(font->name); + tll_free_and_free(font->fallbacks, font_destroy); if (font->face != NULL) { mtx_lock(&ft_lock); @@ -703,15 +640,5 @@ font_destroy(struct font *font) free(font->glyph_cache[i]); } free(font->glyph_cache); - - tll_foreach(font_cache, it) { - if (it->item.font == font) { - tll_remove(font_cache, it); - free(font); - return; - } - } - - LOG_ERR("font not found in cache"); free(font); } diff --git a/font.h b/font.h index 149dda19..3bd10129 100644 --- a/font.h +++ b/font.h @@ -30,6 +30,8 @@ struct glyph { typedef tll(struct glyph) hash_entry_t; struct font { + char *name; + mtx_t lock; FT_Face face; int load_flags; @@ -50,7 +52,7 @@ struct font { } strikeout; bool is_fallback; - tll(char *) fallbacks; + tll(struct font *) fallbacks; size_t ref_counter;