243 lines
8.5 KiB
C
243 lines
8.5 KiB
C
#include "font.h"
|
|
#include "arena.h"
|
|
#include "ttf.h"
|
|
#include "work.h"
|
|
#include "scratch.h"
|
|
#include "asset_cache.h"
|
|
#include "resource.h"
|
|
#include "log.h"
|
|
#include "string.h"
|
|
#include "renderer.h"
|
|
|
|
#define LOOKUP_TABLE_SIZE (256)
|
|
|
|
GLOBAL u32 g_font_codes[] = {
|
|
0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F,0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F,0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5A,0x5B,0x5C,0x5D,0x5E,0x5F,0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F,0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7A,0x7B,0x7C,0x7D,0x7E,0x7F,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F,0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF,0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF,0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF
|
|
};
|
|
|
|
struct font_task_params {
|
|
struct font_task_params *next_free;
|
|
|
|
struct asset *asset;
|
|
f32 point_size;
|
|
u64 path_len;
|
|
char path_cstr[1024];
|
|
};
|
|
|
|
struct font_task_params_store {
|
|
struct font_task_params *head_free;
|
|
struct arena arena;
|
|
struct sys_mutex mutex;
|
|
};
|
|
|
|
/* ========================== *
|
|
* Global state
|
|
* ========================== */
|
|
|
|
GLOBAL struct {
|
|
struct font_task_params_store params;
|
|
} L = { 0 }, DEBUG_LVAR(L_font);
|
|
|
|
/* ========================== *
|
|
* Startup
|
|
* ========================== */
|
|
|
|
struct font_startup_receipt font_startup(struct work_startup_receipt *work_sr,
|
|
struct renderer_startup_receipt *renderer_sr,
|
|
struct asset_cache_startup_receipt *asset_cache_sr,
|
|
struct ttf_startup_receipt *ttf_sr,
|
|
struct resource_startup_receipt *resource_sr)
|
|
{
|
|
(UNUSED)work_sr;
|
|
(UNUSED)renderer_sr;
|
|
(UNUSED)asset_cache_sr;
|
|
(UNUSED)ttf_sr;
|
|
(UNUSED)resource_sr;
|
|
|
|
L.params.arena = arena_alloc(GIGABYTE(64));
|
|
L.params.mutex = sys_mutex_alloc();
|
|
|
|
return (struct font_startup_receipt) { 0 };
|
|
}
|
|
|
|
/* ========================== *
|
|
* Load task param store
|
|
* ========================== */
|
|
|
|
INTERNAL struct font_task_params *font_task_params_alloc(void)
|
|
{
|
|
struct font_task_params *p = NULL;
|
|
sys_mutex_lock(&L.params.mutex);
|
|
{
|
|
if (L.params.head_free) {
|
|
p = L.params.head_free;
|
|
L.params.head_free = p->next_free;
|
|
} else {
|
|
p = arena_push_zero(&L.params.arena, struct font_task_params);
|
|
}
|
|
}
|
|
sys_mutex_unlock(&L.params.mutex);
|
|
return p;
|
|
}
|
|
|
|
INTERNAL void font_task_params_release(struct font_task_params *p)
|
|
{
|
|
sys_mutex_lock(&L.params.mutex);
|
|
{
|
|
p->next_free = L.params.head_free;
|
|
L.params.head_free = p;
|
|
}
|
|
sys_mutex_unlock(&L.params.mutex);
|
|
}
|
|
|
|
/* ========================== *
|
|
* Load
|
|
* ========================== */
|
|
|
|
INTERNAL WORK_TASK_FUNC_DEF(font_load_asset_task, vparams)
|
|
{
|
|
__prof;
|
|
struct font_task_params *params = (struct font_task_params *)vparams;
|
|
struct temp_arena scratch = scratch_begin_no_conflict();
|
|
struct string path = string_from_cstr_len(params->path_cstr, params->path_len);
|
|
f32 point_size = params->point_size;
|
|
struct asset *asset = params->asset;
|
|
|
|
logf_info("Loading font \"%F\" (point size %F)", FMT_STR(path), FMT_FLOAT((f64)point_size));
|
|
sys_timestamp_t start_ts = sys_timestamp();
|
|
|
|
ASSERT(string_ends_with(path, STR(".ttf")));
|
|
if (!resource_exists(path)) {
|
|
/* FIME: Load baked font instead of panicking */
|
|
sys_panic(string_format(scratch.arena,
|
|
STR("Font \"%F\" not found"),
|
|
FMT_STR(path)));
|
|
}
|
|
|
|
ASSERT(ARRAY_COUNT(g_font_codes) < LOOKUP_TABLE_SIZE);
|
|
|
|
/* Decode */
|
|
struct resource res = resource_open(path);
|
|
struct ttf_decode_result result = ttf_decode(scratch.arena, res.bytes, point_size, g_font_codes, ARRAY_COUNT(g_font_codes));
|
|
resource_close(res);
|
|
|
|
/* Send texture to GPU */
|
|
struct renderer_handle texture_renderer_handle = renderer_texture_alloc(result.image_data);
|
|
|
|
/* Allocate store memory */
|
|
struct font *font = NULL;
|
|
{
|
|
struct asset_cache_store store = asset_cache_store_open();
|
|
font = arena_push_zero(store.arena, struct font);
|
|
font->glyphs = arena_push_array(store.arena, struct font_glyph, result.glyphs_count);
|
|
font->lookup = arena_push_array_zero(store.arena, u16, LOOKUP_TABLE_SIZE);
|
|
asset_cache_store_close(&store);
|
|
}
|
|
|
|
/* Set font data */
|
|
font->texture = (struct texture) {
|
|
.renderer_handle = texture_renderer_handle,
|
|
.size = V2(result.image_data.width, result.image_data.height),
|
|
};
|
|
font->glyphs_count = result.glyphs_count;
|
|
font->point_size = point_size;
|
|
|
|
/* FIXME: Load banked font instead of panicking */
|
|
if (font->glyphs_count <= 0) {
|
|
sys_panic(string_format(scratch.arena,
|
|
STR("Parsed 0 glyphs from font \"%F\"!"),
|
|
FMT_STR(path)));
|
|
}
|
|
|
|
/* Copy glyphs from decode result */
|
|
MEMCPY(font->glyphs, result.glyphs, sizeof(*font->glyphs) * result.glyphs_count);
|
|
|
|
/* Build lookup table */
|
|
for (u64 i = 0; i < ARRAY_COUNT(g_font_codes); ++i) {
|
|
u32 codepoint = g_font_codes[i];
|
|
font->lookup[codepoint] = result.cache_indices[i];
|
|
}
|
|
|
|
font_task_params_release(params);
|
|
|
|
logf_info("Finished loading font \"%F\" (point size %F) in %F seconds", FMT_STR(path), FMT_FLOAT((f64)point_size), FMT_FLOAT(sys_timestamp_seconds(sys_timestamp() - start_ts)));
|
|
asset_cache_mark_ready(asset, font);
|
|
|
|
scratch_end_and_decommit(scratch);
|
|
}
|
|
|
|
/* Returns the asset from the asset cache */
|
|
struct asset *font_load_asset(struct string path, f32 point_size, b32 help)
|
|
{
|
|
__prof;
|
|
struct temp_arena scratch = scratch_begin_no_conflict();
|
|
|
|
/* Concatenate point_size to path for key */
|
|
struct string key = string_format(scratch.arena,
|
|
STR("%F%F_font"),
|
|
FMT_STR(path),
|
|
FMT_FLOAT_P((f64)point_size, 3));
|
|
u64 hash = asset_cache_hash(key);
|
|
b32 is_first_touch;
|
|
struct asset *asset = asset_cache_touch(key, hash, &is_first_touch);
|
|
|
|
if (is_first_touch) {
|
|
/* Assemble task params */
|
|
struct font_task_params *params = font_task_params_alloc();
|
|
if (path.len > (sizeof(params->path_cstr) - 1)) {
|
|
sys_panic(string_format(scratch.arena,
|
|
STR("Font path \"%F\" too long!"),
|
|
FMT_STR(path)));
|
|
}
|
|
cstr_buff_from_string(BUFFER_FROM_ARRAY(params->path_cstr), path);
|
|
params->path_len = path.len;
|
|
params->asset = asset;
|
|
params->point_size = point_size;
|
|
|
|
/* Push task */
|
|
asset_cache_mark_loading(asset);
|
|
struct work_handle wh = { 0 };
|
|
if (help) {
|
|
wh = work_push_task_and_help(&font_load_asset_task, params, WORK_PRIORITY_NORMAL);
|
|
} else {
|
|
wh = work_push_task(&font_load_asset_task, params, WORK_PRIORITY_NORMAL);
|
|
}
|
|
asset_cache_set_work(asset, &wh);
|
|
}
|
|
|
|
scratch_end(scratch);
|
|
return asset;
|
|
}
|
|
|
|
struct font *font_load_async(struct string path, f32 point_size)
|
|
{
|
|
__prof;
|
|
struct asset *asset = font_load_asset(path, point_size, false);
|
|
struct font *f = (struct font *)asset_cache_get_store_data(asset);
|
|
return f;
|
|
}
|
|
|
|
struct font *font_load(struct string path, f32 point_size)
|
|
{
|
|
__prof;
|
|
struct asset *asset = font_load_asset(path, point_size, true);
|
|
asset_cache_wait(asset);
|
|
struct font *f = (struct font *)asset_cache_get_store_data(asset);
|
|
return f;
|
|
}
|
|
|
|
/* ========================== *
|
|
* Other
|
|
* ========================== */
|
|
|
|
struct font_glyph *font_get_glyph(struct font *font, u32 codepoint)
|
|
{
|
|
if (codepoint < LOOKUP_TABLE_SIZE) {
|
|
u16 index = font->lookup[codepoint];
|
|
if (index < font->glyphs_count) {
|
|
return &font->glyphs[index];
|
|
}
|
|
}
|
|
return &font->glyphs[0];
|
|
}
|