last cleanup before docs

This commit is contained in:
Edward R. Gonzalez 2024-06-26 22:51:04 -04:00
parent 1e7650970b
commit f08b04e901
6 changed files with 86 additions and 221 deletions

View File

@ -110,9 +110,6 @@ pool_list_erase :: proc( pool : ^PoolList, iter : PoolListIter )
iter_node.prev = -1
iter_node.next = -1
// if pool.dbg_name != "" {
// logf("pool_list: erased %v, at id %v", iter_node.value, iter)
// }
iter_node.value = 0
append( & free_list, iter )
@ -179,13 +176,6 @@ LRU_hash_key :: #force_inline proc( key : u64 ) -> ( hash : u64 ) {
}
LRU_find :: #force_inline proc "contextless" ( cache : ^LRU_Cache, key : u64, must_find := false ) -> (LRU_Link, bool) {
// hash := LRU_hash_key( key )
// link := get( cache.table, hash )
// if link == nil && must_find {
// runtime.debug_trap()
// link = get( cache.table, hash )
// }
link, success := cache.table[key]
return link, success
}
@ -218,7 +208,6 @@ LRU_peek :: #force_inline proc ( cache : ^LRU_Cache, key : u64, must_find := fal
LRU_put :: #force_inline proc ( cache : ^LRU_Cache, key : u64, value : i32 ) -> u64
{
// hash_key := LRU_hash_key( key )
iter, success := cache.table[key]
if success {
LRU_refresh( cache, key )
@ -229,20 +218,11 @@ LRU_put :: #force_inline proc ( cache : ^LRU_Cache, key : u64, value : i32 ) ->
evict := key
if cache.key_queue.size >= cache.capacity {
evict = pool_list_pop_back( & cache.key_queue )
// evict_hash := LRU_hash_key( evict )
// if cache.table.dbg_name != "" {
// logf("%v: Evicted %v with hash: %v", cache.table.dbg_name, evict, evict_hash)
// }
delete_key( & cache.table, evict )
cache.num -= 1
}
pool_list_push_front( & cache.key_queue, key )
// if cache.table.dbg_name != "" {
// logf("%v: Pushed %v with hash: %v", cache.table.dbg_name, key, hash_key )
// }
cache.table[key] = LRU_Link {
value = value,
ptr = cache.key_queue.front
@ -253,9 +233,6 @@ LRU_put :: #force_inline proc ( cache : ^LRU_Cache, key : u64, value : i32 ) ->
LRU_refresh :: proc( cache : ^LRU_Cache, key : u64 ) {
link, success := LRU_find( cache, key )
// if cache.table.dbg_name != "" {
// logf("%v: Refreshed %v", cache.table.dbg_name, 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,6 +4,8 @@ This is a port of the library base on [fork](https://github.com/hypernewbie/VEFo
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
TODO (Making it a more idiomatic library):
* Setup freetype, harfbuzz, depedency management within the library
@ -25,7 +27,3 @@ TODO Additional Features:
* Ability to set a draw transform, viewport and projection
* By default the library's position is in unsigned normalized render space
* Allow curve_quality to be set on a per-font basis
TODO Optimizations:
* Support more granular handling of shapes by chunking any text from draw_text into visible and whitespace/formatting

View File

@ -21,9 +21,10 @@ Vec2 :: [2]f32
Vec2i :: [2]i32
Vec2_64 :: [2]f64
vec2_from_scalar :: #force_inline proc "contextless" ( scalar : f32 ) -> Vec2 { return { scalar, scalar } }
vec2_from_scalar :: #force_inline proc "contextless" ( scalar : f32 ) -> Vec2 { return { scalar, scalar }}
vec2_64_from_vec2 :: #force_inline proc "contextless" ( v2 : Vec2 ) -> Vec2_64 { return { f64(v2.x), f64(v2.y) }}
vec2_from_vec2i :: #force_inline proc( v2i : Vec2i ) -> Vec2 { return { f32(v2i.x), f32(v2i.y) }}
vec2_from_vec2i :: #force_inline proc "contextless" ( v2i : Vec2i ) -> Vec2 { return { f32(v2i.x), f32(v2i.y) }}
vec2i_from_vec2 :: #force_inline proc "contextless" ( v2 : Vec2 ) -> Vec2i { return { i32(v2.x), i32(v2.y) }}
FontID :: distinct i64
Glyph :: distinct i32
@ -131,7 +132,7 @@ InitGlyphDrawParams :: struct {
}
InitGlyphDrawParams_Default :: InitGlyphDrawParams {
over_sample = { 4, 4 },
over_sample = { 8, 8 },
buffer_batch = 4,
draw_padding = InitAtlasParams_Default.glyph_padding,
}
@ -362,7 +363,6 @@ load_font :: proc( ctx : ^Context, label : string, data : []byte, size_px : f32
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 )
// size_scale = 1.0
used = true
@ -429,91 +429,11 @@ draw_text :: proc( ctx : ^Context, font : FontID, text_utf8 : string, position :
text_utf8_bytes := transmute([]u8) text_utf8
text_chunk : string
Text_As_Shape :: true
when Text_As_Shape
{
text_chunk = transmute(string) text_utf8_bytes[ : ]
if len(text_chunk) > 0 {
shaped := shape_text_cached( ctx, font, text_chunk, entry )
ctx.cursor_pos = draw_text_shape( ctx, font, entry, shaped, position, scale, snap_width, snap_height )
}
text_chunk = transmute(string) text_utf8_bytes[ : ]
if len(text_chunk) > 0 {
shaped := shape_text_cached( ctx, font, text_chunk, entry )
ctx.cursor_pos = draw_text_shape( ctx, font, entry, shaped, position, scale, snap_width, snap_height )
}
else
{
last_byte_offset : int = 0
byte_offset : int = 0
for codepoint, offset in text_utf8
{
Rune_Space :: ' '
Rune_Tab :: '\t'
Rune_Carriage_Return :: '\r'
Rune_Line_Feed :: '\n'
// Rune_Tab_Vertical :: '\v'
byte_offset = offset
switch codepoint
{
case Rune_Space: fallthrough
case Rune_Tab: fallthrough
case Rune_Line_Feed: fallthrough
case Rune_Carriage_Return:
if chunk_kind == .Formatting
{
chunk_end = byte_offset
last_byte_offset = byte_offset
}
else
{
text_chunk = transmute(string) text_utf8_bytes[ chunk_start : byte_offset]
if len(text_chunk) > 0 {
shaped := shape_text_cached( ctx, font, text_chunk, entry )
ctx.cursor_pos += draw_text_shape( ctx, font, entry, shaped, position, scale, snap_width, snap_height )
}
chunk_start = byte_offset
chunk_end = chunk_start
chunk_kind = .Formatting
last_byte_offset = byte_offset
continue
}
}
// Visible Chunk
if chunk_kind == .Visible {
chunk_end = byte_offset
last_byte_offset = byte_offset
}
else
{
text_chunk = transmute(string) text_utf8_bytes[ chunk_start : byte_offset ]
if len(text_chunk) > 0 {
shaped := shape_text_cached( ctx, font, text_chunk, entry )
ctx.cursor_pos += draw_text_shape( ctx, font, entry, shaped, position, scale, snap_width, snap_height )
}
chunk_start = byte_offset
chunk_end = chunk_start
chunk_kind = .Visible
last_byte_offset = byte_offset
}
}
text_chunk = transmute(string) text_utf8_bytes[ chunk_start : ]
if len(text_chunk) > 0 {
shaped := shape_text_cached( ctx, font, text_chunk, entry )
ctx.cursor_pos += draw_text_shape( ctx, font, entry, shaped, position, scale, snap_width, snap_height )
}
chunk_start = byte_offset
chunk_end = chunk_start
chunk_kind = .Visible
last_byte_offset = byte_offset
}
return true
}

View File

@ -87,8 +87,6 @@ blit_quad :: proc( draw_list : ^DrawList, p0 : Vec2 = {0, 0}, p1 : Vec2 = {1, 1}
for index : i32 = 0; index < 6; index += 1 {
append( & draw_list.indices, v_offset + quad_indices[ index ] )
}
// draw_list_vert_slice := array_to_slice(draw_list.vertices)
// draw_list_index_slice := array_to_slice(draw_list.indices)
return
}
@ -100,9 +98,6 @@ cache_glyph :: proc( ctx : ^Context, font : FontID, glyph_index : Glyph, entry :
return false
}
// No shpae to retrieve
// if parser_is_glyph_empty( & entry.parser_info, glyph_index ) do return true
// Retrieve the shape definition from the parser.
shape, error := parser_get_glyph_shape( & entry.parser_info, glyph_index )
assert( error == .None )
@ -228,8 +223,10 @@ cache_glyph_to_atlas :: proc( ctx : ^Context,
// Get hb_font text metrics. These are unscaled!
bounds_0, bounds_1 := parser_get_glyph_box( & entry.parser_info, glyph_index )
bounds_width := f32(bounds_1.x - bounds_0.x)
bounds_height := f32(bounds_1.y - bounds_0.y)
bounds_size := Vec2 {
f32(bounds_1.x - bounds_0.x),
f32(bounds_1.y - bounds_0.y)
}
// E region is special case and not cached to atlas.
if region_kind == .None || region_kind == .E do return
@ -260,13 +257,11 @@ cache_glyph_to_atlas :: proc( ctx : ^Context,
assert( LRU_get( & region.state, lru_code ) != - 1 )
}
atlas := & ctx.atlas
atlas_width := f32(atlas.width)
atlas_height := f32(atlas.height)
glyph_buffer := & ctx.glyph_buffer
glyph_buffer_width := f32(glyph_buffer.width)
glyph_buffer_height := f32(glyph_buffer.height)
glyph_padding := cast(f32) atlas.glyph_padding
atlas := & ctx.atlas
glyph_buffer := & ctx.glyph_buffer
atlas_size := Vec2 { f32(atlas.width), f32(atlas.height) }
glyph_buffer_size := Vec2 { f32(glyph_buffer.width), f32(glyph_buffer.height) }
glyph_padding := cast(f32) atlas.glyph_padding
if ctx.debug_print
{
@ -283,36 +278,28 @@ cache_glyph_to_atlas :: proc( ctx : ^Context,
glyph_draw_translate.y = cast(f32) (i32(glyph_draw_translate.y + 0.9999999))
// Allocate a glyph_update_FBO region
gwidth_scaled_px := bounds_width * glyph_draw_scale.x + 1.0 + over_sample.x * glyph_padding
gwidth_scaled_px := bounds_size.x * glyph_draw_scale.x + 1.0 + over_sample.x * glyph_padding
if i32(f32(glyph_buffer.batch_x) + gwidth_scaled_px) >= i32(glyph_buffer.width) {
flush_glyph_buffer_to_atlas( ctx )
}
// Calculate the src and destination regions
dst_position, slot_szie := atlas_bbox( atlas, region_kind, atlas_index )
dst_glyph_position := dst_position
dst_glyph_width := bounds_width * entry.size_scale
dst_glyph_height := bounds_height * entry.size_scale
dst_glyph_width += glyph_padding
dst_glyph_height += glyph_padding
slot_position, slot_szie := atlas_bbox( atlas, region_kind, atlas_index )
dst_size := slot_szie
dst_glyph_size := Vec2 { dst_glyph_width, dst_glyph_height }
screenspace_x_form( & dst_glyph_position, & dst_glyph_size, atlas_width, atlas_height )
screenspace_x_form( & dst_position, & dst_size, atlas_width, atlas_height )
dst_glyph_position := slot_position
dst_glyph_size := bounds_size * entry.size_scale + glyph_padding
dst_size := slot_szie
screenspace_x_form( & dst_glyph_position, & dst_glyph_size, atlas_size )
screenspace_x_form( & slot_position, & dst_size, atlas_size )
src_position := Vec2 { f32(glyph_buffer.batch_x), 0 }
src_size := Vec2 {
bounds_width * glyph_draw_scale.x,
bounds_height * glyph_draw_scale.y,
}
src_size += over_sample * glyph_padding
textspace_x_form( & src_position, & src_size, glyph_buffer_width, glyph_buffer_height )
src_size := bounds_size * glyph_draw_scale + over_sample * glyph_padding
textspace_x_form( & src_position, & src_size, glyph_buffer_size )
// Advance glyph_update_batch_x and calculate final glyph drawing transform
glyph_draw_translate.x += f32(glyph_buffer.batch_x)
glyph_buffer.batch_x += i32(gwidth_scaled_px)
screenspace_x_form( & glyph_draw_translate, & glyph_draw_scale, glyph_buffer_width, glyph_buffer_height )
screenspace_x_form( & glyph_draw_translate, & glyph_draw_scale, glyph_buffer_size )
call : DrawCall
{
@ -321,14 +308,22 @@ cache_glyph_to_atlas :: proc( ctx : ^Context,
pass = .Atlas
region = .Ignore
start_index = cast(u32) len(glyph_buffer.clear_draw_list.indices)
blit_quad( & glyph_buffer.clear_draw_list, dst_position, dst_position + dst_size, { 1.0, 1.0 }, { 1.0, 1.0 } )
blit_quad( & glyph_buffer.clear_draw_list,
slot_position, slot_position + dst_size,
{ 1.0, 1.0 }, { 1.0, 1.0 } )
end_index = cast(u32) len(glyph_buffer.clear_draw_list.indices)
append( & glyph_buffer.clear_draw_list.calls, call )
// Queue up a blit from glyph_update_FBO to the atlas
region = .None
start_index = cast(u32) len(glyph_buffer.draw_list.indices)
blit_quad( & glyph_buffer.draw_list, dst_glyph_position, dst_position + dst_glyph_size, src_position, src_position + src_size )
blit_quad( & glyph_buffer.draw_list,
dst_glyph_position, slot_position + dst_glyph_size,
src_position, src_position + src_size )
end_index = cast(u32) len(glyph_buffer.draw_list.indices)
append( & glyph_buffer.draw_list.calls, call )
}
@ -384,52 +379,40 @@ clear_draw_list :: #force_inline proc ( draw_list : ^DrawList ) {
directly_draw_massive_glyph :: proc( ctx : ^Context,
entry : ^Entry,
glyph : Glyph,
bounds_0, bounds_1 : Vec2,
bounds_width, bounds_height : f32,
bounds_0, bounds_1 : Vec2,
bounds_size : Vec2,
over_sample, position, scale : Vec2 )
{
// profile(#procedure)
flush_glyph_buffer_to_atlas( ctx )
glyph_padding := f32(ctx.atlas.glyph_padding)
glyph_buffer_width := f32(ctx.glyph_buffer.width)
glyph_buffer_height := f32(ctx.glyph_buffer.height)
glyph_padding := f32(ctx.atlas.glyph_padding)
glyph_buffer_size := Vec2 { f32(ctx.glyph_buffer.width), f32(ctx.glyph_buffer.height) }
// Draw un-antialiased glyph to update FBO.
glyph_draw_scale := over_sample * entry.size_scale
glyph_draw_translate := -1 * bounds_0 * glyph_draw_scale + vec2_from_scalar(glyph_padding)
screenspace_x_form( & glyph_draw_translate, & glyph_draw_scale, glyph_buffer_width, glyph_buffer_height )
screenspace_x_form( & glyph_draw_translate, & glyph_draw_scale, glyph_buffer_size )
cache_glyph( ctx, entry.id, glyph, entry, bounds_0, bounds_1, glyph_draw_scale, glyph_draw_translate )
glyph_padding_dbl := glyph_padding * 2
bounds_scaled := Vec2 { bounds_width, bounds_height } * entry.size_scale
bounds_scaled := bounds_size * entry.size_scale
// Figure out the source rect.
glyph_position := Vec2 {}
glyph_width := bounds_scaled.x * over_sample.x
glyph_height := bounds_scaled.y * over_sample.y
glyph_dst_width := bounds_scaled.x
glyph_dst_height := bounds_scaled.y
glyph_height += glyph_padding_dbl
glyph_width += glyph_padding_dbl
glyph_dst_width += glyph_padding_dbl
glyph_dst_height += glyph_padding_dbl
glyph_position := Vec2 {}
glyph_size := vec2(glyph_padding_dbl)
glyph_dst_size := glyph_size + bounds_scaled
glyph_size += bounds_scaled * over_sample
// Figure out the destination rect.
bounds_0_scaled := Vec2 {
cast(f32) i32(bounds_0.x * entry.size_scale - 0.5),
cast(f32) i32(bounds_0.y * entry.size_scale - 0.5),
}
dst := position + scale * bounds_0_scaled
dst_width := scale.x * glyph_dst_width
dst_height := scale.y * glyph_dst_height
dst.x -= scale.x * glyph_padding
dst.y -= scale.y * glyph_padding
dst_size := Vec2{ dst_width, dst_height }
glyph_size := Vec2 { glyph_width, glyph_height }
textspace_x_form( & glyph_position, & glyph_size, glyph_buffer_width, glyph_buffer_height )
dst := position + scale * bounds_0_scaled - glyph_padding * scale
dst_size := glyph_dst_size * scale
textspace_x_form( & glyph_position, & glyph_size, glyph_buffer_size )
// Add the glyph drawcall.
call : DrawCall
@ -438,8 +421,12 @@ directly_draw_massive_glyph :: proc( ctx : ^Context,
pass = .Target_Uncached
colour = ctx.colour
start_index = u32(len(ctx.draw_list.indices))
blit_quad( & ctx.draw_list, dst, dst + dst_size, glyph_position, glyph_position + glyph_size )
end_index = u32(len(ctx.draw_list.indices))
blit_quad( & ctx.draw_list,
dst, dst + dst_size,
glyph_position, glyph_position + glyph_size )
end_index = u32(len(ctx.draw_list.indices))
append( & ctx.draw_list.calls, call )
}
@ -464,13 +451,15 @@ draw_cached_glyph :: proc( ctx : ^Context,
) -> b32
{
// profile(#procedure)
bounds_width := f32(bounds_1.x - bounds_0.x)
bounds_height := f32(bounds_1.y - bounds_0.y)
bounds_size := Vec2 {
f32(bounds_1.x - bounds_0.x),
f32(bounds_1.y - bounds_0.y),
}
// E region is special case and not cached to atlas
if region_kind == .E
{
directly_draw_massive_glyph( ctx, entry, glyph_index, bounds_0, bounds_1, bounds_width, bounds_height, over_sample, position, scale )
directly_draw_massive_glyph( ctx, entry, glyph_index, bounds_0, bounds_1, bounds_size, over_sample, position, scale )
return true
}
@ -480,30 +469,22 @@ draw_cached_glyph :: proc( ctx : ^Context,
}
atlas := & ctx.atlas
atlas_width := f32(atlas.width)
atlas_height := f32(atlas.height)
atlas_size := Vec2 { f32(atlas.width), f32(atlas.height) }
glyph_padding := f32(atlas.glyph_padding)
// Figure out the source bounding box in the atlas texture
glyph_atlas_position, glyph_atlas_size := atlas_bbox( atlas, region_kind, atlas_index )
slot_position, _ := atlas_bbox( atlas, region_kind, atlas_index )
glyph_width := bounds_width * entry.size_scale
glyph_height := bounds_height * entry.size_scale
glyph_scale := bounds_size * entry.size_scale + glyph_padding
glyph_width += glyph_padding
glyph_height += glyph_padding
glyph_scale := Vec2 { glyph_width, glyph_height }
bounds_0_scaled := Vec2{ f32(bounds_0.x), f32(bounds_0.y) } * entry.size_scale //- { 0.5, 0.5 }
bounds_0_scaled := bounds_0 * entry.size_scale //- { 0.5, 0.5 }
bounds_0_scaled = ceil(bounds_0_scaled)
dst := position + bounds_0_scaled * scale
dst -= scale * glyph_padding
dst_width := scale.x * glyph_width
dst_height := scale.y * glyph_height
dst_scale := Vec2 { dst_width, dst_height }
dst := position + bounds_0_scaled * scale
dst -= glyph_padding * scale
dst_scale := glyph_scale * scale
textspace_x_form( & glyph_atlas_position, & glyph_scale, atlas_width, atlas_height )
textspace_x_form( & slot_position, & glyph_scale, atlas_size )
// Add the glyph drawcall
call := DrawCall_Default
@ -513,7 +494,9 @@ draw_cached_glyph :: proc( ctx : ^Context,
colour = ctx.colour
start_index = cast(u32) len(ctx.draw_list.indices)
blit_quad( & ctx.draw_list, dst, dst + dst_scale, glyph_atlas_position, glyph_atlas_position + glyph_scale )
blit_quad( & ctx.draw_list,
dst, dst + dst_scale,
slot_position, slot_position + glyph_scale )
end_index = cast(u32) len(ctx.draw_list.indices)
}
append( & ctx.draw_list.calls, call )

View File

@ -74,6 +74,10 @@ vec2 :: proc {
vec2_from_vec2i,
}
vec2i :: proc {
vec2i_from_vec2,
}
vec2_64 :: proc {
vec2_64_from_vec2,
}

View File

@ -1,18 +1,6 @@
package VEFontCache
font_glyph_lru_code :: #force_inline proc "contextless" ( font : FontID, glyph_index : Glyph ) -> (lru_code : u64)
{
// font := font
// glyph_index := glyph_index
// font_bytes := slice_ptr( transmute(^byte) & font, size_of(FontID) )
// glyph_bytes := slice_ptr( transmute(^byte) & glyph_index, size_of(Glyph) )
// buffer : [32]byte
// copy( buffer[:], font_bytes )
// copy( buffer[ len(font_bytes) :], glyph_bytes )
// hash := fnv64a( transmute([]byte) buffer[: size_of(FontID) + size_of(Glyph) ] )
// lru_code = hash
font_glyph_lru_code :: #force_inline proc "contextless" ( font : FontID, glyph_index : Glyph ) -> (lru_code : u64) {
lru_code = u64(glyph_index) + ( ( 0x100000000 * u64(font) ) & 0xFFFFFFFF00000000 )
return
}
@ -79,8 +67,7 @@ is_empty :: #force_inline proc ( ctx : ^Context, entry : ^Entry, glyph_index : G
return false
}
mark_batch_codepoint_seen :: #force_inline proc ( ctx : ^Context, lru_code : u64 )
{
mark_batch_codepoint_seen :: #force_inline proc ( ctx : ^Context, lru_code : u64 ) {
ctx.temp_codepoint_seen[lru_code] = true
ctx.temp_codepoint_seen_num += 1
}
@ -90,15 +77,13 @@ reset_batch_codepoint_state :: #force_inline proc( ctx : ^Context ) {
ctx.temp_codepoint_seen_num = 0
}
screenspace_x_form :: #force_inline proc "contextless" ( position, scale : ^Vec2, width, height : f32 ) {
screenspace_x_form :: #force_inline proc "contextless" ( position, scale : ^Vec2, size : Vec2 ) {
when true
{
pos_64 := vec2_64_from_vec2(position^)
scale_64 := vec2_64_from_vec2(scale^)
width := f64(width)
height := f64(height)
quotient : Vec2_64 = 1.0 / { width, height }
quotient : Vec2_64 = 1.0 / vec2_64(size)
pos_64 = pos_64 * quotient * 2.0 - 1.0
scale_64 = scale_64 * quotient * 2.0
@ -107,21 +92,19 @@ screenspace_x_form :: #force_inline proc "contextless" ( position, scale : ^Vec2
}
else
{
quotient : Vec2 = 1.0 / { width, height }
quotient : Vec2 = 1.0 / size
(position^) *= quotient * 2.0 - 1.0
(scale^) *= quotient * 2.0
}
}
textspace_x_form :: #force_inline proc "contextless" ( position, scale : ^Vec2, width, height : f32 ) {
textspace_x_form :: #force_inline proc "contextless" ( position, scale : ^Vec2, size : Vec2 ) {
when true
{
pos_64 := vec2_64_from_vec2(position^)
scale_64 := vec2_64_from_vec2(scale^)
width := f64(width)
height := f64(height)
quotient : Vec2_64 = 1.0 / { width, height }
quotient : Vec2_64 = 1.0 / vec2_64(size)
pos_64 *= quotient
scale_64 *= quotient
@ -130,7 +113,7 @@ textspace_x_form :: #force_inline proc "contextless" ( position, scale : ^Vec2,
}
else
{
quotient : Vec2 = 1.0 / { width, height }
quotient : Vec2 = 1.0 / size
(position^) *= quotient
(scale^) *= quotient
}