From 2fec4bfe62ca44c261f65fa3d51fcf8147cf4f71 Mon Sep 17 00:00:00 2001 From: Andrei Alexeyev Date: Tue, 14 May 2019 12:00:51 +0300 Subject: [PATCH] text: fix bboxes, overlay TCs; add "overlay projection" parameter The "overlay projection" allows specifying a rectangle, in the same coordinate system as the text position, that will be used instead of the text's BBox to compute overlay texture coordinates. This is useful to apply an overlay effect to variably-sized text in a consistent fashion, without having to rely on RTT. --- src/resource/font.c | 79 +++++++++++++++++++++++++++++++-------------- src/resource/font.h | 1 + 2 files changed, 55 insertions(+), 25 deletions(-) diff --git a/src/resource/font.c b/src/resource/font.c index 27d45dbe..e18bc22b 100644 --- a/src/resource/font.c +++ b/src/resource/font.c @@ -839,23 +839,25 @@ void text_ucs4_bbox(Font *font, const uint32_t *text, uint maxlines, BBox *bbox) x += apply_kerning(font, prev_glyph_idx, glyph); int g_x0 = x + glyph->metrics.bearing_x; - int g_x1 = g_x0 + glyph->metrics.width; + int g_x1 = g_x0 + imax(glyph->metrics.width, glyph->sprite.w); - bbox->x.max = max(bbox->x.max, g_x0); - bbox->x.max = max(bbox->x.max, g_x1); - bbox->x.min = min(bbox->x.min, g_x0); - bbox->x.min = min(bbox->x.min, g_x1); + bbox->x.max = imax(bbox->x.max, g_x0); + bbox->x.max = imax(bbox->x.max, g_x1); + bbox->x.min = imin(bbox->x.min, g_x0); + bbox->x.min = imin(bbox->x.min, g_x1); int g_y0 = y - glyph->metrics.bearing_y; - int g_y1 = g_y0 + glyph->metrics.height; + int g_y1 = g_y0 + imax(glyph->metrics.height, glyph->sprite.h); - bbox->y.max = max(bbox->y.max, g_y0); - bbox->y.max = max(bbox->y.max, g_y1); - bbox->y.min = min(bbox->y.min, g_y0); - bbox->y.min = min(bbox->y.min, g_y1); + bbox->y.max = imax(bbox->y.max, g_y0); + bbox->y.max = imax(bbox->y.max, g_y1); + bbox->y.min = imin(bbox->y.min, g_y0); + bbox->y.min = imin(bbox->y.min, g_y1); prev_glyph_idx = glyph->ft_index; + x += glyph->metrics.advance; + bbox->x.max = imax(bbox->x.max, x); } } @@ -961,9 +963,16 @@ static double _text_ucs4_draw(Font *font, const uint32_t *ucs4text, const TextPa BBox bbox; double x = params->pos.x; double y = params->pos.y; - double iscale = 1 / font->metrics.scale; + double scale = font->metrics.scale; + double iscale = 1 / scale; + + struct { + struct { double min, max; } x, y; + double w, h; + } overlay; text_ucs4_bbox(font, ucs4text, 0, &bbox); + sp.shader_ptr = params->shader_ptr; if(sp.shader_ptr == NULL) { @@ -991,28 +1000,48 @@ static double _text_ucs4_draw(Font *font, const uint32_t *ucs4text, const TextPa r_mat_push(); r_mat_translate(x, y, 0); r_mat_scale(iscale, iscale, 1); + + double orig_x = x; + double orig_y = y; x = y = 0; - double x_orig = x; - adjust_xpos(font, ucs4text, params->align, x_orig, &x); + adjust_xpos(font, ucs4text, params->align, 0, &x); - // bbox.y.max = imax(bbox.y.max, font->metrics.ascent); - // bbox.y.min = imin(bbox.y.min, font->metrics.descent); + if(params->overlay_projection) { + FloatRect *op = params->overlay_projection; + overlay.x.min = (op->x - orig_x) * scale; + overlay.x.max = overlay.x.min + op->w * scale; + overlay.y.min = (op->y - orig_y) * scale; + overlay.y.max = overlay.y.min + op->h * scale; + } else { + overlay.x.min = bbox.x.min + x; + overlay.x.max = bbox.x.max + x; + overlay.y.min = bbox.y.min - font->metrics.descent; + overlay.y.max = bbox.y.max - font->metrics.descent; + } - double bbox_w = bbox.x.max - bbox.x.min; - double bbox_h = bbox.y.max - bbox.y.min; + overlay.w = overlay.x.max - overlay.x.min; + overlay.h = overlay.y.max - overlay.y.min; #ifdef TEXT_DRAW_BBOX - // TODO: align this correctly in the multi-line case + double bbox_w = bbox.x.max - bbox.x.min; + double bbox_h = bbox.y.max - bbox.y.min; double bbox_x_mid = x + bbox.x.min + bbox_w * 0.5; - double bbox_y_mid = y + bbox.y.min - font->metrics.descent + bbox_h * 0.5; + double bbox_y_mid = y + bbox.y.min + bbox_h * 0.5 - font->metrics.descent; + + #if 0 /* enable to visualize the overlay projection instead */ + bbox_w = overlay.w; + bbox_h = overlay.h; + bbox_x_mid = overlay.x.min + overlay.w * 0.5; + bbox_y_mid = overlay.y.min + overlay.h * 0.5; + #endif r_state_push(); r_shader_standard_notex(); r_mat_push(); r_mat_translate(bbox_x_mid, bbox_y_mid, 0); r_mat_scale(bbox_w, bbox_h, 0); - r_color(color_mul(RGBA(0.5, 0.5, 0.5, 0.5), r_color_current())); + r_color(color_mul(RGBA(0.5, 0.5, 0.5, 0.5), sp.color)); r_draw_quad(); r_mat_pop(); r_state_pop(); @@ -1020,8 +1049,8 @@ static double _text_ucs4_draw(Font *font, const uint32_t *ucs4text, const TextPa r_mat_mode(MM_TEXTURE); r_mat_push(); - r_mat_scale(1/bbox_w, 1/bbox_h, 1.0); - r_mat_translate(-bbox.x.min - (x - x_orig), -bbox.y.min + font->metrics.descent, 0); + r_mat_scale(1/overlay.w, 1/overlay.h, 1.0); + r_mat_translate(-overlay.x.min, overlay.y.min, 0); // FIXME: is there a better way? float texmat_offset_sign; @@ -1039,7 +1068,7 @@ static double _text_ucs4_draw(Font *font, const uint32_t *ucs4text, const TextPa uint32_t uchar = *tptr++; if(uchar == '\n') { - adjust_xpos(font, tptr, params->align, x_orig, &x); + adjust_xpos(font, tptr, params->align, 0, &x); y += font->metrics.lineskip; continue; } @@ -1067,7 +1096,7 @@ static double _text_ucs4_draw(Font *font, const uint32_t *ucs4text, const TextPa sp.scale.both = font->metrics.scale; r_mat_push(); - r_mat_translate(sp.pos.x - x_orig, sp.pos.y * texmat_offset_sign, 0); + r_mat_translate(sp.pos.x, sp.pos.y * texmat_offset_sign + overlay.h, 0); r_mat_scale(w_saved, h_saved, 1.0); r_mat_translate(-0.5, -0.5, 0); @@ -1093,7 +1122,7 @@ static double _text_ucs4_draw(Font *font, const uint32_t *ucs4text, const TextPa r_mat_pop(); r_mat_mode(mm_prev); - return x_orig + (x - x_orig) / font->metrics.scale; + return x * iscale; } static double _text_draw(Font *font, const char *text, const TextParams *params) { diff --git a/src/resource/font.h b/src/resource/font.h index bdb84f6a..5fec42c8 100644 --- a/src/resource/font.h +++ b/src/resource/font.h @@ -70,6 +70,7 @@ typedef struct TextParams { const ShaderCustomParams *shader_params; Texture *aux_textures[R_NUM_SPRITE_AUX_TEXTURES]; double max_width; + FloatRect *overlay_projection; BlendMode blend; Alignment align; } TextParams;