From 3426bc5d737f01517bf73e21d4b5eace6597d7be Mon Sep 17 00:00:00 2001 From: Francesco Abbate Date: Sat, 6 Mar 2021 18:12:02 +0100 Subject: [PATCH] Introduce subpixel text positioning within rencache In order to fix the issue with cursor positioning a subpixel-aware draw text operation within rencache was required. With this modification the cursor positioning problem is completely resolved. A new function renderer.draw_text_subpixel is introduced to perform consecutive, inline, text drawings with subpixel accuracy. --- data/core/docview.lua | 5 +++-- src/api/renderer.c | 29 +++++++++++++++++++++-------- src/rencache.c | 23 +++++++++++++++-------- src/rencache.h | 2 +- src/renderer.c | 15 ++++++++++----- src/renderer.h | 1 + 6 files changed, 51 insertions(+), 24 deletions(-) diff --git a/data/core/docview.lua b/data/core/docview.lua index 7e6dcf30..da34eeef 100644 --- a/data/core/docview.lua +++ b/data/core/docview.lua @@ -309,11 +309,12 @@ end function DocView:draw_line_text(idx, x, y) - local tx, ty = x, y + self:get_line_text_y_offset() local font = self:get_font() + local subpixel_scale = font:subpixel_scale() + local tx, ty = subpixel_scale * x, y + self:get_line_text_y_offset() for _, type, text in self.doc.highlighter:each_token(idx) do local color = style.syntax[type] - tx = renderer.draw_text(font, text, tx, ty, color) + tx = renderer.draw_text_subpixel(font, text, tx, ty, color) end end diff --git a/src/api/renderer.c b/src/api/renderer.c index 6b9a98e9..bba86238 100644 --- a/src/api/renderer.c +++ b/src/api/renderer.c @@ -78,20 +78,33 @@ static int f_draw_text(lua_State *L) { int x = luaL_checknumber(L, 3); int y = luaL_checknumber(L, 4); RenColor color = checkcolor(L, 5, 255); - x = rencache_draw_text(*font, text, x, y, color); + x = rencache_draw_text(*font, text, x, y, color, false); lua_pushnumber(L, x); return 1; } +static int f_draw_text_subpixel(lua_State *L) { + RenFont **font = luaL_checkudata(L, 1, API_TYPE_FONT); + const char *text = luaL_checkstring(L, 2); + int x_subpixel = luaL_checknumber(L, 3); + int y = luaL_checknumber(L, 4); + RenColor color = checkcolor(L, 5, 255); + x_subpixel = rencache_draw_text(*font, text, x_subpixel, y, color, true); + lua_pushnumber(L, x_subpixel); + return 1; +} + + static const luaL_Reg lib[] = { - { "show_debug", f_show_debug }, - { "get_size", f_get_size }, - { "begin_frame", f_begin_frame }, - { "end_frame", f_end_frame }, - { "set_clip_rect", f_set_clip_rect }, - { "draw_rect", f_draw_rect }, - { "draw_text", f_draw_text }, + { "show_debug", f_show_debug }, + { "get_size", f_get_size }, + { "begin_frame", f_begin_frame }, + { "end_frame", f_end_frame }, + { "set_clip_rect", f_set_clip_rect }, + { "draw_rect", f_draw_rect }, + { "draw_text", f_draw_text }, + { "draw_text_subpixel", f_draw_text_subpixel }, { NULL, NULL } }; diff --git a/src/rencache.c b/src/rencache.c index 2c45baf4..8e18e3a6 100644 --- a/src/rencache.c +++ b/src/rencache.c @@ -11,13 +11,15 @@ #define CELL_SIZE 96 #define COMMAND_BUF_SIZE (1024 * 512) -enum { FREE_FONT, SET_CLIP, DRAW_TEXT, DRAW_RECT }; +enum { FREE_FONT, SET_CLIP, DRAW_TEXT, DRAW_RECT, DRAW_TEXT_SUBPIXEL }; typedef struct { int type, size; RenRect rect; RenColor color; RenFont *font; + short int subpixel_scale; + short int x_subpixel_offset; int tab_width; char text[0]; } Command; @@ -128,29 +130,30 @@ void rencache_draw_rect(RenRect rect, RenColor color) { } } - -int rencache_draw_text(RenFont *font, const char *text, int x, int y, RenColor color) { +int rencache_draw_text(RenFont *font, const char *text, int x, int y, RenColor color, bool draw_subpixel) { int subpixel_scale; + int w_subpixel = ren_get_font_width(font, text, &subpixel_scale); RenRect rect; - rect.x = x; + rect.x = (draw_subpixel ? ren_font_subpixel_round(x, subpixel_scale, -1) : x); rect.y = y; - int w = ren_get_font_width(font, text, &subpixel_scale); - rect.width = ren_font_subpixel_round(w, subpixel_scale, 0); + rect.width = ren_font_subpixel_round(w_subpixel, subpixel_scale, 0); rect.height = ren_get_font_height(font); if (rects_overlap(screen_rect, rect)) { int sz = strlen(text) + 1; - Command *cmd = push_command(DRAW_TEXT, sizeof(Command) + sz); + Command *cmd = push_command(draw_subpixel ? DRAW_TEXT_SUBPIXEL : DRAW_TEXT, sizeof(Command) + sz); if (cmd) { memcpy(cmd->text, text, sz); cmd->color = color; cmd->font = font; cmd->rect = rect; + cmd->subpixel_scale = (draw_subpixel ? subpixel_scale : 1); + cmd->x_subpixel_offset = x - subpixel_scale * rect.x; cmd->tab_width = ren_get_font_tab_width(font); } } - return x + rect.width; + return x + (draw_subpixel ? w_subpixel : rect.width); } @@ -261,6 +264,10 @@ void rencache_end_frame(void) { ren_set_font_tab_width(cmd->font, cmd->tab_width); ren_draw_text(cmd->font, cmd->text, cmd->rect.x, cmd->rect.y, cmd->color); break; + case DRAW_TEXT_SUBPIXEL: + ren_set_font_tab_width(cmd->font, cmd->tab_width); + ren_draw_text_subpixel(cmd->font, cmd->text, cmd->subpixel_scale * cmd->rect.x + cmd->x_subpixel_offset, cmd->rect.y, cmd->color); + break; } } diff --git a/src/rencache.h b/src/rencache.h index c5fa80f4..eb988229 100644 --- a/src/rencache.h +++ b/src/rencache.h @@ -8,7 +8,7 @@ void rencache_show_debug(bool enable); void rencache_free_font(RenFont *font); void rencache_set_clip_rect(RenRect rect); void rencache_draw_rect(RenRect rect, RenColor color); -int rencache_draw_text(RenFont *font, const char *text, int x, int y, RenColor color); +int rencache_draw_text(RenFont *font, const char *text, int x, int y, RenColor color, bool draw_subpixel); void rencache_invalidate(void); void rencache_begin_frame(void); void rencache_end_frame(void); diff --git a/src/renderer.c b/src/renderer.c index 262f5814..b0be5bdd 100644 --- a/src/renderer.c +++ b/src/renderer.c @@ -284,26 +284,31 @@ void ren_draw_image(RenImage *image, RenRect *sub, int x, int y, RenColor color) } } -void ren_draw_text(RenFont *font, const char *text, int x, int y, RenColor color) { + +void ren_draw_text_subpixel(RenFont *font, const char *text, int x_subpixel, int y, RenColor color) { const char *p = text; unsigned codepoint; SDL_Surface *surf = SDL_GetWindowSurface(window); FR_Color color_fr = { .r = color.r, .g = color.g, .b = color.b }; - const int subpixel_scale = FR_Subpixel_Scale(font->renderer); - int x_mult = subpixel_scale * x; while (*p) { p = utf8_to_codepoint(p, &codepoint); GlyphSet *set = get_glyphset(font, codepoint); FR_Bitmap_Glyph_Metrics *g = &set->glyphs[codepoint & 0xff]; if (color.a != 0) { FR_Blend_Glyph(font->renderer, &clip, - x_mult, y, (uint8_t *) surf->pixels, surf->w, set->image, g, color_fr); + x_subpixel, y, (uint8_t *) surf->pixels, surf->w, set->image, g, color_fr); } - x_mult += g->xadvance; + x_subpixel += g->xadvance; } } +void ren_draw_text(RenFont *font, const char *text, int x, int y, RenColor color) { + const int subpixel_scale = FR_Subpixel_Scale(font->renderer); + ren_draw_text_subpixel(font, text, subpixel_scale * x, y, color); +} + + int ren_font_subpixel_round(int width, int subpixel_scale, int orientation) { int w_mult; if (orientation < 0) { diff --git a/src/renderer.h b/src/renderer.h index 39551de8..50082073 100644 --- a/src/renderer.h +++ b/src/renderer.h @@ -42,5 +42,6 @@ int ren_font_subpixel_round(int width, int subpixel_scale, int orientation); void ren_draw_rect(RenRect rect, RenColor color); void ren_draw_image(RenImage *image, RenRect *sub, int x, int y, RenColor color); void ren_draw_text(RenFont *font, const char *text, int x, int y, RenColor color); +void ren_draw_text_subpixel(RenFont *font, const char *text, int x_subpixel, int y, RenColor color); #endif