diff --git a/CMakeLists.txt b/CMakeLists.txt index 6610d7d..d5cd436 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,6 +11,8 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/bin/") set(CMAKE_EXPORT_COMPILE_COMMANDS) +set(CMAKE_C_FLAGS "-lm") + find_package(SDL2) find_package(SDL2_image) find_package(SDL2_ttf) diff --git a/TODO.txt b/TODO.txt index 1270ec5..4c55b6f 100644 --- a/TODO.txt +++ b/TODO.txt @@ -1,9 +1,16 @@ # TODOs -[ ] add nineslice drawcmd functions +[x] add nineslice drawcmd functions [x] draw_* [x] exec_* +[ ] add text rendering + [x] loading fonts + [x] managing font style + [x] rendering fonts to surface to texture + [x] correctly scaling fonts + [ ] cache rendered text + [ ] add imgui [ ] ui windows [ ] input fields diff --git a/src/corelib/assets.c b/src/corelib/assets.c index ed853c1..d753967 100644 --- a/src/corelib/assets.c +++ b/src/corelib/assets.c @@ -53,10 +53,13 @@ TTF_Font* load_font(const char* file, int size) { .type = RESOURCETYPE_FONT, .hash = hashstr(file), .name = calloc(len+1, sizeof(char)), - .font = TTF_OpenFont(file, size) + .font = { + .size = size, + .font = TTF_OpenFont(file, size) + } }; strcpy(res.name, file); - return insert_asset(&res)->font; + return insert_asset(&res)->font.font; } SDL_Texture* get_texture(const char* file) { @@ -68,6 +71,19 @@ SDL_Texture* get_texture(const char* file) { } } +TTF_Font* get_font(const char *file, int size) { + uint32_t hash = hashstr(file); + for(resource_t* res = g_assets; res != g_assets_endptr; ++res) { + if(res->hash == hash + && strcmp(res->name, file) == 0 + && res->type == RESOURCETYPE_FONT + && res->font.size == size) { + return res->font.font; + } + } + return load_font(file, size); +} + resource_t* get_asset(const char* file) { uint32_t hash = hashstr(file); for(resource_t* res = g_assets; res != g_assets_endptr; ++res) { @@ -88,7 +104,7 @@ void _delete_referenced_asset(resource_t* res) { SDL_DestroyTexture(res->texture); break; case RESOURCETYPE_FONT: - TTF_CloseFont(res->font); + TTF_CloseFont(res->font.font); break; case RESOURCETYPE_ARBITRARY: res->arbitrary_type.deleter(res->arbitrary_type.memory); diff --git a/src/corelib/assets.h b/src/corelib/assets.h index ee4983c..89360ef 100644 --- a/src/corelib/assets.h +++ b/src/corelib/assets.h @@ -25,7 +25,10 @@ typedef struct resource_t { char* name; union { SDL_Texture* texture; - TTF_Font* font; + struct { + int size; + TTF_Font* font; + } font; struct { void* memory; deleter_t deleter; diff --git a/src/corelib/context.c b/src/corelib/context.c index 9108fa8..a8b84bc 100644 --- a/src/corelib/context.c +++ b/src/corelib/context.c @@ -30,6 +30,7 @@ void init_context() { void close_context() { SDL_DestroyRenderer(g_context.renderer); SDL_DestroyWindow(g_context.window); + TTF_Quit(); IMG_Quit(); SDL_Quit(); } diff --git a/src/corelib/render.c b/src/corelib/render.c index 4f3be4b..543e7d1 100644 --- a/src/corelib/render.c +++ b/src/corelib/render.c @@ -4,10 +4,11 @@ #include "layers.h" #include "signal.h" #include "inttypes.h" +#include "string.h" #include "SDL2/SDL_blendmode.h" #include "SDL2/SDL_render.h" #include "SDL2/SDL_surface.h" -#include "string.h" +#include "SDL2/SDL_ttf.h" #define NUM_DRAWCMDS 2048 @@ -160,7 +161,7 @@ void exec_sliced_cmd(const drawcmd_t* cmd) { 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 + rect.x + sliced->radius, rect.y, sliced->rect.w - sliced->radius * 2, sliced->radius }, cmd->ui); SDL_RenderCopyF(g_context.renderer, t, &srcr, &dstr); @@ -169,7 +170,7 @@ void exec_sliced_cmd(const drawcmd_t* cmd) { 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 + rect.x + rect.w - sliced->radius, rect.y, sliced->radius, sliced->radius }, cmd->ui); SDL_RenderCopyF(g_context.renderer, t, &srcr, &dstr); @@ -178,7 +179,7 @@ void exec_sliced_cmd(const drawcmd_t* cmd) { 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 + rect.x, rect.y + sliced->radius, sliced->radius, rect.h - sliced->radius * 2 }, cmd->ui); SDL_RenderCopyF(g_context.renderer, t, &srcr, &dstr); @@ -187,7 +188,7 @@ void exec_sliced_cmd(const drawcmd_t* cmd) { 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 + rect.x + sliced->radius, rect.y + sliced->radius, rect.w - sliced->radius * 2, rect.h - sliced->radius * 2 }, cmd->ui); SDL_RenderCopyF(g_context.renderer, t, &srcr, &dstr); @@ -196,18 +197,16 @@ void exec_sliced_cmd(const drawcmd_t* cmd) { 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 + rect.x + rect.w - sliced->radius, rect.y + 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 + 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 + rect.x, rect.y + rect.h - sliced->radius, sliced->radius, sliced->radius }, cmd->ui); SDL_RenderCopyF(g_context.renderer, t, &srcr, &dstr); @@ -216,7 +215,7 @@ void exec_sliced_cmd(const drawcmd_t* cmd) { 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 + rect.x + sliced->radius, rect.y + rect.h - sliced->radius, rect.w - sliced->radius * 2, sliced->radius }, cmd->ui); SDL_RenderCopyF(g_context.renderer, t, &srcr, &dstr); @@ -225,32 +224,76 @@ void exec_sliced_cmd(const drawcmd_t* cmd) { 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 + rect.x + rect.w - sliced->radius, rect.y + 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)); + SDL_FRect r = cmd->text.area; + int fh = TTF_FontHeight(cmd->text.style.font); + int wrap = (int)(fh * r.w / cmd->text.style.size); + SDL_Surface* s = TTF_RenderText_Solid_Wrapped(cmd->text.style.font, cmd->text.text, cmd->text.style.color, wrap); if(s != NULL) { - SDL_FreeSurface(s); + SDL_Rect srcr = {0,0,s->w, s->h}; 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_FreeSurface(s); + + float asp_dst = r.w / r.h; + float asp_src = (float)srcr.w / (float)srcr.h; + if((float)s->h / fh * cmd->text.style.size > r.h) { + srcr.h = srcr.w / asp_dst; } + + r.w = (float)srcr.w / fh * cmd->text.style.size; + r.h = (float)srcr.h / fh * cmd->text.style.size; + + r = get_dest_with_size(r, cmd->ui); SDL_RenderCopyF(g_context.renderer, t, &srcr, &r); SDL_DestroyTexture(t); } free(cmd->text.text); } +sprite_t render_text(const char* str, SDL_FRect area, text_style_t style) { + SDL_FRect r = area; + int fh = TTF_FontHeight(style.font); + int wrap = (int)(fh * r.w / style.size); + SDL_Surface* s = TTF_RenderText_Solid_Wrapped(style.font, str, style.color, wrap); + if(s != NULL) { + SDL_Rect srcr = {0,0,s->w, s->h}; + SDL_Texture* t = SDL_CreateTextureFromSurface(g_context.renderer, s); + SDL_FreeSurface(s); + + float asp_dst = r.w / r.h; + float asp_src = (float)srcr.w / (float)srcr.h; + if((float)s->h / fh * style.size > r.h) { + srcr.h = srcr.w / asp_dst; + } + + r.w = (float)srcr.w / fh * style.size; + r.h = (float)srcr.h / fh * style.size; + + return (sprite_t) { + .depth=RLAYER_SPRITES, + .origin={0,0}, + .rot=0, + .sx=area.w, .sy=area.h, + .x=area.x, .y=area.y, + .texture=t, + .uv=srcr, + }; + } + return (sprite_t) { + .depth=0, + .origin={0,0}, .rot=0, + .sx=0, .sy=0, + .texture=NULL, + .uv={0,0,0,0}, + .x=0,.y=0 + }; +} + typedef void(*drawcmd_delegate)(const drawcmd_t*); drawcmd_delegate const drawcmd_funcs[] = { &exec_sprite_cmd, @@ -354,13 +397,12 @@ void draw_sliced(const nineslice_t *sliced) { draw(&d); } -void draw_text(const char *str, SDL_FRect area, TTF_Font *font, SDL_Color font_color, depth_t depth) { +void draw_text(const char *str, SDL_FRect area, text_style_t style, depth_t depth) { int len = strlen(str); textarea_t t = { .text = calloc(len+1, sizeof(char)), .area = area, - .fg = font_color, - .font = font, + .style = style, }; strcpy(t.text, str); drawcmd_t d = { @@ -421,6 +463,15 @@ sprite_t sprite_from_spritesheet(spritesheet_t *sheet, int index) { }; } +text_style_t make_text_style(const char *font, SDL_Color color, int dpi, float size) { + TTF_Font* fnt = get_font(font, dpi); + return (text_style_t){ + .font = fnt, + .size = size, + .color = color + }; +} + SDL_Rect get_srcrect_from(spritesheet_t *sheet, int index) { int pixels = index * sheet->tile_width; int w = sheet->w / sheet->tile_width; diff --git a/src/corelib/render.h b/src/corelib/render.h index 48490a6..62ce38f 100644 --- a/src/corelib/render.h +++ b/src/corelib/render.h @@ -21,6 +21,12 @@ typedef enum drawcmdtype_t { DRAWCMDTYPE_MAX } drawcmdtype_t; +typedef struct text_style_t { + TTF_Font* font; + float size; + SDL_Color color; +} text_style_t; + typedef struct spritesheet_t { SDL_Texture* texture; int w, h; // width and height of texture @@ -37,8 +43,7 @@ typedef struct nineslice_t { typedef struct textarea_t { char* text; - TTF_Font* font; - SDL_Color fg; + text_style_t style; SDL_FRect area; } textarea_t; @@ -86,14 +91,16 @@ extern void clear_buffer(); extern void swap_buffer(); extern void draw(const drawcmd_t* cmd); +extern sprite_t render_text(const char* str, SDL_FRect area, text_style_t style); 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 void draw_text(const char* str, SDL_FRect area, text_style_t style, 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 text_style_t make_text_style(const char* font, SDL_Color color, int dpi, float size); extern SDL_Rect get_srcrect_from(spritesheet_t* sheet, int index); extern void set_active_view(const view_t* view); diff --git a/src/game.c b/src/game.c index 0e61f9f..2e442fd 100644 --- a/src/game.c +++ b/src/game.c @@ -191,7 +191,7 @@ void update_ui() { update_input(); nineslice_t sliced = style.button.active; - sliced.rect = (SDL_FRect){0, 0, 0.1, 0.1}; + sliced.rect = (SDL_FRect){0, 0.25f, 0.2, 0.05}; sliced.depth = RLAYER_UI - 20; draw_sliced(&sliced); @@ -208,6 +208,9 @@ void update_ui() { {100,100,100,255}, {0,0,0,0} }; draw_rect(&shape); + text_style_t style = make_text_style("ui_font.otf", 100, 0.01); + draw_text("M", + (SDL_FRect){0.0, 0.0, 10.0, 10.0}, style, (SDL_Color){255, 255, 255, 255}, RLAYER_UI - 100); float mx, my; mouse_screen_position(&mx, &my);