update vefontcache to latest

This commit is contained in:
Edward R. Gonzalez 2024-11-26 10:42:27 -05:00
parent 08fff0f6fa
commit 109d2b0ff1
11 changed files with 341 additions and 320 deletions

View File

@ -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

View File

@ -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:

View File

@ -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 {

View File

@ -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

View File

@ -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 &&

View File

@ -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")

View File

@ -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

View File

@ -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:

View File

@ -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
}

View File

@ -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^ )
}

View File

@ -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) )