integrate fonts into the resource system (preparing for text rendering rewrite)

This commit is contained in:
Andrei Alexeyev 2018-06-08 07:49:40 +03:00
parent 9d47a59d44
commit 9356f263fd
No known key found for this signature in database
GPG key ID: 363707CD4C7FE8A4
14 changed files with 203 additions and 86 deletions

3
resources/fonts/big.font Normal file
View file

@ -0,0 +1,3 @@
source = res/fonts/immortal.ttf
size = 35

3
resources/fonts/hud.font Normal file
View file

@ -0,0 +1,3 @@
source = res/fonts/Laconic_Regular.otf
size = 19

View file

@ -0,0 +1,3 @@
source = res/fonts/ShareTechMono-Regular.ttf
size = 19

View file

@ -0,0 +1,3 @@
source = res/fonts/ShareTechMono-Regular.ttf
size = 14

View file

@ -0,0 +1,3 @@
source = res/fonts/ShareTechMono-Regular.ttf
size = 10

View file

@ -0,0 +1,3 @@
source = res/fonts/LinBiolinum.ttf
size = 14

View file

@ -0,0 +1,3 @@
source = res/fonts/LinBiolinum.ttf
size = 20

View file

@ -42,7 +42,6 @@ static void taisei_shutdown(void) {
free_all_refs();
free_resources(true);
uninit_fonts();
audio_shutdown();
video_shutdown();
gamepad_shutdown();
@ -236,7 +235,6 @@ int main(int argc, char **argv) {
time_init();
init_global(&a);
events_init();
init_fonts();
video_init();
init_resources();
r_post_init();

View file

@ -13,17 +13,18 @@
#include "util.h"
#include "objectpool.h"
#include "objectpool_util.h"
#include "video.h"
#define CACHE_EXPIRE_TIME 1000
#ifdef DEBUG
// #define VERBOSE_CACHE_LOG
// #define VERBOSE_CACHE_LOG
#endif
#ifdef VERBOSE_CACHE_LOG
#define CACHELOG(fmt, ...) log_debug(fmt, __VA_ARGS__)
#define CACHELOG(fmt, ...) log_debug(fmt, __VA_ARGS__)
#else
#define CACHELOG(fmt, ...)
#define CACHELOG(fmt, ...)
#endif
typedef struct CacheEntry {
@ -35,7 +36,7 @@ typedef struct CacheEntry {
uint32_t ref_time;
struct {
// to simplify invalidation
// to simplify invalidation
Hashtable *ht;
char *ht_key;
} owner;
@ -54,10 +55,72 @@ static FontRenderer font_renderer;
struct Font {
TTF_Font *ttf;
Hashtable *cache;
char *source_path;
int base_size;
};
struct Fonts _fonts;
static void fontrenderer_init(float quality);
static void fontrenderer_free(void);
static void reload_fonts(float quality);
static bool fonts_event(SDL_Event *event, void *arg) {
reload_fonts(video.quality_factor * config_get_float(CONFIG_TEXT_QUALITY));
return false;
}
static void fonts_quality_config_callback(ConfigIndex idx, ConfigValue v) {
config_set_float(idx, v.f);
reload_fonts(video.quality_factor * v.f);
}
static void init_fonts(void) {
TTF_Init();
memset(&font_renderer, 0, sizeof(font_renderer));
cache_pool = OBJPOOL_ALLOC(CacheEntry, 512);
fontrenderer_init(video.quality_factor * config_get_float(CONFIG_TEXT_QUALITY));
events_register_handler(&(EventHandler) {
fonts_event, NULL, EPRIO_SYSTEM, MAKE_TAISEI_EVENT(TE_VIDEO_MODE_CHANGED)
});
config_set_callback(CONFIG_TEXT_QUALITY, fonts_quality_config_callback);
preload_resources(RES_FONT, RESF_PERMANENT,
"standard",
"big",
"small",
"hud",
"mono",
"monosmall",
"monotiny",
NULL);
_fonts.standard = get_resource(RES_FONT, "standard", 0)->data;
_fonts.mainmenu = get_resource(RES_FONT, "big", 0)->data;
_fonts.small = get_resource(RES_FONT, "small", 0)->data;
_fonts.hud = get_resource(RES_FONT, "hud", 0)->data;
_fonts.mono = get_resource(RES_FONT, "mono", 0)->data;
_fonts.monosmall = get_resource(RES_FONT, "monosmall", 0)->data;
_fonts.monotiny = get_resource(RES_FONT, "monotiny", 0)->data;
}
static void uninit_fonts(void) {
events_unregister_handler(fonts_event);
fontrenderer_free();
TTF_Quit();
objpool_free(cache_pool);
}
static char* font_path(const char *name) {
return strjoin(FONT_PATH_PREFIX, name, FONT_EXTENSION, NULL);
}
bool check_font_path(const char *path) {
return strstartswith(path, FONT_PATH_PREFIX) && strendswith(path, FONT_EXTENSION);
}
static TTF_Font* load_ttf(char *vfspath, int size) {
char *syspath = vfs_repr(vfspath, true);
@ -73,7 +136,7 @@ static TTF_Font* load_ttf(char *vfspath, int size) {
TTF_Font *f = TTF_OpenFontRW(rwops, true, size);
if(!f) {
log_fatal("Failed to load font '%s' @ %i: %s", syspath, size, TTF_GetError());
log_warn("Failed to load TTF font '%s' @ %i: %s", syspath, size, TTF_GetError());
}
log_info("Loaded '%s' @ %i", syspath, size);
@ -82,16 +145,57 @@ static TTF_Font* load_ttf(char *vfspath, int size) {
return f;
}
static Font* load_font(char *vfspath, int size) {
TTF_Font *ttf = load_ttf(vfspath, size);
void* load_font_begin(const char *path, uint flags) {
Font font;
memset(&font, 0, sizeof(font));
Font *font = calloc(1, sizeof(Font));
font->ttf = ttf;
font->cache = hashtable_new_stringkeys();
if(!parse_keyvalue_file_with_spec(path, (KVSpec[]){
{ "source", .out_str = &font.source_path },
{ "size", .out_int = &font.base_size },
{ NULL }
})) {
log_warn("Failed to parse font file '%s'", path);
return NULL;
}
return font;
font.ttf = load_ttf(font.source_path, font.base_size);
if(!font.ttf) {
free(font.source_path);
return NULL;
}
font.cache = hashtable_new_stringkeys();
return memdup(&font, sizeof(font));
}
void* load_font_end(void *opaque, const char *path, uint flags) {
return opaque;
}
static void free_font(Font *font);
void unload_font(void *vfont) {
free_font(vfont);
}
ResourceHandler font_res_handler = {
.type = RES_FONT,
.typename = "font",
.subdir = FONT_PATH_PREFIX,
.procs = {
.init = init_fonts,
.shutdown = uninit_fonts,
.find = font_path,
.check = check_font_path,
.begin_load = load_font_begin,
.end_load = load_font_end,
.unload = unload_font,
},
};
static void free_cache_entry(CacheEntry *e) {
if(!e) {
return;
@ -208,60 +312,42 @@ static SDL_Surface* fontrender_render(const char *text, Font *font) {
return surf;
}
void init_fonts(void) {
TTF_Init();
memset(&font_renderer, 0, sizeof(font_renderer));
cache_pool = OBJPOOL_ALLOC(CacheEntry, 512);
}
static void free_font_cache(Hashtable *cache) {
CacheEntry *e;
void uninit_fonts(void) {
free_fonts();
TTF_Quit();
}
void load_fonts(float quality) {
fontrenderer_init(quality);
_fonts.standard = load_font("res/fonts/LinBiolinum.ttf", 20);
_fonts.mainmenu = load_font("res/fonts/immortal.ttf", 35);
_fonts.small = load_font("res/fonts/LinBiolinum.ttf", 14);
_fonts.hud = load_font("res/fonts/Laconic_Regular.otf", 19);
_fonts.mono = load_font("res/fonts/ShareTechMono-Regular.ttf", 19);
_fonts.monosmall = load_font("res/fonts/ShareTechMono-Regular.ttf", 14);
_fonts.monotiny = load_font("res/fonts/ShareTechMono-Regular.ttf", 10);
}
void reload_fonts(float quality) {
if(!font_renderer.quality) {
// never loaded
load_fonts(quality);
return;
}
if(font_renderer.quality != sanitize_scale(quality)) {
free_fonts();
load_fonts(quality);
for(HashtableIterator *i = hashtable_iter(cache); hashtable_iter_next(i, 0, (void**)&e);) {
free_cache_entry(e);
}
}
static void free_font(Font *font) {
CacheEntry *e;
TTF_CloseFont(font->ttf);
for(HashtableIterator *i = hashtable_iter(font->cache); hashtable_iter_next(i, 0, (void**)&e);) {
free_cache_entry(e);
}
free_font_cache(font->cache);
hashtable_free(font->cache);
free(font->source_path);
free(font);
}
void free_fonts(void) {
fontrenderer_free();
static void reload_font(Font *font) {
TTF_CloseFont(font->ttf);
free_font_cache(font->cache);
hashtable_unset_all(font->cache);
font->ttf = load_ttf(font->source_path, font->base_size);
}
Font **last = &_fonts.first + (sizeof(_fonts)/sizeof(Font*) - 1);
for(Font **font = &_fonts.first; font <= last; ++font) {
free_font(*font);
static void* reload_font_callback(const char *name, Resource *res, void *varg) {
reload_font((Font*)res->data);
return NULL;
}
static void reload_fonts(float quality) {
if(font_renderer.quality == sanitize_scale(quality)) {
return;
}
fontrenderer_free();
fontrenderer_init(quality);
resource_for_each(RES_FONT, reload_font_callback, NULL);
}
static void draw_text_line(Alignment align, float x, float y, const char *text, Font *font) {

View file

@ -13,6 +13,7 @@
#include "sprite.h"
#include "hashtable.h"
#include "resource.h"
typedef enum {
AL_Center,
@ -39,13 +40,6 @@ int font_line_spacing(Font *font);
void shorten_text_up_to_width(char *s, float width, Font *font);
void wrap_text(char *buf, size_t bufsize, const char *src, int width, Font *font);
void init_fonts(void);
void uninit_fonts(void);
void load_fonts(float quality);
void reload_fonts(float quality);
void free_fonts(void);
void update_font_cache(void);
extern struct Fonts {
union {
struct {
@ -61,3 +55,8 @@ extern struct Fonts {
Font *first;
};
} _fonts;
extern ResourceHandler font_res_handler;
#define FONT_PATH_PREFIX "res/fonts/"
#define FONT_EXTENSION ".font"

View file

@ -26,6 +26,7 @@
#include "model.h"
#include "postprocess.h"
#include "sprite.h"
#include "font.h"
#include "renderer/common/backend.h"
@ -37,6 +38,7 @@ ResourceHandler *_handlers[] = {
[RES_MODEL] = &model_res_handler,
[RES_POSTPROCESS] = &postprocess_res_handler,
[RES_SPRITE] = &sprite_res_handler,
[RES_FONT] = &font_res_handler,
// FIXME: these are currently handled by the renderer backend completely
[RES_SHADER_OBJECT] = NULL,
@ -415,33 +417,19 @@ void preload_resources(ResourceType type, ResourceFlags flags, const char *first
va_end(args);
}
static void init_sdl_image(void) {
int want_flags = IMG_INIT_JPG | IMG_INIT_PNG;
int init_flags = IMG_Init(want_flags);
if((want_flags & init_flags) != want_flags) {
log_warn(
"SDL_image doesn't support some of the formats we want. "
"Requested: %i, got: %i. "
"Textures may fail to load",
want_flags,
init_flags
);
}
}
void init_resources(void) {
main_thread_id = SDL_ThreadID();
_handlers[RES_SHADER_OBJECT] = _r_backend.res_handlers.shader_object;
_handlers[RES_SHADER_PROGRAM] = _r_backend.res_handlers.shader_program;
init_sdl_image();
for(int i = 0; i < RES_NUMTYPES; ++i) {
ResourceHandler *h = get_handler(i);
alloc_handler(h);
}
main_thread_id = SDL_ThreadID();
if(h->procs.init != NULL) {
h->procs.init();
}
}
if(!env_get("TAISEI_NOASYNC", 0)) {
EventHandler h = {
@ -559,6 +547,10 @@ void free_resources(bool all) {
}
if(all) {
if(handler->procs.shutdown != NULL) {
handler->procs.shutdown();
}
hashtable_free(handler->private->mapping);
free(handler->private);
}
@ -575,6 +567,4 @@ void free_resources(bool all) {
if(!env_get("TAISEI_NOASYNC", 0)) {
events_unregister_handler(resource_asyncload_handler);
}
IMG_Quit();
}

View file

@ -22,6 +22,7 @@ typedef enum ResourceType {
RES_MODEL,
RES_POSTPROCESS,
RES_SPRITE,
RES_FONT,
RES_NUMTYPES,
} ResourceType;
@ -61,6 +62,12 @@ typedef void* (*ResourceEndLoadProc)(void *opaque, const char *path, uint flags)
// Unloads a resource, freeing all allocated to it memory.
typedef void (*ResourceUnloadProc)(void *res);
// Called during resource subsystem initialization
typedef void (*ResourceInit)(void);
// Called during resource subsystem shutdown
typedef void (*ResourceShutdown)(void);
typedef struct ResourceHandlerPrivate ResourceHandlerPrivate;
typedef struct ResourceHandler {
@ -76,6 +83,8 @@ typedef struct ResourceHandler {
ResourceBeginLoadProc begin_load;
ResourceEndLoadProc end_load;
ResourceUnloadProc unload;
ResourceInit init;
ResourceShutdown shutdown;
} procs;
ResourceHandlerPrivate *private;

View file

@ -20,12 +20,29 @@ static void* load_texture_begin(const char *path, uint flags);
static void* load_texture_end(void *opaque, const char *path, uint flags);
static void free_texture(Texture *tex);
static void init_sdl_image(void) {
int want_flags = IMG_INIT_JPG | IMG_INIT_PNG;
int init_flags = IMG_Init(want_flags);
if((want_flags & init_flags) != want_flags) {
log_warn(
"SDL_image doesn't support some of the formats we want. "
"Requested: %i, got: %i. "
"Textures may fail to load",
want_flags,
init_flags
);
}
}
ResourceHandler texture_res_handler = {
.type = RES_TEXTURE,
.typename = "texture",
.subdir = TEX_PATH_PREFIX,
.procs = {
.init = init_sdl_image,
.shutdown = IMG_Quit,
.find = texture_path,
.check = check_texture_path,
.begin_load = load_texture_begin,

View file

@ -92,8 +92,6 @@ static void video_update_quality(void) {
init_fbo_pair(&resources.fbo_pairs.bg, bg, TEX_TYPE_RGB);
init_fbo_pair(&resources.fbo_pairs.fg, fg, TEX_TYPE_RGB);
init_fbo_pair(&resources.fbo_pairs.rgba, fg, TEX_TYPE_RGBA);
reload_fonts(text);
}
static uint32_t get_fullscreen_flag(void) {
@ -520,7 +518,6 @@ void video_init(void) {
config_set_callback(CONFIG_VID_RESIZABLE, video_cfg_resizable_callback);
config_set_callback(CONFIG_FG_QUALITY, video_quality_callback);
config_set_callback(CONFIG_BG_QUALITY, video_quality_callback);
config_set_callback(CONFIG_TEXT_QUALITY, video_quality_callback);
EventHandler h = {
.proc = video_handle_window_event,