Optimize text drawing

This adds a lower-level sprite batching API intended to reduce overhead
of bulk sprite submissions when it is known that no pipeline state needs
to be changed in-between individual sprites.
This commit is contained in:
Andrei Alexeyev 2019-10-03 02:49:10 +03:00
parent 279114cbd5
commit b14b8493d4
No known key found for this signature in database
GPG key ID: 363707CD4C7FE8A4
6 changed files with 243 additions and 197 deletions

View file

@ -354,6 +354,32 @@ typedef union ShaderCustomParams {
Color color;
} ShaderCustomParams;
typedef struct SpriteStateParams {
Texture *primary_texture;
Texture *aux_textures[R_NUM_SPRITE_AUX_TEXTURES];
BlendMode blend;
ShaderProgram *shader;
} SpriteStateParams;
typedef struct SpriteScaleParams {
union {
float x;
float both;
};
float y;
} SpriteScaleParams;
typedef struct SpriteRotationParams {
vec3 vector;
float angle;
} SpriteRotationParams;
typedef struct SpriteFlipParams {
unsigned char x : 1;
unsigned char y : 1;
} SpriteFlipParams;
typedef struct SpriteParams {
const char *sprite;
Sprite *sprite_ptr;
@ -362,34 +388,35 @@ typedef struct SpriteParams {
ShaderProgram *shader_ptr;
Texture *aux_textures[R_NUM_SPRITE_AUX_TEXTURES];
const Color *color;
const ShaderCustomParams *shader_params;
BlendMode blend;
FloatOffset pos;
struct {
union {
float x;
float both;
};
float y;
} scale;
struct {
float angle;
vec3 vector;
} rotation;
const ShaderCustomParams *shader_params;
struct {
unsigned int x : 1;
unsigned int y : 1;
} flip;
SpriteScaleParams scale;
SpriteRotationParams rotation;
SpriteFlipParams flip;
} SpriteParams;
// Matches vertex buffer layout
typedef struct SpriteInstanceAttribs {
mat4 mv_transform;
mat4 tex_transform;
union {
FloatRect texrect;
vec4 texrect_vec4;
};
Color rgba;
FloatExtent sprite_size;
ShaderCustomParams custom;
// offsetof(end_of_fields) == size without padding.
char end_of_fields;
} SpriteInstanceAttribs;
/*
* Creates an SDL window with proper flags, and, if needed, sets up a rendering context associated with it.
* Must be called before anything else.
@ -709,6 +736,9 @@ void r_draw_quad_instanced(uint instances);
void r_draw_model_ptr(Model *model, uint instances, uint base_instance) attr_nonnull(1);
void r_draw_sprite(const SpriteParams *params) attr_nonnull(1);
void r_sprite_batch_prepare_state(const SpriteStateParams *stp);
void r_sprite_batch_add_instance(const SpriteInstanceAttribs *attribs);
void r_flush_sprites(void);
BlendMode r_blend_compose(

View file

@ -22,19 +22,7 @@
#endif
#endif
typedef struct SpriteAttribs {
mat4 transform;
mat4 tex_transform;
float rgba[4];
FloatRect texrect;
float sprite_size[2];
float custom[4];
// offset of this == size without padding.
char end_of_fields;
} SpriteAttribs;
#define SIZEOF_SPRITE_ATTRIBS (offsetof(SpriteAttribs, end_of_fields))
#define SIZEOF_SPRITE_ATTRIBS (offsetof(SpriteInstanceAttribs, end_of_fields))
static struct SpriteBatchState {
// constants (set once on init and not expected to change)
@ -75,7 +63,7 @@ void _r_sprite_batch_init(void) {
size_t sz_attr = SIZEOF_SPRITE_ATTRIBS;
#define VERTEX_OFS(attr) offsetof(GenericModelVertex, attr)
#define INSTANCE_OFS(attr) offsetof(SpriteAttribs, attr)
#define INSTANCE_OFS(attr) offsetof(SpriteInstanceAttribs, attr)
VertexAttribFormat fmt[] = {
// Per-vertex attributes (for the static models buffer, bound at 0)
@ -84,10 +72,10 @@ void _r_sprite_batch_init(void) {
{ { 2, VA_FLOAT, VA_CONVERT_FLOAT, 0 }, sz_vert, VERTEX_OFS(uv), 0 },
// Per-instance attributes (for our own sprites buffer, bound at 1)
{ { 4, VA_FLOAT, VA_CONVERT_FLOAT, 1 }, sz_attr, INSTANCE_OFS(transform[0]), 1 },
{ { 4, VA_FLOAT, VA_CONVERT_FLOAT, 1 }, sz_attr, INSTANCE_OFS(transform[1]), 1 },
{ { 4, VA_FLOAT, VA_CONVERT_FLOAT, 1 }, sz_attr, INSTANCE_OFS(transform[2]), 1 },
{ { 4, VA_FLOAT, VA_CONVERT_FLOAT, 1 }, sz_attr, INSTANCE_OFS(transform[3]), 1 },
{ { 4, VA_FLOAT, VA_CONVERT_FLOAT, 1 }, sz_attr, INSTANCE_OFS(mv_transform[0]), 1 },
{ { 4, VA_FLOAT, VA_CONVERT_FLOAT, 1 }, sz_attr, INSTANCE_OFS(mv_transform[1]), 1 },
{ { 4, VA_FLOAT, VA_CONVERT_FLOAT, 1 }, sz_attr, INSTANCE_OFS(mv_transform[2]), 1 },
{ { 4, VA_FLOAT, VA_CONVERT_FLOAT, 1 }, sz_attr, INSTANCE_OFS(mv_transform[3]), 1 },
{ { 4, VA_FLOAT, VA_CONVERT_FLOAT, 1 }, sz_attr, INSTANCE_OFS(tex_transform[0]), 1 },
{ { 4, VA_FLOAT, VA_CONVERT_FLOAT, 1 }, sz_attr, INSTANCE_OFS(tex_transform[1]), 1 },
{ { 4, VA_FLOAT, VA_CONVERT_FLOAT, 1 }, sz_attr, INSTANCE_OFS(tex_transform[2]), 1 },
@ -140,7 +128,7 @@ void r_flush_sprites(void) {
uint pending = _r_sprite_batch.num_pending;
// needs to be done early to thwart recursive callss
// needs to be done early to thwart recursive calls
_r_sprite_batch.num_pending = 0;
#if SPRITE_BATCH_STATS
@ -201,29 +189,33 @@ void r_flush_sprites(void) {
r_state_pop();
}
static void _r_sprite_batch_add(Sprite *spr, const SpriteParams *params, SDL_RWops *stream) {
SpriteAttribs attribs;
r_mat_current(MM_MODELVIEW, attribs.transform);
static void _r_sprite_batch_compute_attribs(
const Sprite *restrict spr,
const SpriteParams *restrict params,
SpriteInstanceAttribs *out_attribs
) {
SpriteInstanceAttribs attribs;
r_mat_current(MM_MODELVIEW, attribs.mv_transform);
r_mat_current(MM_TEXTURE, attribs.tex_transform);
float scale_x = params->scale.x ? params->scale.x : 1;
float scale_y = params->scale.y ? params->scale.y : scale_x;
if(params->pos.x || params->pos.y) {
glm_translate(attribs.transform, (vec3) { params->pos.x, params->pos.y });
glm_translate(attribs.mv_transform, (vec3) { params->pos.x, params->pos.y });
}
if(params->rotation.angle) {
float *rvec = (float*)params->rotation.vector;
if(rvec[0] == 0 && rvec[1] == 0 && rvec[2] == 0) {
glm_rotate(attribs.transform, params->rotation.angle, (vec3) { 0, 0, 1 });
glm_rotate(attribs.mv_transform, params->rotation.angle, (vec3) { 0, 0, 1 });
} else {
glm_rotate(attribs.transform, params->rotation.angle, rvec);
glm_rotate(attribs.mv_transform, params->rotation.angle, rvec);
}
}
glm_scale(attribs.transform, (vec3) { scale_x * spr->w, scale_y * spr->h, 1 });
glm_scale(attribs.mv_transform, (vec3) { scale_x * spr->w, scale_y * spr->h, 1 });
float ofs_x = sprite_padded_offset_x(spr);
float ofs_y = sprite_padded_offset_y(spr);
@ -237,23 +229,26 @@ static void _r_sprite_batch_add(Sprite *spr, const SpriteParams *params, SDL_RWo
ofs_y *= -1;
}
glm_translate(attribs.transform, (vec3) { ofs_x / spr->w, ofs_y / spr->h });
glm_translate(attribs.mv_transform, (vec3) { ofs_x / spr->w, ofs_y / spr->h });
}
if(params->color == NULL) {
// XXX: should we use r_color_current here?
attribs.rgba[0] = attribs.rgba[1] = attribs.rgba[2] = attribs.rgba[3] = 1;
attribs.rgba = *RGBA(1, 1, 1, 1);
} else {
memcpy(attribs.rgba, params->color, sizeof(attribs.rgba));
attribs.rgba = *params->color;
}
uint tw, th;
r_texture_get_size(spr->tex, 0, &tw, &th);
float rtw = 1.0f / tw;
float rth = 1.0f / th;
attribs.texrect.x = spr->tex_area.x / tw;
attribs.texrect.y = spr->tex_area.y / th;
attribs.texrect.w = spr->tex_area.w / tw;
attribs.texrect.h = spr->tex_area.h / th;
glm_vec4_mul(
(vec4) { spr->tex_area.x, spr->tex_area.y, spr->tex_area.w, spr->tex_area.h },
(vec4) { rtw, rth, rtw, rth },
attribs.texrect_vec4
);
if(params->flip.x) {
attribs.texrect.x += attribs.texrect.w;
@ -265,40 +260,55 @@ static void _r_sprite_batch_add(Sprite *spr, const SpriteParams *params, SDL_RWo
attribs.texrect.h *= -1;
}
attribs.sprite_size[0] = spr->w;
attribs.sprite_size[1] = spr->h;
attribs.sprite_size = spr->extent;
if(params->shader_params != NULL) {
memcpy(attribs.custom, params->shader_params, sizeof(attribs.custom));
if(params->shader_params == NULL) {
memset(&attribs.custom, 0, sizeof(attribs.custom));
} else {
memset(attribs.custom, 0, sizeof(attribs.custom));
attribs.custom = *params->shader_params;
}
SDL_RWwrite(stream, &attribs, SIZEOF_SPRITE_ATTRIBS, 1);
#if SPRITE_BATCH_STATS
_r_sprite_batch.frame_stats.sprites++;
#endif
*out_attribs = attribs;
}
void r_draw_sprite(const SpriteParams *params) {
assert(!(params->shader && params->shader_ptr));
assert(!(params->sprite && params->sprite_ptr));
INLINE void _r_sprite_batch_process_params(
const SpriteParams *restrict sprite_params,
SpriteStateParams *restrict state_params,
Sprite *restrict *sprite
) {
assert(!(sprite_params->shader && sprite_params->shader_ptr));
assert(!(sprite_params->sprite && sprite_params->sprite_ptr));
Sprite *spr = params->sprite_ptr;
if(spr == NULL) {
assert(params->sprite != NULL);
spr = get_sprite(params->sprite);
if((*sprite = sprite_params->sprite_ptr) == NULL) {
assert(sprite_params->sprite != NULL);
*sprite = get_sprite(sprite_params->sprite);
}
if(spr->tex != _r_sprite_batch.primary_texture) {
state_params->primary_texture = (*sprite)->tex;
memcpy(&state_params->aux_textures, &sprite_params->aux_textures, sizeof(sprite_params->aux_textures));
state_params->blend = sprite_params->blend;
if(state_params->blend == 0) {
state_params->blend = r_blend_current();
}
if((state_params->shader = sprite_params->shader_ptr) == NULL) {
if(sprite_params->shader != NULL) {
state_params->shader = r_shader_get(sprite_params->shader);
} else {
state_params->shader = r_shader_current();
}
}
}
void r_sprite_batch_prepare_state(const SpriteStateParams *stp) {
if(stp->primary_texture != _r_sprite_batch.primary_texture) {
r_flush_sprites();
_r_sprite_batch.primary_texture = spr->tex;
_r_sprite_batch.primary_texture = stp->primary_texture;
}
for(uint i = 0; i < R_NUM_SPRITE_AUX_TEXTURES; ++i) {
Texture *aux_tex = params->aux_textures[i];
Texture *aux_tex = stp->aux_textures[i];
if(aux_tex != NULL && aux_tex != _r_sprite_batch.aux_textures[i]) {
r_flush_sprites();
@ -306,21 +316,16 @@ void r_draw_sprite(const SpriteParams *params) {
}
}
ShaderProgram *prog = params->shader_ptr;
if(prog == NULL) {
if(params->shader == NULL) {
prog = r_shader_current();
} else {
prog = r_shader_get(params->shader);
}
if(stp->shader != _r_sprite_batch.shader) {
r_flush_sprites();
_r_sprite_batch.shader = stp->shader;
}
assert(prog != NULL);
BlendMode blend = stp->blend;
if(prog != _r_sprite_batch.shader) {
if(blend != _r_sprite_batch.blend) {
r_flush_sprites();
_r_sprite_batch.shader = prog;
_r_sprite_batch.blend = blend;
}
Framebuffer *fb = r_framebuffer_current();
@ -330,17 +335,6 @@ void r_draw_sprite(const SpriteParams *params) {
_r_sprite_batch.framebuffer = fb;
}
BlendMode blend = params->blend;
if(blend == 0) {
blend = r_blend_current();
}
if(blend != _r_sprite_batch.blend) {
r_flush_sprites();
_r_sprite_batch.blend = blend;
}
r_capability_bits_t caps = r_capabilities_current();
DepthTestFunc depth_func = r_depth_func_current();
CullFaceMode cull_mode = r_cull_current();
@ -366,7 +360,9 @@ void r_draw_sprite(const SpriteParams *params) {
r_flush_sprites();
glm_mat4_copy(*current_projection, _r_sprite_batch.projection);
}
}
static SDL_RWops *_r_sprite_batch_prepare_buffer(void) {
SDL_RWops *stream = r_vertex_buffer_get_stream(_r_sprite_batch.vbuf);
size_t remaining = SDL_RWsize(stream) - SDL_RWtell(stream);
@ -380,8 +376,29 @@ void r_draw_sprite(const SpriteParams *params) {
r_flush_sprites();
}
return stream;
}
void r_sprite_batch_add_instance(const SpriteInstanceAttribs *attribs) {
SDL_RWops *stream = _r_sprite_batch_prepare_buffer();
SDL_RWwrite(stream, attribs, SIZEOF_SPRITE_ATTRIBS, 1);
_r_sprite_batch.num_pending++;
_r_sprite_batch_add(spr, params, stream);
#if SPRITE_BATCH_STATS
_r_sprite_batch.frame_stats.sprites++;
#endif
}
void r_draw_sprite(const SpriteParams *params) {
SpriteStateParams state_params;
SpriteInstanceAttribs attribs;
Sprite *spr;
_r_sprite_batch_process_params(params, &state_params, &spr);
r_sprite_batch_prepare_state(&state_params);
_r_sprite_batch_compute_attribs(spr, params, &attribs);
r_sprite_batch_add_instance(&attribs);
}
#if SPRITE_BATCH_STATS

View file

@ -13,13 +13,14 @@
#include FT_STROKER_H
#include "font.h"
#include "util.h"
#include "util/rectpack.h"
#include "util/graphics.h"
#include "config.h"
#include "video.h"
#include "events.h"
#include "renderer/api.h"
#include "util.h"
#include "util/glm.h"
#include "util/graphics.h"
#include "util/rectpack.h"
#include "video.h"
static void init_fonts(void);
static void post_init_fonts(void);
@ -963,10 +964,8 @@ ShaderProgram* text_get_default_shader(void) {
return globals.default_shader;
}
// #define TEXT_DRAW_BBOX
attr_returns_nonnull
static Font* font_from_params(const TextParams *params) {
static Font *font_from_params(const TextParams *params) {
Font *font = params->font_ptr;
if(font == NULL) {
@ -981,9 +980,38 @@ static Font* font_from_params(const TextParams *params) {
return font;
}
static void set_batch_texture(SpriteStateParams *stp, Texture *tex, float *inverse_tex_w, float *inverse_tex_h) {
if(stp->primary_texture != tex) {
stp->primary_texture = tex;
r_sprite_batch_prepare_state(stp);
uint tw, th;
r_texture_get_size(tex, 0, &tw, &th);
*inverse_tex_w = 1.0f / tw;
*inverse_tex_h = 1.0f / th;
}
}
attr_nonnull(1, 2, 3)
static double _text_ucs4_draw(Font *font, const uint32_t *ucs4text, const TextParams *params) {
SpriteParams sp = { .sprite = NULL };
SpriteStateParams batch_state_params;
memcpy(batch_state_params.aux_textures, params->aux_textures, sizeof(batch_state_params.aux_textures));
if((batch_state_params.blend = params->blend) == 0) {
batch_state_params.blend = r_blend_current();
}
if((batch_state_params.shader = params->shader_ptr) == NULL) {
if(params->shader != NULL) {
batch_state_params.shader = r_shader_get(params->shader);
} else {
batch_state_params.shader = r_shader_current();
}
}
batch_state_params.primary_texture = NULL;
BBox bbox;
double x = params->pos.x;
double y = params->pos.y;
@ -997,33 +1025,31 @@ static double _text_ucs4_draw(Font *font, const uint32_t *ucs4text, const TextPa
text_ucs4_bbox(font, ucs4text, 0, &bbox);
sp.shader_ptr = params->shader_ptr;
Color color;
if(sp.shader_ptr == NULL) {
// don't defer this to r_draw_sprite; we don't want to call r_shader_get more than necessary.
if(params->shader != NULL) {
sp.shader_ptr = r_shader_get(params->shader);
} else {
sp.shader_ptr = r_shader_current();
}
}
sp.color = params->color;
sp.blend = params->blend;
sp.shader_params = params->shader_params;
memcpy(sp.aux_textures, params->aux_textures, sizeof(sp.aux_textures));
if(sp.color == NULL) {
if(params->color == NULL) {
// XXX: sprite batch code defaults this to RGB(1, 1, 1)
sp.color = r_color_current();
color = *r_color_current();
} else {
color = *params->color;
}
MatrixMode mm_prev = r_mat_mode_current();
r_mat_mode(MM_MODELVIEW);
r_mat_push();
r_mat_translate(x, y, 0);
r_mat_scale(iscale, iscale, 1);
ShaderCustomParams shader_params;
if(params->shader_params == NULL) {
memset(&shader_params, 0, sizeof(shader_params));
} else {
shader_params = *params->shader_params;
}
mat4 mat_texture;
mat4 mat_model;
r_mat_current(MM_TEXTURE, mat_texture);
r_mat_current(MM_MODELVIEW, mat_model);
glm_translate(mat_model, (vec3) { x, y } );
glm_scale(mat_model, (vec3) { iscale, iscale, 1 } );
double orig_x = x;
double orig_y = y;
@ -1047,34 +1073,8 @@ static double _text_ucs4_draw(Font *font, const uint32_t *ucs4text, const TextPa
overlay.w = overlay.x.max - overlay.x.min;
overlay.h = overlay.y.max - overlay.y.min;
#ifdef TEXT_DRAW_BBOX
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 + 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), sp.color));
r_draw_quad();
r_mat_pop();
r_state_pop();
#endif
r_mat_mode(MM_TEXTURE);
r_mat_push();
r_mat_scale(1/overlay.w, 1/overlay.h, 1.0);
r_mat_translate(-overlay.x.min, overlay.y.min, 0);
glm_scale(mat_texture, (vec3) { 1/overlay.w, 1/overlay.h, 1.0 });
glm_translate(mat_texture, (vec3) { -overlay.x.min, overlay.y.min, 0 });
// FIXME: is there a better way?
float texmat_offset_sign;
@ -1088,6 +1088,8 @@ static double _text_ucs4_draw(Font *font, const uint32_t *ucs4text, const TextPa
uint prev_glyph_idx = 0;
const uint32_t *tptr = ucs4text;
float inverse_tex_w, inverse_tex_h;
while(*tptr) {
uint32_t uchar = *tptr++;
@ -1105,47 +1107,44 @@ static double _text_ucs4_draw(Font *font, const uint32_t *ucs4text, const TextPa
x += apply_kerning(font, prev_glyph_idx, glyph);
sp.sprite_ptr = &glyph->sprite;
if(glyph->sprite.tex != NULL) {
sp.pos.x = x + glyph->metrics.bearing_x + sp.sprite_ptr->w * 0.5;
sp.pos.y = y - glyph->metrics.bearing_y + sp.sprite_ptr->h * 0.5 - font->metrics.descent;
Sprite *spr = &glyph->sprite;
set_batch_texture(&batch_state_params, spr->tex, &inverse_tex_w, &inverse_tex_h);
// HACK/FIXME: Glyphs have their sprite w/h unadjusted for scale.
// We have to temporarily fix that up here so that the shader gets resolution-independent dimensions.
float w_saved = sp.sprite_ptr->w;
float h_saved = sp.sprite_ptr->h;
sp.sprite_ptr->w /= font->metrics.scale;
sp.sprite_ptr->h /= font->metrics.scale;
sp.scale.both = font->metrics.scale;
SpriteInstanceAttribs attribs;
attribs.rgba = color;
attribs.custom = shader_params;
r_mat_push();
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);
float g_x = x + glyph->metrics.bearing_x + spr->w * 0.5;
float g_y = y - glyph->metrics.bearing_y + spr->h * 0.5 - font->metrics.descent;
glm_translate_to(mat_texture, (vec3) { g_x - spr->w * 0.5, g_y * texmat_offset_sign + overlay.h - spr->h * 0.5 }, attribs.tex_transform );
glm_scale(attribs.tex_transform, (vec3) { spr->w, spr->h, 1.0 });
glm_translate_to(mat_model, (vec3) { g_x, g_y }, attribs.mv_transform);
glm_scale(attribs.mv_transform, (vec3) { spr->w, spr->h, 1.0 } );
glm_vec4_mul(
(vec4) { spr->tex_area.x, spr->tex_area.y, spr->tex_area.w, spr->tex_area.h },
(vec4) { inverse_tex_w, inverse_tex_h, inverse_tex_w, inverse_tex_h },
attribs.texrect_vec4
);
// NOTE: Glyphs have their sprite w/h unadjusted for scale.
attribs.sprite_size.w = spr->w * iscale;
attribs.sprite_size.h = spr->h * iscale;
if(params->glyph_callback.func != NULL) {
x += params->glyph_callback.func(font, uchar, &sp, params->glyph_callback.userdata);
params->glyph_callback.func(font, uchar, &attribs, params->glyph_callback.userdata);
}
r_draw_sprite(&sp);
// HACK/FIXME: See above.
sp.sprite_ptr->w = w_saved;
sp.sprite_ptr->h = h_saved;
r_mat_pop();
r_sprite_batch_add_instance(&attribs);
}
x += glyph->metrics.advance;
prev_glyph_idx = glyph->ft_index;
}
r_mat_pop();
r_mat_mode(MM_MODELVIEW);
r_mat_pop();
r_mat_mode(mm_prev);
return x * iscale;
}

View file

@ -54,7 +54,8 @@ typedef struct BBox {
} y;
} BBox;
typedef int (*GlyphDrawCallback)(Font *font, charcode_t charcode, SpriteParams *spr_params, void *userdata);
// FIXME: this is a quite crude low-level-ish hack, and probably should be replaced with some kind of markup system.
typedef int (*GlyphDrawCallback)(Font *font, charcode_t charcode, SpriteInstanceAttribs *spr_instance, void *userdata);
typedef struct TextParams {
const char *font;

View file

@ -36,22 +36,22 @@ INLINE float sprite_padded_height(const Sprite *restrict spr) {
return spr->extent.h + spr->padding.top + spr->padding.bottom;
}
INLINE FloatExtent sprite_padded_extent(Sprite *restrict spr) {
INLINE FloatExtent sprite_padded_extent(const Sprite *restrict spr) {
FloatExtent e;
e.w = sprite_padded_width(spr);
e.h = sprite_padded_height(spr);
return e;
}
INLINE float sprite_padded_offset_x(Sprite *restrict spr) {
INLINE float sprite_padded_offset_x(const Sprite *restrict spr) {
return (spr->padding.left - spr->padding.right) * 0.5;
}
INLINE float sprite_padded_offset_y(Sprite *restrict spr) {
INLINE float sprite_padded_offset_y(const Sprite *restrict spr) {
return (spr->padding.top - spr->padding.bottom) * 0.5;
}
INLINE FloatOffset sprite_padded_offset(Sprite *restrict spr) {
INLINE FloatOffset sprite_padded_offset(const Sprite *restrict spr) {
FloatOffset o;
o.x = sprite_padded_offset_x(spr);
o.y = sprite_padded_offset_y(spr);

View file

@ -1068,14 +1068,14 @@ struct glyphcb_state {
Color *color1, *color2;
};
static int draw_numeric_callback(Font *font, charcode_t charcode, SpriteParams *spr_params, void *userdata) {
static int draw_numeric_callback(Font *font, charcode_t charcode, SpriteInstanceAttribs *spr_attribs, void *userdata) {
struct glyphcb_state *st = userdata;
if(charcode != '0' && charcode != ',') {
st->color1 = st->color2;
}
spr_params->color = st->color1;
spr_attribs->rgba = *st->color1;
return 0;
}
@ -1325,7 +1325,6 @@ static void stage_draw_hud_text(struct labels_s* labels) {
font_set_kerning_enabled(font, kern_saved);
}
r_mat_push();
r_mat_translate(HUD_X_SECONDARY_OFS_VALUE, 0, 0);
@ -1344,7 +1343,7 @@ static void stage_draw_hud_text(struct labels_s* labels) {
.font_ptr = font,
.glyph_callback = {
draw_numeric_callback,
&(struct glyphcb_state) { &stagedraw.hud_text.color.inactive },
&(struct glyphcb_state) { &stagedraw.hud_text.color.inactive, &stagedraw.hud_text.color.active },
}
});
@ -1391,7 +1390,7 @@ static void stage_draw_hud_text(struct labels_s* labels) {
.font_ptr = font,
.glyph_callback = {
draw_numeric_callback,
&(struct glyphcb_state) { &stagedraw.hud_text.color.inactive },
&(struct glyphcb_state) { &stagedraw.hud_text.color.inactive, &stagedraw.hud_text.color.active },
}
});
@ -1410,7 +1409,7 @@ static void stage_draw_hud_text(struct labels_s* labels) {
.font_ptr = font,
.glyph_callback = {
draw_numeric_callback,
&(struct glyphcb_state) { &stagedraw.hud_text.color.inactive },
&(struct glyphcb_state) { &stagedraw.hud_text.color.inactive, &stagedraw.hud_text.color.active },
}
});