From 671266f22e98fd417afdf4c627b4c4de7813ab60 Mon Sep 17 00:00:00 2001 From: Brendan Hansen Date: Tue, 13 Jul 2021 11:34:43 -0500 Subject: [PATCH] refactored font code so it is reusable --- modules/bmfont/bmfont_loader.onyx | 6 +- .../shaders/alpha_fragment.glsl | 2 +- modules/ui/components/button.onyx | 5 +- modules/ui/components/checkbox.onyx | 5 +- modules/ui/components/radio.onyx | 5 +- modules/ui/components/textbox.onyx | 13 ++-- modules/ui/font.onyx | 70 ++++++++++++++++++- modules/ui/module.onyx | 1 + modules/ui/ui.onyx | 33 ++++----- 9 files changed, 100 insertions(+), 40 deletions(-) diff --git a/modules/bmfont/bmfont_loader.onyx b/modules/bmfont/bmfont_loader.onyx index 83729224..8e98e384 100644 --- a/modules/bmfont/bmfont_loader.onyx +++ b/modules/bmfont/bmfont_loader.onyx @@ -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; diff --git a/modules/immediate_mode/shaders/alpha_fragment.glsl b/modules/immediate_mode/shaders/alpha_fragment.glsl index 38d89add..5e20a208 100644 --- a/modules/immediate_mode/shaders/alpha_fragment.glsl +++ b/modules/immediate_mode/shaders/alpha_fragment.glsl @@ -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 diff --git a/modules/ui/components/button.onyx b/modules/ui/components/button.onyx index 7e27da18..5247d832 100644 --- a/modules/ui/components/button.onyx +++ b/modules/ui/components/button.onyx @@ -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); diff --git a/modules/ui/components/checkbox.onyx b/modules/ui/components/checkbox.onyx index 1d6066a2..c0e867c2 100644 --- a/modules/ui/components/checkbox.onyx +++ b/modules/ui/components/checkbox.onyx @@ -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); diff --git a/modules/ui/components/radio.onyx b/modules/ui/components/radio.onyx index f64c56dd..c3379a1a 100644 --- a/modules/ui/components/radio.onyx +++ b/modules/ui/components/radio.onyx @@ -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); diff --git a/modules/ui/components/textbox.onyx b/modules/ui/components/textbox.onyx index 78c83026..3e528808 100644 --- a/modules/ui/components/textbox.onyx +++ b/modules/ui/components/textbox.onyx @@ -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; diff --git a/modules/ui/font.onyx b/modules/ui/font.onyx index ff0375f6..313d6e64 100644 --- a/modules/ui/font.onyx +++ b/modules/ui/font.onyx @@ -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 }; +} + diff --git a/modules/ui/module.onyx b/modules/ui/module.onyx index 96da6fec..4d22c578 100644 --- a/modules/ui/module.onyx +++ b/modules/ui/module.onyx @@ -42,6 +42,7 @@ package ui #load "./ui" #load "./flow" #load "./input" +#load "./font" #load "./components/button" #load "./components/checkbox" diff --git a/modules/ui/ui.onyx b/modules/ui/ui.onyx index 2f08920c..3a4f4df2 100644 --- a/modules/ui/ui.onyx +++ b/modules/ui/ui.onyx @@ -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); } -- 2.25.1