townengine/src/rendering/twn_text.c

200 lines
6.0 KiB
C
Raw Normal View History

#include "twn_rendering_c.h"
#include "twn_rendering.h"
#include "twn_util.h"
#include "twn_config.h"
#include "twn_engine_context_c.h"
2024-08-23 02:41:52 +00:00
#include <stb_truetype.h>
#define ASCII_START 32
#define ASCII_END 128
#define NUM_DISPLAY_ASCII ((ASCII_END - ASCII_START) + 1)
static FontData *text_load_font_data(const char *path, int height_px) {
FontData *font_data = ccalloc(1, sizeof *font_data);
2024-08-23 02:41:52 +00:00
font_data->file_path = path;
font_data->height_px = height_px;
unsigned char* bitmap = ccalloc(TEXT_FONT_TEXTURE_SIZE * TEXT_FONT_TEXTURE_SIZE, 1);
{
unsigned char *buf = NULL;
int64_t buf_len = file_to_bytes(path, &buf);
stbtt_InitFont(&font_data->info, buf, stbtt_GetFontOffsetForIndex(buf, 0));
/* might as well get these now, for later */
font_data->file_bytes = buf;
font_data->file_bytes_len = buf_len;
font_data->scale_factor = stbtt_ScaleForPixelHeight(&font_data->info, (float)height_px);
stbtt_GetFontVMetrics(
&font_data->info,
&font_data->ascent,
&font_data->descent,
&font_data->line_gap
);
font_data->ascent = (int)((float)font_data->ascent * font_data->scale_factor);
font_data->descent = (int)((float)font_data->descent * font_data->scale_factor);
font_data->line_gap = (int)((float)font_data->line_gap * font_data->scale_factor);
stbtt_pack_context pctx;
stbtt_PackBegin(&pctx, bitmap, TEXT_FONT_TEXTURE_SIZE, TEXT_FONT_TEXTURE_SIZE, 0, 1, NULL);
stbtt_PackSetOversampling(&pctx, TEXT_FONT_OVERSAMPLING, TEXT_FONT_OVERSAMPLING);
stbtt_PackFontRange(&pctx, buf, 0, (float)height_px, ASCII_START, NUM_DISPLAY_ASCII, font_data->char_data);
stbtt_PackEnd(&pctx);
}
font_data->texture = create_gpu_texture(TEXT_FONT_FILTERING, true);
upload_gpu_texture(
font_data->texture,
bitmap,
1,
2024-08-23 02:41:52 +00:00
TEXT_FONT_TEXTURE_SIZE,
TEXT_FONT_TEXTURE_SIZE
2024-08-23 02:41:52 +00:00
);
2024-09-25 22:42:34 +00:00
SDL_free(bitmap);
2024-08-23 02:41:52 +00:00
return font_data;
}
static void text_destroy_font_data(FontData *font_data) {
2024-09-25 22:42:34 +00:00
SDL_free(font_data->file_bytes);
delete_gpu_texture(font_data->texture);
2024-09-25 22:42:34 +00:00
SDL_free(font_data);
2024-08-23 02:41:52 +00:00
}
static void text_draw_with(FontData* font_data, char* text, Vec2 position, Color color) {
VertexBuffer vertex_array = 0;
if (vertex_array == 0)
vertex_array = create_vertex_buffer();
2024-08-23 02:41:52 +00:00
const size_t len = SDL_strlen(text);
2024-08-23 02:41:52 +00:00
VertexBufferBuilder payload = build_vertex_buffer(vertex_array, get_text_payload_size() * len);
2024-08-23 02:41:52 +00:00
for (size_t i = 0; i < len; ++i) {
const char c = text[i];
2024-08-23 02:41:52 +00:00
/* outside the range of what we want to display */
//if (c < ASCII_START || c > ASCII_END)
if (c < ASCII_START)
continue;
/* stb_truetype.h conveniently provides everything we need to draw here! */
stbtt_aligned_quad quad;
stbtt_GetPackedQuad(
font_data->char_data,
TEXT_FONT_TEXTURE_SIZE,
TEXT_FONT_TEXTURE_SIZE,
c - ASCII_START,
&position.x,
&position.y,
&quad,
true
);
/* have to do this so the "origin" is at the top left */
/* maybe there's a better way, or maybe this isn't a good idea... */
/* who knows */
quad.y0 += (float)font_data->ascent;
quad.y1 += (float)font_data->ascent;
push_text_payload_to_vertex_buffer_builder(font_data, &payload, quad);
2024-08-23 02:41:52 +00:00
}
finally_draw_text(font_data, len, color, vertex_array);
2024-08-23 02:41:52 +00:00
}
static void ensure_font_cache(const char *font_path, int height_px) {
/* HACK: stupid, bad, don't do this */
bool is_cached = false;
for (size_t i = 0; i < arrlenu(ctx.text_cache.data); ++i) {
FontData *font_data = ctx.text_cache.data[i];
2024-09-25 22:42:34 +00:00
if ((SDL_strcmp(font_path, font_data->file_path) == 0) && height_px == font_data->height_px) {
2024-08-23 02:41:52 +00:00
is_cached = true;
break;
}
}
if (!is_cached) {
FontData *new_font_data = text_load_font_data(font_path, height_px);
2024-08-23 02:41:52 +00:00
arrput(ctx.text_cache.data, new_font_data);
}
}
static FontData *get_font_data(const char *font_path, int height_px) {
FontData *font_data = NULL;
2024-08-23 02:41:52 +00:00
for (size_t i = 0; i < arrlenu(ctx.text_cache.data); ++i) {
font_data = ctx.text_cache.data[i];
2024-09-25 22:42:34 +00:00
if ((SDL_strcmp(font_path, font_data->file_path) == 0) && height_px == font_data->height_px) {
2024-08-23 02:41:52 +00:00
break;
}
}
return font_data;
}
void render_text(const TextPrimitive *text) {
FontData *font_data = get_font_data(text->font, text->height_px);
2024-08-23 02:41:52 +00:00
text_draw_with(font_data, text->text, text->position, text->color);
}
void text_cache_init(TextCache *cache) {
2024-08-23 02:41:52 +00:00
arrsetlen(cache->data, 0);
}
void text_cache_deinit(TextCache *cache) {
2024-08-23 02:41:52 +00:00
for (size_t i = 0; i < arrlenu(ctx.text_cache.data); ++i) {
text_destroy_font_data(ctx.text_cache.data[i]);
}
arrfree(cache->data);
}
void push_text(char *string, Vec2 position, int height_px, Color color, const char *font_path) {
2024-08-23 02:41:52 +00:00
ensure_font_cache(font_path, height_px);
2024-09-25 22:42:34 +00:00
/* the original string might not be around by the time it's used, so copy it */
2024-08-23 02:41:52 +00:00
/* TODO: arena */
2024-09-25 22:42:34 +00:00
char *dup_string = SDL_strdup(string);
2024-08-23 02:41:52 +00:00
TextPrimitive text = {
2024-08-23 02:41:52 +00:00
.color = color,
.position = position,
.text = dup_string,
.font = font_path,
.height_px = height_px,
};
Primitive2D primitive = {
2024-08-23 02:41:52 +00:00
.type = PRIMITIVE_2D_TEXT,
.text = text,
};
arrput(ctx.render_queue_2d, primitive);
}
int text_get_width(char *string, int height_px, const char *font_path) {
2024-08-23 02:41:52 +00:00
ensure_font_cache(font_path, height_px);
FontData *font_data = get_font_data(font_path, height_px);
2024-08-23 02:41:52 +00:00
int length = 0;
for (const char *p = string; *p != '\0'; ++p) {
const char c = *p;
int advance_width = 0;
int left_side_bearing = 0;
stbtt_GetCodepointHMetrics(&font_data->info, (int)c, &advance_width, &left_side_bearing);
length += advance_width;
}
return (int)((float)length * font_data->scale_factor);
}