From 43462b24f369a1b5d71be455adbcc1f474b33a1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 1 Nov 2019 20:40:42 +0100 Subject: [PATCH] font: cache loaded fonts globally Each font instance has a ref-counter. Whenever we want to instantiate a font that has already been loaded, we instead return the already-loaded instance, and bump the ref counter. When the last font instance is destroyed, it is also removed from the cache. --- font.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/font.c b/font.c index 4b1be833..237f8fee 100644 --- a/font.c +++ b/font.c @@ -22,6 +22,13 @@ static mtx_t ft_lock; static const size_t cache_size = 512; +struct font_cache_entry { + uint64_t hash; + struct font *font; +}; + +static tll(struct font_cache_entry) font_cache = tll_init(); + static void __attribute__((constructor)) init(void) { @@ -33,6 +40,9 @@ init(void) static void __attribute__((destructor)) fini(void) { + while (tll_length(font_cache) > 0) + font_destroy(tll_front(font_cache).font); + mtx_destroy(&ft_lock); FT_Done_FreeType(ft_lib); FcFini(); @@ -275,12 +285,46 @@ from_name(const char *name, bool is_fallback) return font; } +static uint64_t +sdbm_hash(const char *s) +{ + uint64_t hash = 0; + + for (; *s != '\0'; s++) { + int c = *s; + hash = c + (hash << 6) + (hash << 16) - hash; + } + + return hash; +} + +static uint64_t +font_hash(font_list_t names, const char *attributes) +{ + uint64_t hash = 0; + tll_foreach(names, it) + hash ^= sdbm_hash(it->item); + + if (attributes != NULL) + hash ^= sdbm_hash(attributes); + + return hash; +} + struct font * font_from_name(font_list_t names, const char *attributes) { if (tll_length(names) == 0) return false; + uint64_t hash = font_hash(names, attributes); + tll_foreach(font_cache, it) { + if (it->item.hash == hash) { + it->item.font->ref_counter++; + return it->item.font; + } + } + struct font *font = NULL; bool have_attrs = attributes != NULL && strlen(attributes) > 0; @@ -312,6 +356,7 @@ font_from_name(font_list_t names, const char *attributes) font->fallbacks, ((struct font_fallback){.pattern = strdup(name)})); } + tll_push_back(font_cache, ((struct font_cache_entry){.hash = hash, .font = font})); return font; } @@ -605,6 +650,13 @@ font_destroy(struct font *font) if (--font->ref_counter > 0) return; + tll_foreach(font_cache, it) { + if (it->item.font == font) { + tll_remove(font_cache, it); + break; + } + } + free(font->name); tll_foreach(font->fallbacks, it) {