summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpommicket <pommicket@gmail.com>2023-07-18 17:20:11 -0400
committerpommicket <pommicket@gmail.com>2023-07-19 19:02:27 -0400
commit6ba52193b95b78c62ea56f54d94a55d0e31504d1 (patch)
tree9aaff5249db58725d2e39559a902343171074f54
parentfb1c8d184156c9af5432a28831b88776f252da91 (diff)
font fallbacks
-rw-r--r--buffer.c10
-rw-r--r--session.c8
-rw-r--r--ted.c12
-rw-r--r--text.c123
-rw-r--r--text.h14
5 files changed, 113 insertions, 54 deletions
diff --git a/buffer.c b/buffer.c
index 3ca57f9..5cf1820 100644
--- a/buffer.c
+++ b/buffer.c
@@ -956,8 +956,14 @@ bool buffer_pixels_to_pos(TextBuffer *buffer, vec2 pixel_coords, BufferPos *pos)
x -= buffer->x1;
y -= buffer->y1;
- x = clampd(x, 0, buffer->x2 - buffer->x1);
- y = clampd(y, 0, buffer->y2 - buffer->y1);
+ double buffer_width = buffer->x2 - buffer->x1;
+ double buffer_height = buffer->y2 - buffer->y1;
+
+ if (x < 0 || y < 0 || x >= buffer_width || y >= buffer_height)
+ ret = false;
+
+ x = clampd(x, 0, buffer_width);
+ y = clampd(y, 0, buffer_height);
double xoff = x + buffer->scroll_x * text_font_char_width(font, ' ');
diff --git a/session.c b/session.c
index 8860cd1..af55eba 100644
--- a/session.c
+++ b/session.c
@@ -116,7 +116,7 @@ static char read_char(FILE *fp) {
}
static bool read_bool(FILE *fp) {
- return (bool)getc(fp);
+ return getc(fp) != 0;
}
static void write_cstr(FILE *fp, const char *cstr) {
@@ -244,6 +244,12 @@ static void session_read_buffer(Ted *ted, FILE *fp) {
buffer->selection = read_bool(fp);
if (buffer->selection)
buffer->selection_pos = buffer_pos_read(buffer, fp);
+ buffer_pos_validate(buffer, &buffer->cursor_pos);
+ buffer_pos_validate(buffer, &buffer->selection_pos);
+ if (buffer->selection && buffer_pos_eq(buffer->cursor_pos, buffer->selection_pos)) {
+ // this could happen if the file was changed on disk
+ buffer->selection = false;
+ }
}
}
diff --git a/ted.c b/ted.c
index 19d360c..1eec19c 100644
--- a/ted.c
+++ b/ted.c
@@ -313,7 +313,8 @@ static Font *ted_load_single_font(Ted *ted, const char *filename) {
static Font *ted_load_multifont(Ted *ted, const char *filenames) {
char filename[TED_PATH_MAX];
- Font *font = NULL;
+ Font *first_font = NULL;
+ Font *curr_font = NULL;
while (*filenames) {
while (*filenames == ',') ++filenames;
@@ -321,12 +322,17 @@ static Font *ted_load_multifont(Ted *ted, const char *filenames) {
strn_cpy(filename, sizeof filename, filenames, len);
str_trim(filename);
if (*filename) {
- font = ted_load_single_font(ted, filename);
+ Font *font = ted_load_single_font(ted, filename);
+ if (!first_font)
+ first_font = font;
+ if (curr_font)
+ text_font_set_fallback(curr_font, font);
+ curr_font = font;
}
filenames += len;
}
- return font;
+ return first_font;
}
void ted_load_fonts(Ted *ted) {
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) {
diff --git a/text.h b/text.h
index 79f99ad..8d317e8 100644
--- a/text.h
+++ b/text.h
@@ -68,7 +68,15 @@ 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.
+/// Set a fallback font to use if a character is not defined by `font`.
+///
+/// You can pass `NULL` to clear any previous fallback.
+/// Do not create a loop of fallback fonts.
+void text_font_set_fallback(Font *font, Font *fallback);
+/// Change size of font.
+///
+/// Avoid calling this function too often, since all font textures are trashed.
+/// Also changes size of fallback fonts.
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);
@@ -91,8 +99,12 @@ void text_char_with_state(Font *font, TextRenderState *state, char32_t c);
/// Draw some UTF-8 text with a \ref TextRenderState.
void text_utf8_with_state(Font *font, TextRenderState *state, const char *str);
/// Free memory used by font.
+///
+/// Does NOT free the font's fallback.
void text_font_free(Font *font);
/// Render all text drawn with \ref text_utf8, etc.
+///
+/// This will render the fallback font and its fallback, and so on.
void text_render(Font *font);
/// The "default" text rendering state - everything you need to just render text normally.
/// This lets you do stuff like: