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.
This commit is contained in:
Andrei Alexeyev 2019-05-14 12:00:51 +03:00
parent bfb3941cf3
commit 2fec4bfe62
No known key found for this signature in database
GPG key ID: 363707CD4C7FE8A4
2 changed files with 55 additions and 25 deletions

View file

@ -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) {

View file

@ -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;