From 157f04060ea8edffdb99e0ab890b595a618443a3 Mon Sep 17 00:00:00 2001 From: SamratGhale Date: Sun, 22 Mar 2026 14:13:00 +0545 Subject: [PATCH] Optimize glyph calculation --- glyph/glyph.odin | 166 ++++++++++++++++++++++------------------------- 1 file changed, 79 insertions(+), 87 deletions(-) diff --git a/glyph/glyph.odin b/glyph/glyph.odin index ec6b575..a54922b 100644 --- a/glyph/glyph.odin +++ b/glyph/glyph.odin @@ -3,11 +3,11 @@ package e2glyph /* Provides text rendering using glfw, stb_truetype for the engine + TODO: + Optimize for only calculate when there's changes */ - - import "core:math" import os "core:os" import "core:mem" @@ -52,16 +52,10 @@ GlyphState :: struct { } -glyph_init :: proc(glyph: ^GlyphState) { +glyph_init :: proc(glyph: ^GlyphState, filepath : string) { ok : bool - glyph.program_id, ok = gl.load_shaders_source( - #load("./shaders/font_vert.glsl"), - #load("./shaders/font_frag.glsl"), - ) - - //gl.UseProgram(glyph.program_id) - + glyph.program_id, ok = gl.load_shaders_source(#load("./shaders/font_vert.glsl"), #load("./shaders/font_frag.glsl"), ) gl.CreateBuffers(1, &glyph.rect_instances_vbo) gl.CreateVertexArrays(1, &glyph.vao) @@ -88,19 +82,7 @@ glyph_init :: proc(glyph: ^GlyphState) { gl.CreateTextures(gl.TEXTURE_RECTANGLE, 1, &glyph.atlas_texture) gl.TextureStorage2D(glyph.atlas_texture, 1, gl.RGB8, glyph.atlas_width, glyph.atlas_height) - - /* - gl.BindVertexArray(0) - gl.BindBuffer(gl.ARRAY_BUFFER, 0) - gl.BindTexture(gl.TEXTURE_RECTANGLE, 0) - gl.BindTexture(gl.TEXTURE_2D, 0) - gl.DisableVertexArrayAttrib(glyph.vao, 0) - gl.DisableVertexArrayAttrib(glyph.vao, 1) - gl.DisableVertexArrayAttrib(glyph.vao, 2) - gl.DisableVertexArrayAttrib(glyph.vao, 3) - */ - - font_data, _ := os.read_entire_file_from_path("./Ubuntu-Regular.ttf", context.allocator) + font_data, _ := os.read_entire_file_from_path(filepath, context.allocator) stbtt.InitFont(&glyph.font_info, &font_data[0], 0) @@ -160,92 +142,103 @@ glyph_draw_font :: proc(glyph_state: ^GlyphState, text: string, pos : [2]f32, co glyph_state.curr.x += 2 * glyph_state.font_size_pt } else { + horizontal_filter_padding, subpixel_positioning_left_padding: i32 = 1, 1 assert(codepoint <= 127) glyph_atlas := glyph_state.atlas_items[codepoint] - glyph_index := stbtt.FindGlyphIndex(&glyph_state.font_info, c) - - x0, y0, x1, y1: i32 = 0, 0, 0, 0 - - stbtt.GetGlyphBitmapBox(&glyph_state.font_info, glyph_index, font_scale, font_scale, &x0, &y0, &x1, &y1) - - glyph_width_px : i32 = x1 - x0 - glyph_height_px: i32 = y1 - y0 - - distance_from_baseline_to_top_px: i32 = -y0 - - if glyph_width_px > 0 && glyph_height_px > 0 + if !glyph_atlas.filled { - padded_glyph_width_px : i32 = subpixel_positioning_left_padding + horizontal_filter_padding + glyph_width_px + horizontal_filter_padding - padded_glyph_height_px: i32 = glyph_height_px + glyph_index := stbtt.FindGlyphIndex(&glyph_state.font_info, c) - atlas_item_width, atlas_item_height: i32 = 32, 32 + x0, y0, x1, y1: i32 = 0, 0, 0, 0 - atlas_item_x: i32 = i32( codepoint % u32(glyph_state.atlas_width / atlas_item_width), ) * atlas_item_width - atlas_item_y: i32 = i32( codepoint / u32(glyph_state.atlas_height / atlas_item_height), ) * atlas_item_height + stbtt.GetGlyphBitmapBox(&glyph_state.font_info, glyph_index, font_scale, font_scale, &x0, &y0, &x1, &y1) - assert(padded_glyph_width_px <= atlas_item_width && padded_glyph_height_px <= atlas_item_height) + glyph_width_px : i32 = x1 - x0 + glyph_height_px: i32 = y1 - y0 - horizontal_resolution: i32 = 3 - bitmap_stride : i32 = atlas_item_width * horizontal_resolution - bitmap_size : uint = uint(bitmap_stride * atlas_item_height) - glyph_bitmap, _ := mem.alloc_bytes(int(bitmap_size)) - glyph_offset_x := (subpixel_positioning_left_padding + horizontal_filter_padding) * horizontal_resolution + distance_from_baseline_to_top_px: i32 = -y0 - stbtt.MakeGlyphBitmap(&glyph_state.font_info, &glyph_bitmap[glyph_offset_x], atlas_item_width * horizontal_resolution, atlas_item_height, bitmap_stride, font_scale * f32(horizontal_resolution), font_scale, glyph_index) - - atlas_item_bitmap, _ := mem.alloc_bytes(int(bitmap_size)) - - filter_weights: [5]u8 = {0x08, 0x4D, 0x56, 0x4D, 0x08} - - for y in 0 ..< padded_glyph_height_px + if glyph_width_px > 0 && glyph_height_px > 0 { + padded_glyph_width_px : i32 = subpixel_positioning_left_padding + horizontal_filter_padding + glyph_width_px + horizontal_filter_padding + padded_glyph_height_px: i32 = glyph_height_px - x_end: i32 = padded_glyph_width_px * horizontal_resolution - 1 + atlas_item_width, atlas_item_height: i32 = 32, 32 - for x in 4 ..< x_end { - filter_weight_index: i32 = 0 - sum: i32 - kernel_x_end: i32 = (x == x_end - 1) ? x + 1 : x + 2 + atlas_item_x: i32 = i32( codepoint % u32(glyph_state.atlas_width / atlas_item_width), ) * atlas_item_width + atlas_item_y: i32 = i32( codepoint / u32(glyph_state.atlas_height / atlas_item_height), ) * atlas_item_height - for kernel_x in x - 2 ..= kernel_x_end - { - assert(kernel_x >= 0 && kernel_x < x_end + 1) - assert(y >= 0 && y < padded_glyph_height_px) + assert(padded_glyph_width_px <= atlas_item_width && padded_glyph_height_px <= atlas_item_height) - offset: i32 = kernel_x + y * bitmap_stride - assert(offset >= 0 && uint(offset) < bitmap_size) + horizontal_resolution: i32 = 3 + bitmap_stride : i32 = atlas_item_width * horizontal_resolution + bitmap_size : uint = uint(bitmap_stride * atlas_item_height) + glyph_bitmap, _ := mem.alloc_bytes(int(bitmap_size)) + glyph_offset_x := (subpixel_positioning_left_padding + horizontal_filter_padding) * horizontal_resolution - sum += i32(i32(glyph_bitmap[offset]) * i32(filter_weights[filter_weight_index])) + stbtt.MakeGlyphBitmap( + &glyph_state.font_info, + &glyph_bitmap[glyph_offset_x], + atlas_item_width * horizontal_resolution, + atlas_item_height, bitmap_stride, + font_scale * f32(horizontal_resolution), + font_scale, glyph_index + ) - filter_weight_index += 1 + atlas_item_bitmap, _ := mem.alloc_bytes(int(bitmap_size)) + + filter_weights: [5]u8 = {0x08, 0x4D, 0x56, 0x4D, 0x08} + + for y in 0 ..< padded_glyph_height_px + { + + x_end: i32 = padded_glyph_width_px * horizontal_resolution - 1 + + for x in 4 ..< x_end { + filter_weight_index: i32 = 0 + sum: i32 + kernel_x_end: i32 = (x == x_end - 1) ? x + 1 : x + 2 + + for kernel_x in x - 2 ..= kernel_x_end + { + assert(kernel_x >= 0 && kernel_x < x_end + 1) + assert(y >= 0 && y < padded_glyph_height_px) + + offset: i32 = kernel_x + y * bitmap_stride + assert(offset >= 0 && uint(offset) < bitmap_size) + + sum += i32(i32(glyph_bitmap[offset]) * i32(filter_weights[filter_weight_index])) + + filter_weight_index += 1 + } + sum = sum / 255 + atlas_item_bitmap[x + y * bitmap_stride] = (sum > 255) ? 255 : u8(sum) } - sum = sum / 255 - atlas_item_bitmap[x + y * bitmap_stride] = (sum > 255) ? 255 : u8(sum) } + + mem.free_bytes(glyph_bitmap) + gl.TextureSubImage2D(glyph_state.atlas_texture, 0, atlas_item_x, atlas_item_y, atlas_item_width, atlas_item_height, gl.RGB, gl.UNSIGNED_BYTE, rawptr(&atlas_item_bitmap[0])) + + mem.free_bytes(atlas_item_bitmap) + glyph_atlas.tex_coords.l = f32(atlas_item_x) + glyph_atlas.tex_coords.t = f32(atlas_item_y) + glyph_atlas.tex_coords.r = f32(atlas_item_x + padded_glyph_width_px) + glyph_atlas.tex_coords.b = f32(atlas_item_y + padded_glyph_height_px) + } else { + glyph_atlas.tex_coords.l = -1 + glyph_atlas.tex_coords.t = -1 + glyph_atlas.tex_coords.r = -1 + glyph_atlas.tex_coords.b = -1 } - mem.free_bytes(glyph_bitmap) - gl.TextureSubImage2D(glyph_state.atlas_texture, 0, atlas_item_x, atlas_item_y, atlas_item_width, atlas_item_height, gl.RGB, gl.UNSIGNED_BYTE, rawptr(&atlas_item_bitmap[0])) - - mem.free_bytes(atlas_item_bitmap) - glyph_atlas.tex_coords.l = f32(atlas_item_x) - glyph_atlas.tex_coords.t = f32(atlas_item_y) - glyph_atlas.tex_coords.r = f32(atlas_item_x + padded_glyph_width_px) - glyph_atlas.tex_coords.b = f32(atlas_item_y + padded_glyph_height_px) - } else { - glyph_atlas.tex_coords.l = -1 - glyph_atlas.tex_coords.t = -1 - glyph_atlas.tex_coords.r = -1 - glyph_atlas.tex_coords.b = -1 + glyph_atlas.index = glyph_index + glyph_atlas.distance_b2t = distance_from_baseline_to_top_px + glyph_atlas.filled = true + glyph_state.atlas_items[codepoint] = glyph_atlas } - glyph_atlas.index = glyph_index - glyph_atlas.distance_b2t = distance_from_baseline_to_top_px - glyph_atlas.filled = true - glyph_state.atlas_items[codepoint] = glyph_atlas - glyph_advance_width, glyph_left_side_bearing: i32 = 0, 0 stbtt.GetGlyphHMetrics(&glyph_state.font_info, glyph_atlas.index, &glyph_advance_width, &glyph_left_side_bearing) @@ -274,7 +267,6 @@ glyph_draw_font :: proc(glyph_state: ^GlyphState, text: string, pos : [2]f32, co r.color = text_color r.index = u32(i + 1) - append(&glyph_state.rect_buffer, r) }