From e930cc6bdc7f62befac063d7d9d016ffb0a64f1a Mon Sep 17 00:00:00 2001 From: David Moc Date: Sat, 30 May 2026 21:53:05 +0200 Subject: Added the old repo, refactored it, added the C jit. --- src/font.c | 258 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 258 insertions(+) create mode 100644 src/font.c (limited to 'src/font.c') diff --git a/src/font.c b/src/font.c new file mode 100644 index 0000000..62985e4 --- /dev/null +++ b/src/font.c @@ -0,0 +1,258 @@ +#include "font.h" +#include "util.h" + +#define STB_TRUETYPE_IMPLEMENTATION +#include "stb_truetype.h" + +#include +#include +#include + +const char *font_choose_path(const char *explicit_font_path) { + static char candidate[4096]; + + if (explicit_font_path && explicit_font_path[0]) { + return explicit_font_path; + } + + const char *env_font = getenv("ECEX_FONT"); + if (env_font && env_font[0]) { + return env_font; + } + + const char *guix_env = getenv("GUIX_ENVIRONMENT"); + if (guix_env && guix_env[0]) { + snprintf(candidate, sizeof(candidate), + "%s/share/fonts/truetype/DejaVuSansMono.ttf", guix_env); + if (ecex_file_exists(candidate)) return candidate; + + snprintf(candidate, sizeof(candidate), + "%s/share/fonts/truetype/dejavu/DejaVuSansMono.ttf", guix_env); + if (ecex_file_exists(candidate)) return candidate; + + snprintf(candidate, sizeof(candidate), + "%s/share/fonts/dejavu/DejaVuSansMono.ttf", guix_env); + if (ecex_file_exists(candidate)) return candidate; + } + + const char *home = getenv("HOME"); + if (home && home[0]) { + snprintf(candidate, sizeof(candidate), + "%s/.guix-profile/share/fonts/truetype/DejaVuSansMono.ttf", home); + if (ecex_file_exists(candidate)) return candidate; + + snprintf(candidate, sizeof(candidate), + "%s/.guix-profile/share/fonts/truetype/dejavu/DejaVuSansMono.ttf", home); + if (ecex_file_exists(candidate)) return candidate; + + snprintf(candidate, sizeof(candidate), + "%s/.guix-profile/share/fonts/dejavu/DejaVuSansMono.ttf", home); + if (ecex_file_exists(candidate)) return candidate; + } + + if (ecex_file_exists("/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf")) { + return "/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf"; + } + + if (ecex_file_exists("/usr/share/fonts/TTF/DejaVuSansMono.ttf")) { + return "/usr/share/fonts/TTF/DejaVuSansMono.ttf"; + } + + return NULL; +} + +int font_load(font_t *font, const char *path, float size_px) { + if (!font || !path) return -1; + + memset(font, 0, sizeof(*font)); + + size_t ttf_size = 0; + char *ttf_data = ecex_read_entire_file(path, &ttf_size); + if (!ttf_data) { + fprintf(stderr, "failed to read font: %s\n", path); + return -1; + } + + unsigned char *bitmap = calloc(1, ECEX_FONT_BITMAP_W * ECEX_FONT_BITMAP_H); + if (!bitmap) { + free(ttf_data); + return -1; + } + + stbtt_fontinfo info; + int ascent = 0; + int descent = 0; + int line_gap = 0; + + if (stbtt_InitFont(&info, (const unsigned char *)ttf_data, 0)) { + float scale = stbtt_ScaleForPixelHeight(&info, size_px); + stbtt_GetFontVMetrics(&info, &ascent, &descent, &line_gap); + + font->ascent_px = (float)ascent * scale; + font->descent_px = (float)(-descent) * scale; + font->line_gap_px = (float)line_gap * scale; + } + + if (font->ascent_px <= 0.0f) { + font->ascent_px = size_px * 0.80f; + } + + if (font->descent_px <= 0.0f) { + font->descent_px = size_px * 0.20f; + } + + if (font->line_gap_px < 0.0f) { + font->line_gap_px = 0.0f; + } + + int bake_result = stbtt_BakeFontBitmap( + (const unsigned char *)ttf_data, + 0, + size_px, + bitmap, + ECEX_FONT_BITMAP_W, + ECEX_FONT_BITMAP_H, + ECEX_FONT_FIRST_CHAR, + ECEX_FONT_CHAR_COUNT, + font->chars + ); + + free(ttf_data); + + if (bake_result <= 0) { + fprintf(stderr, "failed to bake font atlas from: %s\n", path); + free(bitmap); + return -1; + } + + glGenTextures(1, &font->texture); + glBindTexture(GL_TEXTURE_2D, font->texture); + + glTexImage2D(GL_TEXTURE_2D, + 0, + GL_ALPHA, + ECEX_FONT_BITMAP_W, + ECEX_FONT_BITMAP_H, + 0, + GL_ALPHA, + GL_UNSIGNED_BYTE, + bitmap); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + free(bitmap); + + font->size_px = size_px; + font->line_height = font->ascent_px + font->descent_px + font->line_gap_px; + + if (font->line_height < size_px * 1.15f) { + font->line_height = size_px * 1.15f; + } + + fprintf(stderr, "ecex: loaded font: %s\n", path); + return 0; +} + +void font_free(font_t *font) { + if (!font) return; + + if (font->texture) { + glDeleteTextures(1, &font->texture); + font->texture = 0; + } +} + +void draw_text(font_t *font, float x, float y, const char *text) { + if (!font || !font->texture || !text) return; + + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, font->texture); + + glBegin(GL_QUADS); + + float start_x = x; + + for (const char *p = text; *p; p++) { + unsigned char c = (unsigned char)*p; + + if (c == '\n') { + x = start_x; + y += font->line_height; + continue; + } + + if (c == '\t') { + x += font->size_px * 4.0f; + continue; + } + + if (c < ECEX_FONT_FIRST_CHAR || + c >= ECEX_FONT_FIRST_CHAR + ECEX_FONT_CHAR_COUNT) { + c = '?'; + } + + stbtt_aligned_quad q; + + stbtt_GetBakedQuad(font->chars, + ECEX_FONT_BITMAP_W, + ECEX_FONT_BITMAP_H, + c - ECEX_FONT_FIRST_CHAR, + &x, + &y, + &q, + 1); + + glTexCoord2f(q.s0, q.t0); + glVertex2f(q.x0, q.y0); + + glTexCoord2f(q.s1, q.t0); + glVertex2f(q.x1, q.y0); + + glTexCoord2f(q.s1, q.t1); + glVertex2f(q.x1, q.y1); + + glTexCoord2f(q.s0, q.t1); + glVertex2f(q.x0, q.y1); + } + + glEnd(); + + glDisable(GL_TEXTURE_2D); +} + +float text_width(font_t *font, const char *text) { + if (!font || !text) return 0.0f; + + float x = 0.0f; + float y = 0.0f; + + for (const char *p = text; *p; p++) { + unsigned char c = (unsigned char)*p; + + if (c == '\n') break; + + if (c == '\t') { + x += font->size_px * 4.0f; + continue; + } + + if (c < ECEX_FONT_FIRST_CHAR || + c >= ECEX_FONT_FIRST_CHAR + ECEX_FONT_CHAR_COUNT) { + c = '?'; + } + + stbtt_aligned_quad q; + + stbtt_GetBakedQuad(font->chars, + ECEX_FONT_BITMAP_W, + ECEX_FONT_BITMAP_H, + c - ECEX_FONT_FIRST_CHAR, + &x, + &y, + &q, + 1); + } + + return x; +} -- cgit v1.2.3