diff --git a/code/font/vefontcache/LRU.odin b/code/font/vefontcache/LRU.odin index 69b2615..c263c0b 100644 --- a/code/font/vefontcache/LRU.odin +++ b/code/font/vefontcache/LRU.odin @@ -6,33 +6,33 @@ The choice was made to keep the LRU cache implementation as close to the origina import "base:runtime" -PoolListIter :: i32 -PoolListValue :: u64 +Pool_ListIter :: i32 +Pool_ListValue :: u64 -PoolListItem :: struct { - prev : PoolListIter, - next : PoolListIter, - value : PoolListValue, +Pool_List_Item :: struct { + prev : Pool_ListIter, + next : Pool_ListIter, + value : Pool_ListValue, } -PoolList :: struct { - items : [dynamic]PoolListItem, - free_list : [dynamic]PoolListIter, - front : PoolListIter, - back : PoolListIter, +Pool_List :: struct { + items : [dynamic]Pool_List_Item, + free_list : [dynamic]Pool_ListIter, + front : Pool_ListIter, + back : Pool_ListIter, size : i32, capacity : i32, dbg_name : string, } -pool_list_init :: proc( pool : ^PoolList, capacity : i32, dbg_name : string = "" ) +pool_list_init :: proc( pool : ^Pool_List, capacity : i32, dbg_name : string = "" ) { - error : AllocatorError - pool.items, error = make( [dynamic]PoolListItem, int(capacity) ) + error : Allocator_Error + pool.items, error = make( [dynamic]Pool_List_Item, int(capacity) ) assert( error == .None, "VEFontCache.pool_list_init : Failed to allocate items array") resize( & pool.items, capacity ) - pool.free_list, error = make( [dynamic]PoolListIter, len = 0, cap = int(capacity) ) + pool.free_list, error = make( [dynamic]Pool_ListIter, len = 0, cap = int(capacity) ) assert( error == .None, "VEFontCache.pool_list_init : Failed to allocate free_list array") resize( & pool.free_list, capacity ) @@ -53,17 +53,17 @@ pool_list_init :: proc( pool : ^PoolList, capacity : i32, dbg_name : string = "" back = -1 } -pool_list_free :: proc( pool : ^PoolList ) { +pool_list_free :: proc( pool : ^Pool_List ) { delete( pool.items) delete( pool.free_list) } -pool_list_reload :: proc( pool : ^PoolList, allocator : Allocator ) { +pool_list_reload :: proc( pool : ^Pool_List, allocator : Allocator ) { reload_array( & pool.items, allocator ) reload_array( & pool.free_list, allocator ) } -pool_list_push_front :: proc( pool : ^PoolList, value : PoolListValue ) +pool_list_push_front :: proc( pool : ^Pool_List, value : Pool_ListValue ) { using pool if size >= capacity do return @@ -90,7 +90,7 @@ pool_list_push_front :: proc( pool : ^PoolList, value : PoolListValue ) size += 1 } -pool_list_erase :: proc( pool : ^PoolList, iter : PoolListIter ) +pool_list_erase :: proc( pool : ^Pool_List, iter : Pool_ListIter ) { using pool if size <= 0 do return @@ -119,7 +119,7 @@ pool_list_erase :: proc( pool : ^PoolList, iter : PoolListIter ) } } -pool_list_move_to_front :: #force_inline proc( pool : ^PoolList, iter : PoolListIter ) +pool_list_move_to_front :: #force_inline proc( pool : ^Pool_List, iter : Pool_ListIter ) { using pool @@ -136,13 +136,13 @@ pool_list_move_to_front :: #force_inline proc( pool : ^PoolList, iter : PoolList front = iter } -pool_list_peek_back :: #force_inline proc ( pool : ^PoolList ) -> PoolListValue { +pool_list_peek_back :: #force_inline proc ( pool : ^Pool_List ) -> Pool_ListValue { assert( pool.back != - 1 ) value := pool.items[ pool.back ].value return value } -pool_list_pop_back :: #force_inline proc( pool : ^PoolList ) -> PoolListValue { +pool_list_pop_back :: #force_inline proc( pool : ^Pool_List ) -> Pool_ListValue { if pool.size <= 0 do return 0 assert( pool.back != -1 ) @@ -153,8 +153,10 @@ pool_list_pop_back :: #force_inline proc( pool : ^PoolList ) -> PoolListValue { LRU_Link :: struct { pad_top : u64, + value : i32, - ptr : PoolListIter, + ptr : Pool_ListIter, + pad_bottom : u64, } @@ -162,34 +164,34 @@ LRU_Cache :: struct { capacity : i32, num : i32, table : map[u64]LRU_Link, - key_queue : PoolList, + key_queue : Pool_List, } -LRU_init :: proc( cache : ^LRU_Cache, capacity : i32, dbg_name : string = "" ) { - error : AllocatorError +lru_init :: proc( cache : ^LRU_Cache, capacity : i32, dbg_name : string = "" ) { + error : Allocator_Error cache.capacity = capacity cache.table, error = make( map[u64]LRU_Link, uint(capacity) ) - assert( error == .None, "VEFontCache.LRU_init : Failed to allocate cache's table") + assert( error == .None, "VEFontCache.lru_init : Failed to allocate cache's table") pool_list_init( & cache.key_queue, capacity, dbg_name = dbg_name ) } -LRU_free :: proc( cache : ^LRU_Cache ) { +lru_free :: proc( cache : ^LRU_Cache ) { pool_list_free( & cache.key_queue ) delete( cache.table ) } -LRU_reload :: #force_inline proc( cache : ^LRU_Cache, allocator : Allocator ) { +lru_reload :: #force_inline proc( cache : ^LRU_Cache, allocator : Allocator ) { reload_map( & cache.table, allocator ) pool_list_reload( & cache.key_queue, allocator ) } -LRU_find :: #force_inline proc "contextless" ( cache : ^LRU_Cache, key : u64, must_find := false ) -> (LRU_Link, bool) { +lru_find :: #force_inline proc "contextless" ( cache : ^LRU_Cache, key : u64, must_find := false ) -> (LRU_Link, bool) { link, success := cache.table[key] return link, success } -LRU_get :: #force_inline proc( cache: ^LRU_Cache, key : u64 ) -> i32 { +lru_get :: #force_inline proc( cache: ^LRU_Cache, key : u64 ) -> i32 { if link, ok := &cache.table[ key ]; ok { pool_list_move_to_front(&cache.key_queue, link.ptr) return link.value @@ -197,7 +199,7 @@ LRU_get :: #force_inline proc( cache: ^LRU_Cache, key : u64 ) -> i32 { return -1 } -LRU_get_next_evicted :: #force_inline proc ( cache : ^LRU_Cache ) -> u64 { +lru_get_next_evicted :: #force_inline proc ( cache : ^LRU_Cache ) -> u64 { if cache.key_queue.size >= cache.capacity { evict := pool_list_peek_back( & cache.key_queue ) return evict @@ -205,15 +207,15 @@ LRU_get_next_evicted :: #force_inline proc ( cache : ^LRU_Cache ) -> u64 { return 0xFFFFFFFFFFFFFFFF } -LRU_peek :: #force_inline proc ( cache : ^LRU_Cache, key : u64, must_find := false ) -> i32 { - iter, success := LRU_find( cache, key, must_find ) +lru_peek :: #force_inline proc ( cache : ^LRU_Cache, key : u64, must_find := false ) -> i32 { + iter, success := lru_find( cache, key, must_find ) if success == false { return -1 } return iter.value } -LRU_put :: #force_inline proc( cache : ^LRU_Cache, key : u64, value : i32 ) -> u64 +lru_put :: #force_inline proc( cache : ^LRU_Cache, key : u64, value : i32 ) -> u64 { if link, ok := & cache.table[ key ]; ok { pool_list_move_to_front( & cache.key_queue, link.ptr ) @@ -237,8 +239,8 @@ LRU_put :: #force_inline proc( cache : ^LRU_Cache, key : u64, value : i32 ) -> u return evict } -LRU_refresh :: proc( cache : ^LRU_Cache, key : u64 ) { - link, success := LRU_find( cache, key ) +lru_refresh :: proc( cache : ^LRU_Cache, key : u64 ) { + link, success := lru_find( cache, key ) pool_list_erase( & cache.key_queue, link.ptr ) pool_list_push_front( & cache.key_queue, key ) link.ptr = cache.key_queue.front diff --git a/code/font/vefontcache/Readme.md b/code/font/vefontcache/Readme.md index b959d44..fba2547 100644 --- a/code/font/vefontcache/Readme.md +++ b/code/font/vefontcache/Readme.md @@ -4,24 +4,24 @@ This is a port of the [VEFontCache](https://github.com/hypernewbie/VEFontCache) Its original purpose was for use in game engines, however its rendeirng quality and performance is more than adequate for many other applications. -See: [docs/Readme.md](docs/Readme.md) for the library's interface +See: [docs/Readme.md](docs/Readme.md) for the library's interface. ## Building See [scripts/Readme.md](scripts/Readme.md) for building examples or utilizing the provided backends. -Currently the scripts provided & the library itself where developed & tested on Windows. The library itself should not be limited to that OS platform however, just don't have the configuration setup for alternative platforms (yet). +Currently the scripts provided & the library itself were developed & tested on Windows. There are bash scripts for building on linux (they build on WSL but need additional testing). -The library depends on freetype, harfbuzz, & stb_truetype currently to build. +The library depends on freetype, harfbuzz, & stb_truetype to build. Note: freetype and harfbuzz could technically be gutted if the user removes their definitions, however they have not been made into a conditional compilation option (yet). ## Changes from orignal -* Font Parser & Glyph shaper are abstracted to their own interface +* Font Parser & Glyph shaper are abstracted to their own warpper interface * ve_fontcache_loadfile not ported (ust use core:os or os2, then call load_font) * Macro defines have been coverted (mostly) to runtime parameters * Support for hot_reloading -* Curve quality step granularity for glyph rendering can be set on a per font basis. +* Curve quality step interpolation for glyph rendering can be set on a per font basis. ## TODOs @@ -35,18 +35,12 @@ Note: freetype and harfbuzz could technically be gutted if the user removes thei ### Optimization: -* Check if its better to store the generated glyph vertices if they need to be re-cached or directly drawn. +* Check if its better to store the glyph vertices if they need to be re-cached to atlas or directly drawn. * Look into setting up multi-threading by giving each thread a context * There is a heavy performance bottleneck in iterating the text/shape/glyphs on the cpu (single-thread) vs the actual rendering *(if doing thousands of drawing commands)* * draw_text can provide in the context a job list per thread for the user to thenk hookup to their own threading solution to handle. * Context would need to be segregated into staged data structures for each thread to utilize - * Each should have their own? - * draw_list - * draw_layer - * atlas.next_idx - * glyph_draw_buffer - * shape_cache - * This would need to converge to the singlar draw_list on a per layer basis (then user reqeusts a draw_list layer there could a yield to wait for the jobs to finish); if the interface expects the user to issue the commands single-threaded unless, we just assume the user is going to feed the gpu the commands & data through separate threads as well (not ideal ux). + * This would need to converge to the singlar draw_list on a per layer basis. The interface expects the user to issue commands single-threaded unless, its assumed the user is going to feed the gpu the commands & data through separate threads as well (not ideal ux). * How the contexts are given jobs should be left up to the user (can recommend a screen quadrant based approach in demo examples) Failed Attempts: diff --git a/code/font/vefontcache/atlas.odin b/code/font/vefontcache/atlas.odin index 47876b0..a211026 100644 --- a/code/font/vefontcache/atlas.odin +++ b/code/font/vefontcache/atlas.odin @@ -1,6 +1,6 @@ package vefontcache -AtlasRegionKind :: enum u8 { +Atlas_Region_Kind :: enum u8 { None = 0x00, A = 0x41, B = 0x42, @@ -10,7 +10,7 @@ AtlasRegionKind :: enum u8 { Ignore = 0xFF, // ve_fontcache_cache_glyph_to_atlas uses a -1 value in clear draw call } -AtlasRegion :: struct { +Atlas_Region :: struct { state : LRU_Cache, width : i32, @@ -29,13 +29,13 @@ Atlas :: struct { glyph_padding : i32, - region_a : AtlasRegion, - region_b : AtlasRegion, - region_c : AtlasRegion, - region_d : AtlasRegion, + region_a : Atlas_Region, + region_b : Atlas_Region, + region_c : Atlas_Region, + region_d : Atlas_Region, } -atlas_bbox :: proc( atlas : ^Atlas, region : AtlasRegionKind, local_idx : i32 ) -> (position, size: Vec2) +atlas_bbox :: proc( atlas : ^Atlas, region : Atlas_Region_Kind, local_idx : i32 ) -> (position, size: Vec2) { switch region { @@ -79,14 +79,12 @@ atlas_bbox :: proc( atlas : ^Atlas, region : AtlasRegionKind, local_idx : i32 ) position.x += f32(atlas.region_d.offset.x) position.y += f32(atlas.region_d.offset.y) - case .Ignore: fallthrough - case .None: fallthrough - case .E: + case .Ignore, .None, .E: } return } -decide_codepoint_region :: proc(ctx : ^Context, entry : ^Entry, glyph_index : Glyph ) -> (region_kind : AtlasRegionKind, region : ^AtlasRegion, over_sample : Vec2) +decide_codepoint_region :: proc(ctx : ^Context, entry : ^Entry, glyph_index : Glyph ) -> (region_kind : Atlas_Region_Kind, region : ^Atlas_Region, over_sample : Vec2) { if parser_is_glyph_empty(&entry.parser_info, glyph_index) { return .None, nil, {} @@ -104,11 +102,11 @@ decide_codepoint_region :: proc(ctx : ^Context, entry : ^Entry, glyph_index : Gl bounds_height_scaled := i32(bounds_height * entry.size_scale + glyph_padding) // Use a lookup table for faster region selection - region_lookup := [4]struct { kind: AtlasRegionKind, region: ^AtlasRegion } { - { .A, & atlas.region_a }, - { .B, & atlas.region_b }, - { .C, & atlas.region_c }, - { .D, & atlas.region_d }, + region_lookup := [4]struct { kind: Atlas_Region_Kind, region: ^Atlas_Region } { + { .A, & atlas.region_a }, + { .B, & atlas.region_b }, + { .C, & atlas.region_c }, + { .D, & atlas.region_d }, } for region in region_lookup do if bounds_width_scaled <= region.region.width && bounds_height_scaled <= region.region.height { diff --git a/code/font/vefontcache/docs/Readme.md b/code/font/vefontcache/docs/Readme.md index b0c3aef..cf96e26 100644 --- a/code/font/vefontcache/docs/Readme.md +++ b/code/font/vefontcache/docs/Readme.md @@ -3,7 +3,9 @@ Notes --- -Freetype implementation supports specifying a FT_Memory handle which is a pointer to a FT_MemoryRect. This can be used to define an allocator for the parser. Currently this library does not wrap this interface (yet). If using freetype its recommend to update `parser_init` with the necessary changes to wrap the context's backing allocator for freetype to utilize. +The freetype setup is not finished. Specifically due to cache_glyph_freetype not parsing the glyph outline data structure properly. + +Freetype supports specifying a FT_Memory handle which is a pointer to a FT_MemoryRect. This can be used to define an allocator for the parser. Currently this library does not wrap this interface (yet). If using freetype its recommend to update `parser_init` with the necessary changes to wrap the context's backing allocator for freetype to utilize. ```c struct FT_MemoryRec_ @@ -15,19 +17,19 @@ Freetype implementation supports specifying a FT_Memory handle which is a pointe }; ``` -This library (seems) to perform best if the text commands are fed in 'whitespace aware chunks', where instead of feeding it entire blobs of text, the user identfies the "words" in the text and feeding the visible and whitespce chunks derived from this to draw_text as separate calls. This improves the caching of the text shapes. The downside is there has to be a time where the text is parsed into tokens beforehand so that the this iteration does not have to occur continously. +This library (seems) to perform best if the text commands are fed in 'whitespace aware chunks', where instead of feeding it entire blobs of text, the user identfies "words" in the text and feeding the visible and whitespce chunks derived from this to draw_text as separate calls. It improves the caching of the text shapes. The downside is there has to be a time where the text is parsed into tokens beforehand so that the this iteration does not have to occur continously. ### startup Initializes a provided context. -There are a large amount of parameters to tune the library instance to the user's preference. By default, keep in mind the library defaults to utilize stb_truetype as the font parser and harfbuzz (soon...) for the shaper. +There are a large amount of parameters to tune the library instance to the user's preference. By default, keep in mind the library defaults to utilize stb_truetype as the font parser and harfbuzz for the shaper. -Much of the data structures within the context struct are not fixed-capacity allocations so make sure that the backing allocator utilized can handle it. +Much of the data structures within the context struct are not fixed-capacity allocations so make sure that the backing allocator can handle it. ### hot_reload -The library supports being used in a dynamically loaded module. If this occurs simply make sure to call this procedure with a reference to the backing allocator provided during startup as all dynamic containers tend to lose a proper reference to the allocator's procedure. +The library supports being used in a dynamically loaded module. If its hot-reloaded simply make sure to call this procedure with a reference to the backing allocator provided during startup as all dynamic containers tend to lose a proper reference to the allocator's procedure. ### shutdown @@ -45,7 +47,7 @@ Will provide the current cursor_pos for the resulting text drawn. ### set_color -Sets the color to utilize on `DrawCall`s for FrameBuffer.Target or .Target_Uncached passes +Sets the color to utilize on `Draw_Call`s for FrameBuffer.Target or .Target_Uncached passes ### get_draw_list @@ -55,8 +57,8 @@ By default, if get_draw_list is called, it will first call `optimize_draw_list` ### get_draw_list_layer Get the enqueued draw_list for the current "layer". -A layer is considered the slice of the drawlist's content from the last call to `flush_draw_list_layer` onward. -By default, if get_draw_list_layer is called, it will first call `optimize_draw_list` for the user to optimize the slice (exlusively) of the draw list's draw calls. If this is undesired, make sure to pass `optimize_before_returning = false` in the arguments. +A layer is considered the slice of the `Draw_List`'s content from the last call to `flush_draw_list_layer` onward. +By default, if `get_draw_list_layer` is called, it will first call `optimize_draw_list` for the user to optimize the slice (exlusively) of the draw list's draw calls. If this is undesired, make sure to pass `optimize_before_returning = false` in the arguments. The draw layer offsets are cleared with `flush_draw_list` @@ -70,7 +72,7 @@ Will update the draw list layer with the latest offset based on the current leng ### measure_text_size -Provides a Vec2 the width and height occupied by the provided text string. The y is measured to be the the largest glyph box bounds height of the text. The width is derived from the `end_cursor_pos` field from a `ShapedText` entry. +Provides a Vec2 the width and height occupied by the provided text string. The y is measured to be the the largest glyph box bounds height of the text. The width is derived from the `end_cursor_pos` field from a `Shaped_Text` entry. ### get_font_vertical_metrics diff --git a/code/font/vefontcache/draw.odin b/code/font/vefontcache/draw.odin index 5fcd883..f45e8b6 100644 --- a/code/font/vefontcache/draw.odin +++ b/code/font/vefontcache/draw.odin @@ -8,16 +8,16 @@ Vertex :: struct { u, v : f32, } -DrawCall :: struct { - pass : FrameBufferPass, +Draw_Call :: struct { + pass : Frame_Buffer_Pass, start_index : u32, end_index : u32, clear_before_draw : b32, - region : AtlasRegionKind, + region : Atlas_Region_Kind, colour : Colour, } -DrawCall_Default :: DrawCall { +Draw_Call_Default :: Draw_Call { pass = .None, start_index = 0, end_index = 0, @@ -26,14 +26,14 @@ DrawCall_Default :: DrawCall { colour = { 1.0, 1.0, 1.0, 1.0 } } -DrawList :: struct { +Draw_List :: struct { vertices : [dynamic]Vertex, indices : [dynamic]u32, - calls : [dynamic]DrawCall, + calls : [dynamic]Draw_Call, } // TODO(Ed): This was a rough translation of the raw values the orignal was using, need to give better names... -FrameBufferPass :: enum u32 { +Frame_Buffer_Pass :: enum u32 { None = 0, Glyph = 1, Atlas = 2, @@ -41,7 +41,7 @@ FrameBufferPass :: enum u32 { Target_Uncached = 4, } -GlyphDrawBuffer :: struct { +Glyph_Draw_Buffer :: struct { over_sample : Vec2, batch : i32, width : i32, @@ -49,11 +49,11 @@ GlyphDrawBuffer :: struct { draw_padding : i32, batch_x : i32, - clear_draw_list : DrawList, - draw_list : DrawList, + clear_draw_list : Draw_List, + draw_list : Draw_List, } -blit_quad :: proc( draw_list : ^DrawList, p0 : Vec2 = {0, 0}, p1 : Vec2 = {1, 1}, uv0 : Vec2 = {0, 0}, uv1 : Vec2 = {1, 1} ) +blit_quad :: proc( draw_list : ^Draw_List, p0 : Vec2 = {0, 0}, p1 : Vec2 = {1, 1}, uv0 : Vec2 = {0, 0}, uv1 : Vec2 = {1, 1} ) { // profile(#procedure) // logf("Blitting: xy0: %0.2f, %0.2f xy1: %0.2f, %0.2f uv0: %0.2f, %0.2f uv1: %0.2f, %0.2f", @@ -89,9 +89,9 @@ blit_quad :: proc( draw_list : ^DrawList, p0 : Vec2 = {0, 0}, p1 : Vec2 = {1, 1} } // TODO(Ed): glyph caching cannot be handled in a 'font parser' abstraction. Just going to have explicit procedures to grab info neatly... -cache_glyph_freetype :: proc(ctx: ^Context, font: FontID, glyph_index: Glyph, entry: ^Entry, bounds_0, bounds_1: Vec2, scale, translate: Vec2) -> b32 +cache_glyph_freetype :: proc(ctx: ^Context, font: Font_ID, glyph_index: Glyph, entry: ^Entry, bounds_0, bounds_1: Vec2, scale, translate: Vec2) -> b32 { - draw_filled_path_freetype :: proc( draw_list : ^DrawList, outside_point : Vec2, path : []Vertex, + draw_filled_path_freetype :: proc( draw_list : ^Draw_List, outside_point : Vec2, path : []Vertex, scale := Vec2 { 1, 1 }, translate := Vec2 { 0, 0 }, debug_print_verbose : b32 = false @@ -106,7 +106,7 @@ cache_glyph_freetype :: proc(ctx: ^Context, font: FontID, glyph_index: Glyph, en } v_offset := cast(u32) len(draw_list.vertices) - for point in path + for point in path { transformed_point := Vertex { pos = point.pos * scale + translate, @@ -130,9 +130,9 @@ cache_glyph_freetype :: proc(ctx: ^Context, font: FontID, glyph_index: Glyph, en // Close the path by connecting the last vertex to the first two to_add := [3]u32 { - v_offset, - v_offset + cast(u32)(len(path) - 1), - v_offset + 1 + v_offset, + v_offset + cast(u32)(len(path) - 1), + v_offset + 1 } append( indices, ..to_add[:] ) } @@ -158,8 +158,8 @@ cache_glyph_freetype :: proc(ctx: ^Context, font: FontID, glyph_index: Glyph, en return false } - draw := DrawCall_Default - draw.pass = FrameBufferPass.Glyph + draw := Draw_Call_Default + draw.pass = Frame_Buffer_Pass.Glyph draw.start_index = cast(u32) len(ctx.draw_list.indices) contours := slice.from_ptr(cast( [^]i16) outline.contours, int(outline.n_contours)) @@ -178,57 +178,57 @@ cache_glyph_freetype :: proc(ctx: ^Context, font: FontID, glyph_index: Glyph, en prev_point: Vec2 first_point: Vec2 - for idx := start_index; idx < end_index; idx += 1 + for idx := start_index; idx < end_index; idx += 1 + { + current_pos := Vec2 { f32( points[idx].x ), f32( points[idx].y ) } + if ( tags[idx] & 1 ) == 0 { - current_pos := Vec2 { f32( points[idx].x ), f32( points[idx].y ) } - if ( tags[idx] & 1 ) == 0 + // If current point is off-curve + if (idx == start_index || (tags[ idx - 1 ] & 1) != 0) { - // If current point is off-curve - if (idx == start_index || (tags[ idx - 1 ] & 1) != 0) - { - // current is the first or following an on-curve point - prev_point = current_pos - } - else - { - // current and previous are off-curve, calculate midpoint - midpoint := (prev_point + current_pos) * 0.5 - append( path, Vertex { pos = midpoint } ) // Add midpoint as on-curve point - if idx < end_index - 1 - { - // perform interp from prev_point to current_pos via midpoint - step := 1.0 / entry.curve_quality - for alpha : f32 = 0.0; alpha <= 1.0; alpha += step - { - bezier_point := eval_point_on_bezier3( prev_point, midpoint, current_pos, alpha ) - append( path, Vertex{ pos = bezier_point } ) - } - } - - prev_point = current_pos - } + // current is the first or following an on-curve point + prev_point = current_pos } else { - if idx == start_index { - first_point = current_pos + // current and previous are off-curve, calculate midpoint + midpoint := (prev_point + current_pos) * 0.5 + append( path, Vertex { pos = midpoint } ) // Add midpoint as on-curve point + if idx < end_index - 1 + { + // perform interp from prev_point to current_pos via midpoint + step := 1.0 / entry.curve_quality + for alpha : f32 = 0.0; alpha <= 1.0; alpha += step + { + bezier_point := eval_point_on_bezier3( prev_point, midpoint, current_pos, alpha ) + append( path, Vertex{ pos = bezier_point } ) + } } - if prev_point != (Vec2{}) { - // there was an off-curve point before this - append(path, Vertex{ pos = prev_point}) // Ensure previous off-curve is handled - } - append(path, Vertex{ pos = current_pos}) - prev_point = {} + + prev_point = current_pos } } - - // ensure the contour is closed - if path[0].pos != path[ len(path) - 1 ].pos { - append(path, Vertex{pos = path[0].pos}) + else + { + if idx == start_index { + first_point = current_pos + } + if prev_point != (Vec2{}) { + // there was an off-curve point before this + append(path, Vertex{ pos = prev_point}) // Ensure previous off-curve is handled + } + append(path, Vertex{ pos = current_pos}) + prev_point = {} } - draw_filled_path(&ctx.draw_list, bounds_0, path[:], scale, translate, ctx.debug_print_verbose) - clear(path) - start_index = end_index + } + + // ensure the contour is closed + if path[0].pos != path[ len(path) - 1 ].pos { + append(path, Vertex{pos = path[0].pos}) + } + draw_filled_path(&ctx.draw_list, bounds_0, path[:], scale, translate, ctx.debug_print_verbose) + clear(path) + start_index = end_index } if len(path) > 0 { @@ -244,7 +244,7 @@ cache_glyph_freetype :: proc(ctx: ^Context, font: FontID, glyph_index: Glyph, en } // TODO(Ed): Is it better to cache the glyph vertices for when it must be re-drawn (directly or two atlas)? -cache_glyph :: proc(ctx : ^Context, font : FontID, glyph_index : Glyph, entry : ^Entry, bounds_0, bounds_1 : Vec2, scale, translate : Vec2) -> b32 +cache_glyph :: proc(ctx : ^Context, font : Font_ID, glyph_index : Glyph, entry : ^Entry, bounds_0, bounds_1 : Vec2, scale, translate : Vec2) -> b32 { // profile(#procedure) if glyph_index == Glyph(0) { @@ -265,8 +265,8 @@ cache_glyph :: proc(ctx : ^Context, font : FontID, glyph_index : Glyph, entry : outside := Vec2{bounds_0.x - 21, bounds_0.y - 33} - draw := DrawCall_Default - draw.pass = FrameBufferPass.Glyph + draw := Draw_Call_Default + draw.pass = Frame_Buffer_Pass.Glyph draw.start_index = u32(len(ctx.draw_list.indices)) path := &ctx.temp_path @@ -276,8 +276,8 @@ cache_glyph :: proc(ctx : ^Context, font : FontID, glyph_index : Glyph, entry : { case .Move: if len(path) > 0 { - draw_filled_path(&ctx.draw_list, outside, path[:], scale, translate, ctx.debug_print_verbose) - clear(path) + draw_filled_path(&ctx.draw_list, outside, path[:], scale, translate, ctx.debug_print_verbose) + clear(path) } fallthrough @@ -329,13 +329,13 @@ cache_glyph :: proc(ctx : ^Context, font : FontID, glyph_index : Glyph, entry : * draw_text_shape : Glyph */ cache_glyph_to_atlas :: proc( ctx : ^Context, - font : FontID, + font : Font_ID, glyph_index : Glyph, lru_code : u64, atlas_index : i32, entry : ^Entry, - region_kind : AtlasRegionKind, - region : ^AtlasRegion, + region_kind : Atlas_Region_Kind, + region : ^Atlas_Region, over_sample : Vec2 ) { // profile(#procedure) @@ -356,24 +356,24 @@ cache_glyph_to_atlas :: proc( ctx : ^Context, { if region.next_idx < region.state.capacity { - evicted := LRU_put( & region.state, lru_code, i32(region.next_idx) ) + evicted := lru_put( & region.state, lru_code, i32(region.next_idx) ) atlas_index = i32(region.next_idx) region.next_idx += 1 assert( evicted == lru_code ) } else { - next_evict_codepoint := LRU_get_next_evicted( & region.state ) + next_evict_codepoint := lru_get_next_evicted( & region.state ) assert( next_evict_codepoint != 0xFFFFFFFFFFFFFFFF ) - atlas_index = LRU_peek( & region.state, next_evict_codepoint, must_find = true ) + atlas_index = lru_peek( & region.state, next_evict_codepoint, must_find = true ) assert( atlas_index != -1 ) - evicted := LRU_put( & region.state, lru_code, atlas_index ) + evicted := lru_put( & region.state, lru_code, atlas_index ) assert( evicted == next_evict_codepoint ) } - assert( LRU_get( & region.state, lru_code ) != - 1 ) + assert( lru_get( & region.state, lru_code ) != - 1 ) } atlas := & ctx.atlas @@ -420,7 +420,7 @@ cache_glyph_to_atlas :: proc( ctx : ^Context, glyph_buffer.batch_x += i32(gwidth_scaled_px) screenspace_x_form( & glyph_draw_translate, & glyph_draw_scale, glyph_buffer_size ) - clear_target_region : DrawCall + clear_target_region : Draw_Call { using clear_target_region pass = .Atlas @@ -434,7 +434,7 @@ cache_glyph_to_atlas :: proc( ctx : ^Context, end_index = cast(u32) len(glyph_buffer.clear_draw_list.indices) } - blit_to_atlas : DrawCall + blit_to_atlas : Draw_Call { using blit_to_atlas pass = .Atlas @@ -456,11 +456,11 @@ cache_glyph_to_atlas :: proc( ctx : ^Context, } // If the glyuph is found in the atlas, nothing occurs, otherwise, the glyph call is setup to catch it to the atlas -check_glyph_in_atlas :: #force_inline proc( ctx : ^Context, font : FontID, entry : ^Entry, glyph_index : Glyph, +check_glyph_in_atlas :: #force_inline proc( ctx : ^Context, font : Font_ID, entry : ^Entry, glyph_index : Glyph, lru_code : u64, atlas_index : i32, - region_kind : AtlasRegionKind, - region : ^AtlasRegion, + region_kind : Atlas_Region_Kind, + region : ^Atlas_Region, over_sample : Vec2 ) -> b32 { @@ -476,7 +476,7 @@ check_glyph_in_atlas :: #force_inline proc( ctx : ^Context, font : FontID, entry { if region.next_idx > region.state.capacity { // We will evict LRU. We must predict which LRU will get evicted, and if it's something we've seen then we need to take slowpath and flush batch. - next_evict_codepoint := LRU_get_next_evicted( & region.state ) + next_evict_codepoint := lru_get_next_evicted( & region.state ) seen, success := ctx.temp_codepoint_seen[next_evict_codepoint] assert(success != false) @@ -488,13 +488,13 @@ check_glyph_in_atlas :: #force_inline proc( ctx : ^Context, font : FontID, entry cache_glyph_to_atlas( ctx, font, glyph_index, lru_code, atlas_index, entry, region_kind, region, over_sample ) } - assert( LRU_get( & region.state, lru_code ) != -1 ) + assert( lru_get( & region.state, lru_code ) != -1 ) mark_batch_codepoint_seen( ctx, lru_code) return true } -// ve_fontcache_clear_drawlist -clear_draw_list :: #force_inline proc ( draw_list : ^DrawList ) { +// ve_fontcache_clear_Draw_List +clear_draw_list :: #force_inline proc ( draw_list : ^Draw_List ) { clear( & draw_list.calls ) clear( & draw_list.indices ) clear( & draw_list.vertices ) @@ -538,8 +538,8 @@ directly_draw_massive_glyph :: proc( ctx : ^Context, dst_size := glyph_dst_size * scale textspace_x_form( & glyph_position, & glyph_size, glyph_buffer_size ) - // Add the glyph drawcall. - calls : [2]DrawCall + // Add the glyph Draw_Call. + calls : [2]Draw_Call draw_to_target := & calls[0] { @@ -549,8 +549,8 @@ directly_draw_massive_glyph :: proc( ctx : ^Context, start_index = u32(len(ctx.draw_list.indices)) blit_quad( & ctx.draw_list, - dst, dst + dst_size, - glyph_position, glyph_position + glyph_size ) + dst, dst + dst_size, + glyph_position, glyph_position + glyph_size ) end_index = u32(len(ctx.draw_list.indices)) } @@ -570,9 +570,9 @@ directly_draw_massive_glyph :: proc( ctx : ^Context, // outside_point represents the center point of the fan. // // Note(Original Author): -// WARNING: doesn't actually append drawcall; caller is responsible for actually appending the drawcall. +// WARNING: doesn't actually append Draw_Call; caller is responsible for actually appending the Draw_Call. // ve_fontcache_draw_filled_path -draw_filled_path :: proc( draw_list : ^DrawList, outside_point : Vec2, path : []Vertex, +draw_filled_path :: proc( draw_list : ^Draw_List, outside_point : Vec2, path : []Vertex, scale := Vec2 { 1, 1 }, translate := Vec2 { 0, 0 }, debug_print_verbose : b32 = false @@ -615,7 +615,7 @@ draw_filled_path :: proc( draw_list : ^DrawList, outside_point : Vec2, path : [] } } -draw_text_batch :: proc(ctx: ^Context, entry: ^Entry, shaped: ^ShapedText, +draw_text_batch :: proc(ctx: ^Context, entry: ^Entry, shaped: ^Shaped_Text, batch_start_idx, batch_end_idx : i32, position, scale : Vec2, snap_width, snap_height : f32 ) @@ -634,7 +634,7 @@ draw_text_batch :: proc(ctx: ^Context, entry: ^Entry, shaped: ^ShapedText, region_kind, region, over_sample := decide_codepoint_region( ctx, entry, glyph_index ) lru_code := font_glyph_lru_code( entry.id, glyph_index ) - atlas_index := region_kind != .E ? LRU_get( & region.state, lru_code ) : -1 + atlas_index := region_kind != .E ? lru_get( & region.state, lru_code ) : -1 bounds_0, bounds_1 := parser_get_glyph_box( & entry.parser_info, glyph_index ) vbounds_0 := vec2(bounds_0) vbounds_1 := vec2(bounds_1) @@ -660,7 +660,7 @@ draw_text_batch :: proc(ctx: ^Context, entry: ^Entry, shaped: ^ShapedText, dst_scale := glyph_scale * scale textspace_x_form( & slot_position, & glyph_scale, atlas_size ) - call := DrawCall_Default + call := Draw_Call_Default call.pass = .Target call.colour = ctx.colour call.start_index = u32(len(ctx.draw_list.indices)) @@ -677,9 +677,9 @@ draw_text_batch :: proc(ctx: ^Context, entry: ^Entry, shaped: ^ShapedText, // Helper for draw_text, all raw text content should be confirmed to be either formatting or visible shapes before getting cached. draw_text_shape :: proc( ctx : ^Context, - font : FontID, + font : Font_ID, entry : ^Entry, - shaped : ^ShapedText, + shaped : ^Shaped_Text, position, scale : Vec2, snap_width, snap_height : f32 ) -> (cursor_pos : Vec2) @@ -695,7 +695,7 @@ draw_text_shape :: proc( ctx : ^Context, lru_code := font_glyph_lru_code(entry.id, glyph_index) atlas_index := cast(i32) -1 - if region_kind != .E do atlas_index = LRU_get( & region.state, lru_code ) + if region_kind != .E do atlas_index = lru_get( & region.state, lru_code ) if check_glyph_in_atlas( ctx, font, entry, glyph_index, lru_code, atlas_index, region_kind, region, over_sample ) do continue // We can no longer directly append the shape as it has missing glyphs in the atlas @@ -720,7 +720,7 @@ draw_text_shape :: proc( ctx : ^Context, flush_glyph_buffer_to_atlas :: proc( ctx : ^Context ) { // profile(#procedure) - // Flush drawcalls to draw list + // Flush Draw_Calls to draw list merge_draw_list( & ctx.draw_list, & ctx.glyph_buffer.clear_draw_list ) merge_draw_list( & ctx.draw_list, & ctx.glyph_buffer.draw_list) clear_draw_list( & ctx.glyph_buffer.draw_list ) @@ -729,7 +729,7 @@ flush_glyph_buffer_to_atlas :: proc( ctx : ^Context ) // Clear glyph_update_FBO if ctx.glyph_buffer.batch_x != 0 { - call := DrawCall_Default + call := Draw_Call_Default call.pass = .Glyph call.start_index = 0 call.end_index = 0 @@ -739,11 +739,11 @@ flush_glyph_buffer_to_atlas :: proc( ctx : ^Context ) } } -// ve_fontcache_merge_drawlist -merge_draw_list :: proc( dst, src : ^DrawList ) +// ve_fontcache_merge_Draw_List +merge_draw_list :: proc( dst, src : ^Draw_List ) { // profile(#procedure) - error : AllocatorError + error : Allocator_Error v_offset := cast(u32) len( dst.vertices ) num_appended : int @@ -766,11 +766,11 @@ merge_draw_list :: proc( dst, src : ^DrawList ) } } -optimize_draw_list :: proc(draw_list: ^DrawList, call_offset: int) { +optimize_draw_list :: proc(draw_list: ^Draw_List, call_offset: int) { // profile(#procedure) assert(draw_list != nil) - can_merge_draw_calls :: #force_inline proc "contextless" ( a, b : ^DrawCall ) -> bool { + can_merge_draw_calls :: #force_inline proc "contextless" ( a, b : ^Draw_Call ) -> bool { result := \ a.pass == b.pass && a.end_index == b.start_index && diff --git a/code/font/vefontcache/mappings.odin b/code/font/vefontcache/mappings.odin index 4c38117..7cb620a 100644 --- a/code/font/vefontcache/mappings.odin +++ b/code/font/vefontcache/mappings.odin @@ -27,16 +27,12 @@ import "core:mem" Kilobyte :: mem.Kilobyte slice_ptr :: mem.slice_ptr - Allocator :: mem.Allocator - AllocatorError :: mem.Allocator_Error + Allocator :: mem.Allocator + Allocator_Error :: mem.Allocator_Error Arena :: mem.Arena arena_allocator :: mem.arena_allocator arena_init :: mem.arena_init -import "codebase:grime" - log :: grime.log - logf :: grime.logf - profile :: grime.profile //#region("Proc overload mappings") diff --git a/code/font/vefontcache/misc.odin b/code/font/vefontcache/misc.odin index fe591ea..e34fbf0 100644 --- a/code/font/vefontcache/misc.odin +++ b/code/font/vefontcache/misc.odin @@ -4,7 +4,7 @@ import "base:runtime" import "core:simd" import "core:math" -// import core_log "core:log" +import core_log "core:log" Colour :: [4]f32 Vec2 :: [2]f32 @@ -23,21 +23,21 @@ vec2i_from_vec2 :: #force_inline proc "contextless" ( v2 : Vec2 ) -> Vec2 // This means a single line is limited to 4k buffer // Logger_Allocator_Buffer : [4 * Kilobyte]u8 -// log :: proc( msg : string, level := core_log.Level.Info, loc := #caller_location ) { +log :: proc( msg : string, level := core_log.Level.Info, loc := #caller_location ) { // temp_arena : Arena; arena_init(& temp_arena, Logger_Allocator_Buffer[:]) // context.allocator = arena_allocator(& temp_arena) // context.temp_allocator = arena_allocator(& temp_arena) - // core_log.log( level, msg, location = loc ) -// } + core_log.log( level, msg, location = loc ) +} -// logf :: proc( fmt : string, args : ..any, level := core_log.Level.Info, loc := #caller_location ) { +logf :: proc( fmt : string, args : ..any, level := core_log.Level.Info, loc := #caller_location ) { // temp_arena : Arena; arena_init(& temp_arena, Logger_Allocator_Buffer[:]) // context.allocator = arena_allocator(& temp_arena) // context.temp_allocator = arena_allocator(& temp_arena) - // core_log.logf( level, fmt, ..args, location = loc ) -// } + core_log.logf( level, fmt, ..args, location = loc ) +} reload_array :: proc( self : ^[dynamic]$Type, allocator : Allocator ) { raw := transmute( ^runtime.Raw_Dynamic_Array) self @@ -49,7 +49,7 @@ reload_map :: proc( self : ^map [$KeyType] $EntryType, allocator : Allocator ) { raw.allocator = allocator } -font_glyph_lru_code :: #force_inline proc "contextless" ( font : FontID, glyph_index : Glyph ) -> (lru_code : u64) { +font_glyph_lru_code :: #force_inline proc "contextless" ( font : Font_ID, glyph_index : Glyph ) -> (lru_code : u64) { lru_code = u64(glyph_index) + ( ( 0x100000000 * u64(font) ) & 0xFFFFFFFF00000000 ) return } @@ -71,9 +71,11 @@ reset_batch_codepoint_state :: #force_inline proc( ctx : ^Context ) { ctx.temp_codepoint_seen_num = 0 } +USE_F64_PRECISION_ON_X_FORM_OPS :: false + screenspace_x_form :: #force_inline proc "contextless" ( position, scale : ^Vec2, size : Vec2 ) { - if true + when USE_F64_PRECISION_ON_X_FORM_OPS { pos_64 := vec2_64_from_vec2(position^) scale_64 := vec2_64_from_vec2(scale^) @@ -101,7 +103,7 @@ screenspace_x_form :: #force_inline proc "contextless" ( position, scale : ^Vec2 textspace_x_form :: #force_inline proc "contextless" ( position, scale : ^Vec2, size : Vec2 ) { - if true + when USE_F64_PRECISION_ON_X_FORM_OPS { pos_64 := vec2_64_from_vec2(position^) scale_64 := vec2_64_from_vec2(scale^) @@ -121,9 +123,9 @@ textspace_x_form :: #force_inline proc "contextless" ( position, scale : ^Vec2, } } -Use_SIMD_For_Bezier_Ops :: false +USE_MANUAL_SIMD_FOR_BEZIER_OPS :: false -when ! Use_SIMD_For_Bezier_Ops +when ! USE_MANUAL_SIMD_FOR_BEZIER_OPS { // For a provided alpha value, // allows the function to calculate the position of a point along the curve at any given fraction of its total length diff --git a/code/font/vefontcache/parser.odin b/code/font/vefontcache/parser.odin index 1eef70d..2bb0b6f 100644 --- a/code/font/vefontcache/parser.odin +++ b/code/font/vefontcache/parser.odin @@ -16,14 +16,14 @@ import "core:slice" import stbtt "vendor:stb/truetype" import freetype "thirdparty:freetype" -ParserKind :: enum u32 { +Parser_Kind :: enum u32 { STB_TrueType, Freetype, } -ParserFontInfo :: struct { +Parser_Font_Info :: struct { label : string, - kind : ParserKind, + kind : Parser_Kind, using _ : struct #raw_union { stbtt_info : stbtt.fontinfo, freetype_info : freetype.Face @@ -31,7 +31,7 @@ ParserFontInfo :: struct { data : []byte, } -GlyphVertType :: enum u8 { +Glyph_Vert_Type :: enum u8 { None, Move = 1, Line, @@ -40,22 +40,22 @@ GlyphVertType :: enum u8 { } // Based directly off of stb_truetype's vertex -ParserGlyphVertex :: struct { +Parser_Glyph_Vertex :: struct { x, y : i16, contour_x0, contour_y0 : i16, contour_x1, contour_y1 : i16, - type : GlyphVertType, + type : Glyph_Vert_Type, padding : u8, } // A shape can be a dynamic array free_type or an opaque set of data handled by stb_truetype -ParserGlyphShape :: [dynamic]ParserGlyphVertex +Parser_Glyph_Shape :: [dynamic]Parser_Glyph_Vertex -ParserContext :: struct { - kind : ParserKind, +Parser_Context :: struct { + kind : Parser_Kind, ft_library : freetype.Library, } -parser_init :: proc( ctx : ^ParserContext, kind : ParserKind ) +parser_init :: proc( ctx : ^Parser_Context, kind : Parser_Kind ) { switch kind { @@ -70,17 +70,23 @@ parser_init :: proc( ctx : ^ParserContext, kind : ParserKind ) ctx.kind = kind } -parser_shutdown :: proc( ctx : ^ParserContext ) { +parser_shutdown :: proc( ctx : ^Parser_Context ) { // TODO(Ed): Implement } -parser_load_font :: proc( ctx : ^ParserContext, label : string, data : []byte ) -> (font : ParserFontInfo) +parser_load_font :: proc( ctx : ^Parser_Context, label : string, data : []byte ) -> (font : Parser_Font_Info) { switch ctx.kind { case .Freetype: - error := freetype.new_memory_face( ctx.ft_library, raw_data(data), cast(i32) len(data), 0, & font.freetype_info ) - if error != .Ok do return + when ODIN_OS == .Windows { + error := freetype.new_memory_face( ctx.ft_library, raw_data(data), cast(i32) len(data), 0, & font.freetype_info ) + if error != .Ok do return + } + else when ODIN_OS == .Linux { + error := freetype.new_memory_face( ctx.ft_library, raw_data(data), cast(i64) len(data), 0, & font.freetype_info ) + if error != .Ok do return + } case .STB_TrueType: success := stbtt.InitFont( & font.stbtt_info, raw_data(data), 0 ) @@ -93,7 +99,7 @@ parser_load_font :: proc( ctx : ^ParserContext, label : string, data : []byte ) return } -parser_unload_font :: proc( font : ^ParserFontInfo ) +parser_unload_font :: proc( font : ^Parser_Font_Info ) { switch font.kind { case .Freetype: @@ -105,12 +111,17 @@ parser_unload_font :: proc( font : ^ParserFontInfo ) } } -parser_find_glyph_index :: #force_inline proc "contextless" ( font : ^ParserFontInfo, codepoint : rune ) -> (glyph_index : Glyph) +parser_find_glyph_index :: #force_inline proc "contextless" ( font : ^Parser_Font_Info, codepoint : rune ) -> (glyph_index : Glyph) { switch font.kind { case .Freetype: - glyph_index = transmute(Glyph) freetype.get_char_index( font.freetype_info, transmute(u32) codepoint ) + when ODIN_OS == .Windows { + glyph_index = transmute(Glyph) freetype.get_char_index( font.freetype_info, transmute(u32) codepoint ) + } + else when ODIN_OS == .Linux { + glyph_index = transmute(Glyph) freetype.get_char_index( font.freetype_info, cast(u64) codepoint ) + } return case .STB_TrueType: @@ -120,7 +131,7 @@ parser_find_glyph_index :: #force_inline proc "contextless" ( font : ^ParserFont return Glyph(-1) } -parser_free_shape :: proc( font : ^ParserFontInfo, shape : ParserGlyphShape ) +parser_free_shape :: proc( font : ^Parser_Font_Info, shape : Parser_Glyph_Shape ) { switch font.kind { @@ -132,12 +143,19 @@ parser_free_shape :: proc( font : ^ParserFontInfo, shape : ParserGlyphShape ) } } -parser_get_codepoint_horizontal_metrics :: #force_inline proc "contextless" ( font : ^ParserFontInfo, codepoint : rune ) -> ( advance, to_left_side_glyph : i32 ) +parser_get_codepoint_horizontal_metrics :: #force_inline proc "contextless" ( font : ^Parser_Font_Info, codepoint : rune ) -> ( advance, to_left_side_glyph : i32 ) { switch font.kind { case .Freetype: - glyph_index := transmute(Glyph) freetype.get_char_index( font.freetype_info, transmute(u32) codepoint ) + glyph_index : Glyph + when ODIN_OS == .Windows { + glyph_index = transmute(Glyph) freetype.get_char_index( font.freetype_info, transmute(u32) codepoint ) + } + else when ODIN_OS == .Linux { + glyph_index = transmute(Glyph) freetype.get_char_index( font.freetype_info, cast(u64) codepoint ) + } + if glyph_index != 0 { freetype.load_glyph( font.freetype_info, c.uint(codepoint), { .No_Bitmap, .No_Hinting, .No_Scale } ) @@ -156,13 +174,22 @@ parser_get_codepoint_horizontal_metrics :: #force_inline proc "contextless" ( fo return } -parser_get_codepoint_kern_advance :: #force_inline proc "contextless" ( font : ^ParserFontInfo, prev_codepoint, codepoint : rune ) -> i32 +parser_get_codepoint_kern_advance :: #force_inline proc "contextless" ( font : ^Parser_Font_Info, prev_codepoint, codepoint : rune ) -> i32 { switch font.kind { case .Freetype: - prev_glyph_index := transmute(Glyph) freetype.get_char_index( font.freetype_info, transmute(u32) prev_codepoint ) - glyph_index := transmute(Glyph) freetype.get_char_index( font.freetype_info, transmute(u32) codepoint ) + prev_glyph_index : Glyph + glyph_index : Glyph + when ODIN_OS == .Windows { + prev_glyph_index = transmute(Glyph) freetype.get_char_index( font.freetype_info, transmute(u32) prev_codepoint ) + glyph_index = transmute(Glyph) freetype.get_char_index( font.freetype_info, transmute(u32) codepoint ) + } + else when ODIN_OS == .Linux { + prev_glyph_index = transmute(Glyph) freetype.get_char_index( font.freetype_info, cast(u64) prev_codepoint ) + glyph_index = transmute(Glyph) freetype.get_char_index( font.freetype_info, cast(u64) codepoint ) + } + if prev_glyph_index != 0 && glyph_index != 0 { kerning : freetype.Vector @@ -176,7 +203,7 @@ parser_get_codepoint_kern_advance :: #force_inline proc "contextless" ( font : ^ return -1 } -parser_get_font_vertical_metrics :: #force_inline proc "contextless" ( font : ^ParserFontInfo ) -> (ascent, descent, line_gap : i32 ) +parser_get_font_vertical_metrics :: #force_inline proc "contextless" ( font : ^Parser_Font_Info ) -> (ascent, descent, line_gap : i32 ) { switch font.kind { @@ -192,7 +219,7 @@ parser_get_font_vertical_metrics :: #force_inline proc "contextless" ( font : ^P return } -parser_get_glyph_box :: #force_inline proc ( font : ^ParserFontInfo, glyph_index : Glyph ) -> (bounds_0, bounds_1 : Vec2i) +parser_get_glyph_box :: #force_inline proc ( font : ^Parser_Font_Info, glyph_index : Glyph ) -> (bounds_0, bounds_1 : Vec2i) { switch font.kind { @@ -215,7 +242,7 @@ parser_get_glyph_box :: #force_inline proc ( font : ^ParserFontInfo, glyph_index return } -parser_get_glyph_shape :: proc( font : ^ParserFontInfo, glyph_index : Glyph ) -> (shape : ParserGlyphShape, error : AllocatorError) +parser_get_glyph_shape :: proc( font : ^Parser_Font_Info, glyph_index : Glyph ) -> (shape : Parser_Glyph_Shape, error : Allocator_Error) { switch font.kind { @@ -232,14 +259,14 @@ parser_get_glyph_shape :: proc( font : ^ParserFontInfo, glyph_index : Glyph ) -> shape_raw.len = int(nverts) shape_raw.cap = int(nverts) shape_raw.allocator = runtime.nil_allocator() - error = AllocatorError.None + error = Allocator_Error.None return } return } -parser_is_glyph_empty :: #force_inline proc "contextless" ( font : ^ParserFontInfo, glyph_index : Glyph ) -> b32 +parser_is_glyph_empty :: #force_inline proc "contextless" ( font : ^Parser_Font_Info, glyph_index : Glyph ) -> b32 { switch font.kind { @@ -262,7 +289,7 @@ parser_is_glyph_empty :: #force_inline proc "contextless" ( font : ^ParserFontIn return false } -parser_scale :: #force_inline proc "contextless" ( font : ^ParserFontInfo, size : f32 ) -> f32 +parser_scale :: #force_inline proc "contextless" ( font : ^Parser_Font_Info, size : f32 ) -> f32 { size_scale := size < 0.0 ? \ parser_scale_for_pixel_height( font, -size ) \ @@ -271,7 +298,7 @@ parser_scale :: #force_inline proc "contextless" ( font : ^ParserFontInfo, size return size_scale } -parser_scale_for_pixel_height :: #force_inline proc "contextless" ( font : ^ParserFontInfo, size : f32 ) -> f32 +parser_scale_for_pixel_height :: #force_inline proc "contextless" ( font : ^Parser_Font_Info, size : f32 ) -> f32 { switch font.kind { case .Freetype: @@ -285,7 +312,7 @@ parser_scale_for_pixel_height :: #force_inline proc "contextless" ( font : ^Pars return 0 } -parser_scale_for_mapping_em_to_pixels :: #force_inline proc "contextless" ( font : ^ParserFontInfo, size : f32 ) -> f32 +parser_scale_for_mapping_em_to_pixels :: #force_inline proc "contextless" ( font : ^Parser_Font_Info, size : f32 ) -> f32 { switch font.kind { case .Freetype: diff --git a/code/font/vefontcache/shaped_text.odin b/code/font/vefontcache/shaped_text.odin index 5e7e9bd..ba5c80f 100644 --- a/code/font/vefontcache/shaped_text.odin +++ b/code/font/vefontcache/shaped_text.odin @@ -1,14 +1,14 @@ package vefontcache -ShapedText :: struct { +Shaped_Text :: struct { glyphs : [dynamic]Glyph, positions : [dynamic]Vec2, end_cursor_pos : Vec2, size : Vec2, } -ShapedTextCache :: struct { - storage : [dynamic]ShapedText, +Shaped_Text_Cache :: struct { + storage : [dynamic]Shaped_Text, state : LRU_Cache, next_cache_id : i32, } @@ -19,11 +19,11 @@ shape_lru_hash :: #force_inline proc "contextless" ( hash : ^u64, bytes : []byte } } -shape_text_cached :: proc( ctx : ^Context, font : FontID, text_utf8 : string, entry : ^Entry ) -> ^ShapedText +shape_text_cached :: proc( ctx : ^Context, font : Font_ID, text_utf8 : string, entry : ^Entry ) -> ^Shaped_Text { // profile(#procedure) font := font - font_bytes := slice_ptr( transmute(^byte) & font, size_of(FontID) ) + font_bytes := slice_ptr( transmute(^byte) & font, size_of(Font_ID) ) text_bytes := transmute( []byte) text_utf8 lru_code : u64 @@ -33,23 +33,23 @@ shape_text_cached :: proc( ctx : ^Context, font : FontID, text_utf8 : string, en shape_cache := & ctx.shape_cache state := & ctx.shape_cache.state - shape_cache_idx := LRU_get( state, lru_code ) + shape_cache_idx := lru_get( state, lru_code ) if shape_cache_idx == -1 { if shape_cache.next_cache_id < i32(state.capacity) { shape_cache_idx = shape_cache.next_cache_id shape_cache.next_cache_id += 1 - evicted := LRU_put( state, lru_code, shape_cache_idx ) + evicted := lru_put( state, lru_code, shape_cache_idx ) } else { - next_evict_idx := LRU_get_next_evicted( state ) + next_evict_idx := lru_get_next_evicted( state ) assert( next_evict_idx != 0xFFFFFFFFFFFFFFFF ) - shape_cache_idx = LRU_peek( state, next_evict_idx, must_find = true ) + shape_cache_idx = lru_peek( state, next_evict_idx, must_find = true ) assert( shape_cache_idx != - 1 ) - LRU_put( state, lru_code, shape_cache_idx ) + lru_put( state, lru_code, shape_cache_idx ) } shape_entry := & shape_cache.storage[ shape_cache_idx ] @@ -59,7 +59,7 @@ shape_text_cached :: proc( ctx : ^Context, font : FontID, text_utf8 : string, en return & shape_cache.storage[ shape_cache_idx ] } -shape_text_uncached :: proc( ctx : ^Context, font : FontID, text_utf8 : string, entry : ^Entry, output : ^ShapedText ) +shape_text_uncached :: proc( ctx : ^Context, font : Font_ID, text_utf8 : string, entry : ^Entry, output : ^Shaped_Text ) { // profile(#procedure) assert( ctx != nil ) @@ -106,7 +106,7 @@ shape_text_uncached :: proc( ctx : ^Context, font : FontID, text_utf8 : string, prev_codepoint = rune(0) continue } - if abs( entry.size ) <= Advance_Snap_Smallfont_Size { + if abs( entry.size ) <= ADVANCE_SNAP_SMALLFONT_SIZE { position.x = position.x } diff --git a/code/font/vefontcache/shaper.odin b/code/font/vefontcache/shaper.odin index ecc8495..6993e5f 100644 --- a/code/font/vefontcache/shaper.odin +++ b/code/font/vefontcache/shaper.odin @@ -6,35 +6,35 @@ Note(Ed): The only reason I didn't directly use harfbuzz is because hamza exists import "core:c" import "thirdparty:harfbuzz" -ShaperKind :: enum { +Shaper_Kind :: enum { Naive = 0, Harfbuzz = 1, } -ShaperContext :: struct { +Shaper_Context :: struct { hb_buffer : harfbuzz.Buffer, } -ShaperInfo :: struct { +Shaper_Info :: struct { blob : harfbuzz.Blob, face : harfbuzz.Face, font : harfbuzz.Font, } -shaper_init :: proc( ctx : ^ShaperContext ) +shaper_init :: proc( ctx : ^Shaper_Context ) { ctx.hb_buffer = harfbuzz.buffer_create() assert( ctx.hb_buffer != nil, "VEFontCache.shaper_init: Failed to create harfbuzz buffer") } -shaper_shutdown :: proc( ctx : ^ShaperContext ) +shaper_shutdown :: proc( ctx : ^Shaper_Context ) { if ctx.hb_buffer != nil { harfbuzz.buffer_destroy( ctx.hb_buffer ) } } -shaper_load_font :: proc( ctx : ^ShaperContext, label : string, data : []byte, user_data : rawptr ) -> (info : ShaperInfo) +shaper_load_font :: proc( ctx : ^Shaper_Context, label : string, data : []byte, user_data : rawptr ) -> (info : Shaper_Info) { using info blob = harfbuzz.blob_create( raw_data(data), cast(c.uint) len(data), harfbuzz.Memory_Mode.READONLY, user_data, nil ) @@ -43,7 +43,7 @@ shaper_load_font :: proc( ctx : ^ShaperContext, label : string, data : []byte, u return } -shaper_unload_font :: proc( ctx : ^ShaperInfo ) +shaper_unload_font :: proc( ctx : ^Shaper_Info ) { using ctx if blob != nil do harfbuzz.font_destroy( font ) @@ -51,7 +51,7 @@ shaper_unload_font :: proc( ctx : ^ShaperInfo ) if blob != nil do harfbuzz.blob_destroy( blob ) } -shaper_shape_from_text :: proc( ctx : ^ShaperContext, info : ^ShaperInfo, output :^ShapedText, text_utf8 : string, +shaper_shape_from_text :: proc( ctx : ^Shaper_Context, info : ^Shaper_Info, output :^Shaped_Text, text_utf8 : string, ascent, descent, line_gap : i32, size, size_scale : f32 ) { // profile(#procedure) @@ -69,7 +69,7 @@ shaper_shape_from_text :: proc( ctx : ^ShaperContext, info : ^ShaperInfo, output line_height := (ascent - descent + line_gap) * size_scale position, vertical_position : f32 - shape_run :: proc( buffer : harfbuzz.Buffer, script : harfbuzz.Script, font : harfbuzz.Font, output : ^ShapedText, + shape_run :: proc( buffer : harfbuzz.Buffer, script : harfbuzz.Script, font : harfbuzz.Font, output : ^Shaped_Text, position, vertical_position, max_line_width: ^f32, line_count: ^int, ascent, descent, line_gap, size, size_scale: f32 ) { @@ -105,7 +105,7 @@ shaper_shape_from_text :: proc( ctx : ^ShaperContext, info : ^ShaperInfo, output (line_count^) += 1 continue } - if abs( size ) <= Advance_Snap_Smallfont_Size + if abs( size ) <= ADVANCE_SNAP_SMALLFONT_SIZE { (position^) = ceil( position^ ) } diff --git a/code/font/vefontcache/vefontcache.odin b/code/font/vefontcache/vefontcache.odin index 45da3cb..a4b5df9 100644 --- a/code/font/vefontcache/vefontcache.odin +++ b/code/font/vefontcache/vefontcache.odin @@ -7,15 +7,15 @@ package vefontcache import "base:runtime" -Advance_Snap_Smallfont_Size :: 0 +ADVANCE_SNAP_SMALLFONT_SIZE :: 0 -FontID :: distinct i64 +Font_ID :: distinct i64 Glyph :: distinct i32 Entry :: struct { - parser_info : ParserFontInfo, - shaper_info : ShaperInfo, - id : FontID, + parser_info : Parser_Font_Info, + shaper_info : Shaper_Info, + id : Font_ID, used : b32, curve_quality : f32, size : f32, @@ -32,8 +32,8 @@ Entry_Default :: Entry { Context :: struct { backing : Allocator, - parser_ctx : ParserContext, - shaper_ctx : ShaperContext, + parser_ctx : Parser_Context, + shaper_ctx : Shaper_Context, entries : [dynamic]Entry, @@ -54,10 +54,10 @@ Context :: struct { calls_offset : int, }, - draw_list : DrawList, + draw_list : Draw_List, atlas : Atlas, - glyph_buffer : GlyphDrawBuffer, - shape_cache : ShapedTextCache, + glyph_buffer : Glyph_Draw_Buffer, + shape_cache : Shaped_Text_Cache, default_curve_quality : i32, text_shape_adv : b32, @@ -69,23 +69,23 @@ Context :: struct { //#region("lifetime") -InitAtlasRegionParams :: struct { +Init_Atlas_Region_Params :: struct { width : u32, height : u32, } -InitAtlasParams :: struct { - width : u32, - height : u32, - glyph_padding : u32, +Init_Atlas_Params :: struct { + width : u32, + height : u32, + glyph_padding : u32, - region_a : InitAtlasRegionParams, - region_b : InitAtlasRegionParams, - region_c : InitAtlasRegionParams, - region_d : InitAtlasRegionParams, + region_a : Init_Atlas_Region_Params, + region_b : Init_Atlas_Region_Params, + region_c : Init_Atlas_Region_Params, + region_d : Init_Atlas_Region_Params, } -InitAtlasParams_Default :: InitAtlasParams { +Init_Atlas_Params_Default :: Init_Atlas_Params { width = 4096, height = 2048, glyph_padding = 4, @@ -108,34 +108,34 @@ InitAtlasParams_Default :: InitAtlasParams { } } -InitGlyphDrawParams :: struct { - over_sample : Vec2i, - buffer_batch : u32, - draw_padding : u32, +Init_Glyph_Draw_Params :: struct { + over_sample : Vec2i, + buffer_batch : u32, + draw_padding : u32, } -InitGlyphDrawParams_Default :: InitGlyphDrawParams { +Init_Glyph_Draw_Params_Default :: Init_Glyph_Draw_Params { over_sample = { 8, 8 }, buffer_batch = 4, - draw_padding = InitAtlasParams_Default.glyph_padding, + draw_padding = Init_Atlas_Params_Default.glyph_padding, } -InitShapeCacheParams :: struct { +Init_Shape_Cache_Params :: struct { capacity : u32, reserve_length : u32, } -InitShapeCacheParams_Default :: InitShapeCacheParams { - capacity = 8 * 1024, - reserve_length = 256, +Init_Shape_Cache_Params_Default :: Init_Shape_Cache_Params { + capacity = 8 * 1024, + reserve_length = 256, } // ve_fontcache_init -startup :: proc( ctx : ^Context, parser_kind : ParserKind, +startup :: proc( ctx : ^Context, parser_kind : Parser_Kind = .STB_TrueType, allocator := context.allocator, - atlas_params := InitAtlasParams_Default, - glyph_draw_params := InitGlyphDrawParams_Default, - shape_cache_params := InitShapeCacheParams_Default, + atlas_params := Init_Atlas_Params_Default, + glyph_draw_params := Init_Glyph_Draw_Params_Default, + shape_cache_params := Init_Shape_Cache_Params_Default, use_advanced_text_shaper : b32 = true, snap_shape_position : b32 = true, default_curve_quality : u32 = 3, @@ -158,7 +158,7 @@ startup :: proc( ctx : ^Context, parser_kind : ParserKind, } ctx.default_curve_quality = default_curve_quality - error : AllocatorError + error : Allocator_Error entries, error = make( [dynamic]Entry, len = 0, cap = entires_reserve ) assert(error == .None, "VEFontCache.init : Failed to allocate entries") @@ -174,10 +174,10 @@ startup :: proc( ctx : ^Context, parser_kind : ParserKind, draw_list.indices, error = make( [dynamic]u32, len = 0, cap = 8 * Kilobyte ) assert(error == .None, "VEFontCache.init : Failed to allocate draw_list.indices") - draw_list.calls, error = make( [dynamic]DrawCall, len = 0, cap = 512 ) + draw_list.calls, error = make( [dynamic]Draw_Call, len = 0, cap = 512 ) assert(error == .None, "VEFontCache.init : Failed to allocate draw_list.calls") - init_atlas_region :: proc( region : ^AtlasRegion, params : InitAtlasParams, region_params : InitAtlasRegionParams, factor : Vec2i, expected_cap : i32 ) + init_atlas_region :: proc( region : ^Atlas_Region, params : Init_Atlas_Params, region_params : Init_Atlas_Region_Params, factor : Vec2i, expected_cap : i32 ) { using region @@ -194,8 +194,8 @@ startup :: proc( ctx : ^Context, parser_kind : ParserKind, } assert( capacity.x * capacity.y == expected_cap ) - error : AllocatorError - LRU_init( & state, capacity.x * capacity.y ) + error : Allocator_Error + lru_init( & state, capacity.x * capacity.y ) } init_atlas_region( & atlas.region_a, atlas_params, atlas_params.region_a, { 4, 2}, 1024 ) init_atlas_region( & atlas.region_b, atlas_params, atlas_params.region_b, { 4, 2}, 512 ) @@ -214,9 +214,9 @@ startup :: proc( ctx : ^Context, parser_kind : ParserKind, atlas.region_d.offset.x = atlas.width / 2 atlas.region_d.offset.y = 0 - LRU_init( & shape_cache.state, i32(shape_cache_params.capacity) ) + lru_init( & shape_cache.state, i32(shape_cache_params.capacity) ) - shape_cache.storage, error = make( [dynamic]ShapedText, shape_cache_params.capacity ) + shape_cache.storage, error = make( [dynamic]Shaped_Text, shape_cache_params.capacity ) assert(error == .None, "VEFontCache.init : Failed to allocate shape_cache.storage") for idx : u32 = 0; idx < shape_cache_params.capacity; idx += 1 { @@ -228,7 +228,7 @@ startup :: proc( ctx : ^Context, parser_kind : ParserKind, positions, error = make( [dynamic]Vec2, len = 0, cap = shape_cache_params.reserve_length ) assert( error == .None, "VEFontCache.init : Failed to allocate positions array for shape cache storage" ) - draw_list.calls, error = make( [dynamic]DrawCall, len = 0, cap = glyph_draw_params.buffer_batch * 2 ) + draw_list.calls, error = make( [dynamic]Draw_Call, len = 0, cap = glyph_draw_params.buffer_batch * 2 ) assert( error == .None, "VEFontCache.init : Failed to allocate calls for draw_list" ) draw_list.indices, error = make( [dynamic]u32, len = 0, cap = glyph_draw_params.buffer_batch * 2 * 6 ) @@ -247,7 +247,7 @@ startup :: proc( ctx : ^Context, parser_kind : ParserKind, height = atlas.region_d.height * i32(over_sample.y) draw_padding = cast(i32) glyph_draw_params.draw_padding - draw_list.calls, error = make( [dynamic]DrawCall, len = 0, cap = glyph_draw_params.buffer_batch * 2 ) + draw_list.calls, error = make( [dynamic]Draw_Call, len = 0, cap = glyph_draw_params.buffer_batch * 2 ) assert( error == .None, "VEFontCache.init : Failed to allocate calls for draw_list" ) draw_list.indices, error = make( [dynamic]u32, len = 0, cap = glyph_draw_params.buffer_batch * 2 * 6 ) @@ -256,7 +256,7 @@ startup :: proc( ctx : ^Context, parser_kind : ParserKind, draw_list.vertices, error = make( [dynamic]Vertex, len = 0, cap = glyph_draw_params.buffer_batch * 2 * 4 ) assert( error == .None, "VEFontCache.init : Failed to allocate vertices array for draw_list" ) - clear_draw_list.calls, error = make( [dynamic]DrawCall, len = 0, cap = glyph_draw_params.buffer_batch * 2 ) + clear_draw_list.calls, error = make( [dynamic]Draw_Call, len = 0, cap = glyph_draw_params.buffer_batch * 2 ) assert( error == .None, "VEFontCache.init : Failed to allocate calls for calls for clear_draw_list" ) clear_draw_list.indices, error = make( [dynamic]u32, len = 0, cap = glyph_draw_params.buffer_batch * 2 * 4 ) @@ -285,12 +285,12 @@ hot_reload :: proc( ctx : ^Context, allocator : Allocator ) reload_array( & draw_list.indices, allocator ) reload_array( & draw_list.calls, allocator ) - LRU_reload( & atlas.region_a.state, allocator) - LRU_reload( & atlas.region_b.state, allocator) - LRU_reload( & atlas.region_c.state, allocator) - LRU_reload( & atlas.region_d.state, allocator) + lru_reload( & atlas.region_a.state, allocator) + lru_reload( & atlas.region_b.state, allocator) + lru_reload( & atlas.region_c.state, allocator) + lru_reload( & atlas.region_d.state, allocator) - LRU_reload( & shape_cache.state, allocator ) + lru_reload( & shape_cache.state, allocator ) for idx : i32 = 0; idx < i32(len(shape_cache.storage)); idx += 1 { stroage_entry := & shape_cache.storage[idx] using stroage_entry @@ -308,7 +308,7 @@ hot_reload :: proc( ctx : ^Context, allocator : Allocator ) reload_array( & glyph_buffer.clear_draw_list.vertices, allocator ) reload_array( & shape_cache.storage, allocator ) - LRU_reload( & shape_cache.state, allocator ) + lru_reload( & shape_cache.state, allocator ) } // ve_foncache_shutdown @@ -330,10 +330,10 @@ shutdown :: proc( ctx : ^Context ) delete( draw_list.indices ) delete( draw_list.calls ) - LRU_free( & atlas.region_a.state ) - LRU_free( & atlas.region_b.state ) - LRU_free( & atlas.region_c.state ) - LRU_free( & atlas.region_d.state ) + lru_free( & atlas.region_a.state ) + lru_free( & atlas.region_b.state ) + lru_free( & atlas.region_c.state ) + lru_free( & atlas.region_d.state ) for idx : i32 = 0; idx < i32(len(shape_cache.storage)); idx += 1 { stroage_entry := & shape_cache.storage[idx] @@ -342,7 +342,7 @@ shutdown :: proc( ctx : ^Context ) delete( glyphs ) delete( positions ) } - LRU_free( & shape_cache.state ) + lru_free( & shape_cache.state ) delete( glyph_buffer.draw_list.vertices ) delete( glyph_buffer.draw_list.indices ) @@ -357,7 +357,7 @@ shutdown :: proc( ctx : ^Context ) } // ve_fontcache_load -load_font :: proc( ctx : ^Context, label : string, data : []byte, size_px : f32, glyph_curve_quality : u32 = 0 ) -> (font_id : FontID) +load_font :: proc( ctx : ^Context, label : string, data : []byte, size_px : f32, glyph_curve_quality : u32 = 0 ) -> (font_id : Font_ID) { assert( ctx != nil ) assert( len(data) > 0 ) @@ -386,9 +386,9 @@ load_font :: proc( ctx : ^Context, label : string, data : []byte, size_px : f32, shaper_info = shaper_load_font( & shaper_ctx, label, data, transmute(rawptr) id ) size = size_px - size_scale = size_px < 0.0 ? \ + size_scale = size_px < 0.0 ? \ parser_scale_for_pixel_height( & parser_info, -size_px ) \ - : parser_scale_for_mapping_em_to_pixels( & parser_info, size_px ) + : parser_scale_for_mapping_em_to_pixels( & parser_info, size_px ) if glyph_curve_quality == 0 { curve_quality = f32(ctx.default_curve_quality) @@ -397,15 +397,15 @@ load_font :: proc( ctx : ^Context, label : string, data : []byte, size_px : f32, curve_quality = f32(glyph_curve_quality) } } - entry.id = FontID(id) - ctx.entries[ id ].id = FontID(id) + entry.id = Font_ID(id) + ctx.entries[ id ].id = Font_ID(id) - font_id = FontID(id) + font_id = Font_ID(id) return } // ve_fontcache_unload -unload_font :: proc( ctx : ^Context, font : FontID ) +unload_font :: proc( ctx : ^Context, font : Font_ID ) { assert( ctx != nil ) assert( font >= 0 && int(font) < len(ctx.entries) ) @@ -430,12 +430,12 @@ configure_snap :: #force_inline proc( ctx : ^Context, snap_width, snap_height : ctx.snap_height = f32(snap_height) } -get_cursor_pos :: #force_inline proc "contextless" ( ctx : ^Context ) -> Vec2 { return ctx.cursor_pos } -set_colour :: #force_inline proc "contextless" ( ctx : ^Context, colour : Colour ) { ctx.colour = colour } +get_cursor_pos :: #force_inline proc( ctx : ^Context ) -> Vec2 { assert(ctx != nil); return ctx.cursor_pos } +set_colour :: #force_inline proc( ctx : ^Context, colour : Colour ) { assert(ctx != nil); ctx.colour = colour } -draw_text :: proc( ctx : ^Context, font : FontID, text_utf8 : string, position, scale : Vec2 ) -> b32 +draw_text :: proc( ctx : ^Context, font : Font_ID, text_utf8 : string, position, scale : Vec2 ) -> b32 { - profile(#procedure) + // profile(#procedure) assert( ctx != nil ) assert( font >= 0 && int(font) < len(ctx.entries) ) @@ -463,14 +463,14 @@ draw_text :: proc( ctx : ^Context, font : FontID, text_utf8 : string, position, return true } -// ve_fontcache_drawlist -get_draw_list :: proc( ctx : ^Context, optimize_before_returning := true ) -> ^DrawList { +// ve_fontcache_Draw_List +get_draw_list :: proc( ctx : ^Context, optimize_before_returning := true ) -> ^Draw_List { assert( ctx != nil ) if optimize_before_returning do optimize_draw_list( & ctx.draw_list, 0 ) return & ctx.draw_list } -get_draw_list_layer :: proc( ctx : ^Context, optimize_before_returning := true ) -> (vertices : []Vertex, indices : []u32, calls : []DrawCall) { +get_draw_list_layer :: proc( ctx : ^Context, optimize_before_returning := true ) -> (vertices : []Vertex, indices : []u32, calls : []Draw_Call) { assert( ctx != nil ) if optimize_before_returning do optimize_draw_list( & ctx.draw_list, ctx.draw_layer.calls_offset ) vertices = ctx.draw_list.vertices[ ctx.draw_layer.vertices_offset : ] @@ -479,7 +479,7 @@ get_draw_list_layer :: proc( ctx : ^Context, optimize_before_returning := true ) return } -// ve_fontcache_flush_drawlist +// ve_fontcache_flush_Draw_List flush_draw_list :: proc( ctx : ^Context ) { assert( ctx != nil ) using ctx @@ -501,7 +501,7 @@ flush_draw_list_layer :: proc( ctx : ^Context ) { //#region("metrics") -measure_text_size :: proc( ctx : ^Context, font : FontID, text_utf8 : string ) -> (measured : Vec2) +measure_text_size :: proc( ctx : ^Context, font : Font_ID, text_utf8 : string ) -> (measured : Vec2) { // profile(#procedure) assert( ctx != nil ) @@ -512,7 +512,7 @@ measure_text_size :: proc( ctx : ^Context, font : FontID, text_utf8 : string ) - return shaped.size } -get_font_vertical_metrics :: #force_inline proc ( ctx : ^Context, font : FontID ) -> ( ascent, descent, line_gap : f32 ) +get_font_vertical_metrics :: #force_inline proc ( ctx : ^Context, font : Font_ID ) -> ( ascent, descent, line_gap : f32 ) { assert( ctx != nil ) assert( font >= 0 && int(font) < len(ctx.entries) )