diff --git a/src/corelib/render.c b/src/corelib/render.c index 365ba59..4f3be4b 100644 --- a/src/corelib/render.c +++ b/src/corelib/render.c @@ -6,6 +6,8 @@ #include "inttypes.h" #include "SDL2/SDL_blendmode.h" #include "SDL2/SDL_render.h" +#include "SDL2/SDL_surface.h" +#include "string.h" #define NUM_DRAWCMDS 2048 @@ -56,6 +58,22 @@ void get_scaling_factors(float* width, float* height, (*height) = (*width)*aspectr; } +SDL_FRect get_dest_with_size(SDL_FRect untransformed, int ui) { + float fw, fh, fwm, fhm; + get_scaling_factors(&fw, &fh, &fwm, &fhm, ui); + SDL_FRect r = (SDL_FRect) { + .x=((-g_active_view.x)+(fw*0.5f)+untransformed.x)*fwm, + .y=((-g_active_view.y)+(fh*0.5f)+untransformed.y)*fwm, + .w=untransformed.w*fwm, + .h=untransformed.h*fwm + }; + if(ui) { + r.x = untransformed.x * fwm; + r.y = untransformed.y * fhm; + } + return r; +} + void exec_sprite_cmd(const drawcmd_t* cmd) { const sprite_t* sprite = &cmd->sprite; float fw, fh, fwm, fhm; @@ -115,11 +133,132 @@ void exec_rect_cmd(const drawcmd_t* cmd) { SDL_RenderFillRectF(g_context.renderer, &r); } +void exec_sliced_cmd(const drawcmd_t* cmd) { + const nineslice_t* sliced = &cmd->sliced; + + // target rect in world space + SDL_FRect rect = sliced->rect; + // sliced texture + SDL_Texture* t = sliced->texture; + + // width and height of sliced texture + int tw, th; + SDL_QueryTexture(t, NULL, NULL, &tw, &th); + + // top-left + SDL_Rect srcr = { + 0, 0, sliced->corner_size, sliced->corner_size + }; + SDL_FRect dstr = get_dest_with_size((SDL_FRect){ + rect.x, rect.y, sliced->radius, sliced->radius + }, cmd->ui); + SDL_RenderCopyF(g_context.renderer, t, &srcr, &dstr); + + // top - centre + srcr = (SDL_Rect) { + sliced->corner_size, 0, + tw - sliced->corner_size*2, sliced->corner_size + }; + dstr = get_dest_with_size((SDL_FRect){ + rect.x + sliced->radius, 0.0, sliced->rect.w - sliced->radius * 2, sliced->radius + }, cmd->ui); + SDL_RenderCopyF(g_context.renderer, t, &srcr, &dstr); + + // top-right + srcr = (SDL_Rect) { + tw - sliced->corner_size, 0, sliced->corner_size, sliced->corner_size + }; + dstr = get_dest_with_size((SDL_FRect){ + rect.w - sliced->radius, 0, sliced->radius, sliced->radius + }, cmd->ui); + SDL_RenderCopyF(g_context.renderer, t, &srcr, &dstr); + + // centre-left + srcr = (SDL_Rect) { + 0, sliced->corner_size, sliced->corner_size, th - sliced->corner_size * 2 + }; + dstr = get_dest_with_size((SDL_FRect) { + 0, sliced->radius, sliced->radius, rect.h - sliced->radius * 2 + }, cmd->ui); + SDL_RenderCopyF(g_context.renderer, t, &srcr, &dstr); + + // centre-centre + srcr = (SDL_Rect) { + sliced->corner_size, sliced->corner_size, tw - sliced->corner_size * 2, th - sliced->corner_size * 2 + }; + dstr = get_dest_with_size((SDL_FRect) { + sliced->radius, sliced->radius, rect.w - sliced->radius * 2, rect.h - sliced->radius * 2 + }, cmd->ui); + SDL_RenderCopyF(g_context.renderer, t, &srcr, &dstr); + + // centre-right + srcr = (SDL_Rect) { + tw - sliced->corner_size, sliced->corner_size, sliced->corner_size, th - sliced->corner_size * 2 + }; + dstr = get_dest_with_size((SDL_FRect) { + rect.w - sliced->radius, sliced->radius, sliced->radius, rect.h - sliced->radius * 2 + }, cmd->ui); + SDL_RenderCopyF(g_context.renderer, t, &srcr, &dstr); + + // bottom-left + srcr = (SDL_Rect) { + 0, th - sliced->corner_size, + sliced->corner_size, sliced->corner_size + }; + dstr = get_dest_with_size((SDL_FRect){ + 0, rect.h - sliced->radius, + sliced->radius, sliced->radius + }, cmd->ui); + SDL_RenderCopyF(g_context.renderer, t, &srcr, &dstr); + + // bottom-centre + srcr = (SDL_Rect) { + sliced->corner_size, th - sliced->corner_size, tw - sliced->corner_size * 2, sliced->corner_size + }; + dstr = get_dest_with_size((SDL_FRect) { + sliced->radius, rect.h - sliced->radius, rect.w - sliced->radius * 2, sliced->radius + }, cmd->ui); + SDL_RenderCopyF(g_context.renderer, t, &srcr, &dstr); + + // bottom-right + srcr = (SDL_Rect) { + tw - sliced->corner_size, th - sliced->corner_size, sliced->corner_size, sliced->corner_size + }; + dstr = get_dest_with_size((SDL_FRect) { + rect.w - sliced->radius, rect.h - sliced->radius, sliced->radius, sliced->radius + }, cmd->ui); + SDL_RenderCopyF(g_context.renderer, t, &srcr, &dstr); +} + +void exec_text_cmd(const drawcmd_t* cmd) { + SDL_FRect r = get_dest_with_size(cmd->text.area, cmd->ui); + SDL_FRect rt = {r.x*100, r.y*100, r.w*100, r.h*100}; + SDL_Surface* s = TTF_RenderText_Solid_Wrapped(cmd->text.font, cmd->text.text, cmd->text.fg, floor(cmd->text.area.y * 100)); + if(s != NULL) { + SDL_FreeSurface(s); + SDL_Texture* t = SDL_CreateTextureFromSurface(g_context.renderer, s); + float ar = r.w / r.h; + float ar2 = (float)s->w / (float)s->h; + SDL_Rect srcr = {0, 0, s->w, s->h}; + if(ar > ar2) { + srcr.h = srcr.w * ar; + } else if(ar < ar2) { + r.h = r.w * ar2; + } + SDL_RenderCopyF(g_context.renderer, t, &srcr, &r); + SDL_DestroyTexture(t); + } + free(cmd->text.text); +} + typedef void(*drawcmd_delegate)(const drawcmd_t*); drawcmd_delegate const drawcmd_funcs[] = { &exec_sprite_cmd, - &exec_rect_cmd + &exec_rect_cmd, + &exec_sliced_cmd, + &exec_text_cmd, }; + void exec_buffer() { if(d_debug_next_frame) printf("debug capture of draw buffer\ncount: %zu\n", (size_t)(g_drawdata_endptr - g_drawdata)); for(const drawcmd_t* cmd = g_drawdata; cmd != g_drawdata_endptr; ++cmd) { @@ -206,6 +345,32 @@ void draw_rect(const rectshape_t* rect) { draw(&d); } +void draw_sliced(const nineslice_t *sliced) { + drawcmd_t d = { + .type=DRAWCMDTYPE_SLICED, + .depth=sliced->depth, + .sliced=*sliced + }; + draw(&d); +} + +void draw_text(const char *str, SDL_FRect area, TTF_Font *font, SDL_Color font_color, depth_t depth) { + int len = strlen(str); + textarea_t t = { + .text = calloc(len+1, sizeof(char)), + .area = area, + .fg = font_color, + .font = font, + }; + strcpy(t.text, str); + drawcmd_t d = { + .type=DRAWCMDTYPE_TEXT, + .text=t, + .depth=depth, + }; + draw(&d); +} + spritesheet_t make_spritesheet(const char *file, int tile_width, int tile_height) { spritesheet_t sheet=(spritesheet_t){ .texture=get_texture(file), @@ -217,6 +382,17 @@ spritesheet_t make_spritesheet(const char *file, int tile_width, int tile_height return sheet; } +nineslice_t make_nineslice(const char *file, int corner_px, float radius) { + nineslice_t sliced = { + .depth = RLAYER_UI, + .corner_size = corner_px, + .radius = radius, + .rect= {0.0, 0.0, 1.0, 1.0}, + .texture=get_texture(file) + }; + return sliced; +} + sprite_t make_sprite(const char* file, float x, float y) { sprite_t sprite=(sprite_t){ .texture=get_texture(file), diff --git a/src/corelib/render.h b/src/corelib/render.h index 4f5006f..48490a6 100644 --- a/src/corelib/render.h +++ b/src/corelib/render.h @@ -6,6 +6,7 @@ extern "C" { #endif #include "context.h" +#include "SDL2/SDL_ttf.h" typedef int depth_t; @@ -15,6 +16,8 @@ typedef enum drawcmdtype_t { DRAWCMDTYPE_MIN = -1, DRAWCMDTYPE_SPRITE = 0, DRAWCMDTYPE_RECT = 1, + DRAWCMDTYPE_SLICED = 2, + DRAWCMDTYPE_TEXT = 3, DRAWCMDTYPE_MAX } drawcmdtype_t; @@ -26,9 +29,19 @@ typedef struct spritesheet_t { typedef struct nineslice_t { SDL_Texture* texture; - float corner_height, corner_width; // the width and height of the corners + SDL_FRect rect; // the rectangle to fit into + depth_t depth; + float radius; + int corner_size; } nineslice_t; // nine-sliced texture for ui +typedef struct textarea_t { + char* text; + TTF_Font* font; + SDL_Color fg; + SDL_FRect area; +} textarea_t; + typedef struct sprite_t { SDL_Texture* texture; float x, y; // positions of x,y @@ -54,6 +67,8 @@ typedef struct drawcmd_t { union { sprite_t sprite; // if type is sprite, render this rectshape_t rect; // if type is rect, render this + nineslice_t sliced; + textarea_t text; }; } drawcmd_t; // an orderable command to render, should not be directly used, use draw_* functions instead @@ -73,12 +88,19 @@ extern void draw(const drawcmd_t* cmd); extern void draw_sprite(const sprite_t* sprite); extern void draw_rect(const rectshape_t* rect); +extern void draw_sliced(const nineslice_t* sliced); +extern void draw_text(const char* str, SDL_FRect area, TTF_Font* font, SDL_Color font_color, depth_t depth); extern spritesheet_t make_spritesheet(const char* file, int tile_width, int tile_height); +extern nineslice_t make_nineslice(const char* file, int corner_px, float radius); extern sprite_t make_sprite(const char* file, float x, float y); extern sprite_t sprite_from_spritesheet(spritesheet_t* sheet, int index); extern SDL_Rect get_srcrect_from(spritesheet_t* sheet, int index); extern void set_active_view(const view_t* view); +inline sprite_t no_sprite() { + return (sprite_t){NULL, 0.0, 0.0, {0.0, 0.0}, 0.0, 0.0, 0.0, 0, {0, 0, 0, 0}}; +} + #ifdef __cplusplus } #endif