summaryrefslogtreecommitdiff
path: root/text.c
diff options
context:
space:
mode:
authorpommicket <pommicket@gmail.com>2023-07-18 17:20:11 -0400
committerpommicket <pommicket@gmail.com>2023-07-18 17:20:11 -0400
commiteb1c75720eaa8686c160e3ffa2559a03b5187e5a (patch)
tree47d1b9236ae45ba2392a64994c8fa8181c414630 /text.c
parent9bee6a4bbd74a6ef9c317cfb54ffa7136644de4c (diff)
font fallbacksvariable-width
Diffstat (limited to 'text.c')
-rw-r--r--text.c123
1 files changed, 76 insertions, 47 deletions
diff --git a/text.c b/text.c
index 9d161c4..29d1831 100644
--- a/text.c
+++ b/text.c
@@ -26,9 +26,9 @@ typedef struct {
typedef struct {
char32_t c;
+ bool defined;
u32 texture;
stbtt_packedchar data;
- bool defined; // whether this character is actually defined in the font file
} CharInfo;
// characters are split into this many "buckets" according to
@@ -56,6 +56,7 @@ struct Font {
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)
u8 *ttf_data;
+ Font *fallback;
};
const TextRenderState text_render_state_default = {
@@ -90,6 +91,10 @@ static void text_set_err(const char *fmt, ...) {
}
}
+void text_font_set_fallback(Font *font, Font *fallback) {
+ font->fallback = fallback;
+}
+
static GLuint text_program;
static GLuint text_vbo, text_vao;
static GLuint text_v_pos, text_v_color, text_v_tex_coord;
@@ -137,7 +142,6 @@ static u32 char_bucket_index(char32_t c) {
static FontTexture *font_new_texture(Font *font) {
- debug_println("Create new texture for font %p", (void *)font);
PROFILE_TIME(start);
unsigned char *pixels = calloc(FONT_TEXTURE_WIDTH, FONT_TEXTURE_HEIGHT);
if (!pixels) {
@@ -183,7 +187,9 @@ static void font_texture_free(FontTexture *texture) {
memset(texture, 0, sizeof *texture);
}
-// on success, *info is filled out
+// on success, *info is filled out.
+// success includes cases where c is not defined by the font so a substitute character is used.
+// failure only indicates something very bad.
static Status text_load_char(Font *font, char32_t c, CharInfo *info) {
u32 bucket = char_bucket_index(c);
arr_foreach_ptr(font->char_info[bucket], CharInfo, i) {
@@ -195,51 +201,57 @@ static Status text_load_char(Font *font, char32_t c, CharInfo *info) {
}
glGetError(); // clear error
+ memset(info, 0, sizeof *info);
if (c != UNICODE_BOX_CHARACTER && stbtt_FindGlyphIndex(&font->stb_info, (int)c) == 0) {
- // this code point is not defined by the font — use the box character
- printf("Using box character for U+%04X\n",c);
+ // this code point is not defined by the font
+
+ // use the box character
if (!text_load_char(font, UNICODE_BOX_CHARACTER, info))
return false;
info->c = c;
info->defined = false;
- } else {
- info->defined = true;
- if (!font->textures) {
- if (!font_new_texture(font))
- return false;
- }
-
- int success = 0;
- FontTexture *texture = arr_lastp(font->textures);
- for (int i = 0; i < 2; i++) {
- info->c = c;
- info->texture = arr_len(font->textures) - 1;
- 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(&texture->pack_context);
- font_texture_update_if_needed(texture);
- free(texture->pixels);
- texture->pixels = NULL;
- texture = font_new_texture(font);
- if (!texture)
- return false;
- }
-
- if (!success) {
- // a brand new texture couldn't fit the character.
- // something has gone horribly wrong.
- font_texture_free(texture);
- arr_remove_last(font->textures);
- text_set_err("Error rasterizing character %lc", (wchar_t)c);
+ arr_add(font->char_info[bucket], *info);
+ return true;
+ }
+
+ info->defined = true;
+
+ if (!font->textures) {
+ if (!font_new_texture(font))
return false;
- }
-
- texture->needs_update = true;
}
+ int success = 0;
+ FontTexture *texture = arr_lastp(font->textures);
+ for (int i = 0; i < 2; i++) {
+ info->c = c;
+ info->texture = arr_len(font->textures) - 1;
+ 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(&texture->pack_context);
+ font_texture_update_if_needed(texture);
+ free(texture->pixels);
+ texture->pixels = NULL;
+ debug_println("Create new texture for font %p (triggered by U+%04X)", (void *)font, c);
+ texture = font_new_texture(font);
+ if (!texture)
+ return false;
+ }
+
+ if (!success) {
+ // a brand new texture couldn't fit the character.
+ // something has gone horribly wrong.
+ font_texture_free(texture);
+ arr_remove_last(font->textures);
+ text_set_err("Error rasterizing character %lc", (wchar_t)c);
+ return false;
+ }
+
+ texture->needs_update = true;
+
arr_add(font->char_info[bucket], *info);
return true;
}
@@ -306,10 +318,13 @@ float text_font_char_height(Font *font) {
float text_font_char_width(Font *font, char32_t c) {
CharInfo info = {0};
- if (text_load_char(font, c, &info))
+ if (text_load_char(font, c, &info)) {
+ if (!info.defined && font->fallback)
+ return text_font_char_width(font->fallback, c);
return info.data.xadvance;
- else
+ } else {
return 0;
+ }
}
void text_render(Font *font) {
@@ -336,6 +351,10 @@ void text_render(Font *font) {
glDrawArrays(GL_TRIANGLES, 0, (GLsizei)(3 * ntriangles));
arr_clear(texture->triangles);
}
+
+ if (font->fallback) {
+ text_render(font->fallback);
+ }
}
void text_char_with_state(Font *font, TextRenderState *state, char32_t c) {
@@ -352,6 +371,13 @@ top:
if (!text_load_char(font, c, &info))
return;
+
+ if (!info.defined && font->fallback) {
+ text_char_with_state(font->fallback, state, c);
+ return;
+ }
+
+
const float char_height = font->char_height;
stbtt_aligned_quad q = {0};
@@ -513,12 +539,6 @@ 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);
}
-static void font_free_textures(Font *font) {
- arr_foreach_ptr(font->textures, FontTexture, texture) {
- font_texture_free(texture);
- }
- arr_clear(font->textures);
-}
static void font_free_char_info(Font *font) {
for (u32 i = 0; i < CHAR_BUCKET_COUNT; i++) {
@@ -526,10 +546,19 @@ static void font_free_char_info(Font *font) {
}
}
+static void font_free_textures(Font *font) {
+ arr_foreach_ptr(font->textures, FontTexture, texture) {
+ font_texture_free(texture);
+ }
+ arr_clear(font->textures);
+}
+
void text_font_change_size(Font *font, float new_size) {
font_free_textures(font);
font_free_char_info(font);
font->char_height = new_size;
+ if (font->fallback)
+ text_font_change_size(font->fallback, new_size);
}
void text_font_free(Font *font) {