# HG changeset patch # User Mike Becker # Date 1713558509 -7200 # Node ID 8297afa1c29c670b0e91981e63e6251fe863a911 # Parent 9c44c55d327ab9c8776ca541b0b308be49ab25d6 replaces broken font cache with improved cache - fixes #387 diff -r 9c44c55d327a -r 8297afa1c29c src/Makefile --- a/src/Makefile Thu Apr 18 22:53:55 2024 +0200 +++ b/src/Makefile Fri Apr 19 22:28:29 2024 +0200 @@ -49,15 +49,16 @@ ascension/datatypes.h ascension/window.h ascension/glcontext.h \ ascension/primitives.h ascension/mesh.h ascension/shader.h \ ascension/scene.h ascension/transform.h ascension/camera.h \ - ascension/ui/font.h ascension/error.h ascension/utils.h + ascension/input.h ascension/ui/font.h ascension/error.h \ + ascension/utils.h @echo "Compiling $<" $(CC) -o $@ $(CFLAGS) -c $< $(BUILD_DIR)/error.o: error.c ascension/context.h ascension/datatypes.h \ ascension/window.h ascension/glcontext.h ascension/primitives.h \ ascension/mesh.h ascension/shader.h ascension/scene.h \ - ascension/transform.h ascension/camera.h ascension/ui/font.h \ - ascension/error.h ascension/utils.h + ascension/transform.h ascension/camera.h ascension/input.h \ + ascension/ui/font.h ascension/error.h ascension/utils.h @echo "Compiling $<" $(CC) -o $@ $(CFLAGS) -c $< @@ -69,7 +70,7 @@ ascension/datatypes.h ascension/window.h ascension/glcontext.h \ ascension/primitives.h ascension/mesh.h ascension/shader.h \ ascension/scene.h ascension/transform.h ascension/camera.h \ - ascension/ui/font.h ascension/error.h + ascension/input.h ascension/ui/font.h ascension/error.h @echo "Compiling $<" $(CC) -o $@ $(CFLAGS) -c $< @@ -83,14 +84,15 @@ ascension/mesh.h ascension/error.h ascension/context.h \ ascension/datatypes.h ascension/window.h ascension/glcontext.h \ ascension/primitives.h ascension/shader.h ascension/scene.h \ - ascension/transform.h ascension/camera.h ascension/ui/font.h + ascension/transform.h ascension/camera.h ascension/input.h \ + ascension/ui/font.h @echo "Compiling $<" $(CC) -o $@ $(CFLAGS) -c $< $(BUILD_DIR)/scene.o: scene.c ascension/scene.h ascension/datatypes.h \ ascension/transform.h ascension/camera.h ascension/context.h \ ascension/window.h ascension/glcontext.h ascension/primitives.h \ - ascension/mesh.h ascension/shader.h ascension/scene.h \ + ascension/mesh.h ascension/shader.h ascension/scene.h ascension/input.h \ ascension/ui/font.h ascension/utils.h ascension/shader.h @echo "Compiling $<" $(CC) -o $@ $(CFLAGS) -c $< @@ -106,8 +108,8 @@ ascension/ui/../texture.h ascension/ui/../utils.h ascension/context.h \ ascension/datatypes.h ascension/window.h ascension/glcontext.h \ ascension/primitives.h ascension/mesh.h ascension/shader.h \ - ascension/scene.h ascension/ui/font.h ascension/error.h \ - ascension/shader.h + ascension/scene.h ascension/input.h ascension/ui/font.h \ + ascension/error.h ascension/shader.h @echo "Compiling $<" $(CC) -o $@ $(CFLAGS) -c $< @@ -119,7 +121,8 @@ ascension/glcontext.h ascension/primitives.h ascension/mesh.h \ ascension/shader.h ascension/scene.h ascension/transform.h \ ascension/camera.h ascension/context.h ascension/window.h \ - ascension/ui/font.h ascension/error.h ascension/utils.h + ascension/input.h ascension/ui/font.h ascension/error.h \ + ascension/utils.h @echo "Compiling $<" $(CC) -o $@ $(CFLAGS) -c $< diff -r 9c44c55d327a -r 8297afa1c29c src/ascension/context.h --- a/src/ascension/context.h Thu Apr 18 22:53:55 2024 +0200 +++ b/src/ascension/context.h Fri Apr 19 22:28:29 2024 +0200 @@ -52,11 +52,9 @@ CxBuffer error_buffer; AscInput input; AscWindow windows[ASC_MAX_WINDOWS]; - // TODO: rework how fonts are cached - AscFont fonts[ASC_MAX_FONTS]; + AscFont active_font; unsigned char fonts_loaded; unsigned char active_window; - unsigned char active_font; asc_col4i ink; uint64_t frame_nanos; uint64_t total_nanos; @@ -69,24 +67,23 @@ * The currently active font. * @see asc_font() */ -#define asc_active_font \ - (&asc_context.fonts[asc_context.active_font]) +#define asc_active_font asc_context.active_font +#ifdef NDEBUG /** * The currently active window in the context. * @see asc_window_activate() */ #define asc_active_window \ (&asc_context.windows[asc_context.active_window]) - -static inline bool asc_assert_active_window() { - if (asc_context.active_window < ASC_MAX_WINDOWS) { - return true; - } else { - asc_error("A graphics operation was performed without active window"); - return false; - } -} +#else +/** + * The currently active window in the context. + * @see asc_window_activate() + */ +#define asc_active_window asc_active_window_assert() +AscWindow *asc_active_window_assert(void); +#endif // NDEBUG void asc_context_initialize(void); void asc_context_destroy(void); diff -r 9c44c55d327a -r 8297afa1c29c src/ascension/ui/font.h --- a/src/ascension/ui/font.h Thu Apr 18 22:53:55 2024 +0200 +++ b/src/ascension/ui/font.h Fri Apr 19 22:28:29 2024 +0200 @@ -30,11 +30,6 @@ #include -#ifndef ASC_MAX_FONTS -/** The maximum number of style/size combinations that can be loaded in parallel. */ -#define ASC_MAX_FONTS 64u -#endif // ASC_MAX_FONTS - enum AscFontStyle { ASC_FONT_REGULAR, ASC_FONT_BOLD, @@ -51,11 +46,6 @@ * Point size. */ int size; - /** - * TODO: remove from public struct - * Pointer to the SDL TTF font structure. - */ - TTF_Font *ptr; } AscFont; /** @@ -73,4 +63,18 @@ */ void asc_font(enum AscFontStyle style, int size); +/** + * Loads the specified font. + * + * Loaded fonts are kept within a font cache and returned faster + * on the next invocation. + * + * You will almost never need to use this function on your own. + * Ascension UI elements are loading the required fonts for you. + * + * @param font the font description + * @return a pointer to the SDL TTF font handle, or \c NULL on error + */ +TTF_Font *asc_font_load(AscFont font); + #endif //ASCENSION_FONT_H diff -r 9c44c55d327a -r 8297afa1c29c src/context.c --- a/src/context.c Thu Apr 18 22:53:55 2024 +0200 +++ b/src/context.c Fri Apr 19 22:28:29 2024 +0200 @@ -42,14 +42,24 @@ return 1000000000ull*(uint64_t)ts.tv_sec + (uint64_t)ts.tv_nsec; } +// forward declare non-public font cache functions +void asc_font_cache_init(void); +void asc_font_cache_destroy(void); + void asc_context_initialize(void) { if (asc_test_flag(asc_context.flags, ASC_FLAG_INITILIZED)) return; memset(&asc_context, 0, sizeof(AscContext)); - // nothing is active right after initialization + // initialize the font cache + asc_font_cache_init(); + + // set a default font, but do not load it + asc_context.active_font.style = ASC_FONT_REGULAR; + asc_context.active_font.size = 14; + + // no window, yet asc_context.active_window = ASC_MAX_WINDOWS; - asc_context.active_font = ASC_MAX_FONTS; // initialize error buffer cxBufferInit( @@ -75,10 +85,13 @@ } void asc_context_destroy(void) { + // destroy windows for (unsigned int i = 0 ; i < ASC_MAX_WINDOWS ; i++) { asc_window_destroy(i); } - // TODO: fix that fonts are currently leaking by reworking the font cache + + // destroy the font cache + asc_font_cache_destroy(); // quit SDL if (TTF_WasInit()) @@ -95,6 +108,15 @@ asc_set_flag(asc_context.flags, ASC_FLAG_QUIT); } +AscWindow *asc_active_window_assert() { + if (asc_context.active_window < ASC_MAX_WINDOWS) { + return &asc_context.windows[asc_context.active_window]; + } else { + asc_dprintf("Window access attempted without active window"); + abort(); + } +} + static void asc_event_window_resized(Uint32 id, Sint32 width, Sint32 height) { for (unsigned int i = 0 ; i < ASC_MAX_WINDOWS ; i++) { if (asc_context.windows[i].id == id) { diff -r 9c44c55d327a -r 8297afa1c29c src/font.c --- a/src/font.c Thu Apr 18 22:53:55 2024 +0200 +++ b/src/font.c Fri Apr 19 22:28:29 2024 +0200 @@ -29,6 +29,14 @@ #include "ascension/context.h" #include "ascension/error.h" +#include +#include + +void asc_font(enum AscFontStyle style, int size) { + asc_context.active_font.style = style; + asc_context.active_font.size = size; +} + static char const *asc_font_filename(enum AscFontStyle style) { // TODO: do not assume we are running from the program dir switch (style) { @@ -43,31 +51,49 @@ } } -void asc_font(enum AscFontStyle style, int size) { - for (unsigned int i = 0 ; i < asc_context.fonts_loaded ; i++) { - AscFont *font = &asc_context.fonts[i]; - if (font->size == size && font->style == style) { - asc_context.active_font = i; - return; +struct asc_font_cache_entry { + AscFont font; + TTF_Font *ttf; +}; + +static CxList *asc_font_cache; + +static void asc_font_unload(struct asc_font_cache_entry *entry) { + TTF_CloseFont(entry->ttf); + asc_dprintf("Closed font size %u, style %u", entry->font.size, entry->font.style); +} + +void asc_font_cache_init(void) { + assert(asc_font_cache == NULL); + asc_font_cache = cxArrayListCreateSimple( + sizeof(struct asc_font_cache_entry), 16 + ); + asc_font_cache->simple_destructor = (cx_destructor_func) asc_font_unload; +} + +void asc_font_cache_destroy(void) { + assert(asc_font_cache != NULL); + cxListDestroy(asc_font_cache); +} + + +TTF_Font *asc_font_load(AscFont font) { + CxIterator iter = cxListIterator(asc_font_cache); + cx_foreach(struct asc_font_cache_entry*, cache, iter) { + if (cache->font.style == font.style && cache->font.size == font.size) { + return cache->ttf; } } - if (asc_context.fonts_loaded == ASC_MAX_FONTS) { - asc_error("Too many fonts. Cannot load more until cache is repaired."); - asc_context.active_font = ASC_MAX_FONTS; - return; - } - - unsigned int slot = asc_context.fonts_loaded++; - AscFont *font = &asc_context.fonts[slot]; - font->size = size; - font->style = style; - font->ptr = TTF_OpenFont(asc_font_filename(style), size); - if (font->ptr == NULL) { - asc_context.fonts_loaded--; + struct asc_font_cache_entry entry; + entry.font = font; + entry.ttf = TTF_OpenFont(asc_font_filename(font.style), font.size); + if (entry.ttf == NULL) { asc_error(TTF_GetError()); + return NULL; } else { - asc_dprintf("Loaded font size %u, style %u in slot %u", size, style, slot); - asc_context.active_font = slot; + cxListAdd(asc_font_cache, &entry); + asc_dprintf("Loaded font size %d, style %d from disk", font.size, font.style); + return entry.ttf; } } diff -r 9c44c55d327a -r 8297afa1c29c src/text.c --- a/src/text.c Thu Apr 18 22:53:55 2024 +0200 +++ b/src/text.c Fri Apr 19 22:28:29 2024 +0200 @@ -59,8 +59,7 @@ } // Render text onto a surface - // TODO: obtain TTF_Font from font cache once it is repaired - TTF_Font *font = node->font.ptr; + TTF_Font *font = asc_font_load(node->font); static int alignments[] = { TTF_WRAPPED_ALIGN_LEFT, TTF_WRAPPED_ALIGN_CENTER, @@ -102,7 +101,7 @@ node->base.position.x = (float) args.x; node->base.position.y = (float) args.y; node->max_width = args.max_width; - node->font = *asc_active_font; + node->font = asc_active_font; node->color = asc_context.ink; if (args.text == NULL) { node->text = cx_mutstr(strdup(" "));