From 94a6d21d4c2fe5fc2806cd28ae36a2b0e1928914 Mon Sep 17 00:00:00 2001 From: pommicket Date: Tue, 18 Jul 2023 16:09:50 -0400 Subject: new font management system this lets us avoid reloading the font file from disk whenever the font size changes --- command.c | 4 +-- main.c | 5 +-- ted.c | 55 +++++++++++++++++++++---------- ted.h | 12 ++++++- text.c | 46 ++++++++++++++++---------- text.h | 2 ++ valgrind_suppresions.txt | 85 ++---------------------------------------------- 7 files changed, 86 insertions(+), 123 deletions(-) diff --git a/command.c b/command.c index 96c04b6..65d2072 100644 --- a/command.c +++ b/command.c @@ -532,7 +532,7 @@ void command_execute_ex(Ted *ted, Command c, CommandArgument full_argument, Comm i64 new_text_size = settings->text_size + argument; if (new_text_size >= TEXT_SIZE_MIN && new_text_size <= TEXT_SIZE_MAX) { settings->text_size = (u16)new_text_size; - ted_load_fonts(ted); + ted_change_text_size(ted, (float)new_text_size); } } break; @@ -541,7 +541,7 @@ void command_execute_ex(Ted *ted, Command c, CommandArgument full_argument, Comm i64 new_text_size = settings->text_size - argument; if (new_text_size >= TEXT_SIZE_MIN && new_text_size <= TEXT_SIZE_MAX) { settings->text_size = (u16)new_text_size; - ted_load_fonts(ted); + ted_change_text_size(ted, (float)new_text_size); } } break; diff --git a/main.c b/main.c index 526eeb2..8fa816c 100644 --- a/main.c +++ b/main.c @@ -1,4 +1,6 @@ /* +TODO: +- clean up valgrind_suppresions.txt FUTURE FEATURES: - autodetect indentation (tabs vs spaces) - font setting & support for multiple fonts to cover more characters @@ -1214,8 +1216,7 @@ int main(int argc, char **argv) { buffer_free(&ted->replace_buffer); buffer_free(&ted->build_buffer); buffer_free(&ted->argument_buffer); - text_font_free(ted->font); - text_font_free(ted->font_bold); + ted_free_fonts(ted); config_free(ted); macros_free(ted); free(ted); diff --git a/ted.c b/ted.c index f526947..73a5969 100644 --- a/ted.c +++ b/ted.c @@ -287,31 +287,50 @@ Status ted_get_file(Ted const *ted, const char *name, char *out, size_t outsz) { return false; } -// Loads font from filename into *out, freeing any font that was previously there. -// *out is left unchanged on failure. -static void ted_load_font(Ted *ted, const char *filename, Font **out) { - char font_filename[TED_PATH_MAX]; - if (ted_get_file(ted, filename, font_filename, sizeof font_filename)) { - Font *font = text_font_load(font_filename, ted_active_settings(ted)->text_size); - if (font) { - if (*out) { - text_font_free(*out); - } - *out = font; - } else { - die("Couldn't load font: %s", text_get_err()); - } - } else { - die("Couldn't find font file. There is probably a problem with your ted installation."); +static Font *ted_load_font(Ted *ted, const char *filename) { + char path[TED_PATH_MAX]; + if (!ted_get_file(ted, filename, path, sizeof path)) { + die("Couldn't find font file %s", filename); + } + + arr_foreach_ptr(ted->all_fonts, LoadedFont, f) { + if (paths_eq(path, f->path)) + return f->font; + } + + Font *font = text_font_load(path, ted_active_settings(ted)->text_size); + if (!font) { + die("Couldn't load font %s: %s\n", path, text_get_err()); } + LoadedFont *f = arr_addp(ted->all_fonts); + f->path = str_dup(path); + f->font = font; + + return font; } void ted_load_fonts(Ted *ted) { - ted_load_font(ted, "assets/font.ttf", &ted->font); - ted_load_font(ted, "assets/font-bold.ttf", &ted->font_bold); + ted_free_fonts(ted); + ted->font = ted_load_font(ted, "assets/font.ttf"); + ted->font_bold = ted_load_font(ted, "assets/font-bold.ttf"); } +void ted_change_text_size(Ted *ted, float new_size) { + arr_foreach_ptr(ted->all_fonts, LoadedFont, f) { + text_font_change_size(f->font, new_size); + } +} + +void ted_free_fonts(Ted *ted) { + arr_foreach_ptr(ted->all_fonts, LoadedFont, f) { + free(f->path); + text_font_free(f->font); + } + arr_clear(ted->all_fonts); + ted->font = NULL; + ted->font_bold = NULL; +} void ted_switch_to_buffer(Ted *ted, TextBuffer *buffer) { TextBuffer *search_buffer = find_search_buffer(ted); diff --git a/ted.h b/ted.h index 968b2ee..e3e75e1 100644 --- a/ted.h +++ b/ted.h @@ -725,6 +725,11 @@ typedef struct { Action *actions; } Macro; +typedef struct { + char *path; + Font *font; +} LoadedFont; + #define TED_MACRO_MAX 256 /// (almost) all data used by the ted application @@ -739,6 +744,7 @@ typedef struct Ted { bool executing_macro; SDL_Window *window; + LoadedFont *all_fonts; Font *font_bold; Font *font; TextBuffer *active_buffer; @@ -1655,8 +1661,12 @@ TextBuffer *ted_get_buffer_with_file(Ted *ted, const char *path); bool ted_save_all(Ted *ted); /// reload all buffers from their files void ted_reload_all(Ted *ted); -/// Load all the fonts ted will use. +/// Load all the fonts ted will use, freeing any previous ones. void ted_load_fonts(Ted *ted); +/// Change ted's font size. Avoid calling this super often since it trashes all current font textures. +void ted_change_text_size(Ted *ted, float new_size); +/// Free all of ted's fonts. +void ted_free_fonts(Ted *ted); /// Get likely root directory of project containing `path`. /// The returned value should be freed. char *ted_get_root_dir_of(Ted *ted, const char *path); diff --git a/text.c b/text.c index 670f2d0..bcca150 100644 --- a/text.c +++ b/text.c @@ -42,15 +42,13 @@ typedef struct { GLuint tex; bool needs_update; unsigned char *pixels; + stbtt_pack_context pack_context; TextTriangle *triangles; } FontTexture; struct Font { bool force_monospace; - float space_width; // width of the character ' '. calculated when font is loaded. float char_height; - stbtt_pack_context pack_context; - stbtt_fontinfo stb_info; FontTexture *textures; // dynamic array of textures CharInfo *char_info[CHAR_BUCKET_COUNT]; // each entry is a dynamic array of char info // TTF data (i.e. the contents of the TTF file) @@ -144,7 +142,7 @@ static FontTexture *font_new_texture(Font *font) { return NULL; } FontTexture *texture = arr_addp(font->textures); - stbtt_PackBegin(&font->pack_context, pixels, FONT_TEXTURE_WIDTH, FONT_TEXTURE_HEIGHT, + stbtt_PackBegin(&texture->pack_context, pixels, FONT_TEXTURE_WIDTH, FONT_TEXTURE_HEIGHT, FONT_TEXTURE_WIDTH, 1, NULL); glGenTextures(1, &texture->tex); PROFILE_TIME(end); @@ -175,8 +173,10 @@ static void font_texture_update_if_needed(FontTexture *texture) { static void font_texture_free(FontTexture *texture) { glDeleteTextures(1, &texture->tex); arr_free(texture->triangles); - if (texture->pixels) + if (texture->pixels) { free(texture->pixels); + stbtt_PackEnd(&texture->pack_context); + } memset(texture, 0, sizeof *texture); } @@ -203,11 +203,11 @@ static Status text_load_char(Font *font, char32_t c, CharInfo *info) { for (int i = 0; i < 2; i++) { info->c = c; info->texture = arr_len(font->textures) - 1; - success = stbtt_PackFontRange(&font->pack_context, font->ttf_data, 0, font->char_height, + success = stbtt_PackFontRange(&texture->pack_context, font->ttf_data, 0, font->char_height, (int)c, 1, &info->data); if (success) break; // texture is full; create a new one - stbtt_PackEnd(&font->pack_context); + stbtt_PackEnd(&texture->pack_context); font_texture_update_if_needed(texture); free(texture->pixels); texture->pixels = NULL; @@ -257,11 +257,6 @@ Font *text_font_load(const char *ttf_filename, float font_size) { if (bytes_read == file_size) { font->char_height = font_size; font->ttf_data = file_data; - CharInfo space = {0}; - if (text_load_char(font, ' ', &space)) { - // calculate width of the character ' ' - font->space_width = space.data.xadvance; - } } else { text_set_err("Couldn't read font file."); } @@ -286,7 +281,6 @@ float text_font_char_height(Font *font) { } float text_font_char_width(Font *font, char32_t c) { - if (c == ' ') return font->space_width; CharInfo info = {0}; if (text_load_char(font, c, &info)) return info.data.xadvance; @@ -358,7 +352,7 @@ top: q.y1 += (float)floor(state->y); if (font->force_monospace) { - state->x += font->space_width; // ignore actual character width + state->x += text_font_char_width(font, ' '); } else { state->x = x + floor(state->x); state->y = y + floor(state->y); @@ -495,11 +489,29 @@ void text_get_size32(Font *font, const char32_t *text, u64 len, float *width, fl if (height) *height = (float)render_state.y + font->char_height * (2/3.0f); } -void text_font_free(Font *font) { - free(font->ttf_data); +static void font_free_textures(Font *font) { arr_foreach_ptr(font->textures, FontTexture, texture) { font_texture_free(texture); } - arr_free(font->textures); + arr_clear(font->textures); +} + +static void font_free_char_info(Font *font) { + for (u32 i = 0; i < CHAR_BUCKET_COUNT; i++) { + arr_free(font->char_info[i]); + } +} + +void text_font_change_size(Font *font, float new_size) { + font_free_textures(font); + font_free_char_info(font); + font->char_height = new_size; +} + +void text_font_free(Font *font) { + free(font->ttf_data); + font_free_textures(font); + font_free_char_info(font); + memset(font, 0, sizeof *font); free(font); } diff --git a/text.h b/text.h index c0cb260..79f99ad 100644 --- a/text.h +++ b/text.h @@ -68,6 +68,8 @@ const char *text_get_err(void); void text_clear_err(void); /// Load a TTF font found in ttf_filename with the given font size (character pixel height) Font *text_font_load(const char *ttf_filename, float font_size); +/// Change size of font. Avoid calling this function too often, since all font textures are trashed. +void text_font_change_size(Font *font, float new_size); /// Height of a character of this font in pixels. float text_font_char_height(Font *font); /// Width of the given character in pixels. diff --git a/valgrind_suppresions.txt b/valgrind_suppresions.txt index af09ee7..93c1872 100644 --- a/valgrind_suppresions.txt +++ b/valgrind_suppresions.txt @@ -152,42 +152,6 @@ obj:/usr/lib/x86_64-linux-gnu/libSDL2-2.0.so.0.12.0 obj:/usr/lib/x86_64-linux-gnu/libSDL2-2.0.so.0.12.0 } -{ - - Memcheck:Addr8 - fun:strncmp - fun:is_dst - fun:_dl_dst_count - fun:expand_dynamic_string_token - fun:fillin_rpath.isra.0 - fun:decompose_rpath - fun:cache_rpath - fun:cache_rpath - fun:_dl_map_object - fun:openaux - fun:_dl_catch_exception - fun:_dl_map_object_deps - fun:dl_open_worker - fun:_dl_catch_exception -} -{ - - Memcheck:Addr8 - fun:strncmp - fun:is_dst - fun:_dl_dst_substitute - fun:fillin_rpath.isra.0 - fun:decompose_rpath - fun:cache_rpath - fun:cache_rpath - fun:_dl_map_object - fun:openaux - fun:_dl_catch_exception - fun:_dl_map_object_deps - fun:dl_open_worker - fun:_dl_catch_exception - fun:_dl_open -} { Memcheck:Addr8 @@ -42907,52 +42871,7 @@ Memcheck:Leak match-leak-kinds: reachable - fun:calloc - fun:_dl_new_object - fun:_dl_map_object_from_fd - fun:_dl_map_object - fun:openaux - fun:_dl_catch_exception - fun:_dl_map_object_deps - fun:dl_open_worker - fun:_dl_catch_exception - fun:_dl_open - fun:dlopen_doit - fun:_dl_catch_exception -} -{ - - Memcheck:Leak - match-leak-kinds: reachable - fun:calloc - fun:_ZN4llvm13StringMapImpl11RehashTableEj - obj:/usr/lib/x86_64-linux-gnu/libLLVM-11.so.1 - obj:/usr/lib/x86_64-linux-gnu/libLLVM-11.so.1 - fun:_ZN4llvm2cl6Option11addArgumentEv - obj:/usr/lib/x86_64-linux-gnu/libLLVM-11.so.1 - fun:call_init.part.0 - fun:call_init - fun:_dl_init - fun:_dl_catch_exception - fun:dl_open_worker - fun:_dl_catch_exception - fun:_dl_open -} -{ - - Memcheck:Leak - match-leak-kinds: reachable - fun:malloc - obj:/usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.28 - fun:call_init.part.0 - fun:call_init - fun:_dl_init - fun:_dl_catch_exception - fun:dl_open_worker - fun:_dl_catch_exception - fun:_dl_open - fun:dlopen_doit + ... fun:_dl_catch_exception - fun:_dl_catch_error - fun:_dlerror_run + ... } -- cgit v1.2.3