#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); }