refactored font code so it is reusable
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Tue, 13 Jul 2021 16:34:43 +0000 (11:34 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Tue, 13 Jul 2021 16:34:43 +0000 (11:34 -0500)
modules/bmfont/bmfont_loader.onyx
modules/immediate_mode/shaders/alpha_fragment.glsl
modules/ui/components/button.onyx
modules/ui/components/checkbox.onyx
modules/ui/components/radio.onyx
modules/ui/components/textbox.onyx
modules/ui/font.onyx
modules/ui/module.onyx
modules/ui/ui.onyx

index 837292242e2904bec3fb4c821bb10954f6bc160f..8e98e384f185206450f458022d9bff6e8da835c3 100644 (file)
@@ -5,6 +5,8 @@ use package core
 
 load_bmfont :: (fnt_data: [] u8) -> BMFont {
     bmf: BMFont;
+    memory.set(^bmf, 0, sizeof BMFont);
+    
     map.init(^bmf.pages);
     map.init(^bmf.glyphs);
 
@@ -13,9 +15,9 @@ load_bmfont :: (fnt_data: [] u8) -> BMFont {
     @Cleanup // this was a stupid way of doing this. Just use a f-ing for loop.
     array.map(^bmf.glyphs.entries, ^bmf, (glyph: ^map.Map.Entry(i32, BMFont_Glyph), font: ^BMFont) {
         glyph.value.tex_x = ~~ glyph.value.x / cast(f32) font.common.scale_width;        
-        glyph.value.tex_y = ~~ glyph.value.y / cast(f32) font.common.scale_width;        
+        glyph.value.tex_y = ~~ glyph.value.y / cast(f32) font.common.scale_height;        
         glyph.value.tex_w = ~~ glyph.value.w / cast(f32) font.common.scale_width;        
-        glyph.value.tex_h = ~~ glyph.value.h / cast(f32) font.common.scale_width;        
+        glyph.value.tex_h = ~~ glyph.value.h / cast(f32) font.common.scale_height;        
     });
 
     return bmf;
index 38d89addfb75e448fdcf538abb280e8a7605939e..5e20a208cf54738c62b4586c975ae340bf8f60fd 100644 (file)
@@ -10,6 +10,6 @@ in vec2 v_texture;
 out vec4 fragColor;
 
 void main() {
-    float alpha = texture(u_texture, v_texture).a;
+    float alpha = texture(u_texture, v_texture).r;
     fragColor = v_color * alpha;
 }
\ No newline at end of file
index 7e27da186a833babd33311f1ed40fdceed05ab52..5247d8322af48ecc7a808529c41bfb5f44ffaaab 100644 (file)
@@ -58,13 +58,12 @@ button :: (use r: Rectangle, text: str, theme := ^default_button_theme, site :=
     surface_color  = color_lerp(animation_state.click_time, surface_color, theme.click_color);
     gfx.rect(.{ x0 + border_width, y0 + border_width }, .{ width - border_width * 2, height - border_width * 2 }, surface_color);
 
-    text_width  := bmfont.get_width(^font, text, theme.font_size);
-    text_height := bmfont.get_height(^font, text, theme.font_size);
+    text_width, text_height := main_font->get_dimensions(text, theme.font_size);
 
     @ThemeConfiguration // This always draws the text centered on the button surface.
     draw_text_raw(text,
             x0 + (width - text_width) / 2,
-            y0 + ~~ font.common.baseline * theme.font_size + (height - text_height) / 2,
+            y0 + main_font->get_baseline(theme.font_size) + (height - text_height) / 2,
             theme.font_size, theme.text_color);
 
     move_towards(^animation_state.click_time, 0.0f, theme.click_decay_speed);
index 1d6066a2b139f39d558cb5aaf15fe72fe43f675e..c0e867c2bc89c626bface628de362de36f40557c 100644 (file)
@@ -83,13 +83,12 @@ checkbox :: (use r: Rectangle, value: ^bool, text: str, theme := ^default_checkb
         surface_color
     );
 
-    text_width  := bmfont.get_width(^font, text, theme.font_size);
-    text_height := bmfont.get_height(^font, text, theme.font_size);
+    text_width, text_height := main_font->get_dimensions(text, theme.font_size);
 
     draw_text_raw(
         text,
         x0 + box_size + 4 * 2,   @Cleanup @ThemeConfiguration
-        y0 + ~~ font.common.baseline * theme.font_size + (height - text_height) / 2,
+        y0 + main_font->get_baseline(theme.font_size) + (height - text_height) / 2,
         theme.font_size, theme.text_color);
 
     move_towards(^animation_state.click_time, 0.0f, theme.click_decay_speed);
index f64c56dd2c5c947409afe0e2c10467749d207bc9..c3379a1a6c03ab05911978e77529e42757a78b8c 100644 (file)
@@ -76,13 +76,12 @@ radio :: (use r: Rectangle, selected: ^$T, value: T, text: str, theme := ^defaul
 
     gfx.circle(.{ cx, cy }, radius - theme.radio_border_radius, color=surface_color);
 
-    text_width  := bmfont.get_width(^font, text, theme.font_size);
-    text_height := bmfont.get_height(^font, text, theme.font_size);
+    text_width, text_height := main_font->get_dimensions(text, theme.font_size);
 
     draw_text_raw(
         text,
         x0 + 2 * radius + 4, @ThemeConfiguration
-        y0 + ~~ font.common.baseline * theme.font_size + (height - text_height) / 2,
+        y0 + main_font->get_baseline(theme.font_size) + (height - text_height) / 2,
         theme.font_size, theme.text_color);
 
     move_towards(^animation_state.click_time, 0.0f, theme.click_decay_speed);
index 78c83026a950190b610ac13e3ccc4ecc1e19a317..3e52880868de254de9995c90e22cb9713434fd2c 100644 (file)
@@ -59,11 +59,11 @@ textbox :: (use r: Rectangle, text_buffer: ^string.String_Buffer, placeholder :=
         text = placeholder;
         text_color = theme.placeholder_text_color;
     }
-    text_width  := bmfont.get_width(^font, text, theme.font_size);
-    text_height := bmfont.get_height(^font, text, theme.font_size);
+
+    text_width, text_height := main_font->get_dimensions(text, theme.font_size);
 
     text_x := x0 + border_width;
-    text_y := y0 + ~~ font.common.baseline * theme.font_size + (height - text_height) / 2;
+    text_y := y0 + main_font->get_baseline(theme.font_size) + (height - text_height) / 2;
 
     if is_hot_item(hash) && !is_active_item(hash) {
         if mouse_state.left_button_down && Rectangle.contains(r, mx, my) {
@@ -191,7 +191,8 @@ get_cursor_location :: (text_buffer: ^string.String_Buffer, text_x: f32, text_y:
 
     text := string.buffer_to_str(text_buffer);
 
-    for glyph: bmfont.get_character_positions(^font, text_size, text, text_x, text_y) {
+    bm_font := ^main_font.font;
+    for glyph: bmfont.get_character_positions(bm_font, text_size * main_font.em / ~~bm_font.common.line_height, text, text_x, text_y) {
         if countdown == 0 do return last_x;
 
         last_x = glyph.pos_x;
@@ -212,7 +213,9 @@ get_cursor_position :: (text_buffer: ^string.String_Buffer, text_x: f32, text_y:
     last_x: f32 = text_x;
     text := string.buffer_to_str(text_buffer);
 
-    for glyph: bmfont.get_character_positions(^font, text_size, text, text_x, text_y) {
+    @FontCleanup
+    bm_font := ^main_font.font;
+    for glyph: bmfont.get_character_positions(bm_font, text_size * main_font.em / ~~bm_font.common.line_height, text, text_x, text_y) {
         cursor_position += 1;
         if cursor_position == 1 do continue;
 
index ff0375f6abcf5706a0df8253f8869da784fe7261..313d6e64587e04ea9b2b4bf9edfbd498e2887dd8 100644 (file)
@@ -1,5 +1,71 @@
 package ui
 
+// A simple wrapper for a BMFont and an OpenGL Texture
+
+#private_file RK :: enum { Luminance; Color; }
+
 Font :: struct {
-    texture: gfx.Texture;
-}
\ No newline at end of file
+    texture : gfx.Texture;
+    font    : bmfont.BMFont;
+
+    em: f32 = 32;
+    rendering_kind := RK.Luminance;
+
+    get_width :: (use f: ^Font, text: str, font_size: f32) -> f32 {
+        size := font_size * (em / ~~font.common.line_height);
+        return bmfont.get_width(^font, text, size);
+    }
+
+    get_height :: (use f: ^Font, text: str, font_size: f32) -> f32 {
+        size := font_size * (em / ~~font.common.line_height);
+        return bmfont.get_height(^font, text, size);
+    }
+
+    get_dimensions :: (use f: ^Font, text: str, font_size: f32) -> (width: f32, height: f32) {
+        size := font_size * (em / ~~font.common.line_height);
+        return bmfont.get_width(^font, text, size),
+               bmfont.get_height(^font, text, size);
+    }
+
+    get_baseline :: (use f: ^Font, font_size: f32) -> f32 {
+        size := font_size * (em / ~~font.common.line_height);
+        return ~~font.common.baseline * size;
+    }
+
+    render :: (use f: ^Font, text: str, x: f32, y: f32, font_size: f32, color := gfx.Color4.{1,1,1}) {
+        gfx.set_texture(^texture);
+
+        switch rendering_kind {
+            case .Luminance do gfx.use_alpha_shader(0);
+            case .Color     do gfx.set_texture(0);
+        } 
+
+        size := font_size * (em / ~~font.common.line_height);
+        for glyph: bmfont.get_character_positions(^font, size, text, x, y) {
+            gfx.textured_rect(
+                .{ glyph.pos_x, glyph.pos_y }, .{ glyph.pos_w, glyph.pos_h },
+                .{ glyph.tex_x, glyph.tex_y }, .{ glyph.tex_w, glyph.tex_h },
+                color = color);
+        }
+
+        gfx.set_texture();
+    }
+}
+
+create_font :: (bmfont_data: [] u8, font_texture_data: [] u8) -> Font {
+    font := bmfont.load_bmfont(bmfont_data);
+
+    color_channels_have_data := (font.common.blue_channel + font.common.green_channel + font.common.red_channel) == ~~0;
+
+    // This may not always be right
+    texture_width, texture_height := font.common.scale_width, font.common.scale_height;
+    assert(texture_width * texture_height * (4 if color_channels_have_data else 1) == font_texture_data.count, "Bad font texture size.");
+
+    rendering_kind: RK = (.Color) if color_channels_have_data else .Luminance;
+    font_format := gl.RGBA if color_channels_have_data else gl.LUMINANCE;
+
+    texture := gfx.load_texture(texture_width, texture_height, font_texture_data, font_format, font_format);
+
+    return .{ texture=texture, font=font, rendering_kind=rendering_kind };
+}
+
index 96da6fec582bb63070b05b0e23284a1b5122f79c..4d22c578fbcc1297837223615754a49ba571ae17 100644 (file)
@@ -42,6 +42,7 @@ package ui
 #load "./ui"
 #load "./flow"
 #load "./input"
+#load "./font"
 
 #load "./components/button"
 #load "./components/checkbox"
index 2f08920c417b169ae2600dd2c005435d4f97e15f..3a4f4df265953166c3cea7bdceb0df326490a8a6 100644 (file)
@@ -4,8 +4,7 @@ use package core
 
 @Cleanup // Move these to the theme?
 // Or create a cache of fonts and put pointers/string in the themes?
-#private font : bmfont.BMFont;
-#private font_texture : gfx.Texture;
+#private main_font : Font;
 
 @Temporary
 DEFAULT_TEXT_SIZE :: 1.0f
@@ -78,18 +77,7 @@ is_hot_item :: (id: UI_Id) -> bool {
 
 @FontSizing // Currently, `size` is just a multipler for the baked font size. This should be changed to be height in pixels, or 'em's.
 draw_text_raw :: (text: str, x: f32, y: f32, size := DEFAULT_TEXT_SIZE, color := gfx.Color4.{1,1,1}) {
-    gfx.set_texture(^font_texture);
-    gfx.use_alpha_shader(0);
-
-    for glyph: bmfont.get_character_positions(^font, size, text, x, y) {
-        gfx.textured_rect(
-            .{ glyph.pos_x, glyph.pos_y }, .{ glyph.pos_w, glyph.pos_h },
-            .{ glyph.tex_x, glyph.tex_y }, .{ glyph.tex_w, glyph.tex_h },
-            color = color);
-    }
-    
-    gfx.flush();
-    gfx.set_texture();
+    main_font->render(text, x, y, size, color);
 }
 
 draw_rect :: #match {
@@ -107,7 +95,7 @@ draw_rect :: #match {
 }
 
 draw_text :: (use r: Rectangle, text: str, theme := ^default_text_theme, site := #callsite) -> bool {
-    draw_text_raw(text, x0, y0 + ~~font.common.baseline * theme.font_size, theme.font_size, theme.text_color);
+    draw_text_raw(text, x0, y0 + main_font->get_baseline(theme.font_size), theme.font_size, theme.text_color);
 }
 
 Rectangle :: struct {
@@ -185,7 +173,7 @@ get_site_hash :: (site: CallSite, increment := 0) -> UI_Id {
 }
 
 get_text_width :: (text: str, size := DEFAULT_TEXT_SIZE) -> f32 {
-    return font->get_width(text, size);
+    return main_font->get_width(text, size);
 }
 
 
@@ -195,14 +183,17 @@ get_text_width :: (text: str, size := DEFAULT_TEXT_SIZE) -> f32 {
 // to a font and it will remain valid. Probably want some kind of arena
 // with a map from i32 -> ^BMFont.
 #private init_font :: () {
-    fnt_file_data := #file_contents "./resources/fonts/Calibri.fnt";
-    texture_data := #file_contents "./resources/fonts/Calibri_0.data";
+    fnt_file_data := #file_contents "./resources/fonts/FiraCode.fnt";
+    texture_data := #file_contents "./resources/fonts/FiraCode.data";
 
-    font = bmfont.load_bmfont(fnt_file_data);
+    main_font = create_font(fnt_file_data, texture_data);
+}
 
-    tex_width, tex_height := font.common.scale_width, font.common.scale_height;
+TEMP_switch_font :: () {
+    fnt_file_data := #file_contents "./resources/fonts/Calibri.fnt";
+    texture_data := #file_contents "./resources/fonts/Calibri_0.data";
 
-    font_texture = gfx.load_texture(tex_width, tex_height, texture_data, gl.ALPHA, gl.ALPHA);
+    main_font = create_font(fnt_file_data, texture_data);
 }