From 0adaf17d5f94d7f4a834d8eb8779a3f83d21e30a Mon Sep 17 00:00:00 2001 From: Andrei Alexeyev Date: Sun, 27 Nov 2022 20:08:24 +0100 Subject: [PATCH] sprite: handle virtual paddings transparently Most client code no longer needs to care about paddings at all, aside from some very special cases involving dynamic creation of padded sprites. So far we only have one known example of this in portrait_render(). This should let us optimize most of our sprites with autotrimming in the future. Additionally, support for deprecated offset_{x,y} keys in sprite files has been removed. --- src/credits.c | 4 +-- src/dialog.c | 7 ++--- src/portrait.c | 10 ++++---- src/renderer/common/sprite_batch.c | 17 +++++++------ src/resource/sprite.c | 26 ++++++++++--------- src/resource/sprite.h | 41 +++++------------------------- 6 files changed, 38 insertions(+), 67 deletions(-) diff --git a/src/credits.c b/src/credits.c index 335ed634..37ad462c 100644 --- a/src/credits.c +++ b/src/credits.c @@ -348,7 +348,7 @@ static double entry_height(CreditsEntry *e, double *head, double *body) { if(e->lines > 0) { if(*(e->data[0]) == '*') { - total += *head = sprite_padded_height(res_sprite("kyoukkuri")); + total += *head = res_sprite("kyoukkuri")->h; } else { total += *head = font_get_lineskip(res_font("big")); } @@ -435,7 +435,7 @@ static void credits_draw_entry(CreditsEntry *e) { float t = ((global.frames) % 90) / 59.0; float elevation = yukkuri_jump(t); float squeeze = (elevation - yukkuri_jump(t - 0.03)) * 0.4; - float halfheight = sprite_padded_height(yukkuri_spr) * 0.5; + float halfheight = yukkuri_spr->h * 0.5; r_draw_sprite(&(SpriteParams) { .sprite_ptr = yukkuri_spr, diff --git a/src/dialog.c b/src/dialog.c index cf54a429..62bb855f 100644 --- a/src/dialog.c +++ b/src/dialog.c @@ -274,9 +274,6 @@ void dialog_draw(Dialog *dialog) { Sprite *portrait = &a->composite; assume(portrait->tex != NULL); - float portrait_w = sprite_padded_width(portrait); - float portrait_h = sprite_padded_height(portrait); - r_mat_mv_push(); if(a->side == DIALOG_SIDE_LEFT) { @@ -302,8 +299,8 @@ void dialog_draw(Dialog *dialog) { r_draw_sprite(&(SpriteParams) { .blend = BLEND_PREMUL_ALPHA, .color = &clr, - .pos.x = (dialog_width - portrait_w) / 2 + 32 + a->offset.x, - .pos.y = VIEWPORT_H - portrait_h / 2 + a->offset.y, + .pos.x = (dialog_width - portrait->w) / 2 + 32 + a->offset.x, + .pos.y = VIEWPORT_H - portrait->h / 2 + a->offset.y, .sprite_ptr = portrait, }); diff --git a/src/portrait.c b/src/portrait.c index fdcd47a1..2163b992 100644 --- a/src/portrait.c +++ b/src/portrait.c @@ -61,8 +61,8 @@ void portrait_render(Sprite *s_base, Sprite *s_face, Sprite *s_out) { uint tex_w = imax(itc.w, 1); uint tex_h = imax(itc.h, 1); - uint spr_w = s_base->extent.w; - uint spr_h = s_base->extent.h; + float spr_w = s_base->extent.w; + float spr_h = s_base->extent.h; Texture *ptex = r_texture_create(&(TextureParams) { .type = TEX_TYPE_RGBA_8, @@ -82,14 +82,14 @@ void portrait_render(Sprite *s_base, Sprite *s_face, Sprite *s_out) { r_framebuffer(fb); r_framebuffer_clear(fb, CLEAR_COLOR, RGBA(0, 0, 0, 0), 1); - r_mat_proj_push_ortho(spr_w, spr_h); + r_mat_proj_push_ortho(spr_w - s_base->padding.w, spr_h - s_base->padding.h); r_mat_mv_push_identity(); SpriteParams sp = { 0 }; sp.sprite_ptr = s_base; sp.blend = BLEND_NONE; - sp.pos.x = spr_w * 0.5f - sprite_padded_offset_x(s_base); - sp.pos.y = spr_h * 0.5f - sprite_padded_offset_y(s_base); + sp.pos.x = spr_w * 0.5f - s_base->padding.offset.x; + sp.pos.y = spr_h * 0.5f - s_base->padding.offset.y; sp.color = RGBA(1, 1, 1, 1); sp.shader_ptr = res_shader("sprite_default"), r_draw_sprite(&sp); diff --git a/src/renderer/common/sprite_batch.c b/src/renderer/common/sprite_batch.c index 76cc1846..38270947 100644 --- a/src/renderer/common/sprite_batch.c +++ b/src/renderer/common/sprite_batch.c @@ -181,6 +181,10 @@ static void _r_sprite_batch_compute_attribs( float scale_x = params->scale.x ? params->scale.x : 1; float scale_y = params->scale.y ? params->scale.y : scale_x; + FloatOffset ofs = spr->padding.offset; + FloatExtent imgdims = spr->extent; + imgdims.as_cmplx -= spr->padding.extent.as_cmplx; + if(params->pos.x || params->pos.y) { glm_translate(attribs.mv_transform, (vec3) { params->pos.x, params->pos.y }); } @@ -195,21 +199,18 @@ static void _r_sprite_batch_compute_attribs( } } - glm_scale(attribs.mv_transform, (vec3) { scale_x * spr->w, scale_y * spr->h, 1 }); + glm_scale(attribs.mv_transform, (vec3) { scale_x * imgdims.w, scale_y * imgdims.h, 1 }); - float ofs_x = sprite_padded_offset_x(spr); - float ofs_y = sprite_padded_offset_y(spr); - - if(ofs_x || ofs_y) { + if(ofs.x || ofs.y) { if(params->flip.x) { - ofs_x *= -1; + ofs.x *= -1; } if(params->flip.y) { - ofs_y *= -1; + ofs.y *= -1; } - glm_translate(attribs.mv_transform, (vec3) { ofs_x / spr->w, ofs_y / spr->h }); + glm_translate(attribs.mv_transform, (vec3) { ofs.x / imgdims.w, ofs.y / imgdims.h }); } if(params->color == NULL) { diff --git a/src/resource/sprite.c b/src/resource/sprite.c index a0314b22..02cef1bc 100644 --- a/src/resource/sprite.c +++ b/src/resource/sprite.c @@ -50,7 +50,6 @@ static void load_sprite_stage1(ResourceLoadState *st) { return; } - float ofs_x = 0, ofs_y = 0; SDL_RWops *rw = res_open_file(st, st->path, VFS_MODE_READ); @@ -62,6 +61,8 @@ static void load_sprite_stage1(ResourceLoadState *st) { return; } + struct { float top, bottom, left, right; } pad = { }; + bool parsed = parse_keyvalue_stream_with_spec(rw, (KVSpec[]) { { "texture", .out_str = &state->texture_name }, { "region_x", .out_float = &spr->tex_area.x }, @@ -70,12 +71,10 @@ static void load_sprite_stage1(ResourceLoadState *st) { { "region_h", .out_float = &spr->tex_area.h }, { "w", .out_float = &spr->w }, { "h", .out_float = &spr->h }, - { "offset_x", .out_float = &ofs_x, KVSPEC_DEPRECATED("margin_left; margin_right") }, - { "offset_y", .out_float = &ofs_y, KVSPEC_DEPRECATED("margin_top; margin_bottom") }, - { "padding_top", .out_float = &spr->padding.top }, - { "padding_bottom", .out_float = &spr->padding.bottom }, - { "padding_left", .out_float = &spr->padding.left }, - { "padding_right", .out_float = &spr->padding.right }, + { "padding_top", .out_float = &pad.top }, + { "padding_bottom", .out_float = &pad.bottom }, + { "padding_left", .out_float = &pad.left }, + { "padding_right", .out_float = &pad.right }, { NULL } }); @@ -97,10 +96,13 @@ static void load_sprite_stage1(ResourceLoadState *st) { res_load_dependency(st, RES_TEXTURE, state->texture_name); - spr->padding.left += ofs_x; - spr->padding.right -= ofs_x; - spr->padding.top += ofs_y; - spr->padding.bottom -= ofs_y; + spr->padding.extent.w = pad.left + pad.right; + spr->padding.extent.h = pad.top + pad.bottom; + + spr->padding.offset.x = 0.5f * (pad.left - pad.right); + spr->padding.offset.y = 0.5f * (pad.top - pad.bottom); + + spr->extent.as_cmplx += spr->padding.extent.as_cmplx; res_load_continue_after_dependencies(st, load_sprite_stage2, state); return; @@ -154,7 +156,7 @@ Sprite *prefix_get_sprite(const char *name, const char *prefix) { } static void begin_draw_sprite(float x, float y, float scale_x, float scale_y, Sprite *spr) { - FloatOffset o = sprite_padded_offset(spr); + FloatOffset o = spr->padding.offset; begin_draw_texture( (FloatRect){ x + o.x * scale_x, y + o.y * scale_y, spr->w * scale_x, spr->h * scale_y }, diff --git a/src/resource/sprite.h b/src/resource/sprite.h index 6e8f083e..ccefcd5a 100644 --- a/src/resource/sprite.h +++ b/src/resource/sprite.h @@ -12,50 +12,21 @@ #include "resource.h" #include "texture.h" -typedef struct SpriteMargin { - float top, bottom, left, right; -} SpriteMargin; - typedef struct Sprite { Texture *tex; FloatRect tex_area; union { + // NOTE: This is stored with padding pre-applied. + // To get the area actually occupied by the image, subtract `padding.extent` from the size + // and bias the origin by `padding.offset`. + // You shouldn't need to worry about it in most cases, unless you're doing some low-level + // sprite rendering or want to add virtual paddings to your own Sprite instance. FloatExtent extent; struct { float w, h; }; }; - SpriteMargin padding; + FloatRect padding; } Sprite; -INLINE float sprite_padded_width(const Sprite *restrict spr) { - return spr->extent.w + spr->padding.left + spr->padding.right; -} - -INLINE float sprite_padded_height(const Sprite *restrict spr) { - return spr->extent.h + spr->padding.top + spr->padding.bottom; -} - -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(const Sprite *restrict spr) { - return (spr->padding.left - spr->padding.right) * 0.5; -} - -INLINE float sprite_padded_offset_y(const Sprite *restrict spr) { - return (spr->padding.top - spr->padding.bottom) * 0.5; -} - -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); - return o; -} - FloatRect sprite_denormalized_tex_coords(const Sprite *restrict spr); IntRect sprite_denormalized_int_tex_coords(const Sprite *restrict spr); void sprite_set_denormalized_tex_coords(Sprite *restrict spr, FloatRect tc);