From d883ff69381c9c95ecf13f0efc85c50c71584188 Mon Sep 17 00:00:00 2001 From: Dav999-v Date: Mon, 20 Feb 2023 04:35:19 +0100 Subject: [PATCH] Add support for button glyph fallback fonts In a button glyph font (like buttons_8x8.fontmeta) you can now specify buttons to indicate that it's a button glyphs font. In a normal font, you can specify buttons_8x8. This will make it such that if a character is not found in the main font, it will instead be looked for in buttons_8x8. If not found there either, the main font's U+FFFD or '?' will be used as before. --- desktop_version/src/Font.cpp | 130 +++++++++++++++++++++++++++++++---- 1 file changed, 117 insertions(+), 13 deletions(-) diff --git a/desktop_version/src/Font.cpp b/desktop_version/src/Font.cpp index 0f3aec42..562ab506 100644 --- a/desktop_version/src/Font.cpp +++ b/desktop_version/src/Font.cpp @@ -40,17 +40,29 @@ struct GlyphInfo #define FONT_N_PAGES 0x110 #define FONT_PAGE_SIZE 0x1000 +enum FontType +{ + FontType_FONT, + FontType_BUTTONS +}; + struct Font { char name[64]; char display_name[SCREEN_WIDTH_CHARS + 1]; + FontType type; + uint8_t glyph_w; uint8_t glyph_h; SDL_Texture* image; GlyphInfo* glyph_page[FONT_N_PAGES]; + + char fallback_key[64]; + uint8_t fallback_idx; + bool fallback_idx_valid; }; struct FontContainer @@ -149,15 +161,35 @@ static bool glyph_is_valid(const GlyphInfo* glyph) return glyph->flags & GLYPH_EXISTS; } -static GlyphInfo* find_glyphinfo(const Font* f, const uint32_t codepoint) +static Font* fallback_for(const Font* f) +{ + if (!f->fallback_idx_valid) + { + return NULL; + } + return &fonts_main.fonts[f->fallback_idx]; +} + +static GlyphInfo* find_glyphinfo(const Font* f, const uint32_t codepoint, const Font** f_glyph) { /* Get the GlyphInfo for a specific codepoint, or or ? if it doesn't exist. + * f_glyph may be either set to f (the main specified font) or its fallback font, if it exists. * As a last resort, may return NULL. */ + *f_glyph = f; + GlyphInfo* glyph = get_glyphinfo(f, codepoint); if (glyph != NULL && glyph_is_valid(glyph)) { return glyph; } + + Font* f_fallback = fallback_for(f); + if (f_fallback != NULL && (glyph = get_glyphinfo(f_fallback, codepoint)) != NULL && glyph_is_valid(glyph)) + { + *f_glyph = f_fallback; + return glyph; + } + glyph = get_glyphinfo(f, 0xFFFD); if (glyph != NULL && glyph_is_valid(glyph)) { @@ -172,6 +204,26 @@ static GlyphInfo* find_glyphinfo(const Font* f, const uint32_t codepoint) return NULL; } +static int get_advance_ff(const Font* f, const Font* f_glyph, const GlyphInfo* glyph) +{ + /* Internal function - get the correct advance after we have + * determined whether the glyph is from the fallback font or not. */ + + if (glyph == NULL) + { + return f->glyph_w; + } + + /* If the glyph is a fallback glyph, center it relative to the main font + * instead of trusting the fallback's width */ + if (f_glyph != f) + { + return f->glyph_w; + } + + return glyph->advance; +} + int get_advance(const Font* f, const uint32_t codepoint) { // Get the width of a single character in a font @@ -180,13 +232,9 @@ int get_advance(const Font* f, const uint32_t codepoint) return 8; } - GlyphInfo* glyph = find_glyphinfo(f, codepoint); - if (glyph == NULL) - { - return f->glyph_w; - } - - return glyph->advance; + const Font* f_glyph; + GlyphInfo* glyph = find_glyphinfo(f, codepoint, &f_glyph); + return get_advance_ff(f, f_glyph, glyph); } static bool decode_xml_range(tinyxml2::XMLElement* elem, unsigned* start, unsigned* end) @@ -230,9 +278,14 @@ static uint8_t load_font(FontContainer* container, const char* name) SDL_strlcpy(f->name, name, sizeof(f->name)); SDL_strlcpy(f->display_name, name, sizeof(f->display_name)); + f->type = FontType_FONT; + f->glyph_w = 8; f->glyph_h = 8; + f->fallback_key[0] = '\0'; + f->fallback_idx_valid = false; + if (container->map_name_idx == NULL) { container->map_name_idx = hashmap_create(); @@ -257,6 +310,13 @@ static uint8_t load_font(FontContainer* container, const char* name) { SDL_strlcpy(f->display_name, pElem->GetText(), sizeof(f->display_name)); } + if ((pElem = hDoc.FirstChildElement("type").ToElement()) != NULL) + { + if (SDL_strcmp(pElem->GetText(), "buttons") == 0) + { + f->type = FontType_BUTTONS; + } + } if ((pElem = hDoc.FirstChildElement("width").ToElement()) != NULL) { f->glyph_w = help.Int(pElem->GetText()); @@ -270,6 +330,10 @@ static uint8_t load_font(FontContainer* container, const char* name) // If 1, we don't need to whiten the entire font (like in old versions) white_teeth = help.Int(pElem->GetText()); } + if ((pElem = hDoc.FirstChildElement("fallback").ToElement()) != NULL) + { + SDL_strlcpy(f->fallback_key, pElem->GetText(), sizeof(f->fallback_key)); + } } f->image = LoadImage(name_png, white_teeth ? TEX_COLOR : TEX_WHITE); @@ -493,6 +557,20 @@ void set_level_font_new(void) #endif } +static void set_fallbacks(FontContainer* container) +{ + /* Initialize the value of fallback_idx for all fonts in this container. + * Only main fonts can be fallback fonts. */ + for (uint8_t i = 0; i < container->count; i++) + { + Font* f = &container->fonts[i]; + if (find_main_font_by_name(f->fallback_key, &f->fallback_idx)) + { + f->fallback_idx_valid = true; + } + } +} + static void load_font_filename(bool is_custom, const char* filename) { // Load font.png, and everything that matches *.fontmeta (but not font.fontmeta) @@ -533,6 +611,8 @@ void load_main(void) FILESYSTEM_freeEnumerate(&handle); font_idx_level = font_idx_8x8; + set_fallbacks(&fonts_main); + // Initialize the font menu options, 8x8 font first font_idx_options[0] = font_idx_8x8; font_idx_options_n = 1; @@ -543,6 +623,10 @@ void load_main(void) { continue; } + if (fonts_main.fonts[i].type != FontType_FONT) + { + continue; + } font_idx_options[font_idx_options_n++] = i; if (font_idx_options_n >= sizeof(font_idx_options)) { @@ -563,6 +647,8 @@ void load_custom(const char* name) } FILESYSTEM_freeEnumerate(&handle); + set_fallbacks(&fonts_custom); + set_level_font(name); } @@ -926,8 +1012,8 @@ std::string string_unwordwrap(const std::string& s) static int print_char( const Font* f, const uint32_t codepoint, - const int x, - const int y, + int x, + int y, const int scale, uint8_t r, uint8_t g, @@ -937,7 +1023,8 @@ static int print_char( { /* Draws the glyph for a codepoint at x,y. * Returns the amount of pixels to advance the cursor. */ - GlyphInfo* glyph = find_glyphinfo(f, codepoint); + const Font* f_glyph; + GlyphInfo* glyph = find_glyphinfo(f, codepoint, &f_glyph); if (glyph == NULL) { return f->glyph_w * scale; @@ -955,9 +1042,26 @@ static int print_char( b *= bri_factor; } - graphics.draw_grid_tile(f->image, glyph->image_idx, x, y, f->glyph_w, f->glyph_h, r, g, b, scale, scale * (graphics.flipmode ? -1 : 1)); + // If the glyph is a fallback glyph, center it + if (f_glyph != f) + { + x += (f->glyph_w - f_glyph->glyph_w) / 2; + y += (f->glyph_h - f_glyph->glyph_h) / 2; + } - return glyph->advance * scale; + graphics.draw_grid_tile( + f_glyph->image, + glyph->image_idx, + x, + y, + f_glyph->glyph_w, + f_glyph->glyph_h, + r, g, b, + scale, + scale * (graphics.flipmode ? -1 : 1) + ); + + return get_advance_ff(f, f_glyph, glyph) * scale; } const char* get_main_font_name(uint8_t idx)