mirror of
https://github.com/TerryCavanagh/VVVVVV.git
synced 2024-12-22 17:49:43 +01:00
Add support for button glyph fallback fonts
In a button glyph font (like buttons_8x8.fontmeta) you can now specify <type>buttons</type> to indicate that it's a button glyphs font. In a normal font, you can specify <fallback>buttons_8x8</fallback>. 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.
This commit is contained in:
parent
7a06b61f5d
commit
d883ff6938
1 changed files with 117 additions and 13 deletions
|
@ -40,17 +40,29 @@ struct GlyphInfo
|
||||||
#define FONT_N_PAGES 0x110
|
#define FONT_N_PAGES 0x110
|
||||||
#define FONT_PAGE_SIZE 0x1000
|
#define FONT_PAGE_SIZE 0x1000
|
||||||
|
|
||||||
|
enum FontType
|
||||||
|
{
|
||||||
|
FontType_FONT,
|
||||||
|
FontType_BUTTONS
|
||||||
|
};
|
||||||
|
|
||||||
struct Font
|
struct Font
|
||||||
{
|
{
|
||||||
char name[64];
|
char name[64];
|
||||||
char display_name[SCREEN_WIDTH_CHARS + 1];
|
char display_name[SCREEN_WIDTH_CHARS + 1];
|
||||||
|
|
||||||
|
FontType type;
|
||||||
|
|
||||||
uint8_t glyph_w;
|
uint8_t glyph_w;
|
||||||
uint8_t glyph_h;
|
uint8_t glyph_h;
|
||||||
|
|
||||||
SDL_Texture* image;
|
SDL_Texture* image;
|
||||||
|
|
||||||
GlyphInfo* glyph_page[FONT_N_PAGES];
|
GlyphInfo* glyph_page[FONT_N_PAGES];
|
||||||
|
|
||||||
|
char fallback_key[64];
|
||||||
|
uint8_t fallback_idx;
|
||||||
|
bool fallback_idx_valid;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FontContainer
|
struct FontContainer
|
||||||
|
@ -149,15 +161,35 @@ static bool glyph_is_valid(const GlyphInfo* glyph)
|
||||||
return glyph->flags & GLYPH_EXISTS;
|
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.
|
/* 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. */
|
* As a last resort, may return NULL. */
|
||||||
|
*f_glyph = f;
|
||||||
|
|
||||||
GlyphInfo* glyph = get_glyphinfo(f, codepoint);
|
GlyphInfo* glyph = get_glyphinfo(f, codepoint);
|
||||||
if (glyph != NULL && glyph_is_valid(glyph))
|
if (glyph != NULL && glyph_is_valid(glyph))
|
||||||
{
|
{
|
||||||
return 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);
|
glyph = get_glyphinfo(f, 0xFFFD);
|
||||||
if (glyph != NULL && glyph_is_valid(glyph))
|
if (glyph != NULL && glyph_is_valid(glyph))
|
||||||
{
|
{
|
||||||
|
@ -172,6 +204,26 @@ static GlyphInfo* find_glyphinfo(const Font* f, const uint32_t codepoint)
|
||||||
return NULL;
|
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)
|
int get_advance(const Font* f, const uint32_t codepoint)
|
||||||
{
|
{
|
||||||
// Get the width of a single character in a font
|
// 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;
|
return 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
GlyphInfo* glyph = find_glyphinfo(f, codepoint);
|
const Font* f_glyph;
|
||||||
if (glyph == NULL)
|
GlyphInfo* glyph = find_glyphinfo(f, codepoint, &f_glyph);
|
||||||
{
|
return get_advance_ff(f, f_glyph, glyph);
|
||||||
return f->glyph_w;
|
|
||||||
}
|
|
||||||
|
|
||||||
return glyph->advance;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool decode_xml_range(tinyxml2::XMLElement* elem, unsigned* start, unsigned* end)
|
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->name, name, sizeof(f->name));
|
||||||
SDL_strlcpy(f->display_name, name, sizeof(f->display_name));
|
SDL_strlcpy(f->display_name, name, sizeof(f->display_name));
|
||||||
|
|
||||||
|
f->type = FontType_FONT;
|
||||||
|
|
||||||
f->glyph_w = 8;
|
f->glyph_w = 8;
|
||||||
f->glyph_h = 8;
|
f->glyph_h = 8;
|
||||||
|
|
||||||
|
f->fallback_key[0] = '\0';
|
||||||
|
f->fallback_idx_valid = false;
|
||||||
|
|
||||||
if (container->map_name_idx == NULL)
|
if (container->map_name_idx == NULL)
|
||||||
{
|
{
|
||||||
container->map_name_idx = hashmap_create();
|
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));
|
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)
|
if ((pElem = hDoc.FirstChildElement("width").ToElement()) != NULL)
|
||||||
{
|
{
|
||||||
f->glyph_w = help.Int(pElem->GetText());
|
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)
|
// If 1, we don't need to whiten the entire font (like in old versions)
|
||||||
white_teeth = help.Int(pElem->GetText());
|
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);
|
f->image = LoadImage(name_png, white_teeth ? TEX_COLOR : TEX_WHITE);
|
||||||
|
@ -493,6 +557,20 @@ void set_level_font_new(void)
|
||||||
#endif
|
#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)
|
static void load_font_filename(bool is_custom, const char* filename)
|
||||||
{
|
{
|
||||||
// Load font.png, and everything that matches *.fontmeta (but not font.fontmeta)
|
// Load font.png, and everything that matches *.fontmeta (but not font.fontmeta)
|
||||||
|
@ -533,6 +611,8 @@ void load_main(void)
|
||||||
FILESYSTEM_freeEnumerate(&handle);
|
FILESYSTEM_freeEnumerate(&handle);
|
||||||
font_idx_level = font_idx_8x8;
|
font_idx_level = font_idx_8x8;
|
||||||
|
|
||||||
|
set_fallbacks(&fonts_main);
|
||||||
|
|
||||||
// Initialize the font menu options, 8x8 font first
|
// Initialize the font menu options, 8x8 font first
|
||||||
font_idx_options[0] = font_idx_8x8;
|
font_idx_options[0] = font_idx_8x8;
|
||||||
font_idx_options_n = 1;
|
font_idx_options_n = 1;
|
||||||
|
@ -543,6 +623,10 @@ void load_main(void)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (fonts_main.fonts[i].type != FontType_FONT)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
font_idx_options[font_idx_options_n++] = i;
|
font_idx_options[font_idx_options_n++] = i;
|
||||||
if (font_idx_options_n >= sizeof(font_idx_options))
|
if (font_idx_options_n >= sizeof(font_idx_options))
|
||||||
{
|
{
|
||||||
|
@ -563,6 +647,8 @@ void load_custom(const char* name)
|
||||||
}
|
}
|
||||||
FILESYSTEM_freeEnumerate(&handle);
|
FILESYSTEM_freeEnumerate(&handle);
|
||||||
|
|
||||||
|
set_fallbacks(&fonts_custom);
|
||||||
|
|
||||||
set_level_font(name);
|
set_level_font(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -926,8 +1012,8 @@ std::string string_unwordwrap(const std::string& s)
|
||||||
static int print_char(
|
static int print_char(
|
||||||
const Font* f,
|
const Font* f,
|
||||||
const uint32_t codepoint,
|
const uint32_t codepoint,
|
||||||
const int x,
|
int x,
|
||||||
const int y,
|
int y,
|
||||||
const int scale,
|
const int scale,
|
||||||
uint8_t r,
|
uint8_t r,
|
||||||
uint8_t g,
|
uint8_t g,
|
||||||
|
@ -937,7 +1023,8 @@ static int print_char(
|
||||||
{
|
{
|
||||||
/* Draws the glyph for a codepoint at x,y.
|
/* Draws the glyph for a codepoint at x,y.
|
||||||
* Returns the amount of pixels to advance the cursor. */
|
* 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)
|
if (glyph == NULL)
|
||||||
{
|
{
|
||||||
return f->glyph_w * scale;
|
return f->glyph_w * scale;
|
||||||
|
@ -955,9 +1042,26 @@ static int print_char(
|
||||||
b *= bri_factor;
|
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)
|
const char* get_main_font_name(uint8_t idx)
|
||||||
|
|
Loading…
Reference in a new issue