This commit is contained in:
agra
2026-03-02 19:47:25 +02:00
parent 812bc6d6ec
commit e63c946116
33 changed files with 32185 additions and 202 deletions

104
ui/font.sx Normal file
View File

@@ -0,0 +1,104 @@
#import "modules/std.sx";
#import "ui/types.sx";
FIRST_CHAR :s32: 32;
NUM_CHARS :s32: 96;
ATLAS_W :s32: 512;
ATLAS_H :s32: 512;
// Matches stbtt_bakedchar memory layout
BakedChar :: struct {
x0, y0, x1, y1: u16;
xoff, yoff, xadvance: f32;
}
// Matches stbtt_aligned_quad memory layout
AlignedQuad :: struct {
x0, y0, s0, t0: f32;
x1, y1, s1, t1: f32;
}
FontAtlas :: struct {
texture_id: u32;
font_size: f32;
char_data: [*]BakedChar;
bitmap: [*]u8;
ascent: f32;
descent: f32;
line_height: f32;
// Bake font glyphs into a bitmap. Call upload_texture() after GL is ready.
init :: (self: *FontAtlas, path: [:0]u8, size: f32) {
file_size : s32 = 0;
font_data := read_file_bytes(path, @file_size);
if xx font_data == 0 {
out("Failed to load font: ");
out(path);
out("\n");
return;
}
self.font_size = size;
// Allocate baked char data (96 entries for ASCII 32..127)
self.char_data = xx context.allocator.alloc(xx NUM_CHARS * size_of(BakedChar));
// Bake font bitmap (512x512 single-channel alpha)
bitmap_size : s64 = xx ATLAS_W * xx ATLAS_H;
self.bitmap = xx context.allocator.alloc(bitmap_size);
stbtt_BakeFontBitmap(font_data, 0, size, self.bitmap, ATLAS_W, ATLAS_H, FIRST_CHAR, NUM_CHARS, xx self.char_data);
// Get font vertical metrics
fontinfo : [256]u8 = ---;
stbtt_InitFont(xx @fontinfo, font_data, 0);
ascent_i : s32 = 0;
descent_i : s32 = 0;
linegap_i : s32 = 0;
stbtt_GetFontVMetrics(xx @fontinfo, @ascent_i, @descent_i, @linegap_i);
scale := stbtt_ScaleForPixelHeight(xx @fontinfo, size);
self.ascent = xx ascent_i * scale;
self.descent = xx descent_i * scale;
self.line_height = self.ascent - self.descent + xx linegap_i * scale;
font_data_ptr : *void = xx font_data;
free(font_data_ptr);
out("Font loaded: ");
out(path);
out("\n");
}
measure_text :: (self: *FontAtlas, text: string, font_size: f32) -> Size {
if self.char_data == null { return Size.zero(); }
scale := font_size / self.font_size;
xpos : f32 = 0.0;
ypos : f32 = 0.0;
q : AlignedQuad = ---;
i : s64 = 0;
while i < text.len {
ch : s32 = xx text[i];
if ch >= FIRST_CHAR and ch < FIRST_CHAR + NUM_CHARS {
stbtt_GetBakedQuad(xx self.char_data, ATLAS_W, ATLAS_H, ch - FIRST_CHAR, @xpos, @ypos, xx @q, 1);
}
i += 1;
}
Size.{ width = xpos * scale, height = self.line_height * scale };
}
}
// Global font atlas pointer for views (Label, Button) to access
g_font : *FontAtlas = xx 0;
set_global_font :: (font: *FontAtlas) {
g_font = font;
}
// Convenience measurement function for views
measure_text :: (text: string, font_size: f32) -> Size {
if xx g_font == 0 {
// Fallback approximate measurement
scale := font_size / 16.0;
return Size.{ width = xx text.len * 8.0 * scale, height = font_size };
}
g_font.measure_text(text, font_size);
}