From 0d84543e4a88b74aed0dec6a9ceab80b8e44c131 Mon Sep 17 00:00:00 2001 From: pommicket Date: Wed, 19 Jul 2023 15:16:51 -0400 Subject: kerning --- README.md | 2 +- buffer.c | 2 ++ main.c | 2 -- node.c | 1 + text.c | 20 ++++++++++++++++---- text.h | 8 ++++++++ ui.c | 1 + 7 files changed, 29 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 13f454e..bcb85b8 100644 --- a/README.md +++ b/README.md @@ -318,7 +318,7 @@ Then, open windows\_installer\\ted\\ted.sln, and build. 2.3.2 Misc bugfixes 2023 Jun 17 2.3.3 JS highlighting improvments, fix TODO highlighting for single-line comments 2023 Jul 6 2.3.4 Unicode bugfix, `:copy-path` 2023 Jul 14 -2.4 Font overhaul — allow multiple fonts, and variable-width fonts. 2023 Jul 18 +2.4 Font overhaul — allow multiple fonts, and variable-width fonts. 2023 Jul 19 ## License diff --git a/buffer.c b/buffer.c index 0dd397d..b98717d 100644 --- a/buffer.c +++ b/buffer.c @@ -3021,6 +3021,7 @@ void buffer_render(TextBuffer *buffer, Rect r) { rgba_u32_to_floats(colors[line == cursor_line ? COLOR_CURSOR_LINE_NUMBER : COLOR_LINE_NUMBERS], text_state.color); text_state.x = x; text_state.y = y; + text_state_break_kerning(&text_state); text_utf8_with_state(font, &text_state, str); y += char_height; if (y > y2) break; @@ -3196,6 +3197,7 @@ void buffer_render(TextBuffer *buffer, Rect r) { } // next line + text_state_break_kerning(&text_state); text_state.x = render_start_x; if (text_state.y > text_state.max_y) { buffer->last_line_on_screen = line_idx; diff --git a/main.c b/main.c index 2985f3c..0f9b0e7 100644 --- a/main.c +++ b/main.c @@ -1,6 +1,4 @@ /* -TODO: -- kerning FUTURE FEATURES: - autodetect indentation (tabs vs spaces) - font setting & support for multiple fonts to cover more characters diff --git a/node.c b/node.c index 29f08b9..43e83fc 100644 --- a/node.c +++ b/node.c @@ -324,6 +324,7 @@ void node_frame(Ted *ted, Node *node, Rect r) { rgba_u32_to_floats(colors[COLOR_TEXT], text_state.color); text_state.x = tab_rect.pos.x; text_state.y = tab_rect.pos.y; + text_state_break_kerning(&text_state); text_utf8_with_state(font, &text_state, tab_title); if (i == node->active_tab) { diff --git a/text.c b/text.c index 29d1831..70941de 100644 --- a/text.c +++ b/text.c @@ -66,7 +66,8 @@ const TextRenderState text_render_state_default = { .min_x = -FLT_MAX, .max_x = +FLT_MAX, .min_y = -FLT_MAX, .max_y = +FLT_MAX, .color = {1, 0, 1, 1}, - .x_largest = -FLT_MAX, .y_largest = -FLT_MAX + .x_largest = -FLT_MAX, .y_largest = -FLT_MAX, + .prev_char = 0, }; static char text_err[200]; @@ -388,12 +389,18 @@ top: goto ret; } + if (!font->force_monospace && state->prev_char && state->prev_char != '\n') { + // kerning + state->x += (float)stbtt_GetCodepointKernAdvance(&font->stb_info, (int)state->prev_char, (int)c) + * stbtt_ScaleForPixelHeight(&font->stb_info, font->char_height); + } + { float x, y; x = (float)(state->x - floor(state->x)); y = (float)(state->y - floor(state->y)); y += char_height * 0.75f; - stbtt_GetPackedQuad(&info.data, FONT_TEXTURE_WIDTH, FONT_TEXTURE_HEIGHT, 0, &x, &y, &q, 1); + stbtt_GetPackedQuad(&info.data, FONT_TEXTURE_WIDTH, FONT_TEXTURE_HEIGHT, 0, &x, &y, &q, 0); y -= char_height * 0.75f; q.x0 += (float)floor(state->x); @@ -411,8 +418,8 @@ top: float s0 = q.s0, t0 = q.t0; float s1 = q.s1, t1 = q.t1; - float x0 = q.x0, y0 = q.y0; - float x1 = q.x1, y1 = q.y1; + float x0 = roundf(q.x0), y0 = roundf(q.y0); + float x1 = roundf(q.x1), y1 = roundf(q.y1); const float min_x = state->min_x, max_x = state->max_x; const float min_y = state->min_y, max_y = state->max_y; @@ -460,6 +467,7 @@ top: state->x_largest = state->x; if (state->y > state->y_largest) state->y_largest = state->y; + state->prev_char = c; } void text_utf8_with_state(Font *font, TextRenderState *state, const char *str) { @@ -553,6 +561,10 @@ static void font_free_textures(Font *font) { arr_clear(font->textures); } +void text_state_break_kerning(TextRenderState *state) { + state->prev_char = 0; +} + void text_font_change_size(Font *font, float new_size) { font_free_textures(font); font_free_char_info(font); diff --git a/text.h b/text.h index 8d317e8..ff07e79 100644 --- a/text.h +++ b/text.h @@ -41,6 +41,9 @@ typedef struct { /// largest y achieved (for computing size) double y_largest; + /// previous character rendered, or 0 if this is the first + char32_t prev_char; + /// used for forwards-compatibility char _reserved[64]; } TextRenderState; @@ -98,6 +101,11 @@ void text_utf8_anchored(Font *font, const char *text, double x, double y, u32 co 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); +/// Used to indicate that the next character drawn should not +/// kern with the previous character. +/// +/// Use this when you go to the next line or something. +void text_state_break_kerning(TextRenderState *state); /// Free memory used by font. /// /// Does NOT free the font's fallback. diff --git a/ui.c b/ui.c index 0c398b3..abcfad7 100644 --- a/ui.c +++ b/ui.c @@ -160,6 +160,7 @@ void selector_render(Ted *ted, Selector *s) { // draw name rgba_u32_to_floats(entry->color, text_state.color); + text_state_break_kerning(&text_state); text_utf8_with_state(font, &text_state, entry->name); if (entry->detail) { -- cgit v1.2.3