WIP - VEFontCache: Working on getting font size usage and super-sampling via scaling working on library side...

This commit is contained in:
Edward R. Gonzalez 2025-01-06 11:00:55 -05:00
parent c0b439bc30
commit 840e6053ff
12 changed files with 304 additions and 216 deletions

2
.gitignore vendored
View File

@ -9,5 +9,5 @@ Sectr.sublime-project
Sectr.sublime-workspace
ols.json
.vscode/settings.json
thirdparty
# thirdparty
# toolchain

View File

@ -470,18 +470,18 @@ batch_generate_glyphs_draw_list :: proc ( draw_list : ^Draw_List,
f32_allocated_x := cast(f32) glyph_buffer.allocated_x
// Resolve how much space this glyph will allocate in the buffer
buffer_size := (glyph.bounds_size_scaled + glyph_buffer.draw_padding) * glyph_buffer.over_sample + glyph.over_sample
buffer_size := (glyph.bounds_size_scaled + glyph_buffer.draw_padding) * glyph_buffer.over_sample
// Allocate a glyph glyph render target region (FBO)
to_allocate_x := buffer_size.x
to_allocate_x := buffer_size.x + 2.0
// If allocation would exceed buffer's bounds the buffer must be flush before this glyph can be rendered.
glyph.flush_glyph_buffer = i32(f32_allocated_x + to_allocate_x) >= i32(glyph_buffer_size.x)
glyph.buffer_x = glyph.flush_glyph_buffer ? 0 : f32_allocated_x
glyph.buffer_x = f32_allocated_x * f32( i32( ! glyph.flush_glyph_buffer ) )
// The glyph buffer space transform for generate_glyph_pass_draw_list
draw_transform := & glyph.draw_transform
draw_transform.scale = font_scale * glyph_buffer.over_sample
draw_transform.pos = -1 * (glyph.bounds.p0) * draw_transform.scale + atlas.glyph_padding
draw_transform.pos = -1 * (glyph.bounds.p0) * draw_transform.scale + glyph_buffer.draw_padding
draw_transform.pos.x += glyph.buffer_x
to_glyph_buffer_space( & draw_transform.pos, & draw_transform.scale, glyph_buffer_size )
@ -506,20 +506,15 @@ batch_generate_glyphs_draw_list :: proc ( draw_list : ^Draw_List,
f32_allocated_x := cast(f32) glyph_buffer.allocated_x
// Resolve how much space this glyph will allocate in the buffer
buffer_size := (glyph.bounds_size_scaled + glyph_buffer.draw_padding) * glyph.over_sample + glyph.over_sample
buffer_size := (glyph.bounds_size_scaled + glyph_buffer.draw_padding) * glyph.over_sample
// Allocate a glyph glyph render target region (FBO)
to_allocate_x := buffer_size.x
to_allocate_x := buffer_size.x + 2.0
glyph_buffer.allocated_x += i32(to_allocate_x)
// If allocation would exceed buffer's bounds the buffer must be flush before this glyph can be rendered.
glyph.flush_glyph_buffer = i32(f32_allocated_x + to_allocate_x) >= i32(glyph_buffer_size.x)
// glyph.buffer_x = f32_allocated_x * f32( i32( glyph.flush_glyph_buffer ) )
glyph.buffer_x = glyph.flush_glyph_buffer ? 0 : f32_allocated_x
// }
// for id, index in oversized
// {
// glyph := & glyph_pack[id]
glyph.buffer_x = f32_allocated_x * f32( i32( ! glyph.flush_glyph_buffer ) )
// Quad to for drawing atlas slot to target
draw_quad := & glyph.draw_quad
@ -531,9 +526,9 @@ batch_generate_glyphs_draw_list :: proc ( draw_list : ^Draw_List,
draw_quad.dst_scale = (glyph.bounds_size_scaled + glyph_padding) * target_scale
// The glyph buffer space transform for generate_glyph_pass_draw_list
draw_transform := & glyph.draw_transform
draw_transform.scale = font_scale * glyph.over_sample
draw_transform.pos = -1 * glyph.bounds.p0 * draw_transform.scale + vec2(atlas.glyph_padding)
draw_transform := & glyph.draw_transform
draw_transform.scale = font_scale * glyph.over_sample
draw_transform.pos = -1 * glyph.bounds.p0 * draw_transform.scale + vec2(atlas.glyph_padding)
draw_transform.pos.x += glyph.buffer_x
to_glyph_buffer_space( & draw_transform.pos, & draw_transform.scale, glyph_buffer_size )
@ -615,8 +610,8 @@ batch_generate_glyphs_draw_list :: proc ( draw_list : ^Draw_List,
& glyph_buffer.allocated_x
)
dst_region_pos := glyph.region_pos
dst_region_size := glyph.region_size
dst_region_pos := glyph.region_pos
dst_region_size := glyph.region_size
to_glyph_buffer_space( & dst_region_pos, & dst_region_size, atlas_size )
clear_target_region : Draw_Call
@ -634,12 +629,14 @@ batch_generate_glyphs_draw_list :: proc ( draw_list : ^Draw_List,
end_index = cast(u32) len(glyph_buffer.clear_draw_list.indices)
}
dst_glyph_pos := glyph.region_pos
dst_glyph_size := (glyph.bounds_size_scaled) + atlas.glyph_padding
to_glyph_buffer_space( & dst_glyph_pos, & dst_glyph_size, atlas_size )
dst_glyph_pos := glyph.region_pos
dst_glyph_size := glyph.bounds_size_scaled + atlas.glyph_padding
// dst_glyph_size.y = ceil(dst_glyph_size.y) // Note(Ed): Seems to improve hinting
to_glyph_buffer_space( & dst_glyph_pos, & dst_glyph_size, atlas_size )
src_position := Vec2 { glyph.buffer_x, 0 }
src_size := (glyph.bounds_size_scaled + atlas.glyph_padding) * glyph_buffer.over_sample
src_position := Vec2 { glyph.buffer_x, 0 }
src_size := (glyph.bounds_size_scaled + atlas.glyph_padding) * glyph_buffer.over_sample
// src_size.y = ceil(src_size.y) // Note(Ed): Seems to improve hinting
to_target_space( & src_position, & src_size, glyph_buffer_size )

View File

@ -74,23 +74,22 @@ parser_shutdown :: proc( ctx : ^Parser_Context ) {
// TODO(Ed): Implement
}
parser_load_font :: proc( ctx : ^Parser_Context, label : string, data : []byte ) -> (font : Parser_Font_Info)
parser_load_font :: proc( ctx : ^Parser_Context, label : string, data : []byte ) -> (font : Parser_Font_Info, error : b32)
{
// switch ctx.kind
// {
// case .Freetype:
// 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
// error_status := freetype.new_memory_face( ctx.ft_library, raw_data(data), cast(i32) len(data), 0, & font.freetype_info )
// if error != .Ok do error = true
// }
// 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
// if error_status != .Ok do error = true
// }
// case .STB_TrueType:
success := stbtt.InitFont( & font.stbtt_info, raw_data(data), 0 )
if ! success do return
error = ! stbtt.InitFont( & font.stbtt_info, raw_data(data), 0 )
// }
font.label = label
@ -298,10 +297,8 @@ parser_is_glyph_empty :: #force_inline proc "contextless" ( font : Parser_Font_I
parser_scale :: #force_inline proc "contextless" ( font : Parser_Font_Info, size : f32 ) -> f32
{
profile(#procedure)
size_scale := size < 0.0 ? \
parser_scale_for_pixel_height( font, -size ) \
: parser_scale_for_mapping_em_to_pixels( font, size )
// size_scale = 1.0
// size_scale := size < 0.0 ? parser_scale_for_pixel_height( font, -size ) : parser_scale_for_mapping_em_to_pixels( font, size )
size_scale := size > 0.0 ? parser_scale_for_pixel_height( font, size ) : parser_scale_for_mapping_em_to_pixels( font, -size )
return size_scale
}

View File

@ -82,7 +82,7 @@ shaper_shape_harfbuzz :: #force_inline proc( ctx : ^Shaper_Context, text_utf8 :
ascent := entry.ascent
descent := entry.descent
line_gap :=entry.line_gap
line_gap := entry.line_gap
max_line_width := f32(0)
line_count := 1
@ -226,7 +226,13 @@ shaper_shape_harfbuzz :: #force_inline proc( ctx : ^Shaper_Context, text_utf8 :
return
}
shaper_shape_text_uncached_advanced :: #force_inline proc( ctx : ^Shaper_Context, entry : Entry, font_px_size : f32, font_scale : f32, text_utf8 : string, output : ^Shaped_Text )
shaper_shape_text_uncached_advanced :: #force_inline proc( ctx : ^Shaper_Context,
entry : Entry,
font_px_size : f32,
font_scale : f32,
text_utf8 : string,
output : ^Shaped_Text
)
{
profile(#procedure)
assert( ctx != nil )
@ -237,7 +243,13 @@ shaper_shape_text_uncached_advanced :: #force_inline proc( ctx : ^Shaper_Context
shaper_shape_harfbuzz( ctx, text_utf8, entry, font_px_size, font_scale, output )
}
shaper_shape_text_latin :: #force_inline proc( ctx : ^Shaper_Context, entry : Entry, font_px_Size, font_scale : f32, text_utf8 : string, output : ^Shaped_Text )
shaper_shape_text_latin :: #force_inline proc( ctx : ^Shaper_Context,
entry : Entry,
font_px_Size : f32,
font_scale : f32,
text_utf8 : string,
output : ^Shaped_Text
)
{
profile(#procedure)
assert( ctx != nil )
@ -278,8 +290,8 @@ shaper_shape_text_latin :: #force_inline proc( ctx : ^Shaper_Context, entry : En
{
append( & output.glyphs, glyph_index)
append( & output.positions, Vec2 {
floor(position.x),
floor(position.y)
ceil(position.x),
ceil(position.y)
})
}

View File

@ -15,6 +15,11 @@ DISABLE_PROFILING :: true
Font_ID :: distinct i32
Glyph :: distinct i32
Load_Font_Error :: enum(i32) {
None,
Parser_Failed,
}
Entry :: struct {
parser_info : Parser_Font_Info,
shaper_info : Shaper_Info,
@ -54,22 +59,25 @@ Context :: struct {
calls_offset : int,
},
debug_print : b32,
debug_print_verbose : b32,
//TODO(Ed): Add a push/pop stack for the below
// Helps with hinting
snap_width : f32,
snap_height : f32,
colour : Colour, // Color used in draw interface
cursor_pos : Vec2,
alpha_scalar : f32, // Will apply a multiplier to the colour's alpha which provides some sharpening of the edges.
// camera : Camera, // TODO(Ed): Add camera support
colour : Colour, // Color used in draw interface
cursor_pos : Vec2,
alpha_sharpen : f32, // Will apply a boost scalar (1.0 + alpha sharpen) to the colour's alpha which provides some sharpening of the edges.
// Used by draw interface to super-scale the text by
// upscaling px_size with px_scalar and then down-scaling
// the draw_list result by the same amount.
px_scalar : f32,
px_scalar : f32, // Used to upscale which font size is used to render (improves hinting)
default_curve_quality : i32,
debug_print : b32,
debug_print_verbose : b32,
}
Init_Atlas_Region_Params :: struct {
@ -135,7 +143,7 @@ Init_Shaper_Params :: struct {
}
Init_Shaper_Params_Default :: Init_Shaper_Params {
snap_glyph_position = true,
snap_glyph_position = false,
adv_snap_small_font_threshold = 0,
}
@ -158,7 +166,9 @@ startup :: proc( ctx : ^Context, parser_kind : Parser_Kind = .STB_TrueType,
glyph_draw_params := Init_Glyph_Draw_Params_Default,
shape_cache_params := Init_Shape_Cache_Params_Default,
shaper_params := Init_Shaper_Params_Default,
alpha_sharpen := 0.2,
alpha_sharpen : f32 = 0.2,
px_scalar : f32 = 1.0,
// Curve quality to use for a font when unspecified,
// Affects step size for bezier curve passes in generate_glyph_pass_draw_list
default_curve_quality : u32 = 3,
@ -170,7 +180,9 @@ startup :: proc( ctx : ^Context, parser_kind : Parser_Kind = .STB_TrueType,
ctx.backing = allocator
context.allocator = ctx.backing
ctx.colour = { 1, 1, 1, 1 }
ctx.colour = { 1, 1, 1, 1 }
ctx.alpha_sharpen = alpha_sharpen
ctx.px_scalar = px_scalar
shaper_ctx := & ctx.shaper_ctx
shaper_ctx.adv_snap_small_font_threshold = f32(shaper_params.adv_snap_small_font_threshold)
@ -292,7 +304,7 @@ startup :: proc( ctx : ^Context, parser_kind : Parser_Kind = .STB_TrueType,
glyph_buffer.shape_gen_scratch, error = make( [dynamic]Vertex, len = 0, cap = 4 * Kilobyte )
assert(error == .None, "VEFontCache.init : Failed to allocate shape_gen_scratch")
batch_cache := & glyph_buffer.batch_cache
batch_cache := & glyph_buffer.batch_cache
batch_cache.cap = i32(glyph_draw_params.batch_glyph_limit)
batch_cache.num = 0
batch_cache.table, error = make( map[u32]b8, uint(glyph_draw_params.shape_gen_scratch_reserve) )
@ -314,12 +326,25 @@ hot_reload :: proc( ctx : ^Context, allocator : Allocator )
ctx.backing = allocator
context.allocator = ctx.backing
using ctx
// using ctx.atlas
reload_array( & entries, allocator )
reload_array( & draw_list.vertices, allocator)
reload_array( & draw_list.indices, allocator )
reload_array( & draw_list.calls, allocator )
reload_array( & glyph_buffer.draw_list.calls, allocator )
reload_array( & glyph_buffer.draw_list.indices, allocator )
reload_array( & glyph_buffer.draw_list.vertices, allocator )
reload_array( & glyph_buffer.clear_draw_list.calls, allocator )
reload_array( & glyph_buffer.clear_draw_list.indices, allocator )
reload_array( & glyph_buffer.clear_draw_list.vertices, allocator )
reload_map( & glyph_buffer.batch_cache.table, allocator )
reload_array( & glyph_buffer.shape_gen_scratch, allocator )
reload_array_soa( & glyph_buffer.glyph_pack, allocator )
reload_array( & glyph_buffer.oversized, allocator )
reload_array( & glyph_buffer.to_cache, allocator )
reload_array( & glyph_buffer.cached, allocator )
lru_reload( & atlas.region_a.state, allocator)
lru_reload( & atlas.region_b.state, allocator)
@ -330,28 +355,14 @@ hot_reload :: proc( ctx : ^Context, allocator : Allocator )
for idx : i32 = 0; idx < i32(len(shape_cache.storage)); idx += 1 {
stroage_entry := & shape_cache.storage[idx]
using stroage_entry
reload_array( & glyphs, allocator )
reload_array( & glyphs, allocator )
reload_array( & positions, allocator )
}
reload_array( & glyph_buffer.draw_list.calls, allocator )
reload_array( & glyph_buffer.draw_list.indices, allocator )
reload_array( & glyph_buffer.draw_list.vertices, allocator )
reload_array( & glyph_buffer.clear_draw_list.calls, allocator )
reload_array( & glyph_buffer.clear_draw_list.indices, allocator )
reload_array( & glyph_buffer.clear_draw_list.vertices, allocator )
reload_array_soa( & glyph_buffer.glyph_pack, allocator )
reload_array( & glyph_buffer.oversized, allocator )
reload_array( & glyph_buffer.to_cache, allocator )
reload_array( & glyph_buffer.cached, allocator )
reload_array( & glyph_buffer.shape_gen_scratch, allocator )
reload_map( & glyph_buffer.batch_cache.table, allocator )
reload_array( & shape_cache.storage, allocator )
reload_array( & draw_list.vertices, allocator)
reload_array( & draw_list.indices, allocator )
reload_array( & draw_list.calls, allocator )
}
shutdown :: proc( ctx : ^Context )
@ -363,12 +374,23 @@ shutdown :: proc( ctx : ^Context )
for & entry in entries {
unload_font( ctx, entry.id )
}
delete( entries )
delete( glyph_buffer.draw_list.vertices )
delete( glyph_buffer.draw_list.indices )
delete( glyph_buffer.draw_list.calls )
delete( draw_list.vertices )
delete( draw_list.indices )
delete( draw_list.calls )
delete( glyph_buffer.clear_draw_list.vertices )
delete( glyph_buffer.clear_draw_list.indices )
delete( glyph_buffer.clear_draw_list.calls )
delete( glyph_buffer.batch_cache.table )
delete( glyph_buffer.shape_gen_scratch )
delete_soa( glyph_buffer.glyph_pack)
delete( glyph_buffer.oversized)
delete( glyph_buffer.to_cache)
delete( glyph_buffer.cached)
lru_free( & atlas.region_a.state )
lru_free( & atlas.region_b.state )
@ -383,28 +405,16 @@ shutdown :: proc( ctx : ^Context )
delete( positions )
}
lru_free( & shape_cache.state )
delete( glyph_buffer.draw_list.vertices )
delete( glyph_buffer.draw_list.indices )
delete( glyph_buffer.draw_list.calls )
delete( glyph_buffer.clear_draw_list.vertices )
delete( glyph_buffer.clear_draw_list.indices )
delete( glyph_buffer.clear_draw_list.calls )
delete_soa( glyph_buffer.glyph_pack)
delete( glyph_buffer.oversized)
delete( glyph_buffer.to_cache)
delete( glyph_buffer.cached)
delete( glyph_buffer.shape_gen_scratch )
delete( glyph_buffer.batch_cache.table )
delete( draw_list.vertices )
delete( draw_list.indices )
delete( draw_list.calls )
shaper_shutdown( & shaper_ctx )
parser_shutdown( & parser_ctx )
}
load_font :: proc( ctx : ^Context, label : string, data : []byte, size_px : f32, glyph_curve_quality : u32 = 0 ) -> (font_id : Font_ID)
load_font :: proc( ctx : ^Context, label : string, data : []byte, glyph_curve_quality : u32 = 0 ) -> (font_id : Font_ID, error : Load_Font_Error)
{
profile(#procedure)
assert( ctx != nil )
@ -430,7 +440,12 @@ load_font :: proc( ctx : ^Context, label : string, data : []byte, size_px : f32,
entry.used = true
profile_begin("calling loaders")
entry.parser_info = parser_load_font( & parser_ctx, label, data )
parser_error : b32
entry.parser_info, parser_error = parser_load_font( & parser_ctx, label, data )
if parser_error {
error = .Parser_Failed
return
}
entry.shaper_info = shaper_load_font( & shaper_ctx, label, data )
profile_end()
@ -459,7 +474,6 @@ unload_font :: proc( ctx : ^Context, font : Font_ID )
assert( font >= 0 && int(font) < len(ctx.entries) )
context.allocator = ctx.backing
using ctx
entry := & ctx.entries[ font ]
entry.used = false
@ -477,17 +491,20 @@ configure_snap :: #force_inline proc( ctx : ^Context, snap_width, snap_height :
ctx.snap_height = f32(snap_height)
}
get_cursor_pos :: #force_inline proc( ctx : ^Context ) -> Vec2 { assert(ctx != nil); return ctx.cursor_pos }
set_alpha_scalar :: #force_inline proc( ctx : ^Context, scalar : f32 ) { assert(ctx != nil); ctx.alpha_scalar = scalar }
set_colour :: #force_inline proc( ctx : ^Context, colour : Colour ) { assert(ctx != nil); ctx.colour = colour }
get_cursor_pos :: #force_inline proc( ctx : ^Context ) -> Vec2 { assert(ctx != nil); return ctx.cursor_pos }
set_alpha_scalar :: #force_inline proc( ctx : ^Context, scalar : f32 ) { assert(ctx != nil); ctx.alpha_sharpen = scalar }
set_px_scalar :: #force_inline proc( ctx : ^Context, scalar : f32 ) { assert(ctx != nil); ctx.px_scalar = scalar }
set_colour :: #force_inline proc( ctx : ^Context, colour : Colour ) { assert(ctx != nil); ctx.colour = colour }
draw_text :: #force_inline proc( ctx : ^Context, font : Font_ID, px_size : f32, position, scale : Vec2, text_utf8 : string ) -> b32
draw_text :: #force_inline proc( ctx : ^Context, font : Font_ID, px_size : f32, position, scale : Vec2, text_utf8 : string )
{
profile(#procedure)
assert( ctx != nil )
assert( font >= 0 && int(font) < len(ctx.entries) )
assert( len(text_utf8) > 0 )
entry := ctx.entries[ font ]
ctx.cursor_pos = {}
position := position
@ -495,49 +512,57 @@ draw_text :: #force_inline proc( ctx : ^Context, font : Font_ID, px_size : f32,
position.y = ceil(position.y * ctx.snap_height) / ctx.snap_height
colour := ctx.colour
colour.a = 1.0 + ctx.alpha_scalar
colour.a = 1.0 + ctx.alpha_sharpen
// TODO(Ed): Test this.
// px_size_scalar :: 2
// px_size := px_size * px_size_scalar
// scale := scale * (1 / px_size_scalar)
font_scale := parser_scale( entry.parser_info, px_size )
entry := ctx.entries[ font ]
font_scale := parser_scale( entry.parser_info, px_size )
shape := shaper_shape_text_cached( text_utf8, & ctx.shaper_ctx, & ctx.shape_cache, font, entry, px_size, font_scale, shaper_shape_text_uncached_advanced )
px_upscale := px_size * ctx.px_scalar
downscale := scale * (1 / ctx.px_scalar)
font_scale_upscale := parser_scale( entry.parser_info, px_upscale )
shape := shaper_shape_text_cached( text_utf8, & ctx.shaper_ctx, & ctx.shape_cache,
font,
entry,
px_upscale,
font_scale_upscale,
shaper_shape_text_uncached_advanced
)
ctx.cursor_pos = generate_shape_draw_list( & ctx.draw_list, shape, & ctx.atlas, & ctx.glyph_buffer,
colour,
entry,
font_scale,
position,
scale,
font_scale_upscale,
position,
downscale,
ctx.snap_width,
ctx.snap_height
)
return true
}
draw_text_no_snap :: #force_inline proc( ctx : ^Context, font : Font_ID, px_size : f32, position, scale : Vec2, text_utf8 : string ) -> b32
{
profile(#procedure)
assert( ctx != nil )
assert( font >= 0 && int(font) < len(ctx.entries) )
assert( len(text_utf8) > 0 )
// draw_text_no_snap :: #force_inline proc( ctx : ^Context, font : Font_ID, px_size : f32, position, scale : Vec2, text_utf8 : string )
// {
// profile(#procedure)
// assert( ctx != nil )
// assert( font >= 0 && int(font) < len(ctx.entries) )
// assert( len(text_utf8) > 0 )
ctx.cursor_pos = {}
// ctx.cursor_pos = {}
colour := ctx.colour
colour.a = 1.0 + ctx.alpha_scalar
// entry := ctx.entries[ font ]
entry := ctx.entries[ font ]
font_scale := parser_scale( entry.parser_info, px_size )
shape := shaper_shape_text_cached( text_utf8, & ctx.shaper_ctx, & ctx.shape_cache, font, entry, px_size, font_scale, shaper_shape_text_latin )
ctx.cursor_pos = generate_shape_draw_list( & ctx.draw_list, shape, & ctx.atlas, & ctx.glyph_buffer, colour, entry, font_scale, position, scale, ctx.snap_width, ctx.snap_height )
return true
}
// colour := ctx.colour
// colour.a = 1.0 + ctx.alpha_sharpen
// px_size_upscaled := px_size * ctx.px_scalar
// scale_downsample := scale * (1 / ctx.px_scalar)
// font_scale_upscale := parser_scale( entry.parser_info, px_size_upscaled )
// font_scale := parser_scale( entry.parser_info, -px_size )
// shape := shaper_shape_text_cached( text_utf8, & ctx.shaper_ctx, & ctx.shape_cache, font, entry, px_size, font_scale, shaper_shape_text_latin )
// ctx.cursor_pos = generate_shape_draw_list( & ctx.draw_list, shape, & ctx.atlas, & ctx.glyph_buffer, colour, entry, font_scale, position, scale, ctx.snap_width, ctx.snap_height )
// }
// Resolve the shape and track it to reduce iteration overhead
draw_text_shape :: #force_inline proc( ctx : ^Context, font : Font_ID, px_size : f32, position, scale : Vec2, shape : Shaped_Text ) -> b32
draw_text_shape :: #force_inline proc( ctx : ^Context, font : Font_ID, px_size : f32, position, scale : Vec2, shape : Shaped_Text )
{
profile(#procedure)
assert( ctx != nil )
@ -546,30 +571,45 @@ draw_text_shape :: #force_inline proc( ctx : ^Context, font : Font_ID, px_size :
position.x = ceil(position.x * ctx.snap_width ) / ctx.snap_width
position.y = ceil(position.y * ctx.snap_height) / ctx.snap_height
colour := ctx.colour
colour.a = 1.0 + ctx.alpha_scalar
entry := ctx.entries[ font ]
entry := ctx.entries[ font ]
font_scale := parser_scale( entry.parser_info, px_size )
ctx.cursor_pos = generate_shape_draw_list( & ctx.draw_list, shape, & ctx.atlas, & ctx.glyph_buffer, colour, entry, font_scale, position, scale, ctx.snap_width, ctx.snap_height )
return true
colour := ctx.colour
colour.a = 1.0 + ctx.alpha_sharpen
font_scale := parser_scale( entry.parser_info, px_size )
px_upscale := px_size * ctx.px_scalar
downscale := scale * (1 / ctx.px_scalar)
font_scale_upscale := parser_scale( entry.parser_info, px_upscale )
ctx.cursor_pos = generate_shape_draw_list( & ctx.draw_list, shape, & ctx.atlas, & ctx.glyph_buffer,
colour,
entry,
font_scale_upscale,
position,
downscale,
ctx.snap_width,
ctx.snap_height
)
}
// Resolve the shape and track it to reduce iteration overhead
draw_text_shape_no_snap :: #force_inline proc( ctx : ^Context, font : Font_ID, px_size : f32, position, scale : Vec2, shape : Shaped_Text ) -> b32
{
profile(#procedure)
assert( ctx != nil )
assert( font >= 0 && int(font) < len(ctx.entries) )
// draw_text_shape_no_snap :: #force_inline proc( ctx : ^Context, font : Font_ID, px_size : f32, position, scale : Vec2, shape : Shaped_Text )
// {
// profile(#procedure)
// assert( ctx != nil )
// assert( font >= 0 && int(font) < len(ctx.entries) )
colour := ctx.colour
colour.a = 1.0 + ctx.alpha_scalar
// colour := ctx.colour
// colour.a = 1.0 + ctx.alpha_sharpen
entry := ctx.entries[ font ]
font_scale := parser_scale( entry.parser_info, px_size )
ctx.cursor_pos = generate_shape_draw_list( & ctx.draw_list, shape, & ctx.atlas, & ctx.glyph_buffer, colour, entry, font_scale, position, scale, ctx.snap_width, ctx.snap_height )
return true
}
// px_size_upscaled := px_size * ctx.px_scalar
// scale := scale * (1 / ctx.px_scalar)
// entry := ctx.entries[ font ]
// font_scale := parser_scale( entry.parser_info, px_size )
// ctx.cursor_pos = generate_shape_draw_list( & ctx.draw_list, shape, & ctx.atlas, & ctx.glyph_buffer, colour, entry, font_scale, position, scale, ctx.snap_width, ctx.snap_height )
// }
get_draw_list :: #force_inline proc( ctx : ^Context, optimize_before_returning := true ) -> ^Draw_List {
assert( ctx != nil )
@ -613,20 +653,28 @@ measure_text_size :: #force_inline proc( ctx : ^Context, font : Font_ID, px_size
assert( ctx != nil )
assert( font >= 0 && int(font) < len(ctx.entries) )
entry := ctx.entries[font]
font_scale := parser_scale( entry.parser_info, px_size )
shaped := shaper_shape_text_cached( text_utf8, & ctx.shaper_ctx, & ctx.shape_cache, font, entry, px_size, font_scale, shaper_shape_text_uncached_advanced )
entry := ctx.entries[font]
px_size_upscaled := px_size * ctx.px_scalar
font_scale_upscaled := parser_scale( entry.parser_info, px_size_upscaled )
// font_scale := parser_scale( entry.parser_info, px_size )
shaped := shaper_shape_text_cached( text_utf8, & ctx.shaper_ctx, & ctx.shape_cache, font, entry, px_size_upscaled, font_scale_upscaled, shaper_shape_text_uncached_advanced )
return shaped.size
}
get_font_vertical_metrics :: #force_inline proc ( ctx : ^Context, font : Font_ID, px_szie : f32 ) -> ( ascent, descent, line_gap : f32 )
get_font_vertical_metrics :: #force_inline proc ( ctx : ^Context, font : Font_ID, px_size : f32 ) -> ( ascent, descent, line_gap : f32 )
{
assert( ctx != nil )
assert( font >= 0 && int(font) < len(ctx.entries) )
entry := & ctx.entries[ font ]
// ascent_i32, descent_i32, line_gap_i32 := parser_get_font_vertical_metrics( entry.parser_info )
font_scale := parser_scale( entry.parser_info, px_szie )
entry := ctx.entries[ font ]
font_scale := parser_scale( entry.parser_info, px_size )
px_size_upscaled := px_size * ctx.px_scalar
downscale := 1 / px_size_upscaled
font_scale_upscaled := parser_scale( entry.parser_info, px_size_upscaled )
ascent = font_scale * entry.ascent
descent = font_scale * entry.descent
@ -643,8 +691,19 @@ shape_text_latin :: #force_inline proc( ctx : ^Context, font : Font_ID, px_size
profile(#procedure)
assert( len(text_utf8) > 0 )
entry := ctx.entries[ font ]
font_scale := parser_scale( entry.parser_info, px_size )
return shaper_shape_text_cached( text_utf8, & ctx.shaper_ctx, & ctx.shape_cache, font, entry, px_size, font_scale, shaper_shape_text_latin )
px_size_upscaled := px_size * ctx.px_scalar
font_scale_upscaled := parser_scale( entry.parser_info, px_size_upscaled )
return shaper_shape_text_cached( text_utf8, & ctx.shaper_ctx, & ctx.shape_cache,
font,
entry,
px_size_upscaled,
font_scale_upscaled,
shaper_shape_text_latin
)
}
shape_text_advanced :: #force_inline proc( ctx : ^Context, font : Font_ID, px_size : f32, text_utf8 : string ) -> Shaped_Text
@ -652,20 +711,33 @@ shape_text_advanced :: #force_inline proc( ctx : ^Context, font : Font_ID, px_si
profile(#procedure)
assert( len(text_utf8) > 0 )
entry := ctx.entries[ font ]
font_scale := parser_scale( entry.parser_info, px_size )
return shaper_shape_text_cached( text_utf8, & ctx.shaper_ctx, & ctx.shape_cache, font, entry, px_size, font_scale, shaper_shape_text_uncached_advanced )
px_size_upscaled := px_size * ctx.px_scalar
font_scale_upscaled := parser_scale( entry.parser_info, px_size_upscaled )
return shaper_shape_text_cached( text_utf8, & ctx.shaper_ctx, & ctx.shape_cache,
font,
entry,
px_size_upscaled,
font_scale_upscaled,
shaper_shape_text_uncached_advanced
)
}
// User handled shaped text. Will not be cached
shape_text_latin_uncached :: #force_inline proc( ctx : ^Context, font : Font_ID, text_utf8 : string, entry : ^Entry, allocator := context.allocator ) -> Shaped_Text
shape_text_latin_uncached :: #force_inline proc( ctx : ^Context, font : Font_ID, text_utf8 : string, entry : ^Entry, shape : ^Shaped_Text )
{
return {}
assert(false)
return
}
// User handled shaped text. Will not be cached
shape_text_advanced_uncahed :: #force_inline proc( ctx : ^Context, font : Font_ID, text_utf8 : string, entry : ^Entry, allocator := context.allocator ) -> Shaped_Text
shape_text_advanced_uncahed :: #force_inline proc( ctx : ^Context, font : Font_ID, text_utf8 : string, entry : ^Entry, shape : ^Shaped_Text )
{
return {}
assert(false)
return
}
//#endregion("shaping")

View File

@ -4,7 +4,7 @@ package sectr
import sokol_gfx "thirdparty:sokol/gfx"
DebugData :: struct {
ScratchData :: struct {
square_size : i32,
square_pos : Vec2,
@ -33,11 +33,4 @@ DebugData :: struct {
path_lorem : string,
lorem_content : []byte,
lorem_parse : PWS_ParseResult,
gfx_clear_demo_pass_action : sokol_gfx.Pass_Action,
gfx_tri_demo_state : struct {
pipeline : sokol_gfx.Pipeline,
bindings : sokol_gfx.Bindings,
pass_action : sokol_gfx.Pass_Action,
},
}

View File

@ -221,7 +221,7 @@ State :: struct {
staged_input_events : Array(InputEvent),
// TODO(Ed): Add a multi-threaded guard for accessing or mutating staged_input_events.
debug : DebugData,
debug : ScratchData,
project : Project,
@ -252,7 +252,6 @@ State :: struct {
font_rec_mono_semicasual_reg : FontID,
default_font : FontID,
// Context tracking
// These are used as implicit contextual states when doing immediate mode interfaces
// or for event callbacks that need their context assigned
@ -281,7 +280,7 @@ frametime_delta32 :: #force_inline proc "contextless" () -> f32 {
app_config :: #force_inline proc "contextless" () -> AppConfig { return get_state().config }
app_color_theme :: #force_inline proc "contextless" () -> AppColorTheme { return get_state().config.color_theme }
debug_data :: #force_inline proc "contextless" () -> DebugData { return get_state().debug }
debug_data :: #force_inline proc "contextless" () -> ScratchData { return get_state().debug }
get_frametime :: #force_inline proc "contextless" () -> FrameTime { return get_state().frametime }
get_default_font :: #force_inline proc "contextless" () -> FontID { return get_state().default_font }
get_input_state :: #force_inline proc "contextless" () -> InputState { return (get_state().input ^) }

View File

@ -152,8 +152,8 @@ startup :: proc( prof : ^SpallProfiler, persistent_mem, frame_mem, transient_mem
color_theme = App_Thm_Dusk
font_size_screen_scalar = 2.0
font_size_canvas_scalar = 2.0
font_size_screen_scalar = 1.0
font_size_canvas_scalar = 1.0
}
Desired_OS_Scheduler_MS :: 1
@ -265,43 +265,44 @@ startup :: proc( prof : ^SpallProfiler, persistent_mem, frame_mem, transient_mem
if true
{
font_provider_startup( & font_provider_ctx )
path_rec_mono_semicasual_reg := strings.concatenate( { Path_Assets, "RecMonoSemicasual-Regular-1.084.ttf" })
font_rec_mono_semicasual_reg = font_load( path_rec_mono_semicasual_reg, 16.0, "RecMonoSemiCasual_Regular" )
// path_squidgy_slimes := strings.concatenate( { Path_Assets, "Squidgy Slimes.ttf" } )
// font_squidgy_slimes = font_load( path_squidgy_slimes, 32.0, "Squidgy_Slime" )
// Load default font
path_fira_cousine := strings.concatenate( { Path_Assets, "FiraCousine-Regular.ttf" } )
font_fira_cousine = font_load( path_fira_cousine, "Fira Cousine", 16.0 )
default_font = font_fira_cousine
// Aysnc load the others
// path_arial_unicode_ms := strings.concatenate( { Path_Assets, "Arial Unicode MS.ttf" } )
// font_arial_unicode_ms = font_load( path_arial_unicode_ms, "Arial_Unicode_MS", 16.0 )
// path_firacode := strings.concatenate( { Path_Assets, "FiraCode-Regular.ttf" } )
// font_firacode = font_load( path_firacode, 16.0, "FiraCode" )
path_fira_cousine := strings.concatenate( { Path_Assets, "FiraCousine-Regular.ttf" } )
font_fira_cousine = font_load( path_fira_cousine, 16.0, "Fira Cousine" )
// path_open_sans := strings.concatenate( { Path_Assets, "OpenSans-Regular.ttf" } )
// font_open_sans = font_load( path_open_sans, 16.0, "OpenSans" )
// path_noto_sans := strings.concatenate( { Path_Assets, "NotoSans-Regular.ttf" } )
// font_noto_sans = font_load( path_noto_sans, 16.0, "NotoSans" )
// font_firacode = font_load( path_firacode, "FiraCode", 16.0 )
// path_neodgm_code := strings.concatenate( { Path_Assets, "neodgm_code.ttf"} )
// font_neodgm_code = font_load( path_neodgm_code, 32.0, "NeoDunggeunmo Code" )
// font_neodgm_code = font_load( path_neodgm_code, "NeoDunggeunmo Code", 32.0 )
// path_noto_sans := strings.concatenate( { Path_Assets, "NotoSans-Regular.ttf" } )
// font_noto_sans = font_load( path_noto_sans, "NotoSans", 16.0 )
// path_open_sans := strings.concatenate( { Path_Assets, "OpenSans-Regular.ttf" } )
// font_open_sans = font_load( path_open_sans, "OpenSans", 16.0 )
// path_rec_mono_linear := strings.concatenate( { Path_Assets, "RecMonoLinear-Regular-1.084.ttf" })
// font_rec_mono_linear = font_load( path_rec_mono_linear, 16.0, "RecMonoLinear Regular" )
// font_rec_mono_linear = font_load( path_rec_mono_linear, "RecMonoLinear Regular", 32.0 )
// path_roboto_regular := strings.concatenate( { Path_Assets, "Roboto-Regular.ttf"} )
// font_roboto_regular = font_load( path_roboto_regular, 32.0, "Roboto Regular" )
// font_roboto_regular = font_load( path_roboto_regular, "Roboto Regular", 32.0 )
// path_roboto_mono_regular := strings.concatenate( { Path_Assets, "RobotoMono-Regular.ttf"} )
// font_roboto_mono_regular = font_load( path_roboto_mono_regular, 32.0, "Roboto Mono Regular" )
// font_roboto_mono_regular = font_load( path_roboto_mono_regular, "Roboto Mono Regular", 32.0 )
// path_arial_unicode_ms := strings.concatenate( { Path_Assets, "Arial Unicode MS.ttf" } )
// font_arial_unicode_ms = font_load( path_arial_unicode_ms, 16.0, "Arial_Unicode_MS" )
// path_rec_mono_semicasual_reg := strings.concatenate( { Path_Assets, "RecMonoSemicasual-Regular-1.084.ttf" })
// font_rec_mono_semicasual_reg = font_load( path_rec_mono_semicasual_reg, "RecMonoSemiCasual_Regular", 16 )
// path_arial_unicode_ms := strings.concatenate( { Path_Assets, "Arial Unicode MS.ttf" } )
// font_arial_unicode_ms = font_load( path_arial_unicode_ms, 16.0, "Arial_Unicode_MS" )
// path_squidgy_slimes := strings.concatenate( { Path_Assets, "Squidgy Slimes.ttf" } )
// font_squidgy_slimes = font_load( path_squidgy_slimes, "Squidgy_Slime", 32.0 )
default_font = font_fira_cousine
log( "Default font loaded" )
}
@ -451,6 +452,9 @@ hot_reload :: proc( prof : ^SpallProfiler, persistent_mem, frame_mem, transient_
slab_reload( persistent_slab, persistent_allocator() )
str_cache_reload( & string_cache, persistent_allocator(), persistent_allocator() )
str_cache_set_module_ctx( & string_cache )
// input_reload()
{
using input_events
@ -463,9 +467,6 @@ hot_reload :: proc( prof : ^SpallProfiler, persistent_mem, frame_mem, transient_
font_provider_reload( & font_provider_ctx )
str_cache_reload( & string_cache, persistent_allocator(), persistent_allocator() )
str_cache_set_module_ctx( & string_cache )
slab_reload( frame_slab, frame_allocator())
slab_reload( transient_slab, transient_allocator())
@ -532,8 +533,8 @@ tick_work_frame :: #force_inline proc( host_delta_time_ms : f64 ) -> b32
// config.engine_refresh_hz = 165
// config.color_theme = App_Thm_Light
config.color_theme = App_Thm_Dusk
// config.color_theme = App_Thm_Dark
// config.color_theme = App_Thm_Dusk
config.color_theme = App_Thm_Dark
sokol_width := sokol_app.widthf()
sokol_height := sokol_app.heightf()

View File

@ -116,7 +116,7 @@ render_mode_2d_workspace :: proc( screen_extent : Vec2, cam : Camera, input : In
}
}
render_mode_screenspace :: proc( screen_extent : Extents2, screen_ui : ^UI_State, ve_ctx : ^ve.Context, ve_render : VE_RenderData, config : AppConfig, debug : ^DebugData )
render_mode_screenspace :: proc( screen_extent : Extents2, screen_ui : ^UI_State, ve_ctx : ^ve.Context, ve_render : VE_RenderData, config : AppConfig, debug : ^ScratchData )
{
profile(#procedure)
screen_size := screen_extent * 2
@ -158,7 +158,7 @@ render_mode_screenspace :: proc( screen_extent : Extents2, screen_ui : ^UI_State
font := font
if font.key == Font_Default.key do font = default_font
shape := shape_text_cached( content, font, size, app_config().font_size_screen_scalar )
shape := shape_text_cached( content, font, size, 1.0 )
draw_text_shape_pos_extent( shape, font, size, pos, color )
}
@ -174,14 +174,14 @@ render_mode_screenspace :: proc( screen_extent : Extents2, screen_ui : ^UI_State
position.y -= debug.draw_debug_text_y
content := str_fmt( format, ..args )
text_size := measure_text_size( content, default_font, 14.0, 0.0 )
debug_draw_text( content, position, 12.0 )
text_size := measure_text_size( content, default_font, 16.0, 0.0 )
debug_draw_text( content, position, 16.0 )
debug.draw_debug_text_y += text_size.y
}
profile("debug_text_vis")
if true {
fps_size : f32 = 14.0
fps_size : f32 = 20.0
fps_msg := str_fmt( "FPS: %0.2f", fps_avg)
fps_msg_size := measure_text_size( fps_msg, default_font, fps_size, 0.0 )
fps_msg_pos := screen_get_corners().top_right - { fps_msg_size.x, fps_msg_size.y }
@ -906,13 +906,16 @@ draw_text_string_pos_norm :: #force_inline proc( text : string, id : FontID, fon
width := app_window.extent.x * 2
height := app_window.extent.y * 2
ve_id, resolved_size := font_provider_resolve_draw_id( id, font_size * config.font_size_screen_scalar )
ve_id, resolved_size := font_provider_resolve_draw_id( id, font_size )
color_norm := normalize_rgba8(color)
screen_size_norm := Vec2{1 / width, 1 / height}
screen_size_norm := 1 / Vec2 { width, height }
draw_scale := screen_size_norm * scale
// ve.set_px_scalar( & font_provider_ctx.ve_ctx, config.font_size_screen_scalar )
ve.set_colour( & font_provider_ctx.ve_ctx, color_norm )
ve.draw_text( & font_provider_ctx.ve_ctx, ve_id, f32(resolved_size), pos, screen_size_norm * scale * (1 / config.font_size_screen_scalar), text )
ve.draw_text( & font_provider_ctx.ve_ctx, ve_id, f32(resolved_size), pos, draw_scale, text )
return
}
@ -934,13 +937,14 @@ draw_text_shape_pos_norm :: #force_inline proc( shape : ShapedText, id : FontID,
width := app_window.extent.x * 2
height := app_window.extent.y * 2
ve_id, resolved_size := font_provider_resolve_draw_id( id, font_size * config.font_size_screen_scalar )
ve_id, resolved_size := font_provider_resolve_draw_id( id, font_size )
color_norm := normalize_rgba8(color)
screen_size_norm := Vec2 { 1 / width, 1 / height }
// ve.set_px_scalar( & font_provider_ctx.ve_ctx, config.font_size_screen_scalar )
ve.set_colour( & font_provider_ctx.ve_ctx, color_norm )
ve.draw_text_shape( & font_provider_ctx.ve_ctx, ve_id, f32(resolved_size), pos, screen_size_norm * scale * (1 / config.font_size_screen_scalar), shape )
ve.draw_text_shape( & font_provider_ctx.ve_ctx, ve_id, f32(resolved_size), pos, screen_size_norm * scale, shape )
return
}
@ -964,8 +968,8 @@ draw_text_string_pos_extent_zoomed :: #force_inline proc( text : string, id : Fo
zoom_adjust_size := size * zoom
// Over-sample font-size for any render under a camera
over_sample : f32 = f32(config.font_size_canvas_scalar)
zoom_adjust_size *= over_sample
// over_sample : f32 = f32(config.font_size_canvas_scalar)
// zoom_adjust_size *= over_sample
pos_offset := (pos + cam_offset)
render_pos := ws_view_to_render_pos(pos)
@ -984,9 +988,10 @@ draw_text_string_pos_extent_zoomed :: #force_inline proc( text : string, id : Fo
}
// Down-sample back
text_scale /= over_sample
// text_scale /= over_sample
color_norm := normalize_rgba8(color)
// ve.set_px_scalar( & get_state().font_provider_ctx.ve_ctx, config.font_size_canvas_scalar )
ve.set_colour( & get_state().font_provider_ctx.ve_ctx, color_norm )
ve.draw_text( & get_state().font_provider_ctx.ve_ctx, ve_id, f32(resolved_size), normalized_pos, text_scale, text )
}
@ -1000,7 +1005,7 @@ draw_text_shape_pos_extent_zoomed :: #force_inline proc( shape : ShapedText, id
// Over-sample font-size for any render under a camera
over_sample : f32 = f32(state.config.font_size_canvas_scalar)
zoom_adjust_size *= over_sample
// zoom_adjust_size *= over_sample
pos_offset := (pos + cam_offset)
render_pos := ws_view_to_render_pos(pos)
@ -1019,9 +1024,10 @@ draw_text_shape_pos_extent_zoomed :: #force_inline proc( shape : ShapedText, id
}
// Down-sample back
text_scale /= over_sample
// text_scale /= over_sample
color_norm := normalize_rgba8(color)
// ve.set_px_scalar( & get_state().font_provider_ctx.ve_ctx, config.font_size_canvas_scalar )
ve.set_colour( & font_provider_ctx.ve_ctx, color_norm )
ve.draw_text_shape( & font_provider_ctx.ve_ctx, ve_id, f32_resolved_size, normalized_pos, text_scale, shape )
}

View File

@ -302,11 +302,13 @@ update :: proc( delta_time : f64 ) -> b32
// TODO(Ed): We need input buffer so that we can consume input actions based on the UI with priority
font_provider_set_px_scalar( app_config().font_size_screen_scalar )
ui_screen_tick( & get_state().screen_ui )
//region WorkspaceImgui Tick
if true
{
font_provider_set_px_scalar( app_config().font_size_canvas_scalar )
profile("Workspace Imgui")
// Creates the root box node, set its as the first parent.

View File

@ -66,8 +66,8 @@ font_provider_shutdown :: proc( ctx : ^FontProviderContext )
}
font_load :: proc(path_file : string,
desired_id : string = Font_Load_Gen_ID,
default_size : i32 = Font_Load_Use_Default_Size,
desired_id : string = Font_Load_Gen_ID
) -> FontID
{
provider_data := & get_state().font_provider_ctx; using provider_data
@ -108,7 +108,7 @@ font_load :: proc(path_file : string,
// logf("Loading at size %v", font_size)
id := (font_size / Font_Size_Interval) + (font_size % Font_Size_Interval)
ve_id := & def.size_table[id - 1]
ve_ret_id := ve.load_font( & ve_ctx, desired_id, font_data, f32(font_size) )
ve_ret_id, error := ve.load_font( & ve_ctx, desired_id, font_data )
(ve_id^) = ve_ret_id
}
@ -116,6 +116,10 @@ font_load :: proc(path_file : string,
return fid
}
font_provider_set_px_scalar :: #force_inline proc( scalar : f32 ) {
ve.set_px_scalar( & get_state().font_provider_ctx.ve_ctx, scalar )
}
Font_Use_Default_Size :: f32(0.0)
font_provider_resolve_draw_id :: #force_inline proc( id : FontID, size := Font_Use_Default_Size ) -> (ve_id :ve.Font_ID, resolved_size : i32)

View File

@ -222,7 +222,9 @@ push-location $path_root
# $build_args += $flag_sanitize_address
# $build_args += $flag_sanitize_memory
# $build_args += $flag_show_debug_messages
write-host $build_args
foreach ($arg in $build_args) {
write-host `t $arg -ForegroundColor Cyan
}
if ( Test-Path $module_dll) {
$module_dll_pre_build_hash = get-filehash -path $module_dll -Algorithm MD5
@ -299,6 +301,9 @@ push-location $path_root
$build_args += ($flag_max_error_count + '10')
# $build_args += $flag_sanitize_address
# $build_args += $flag_sanitize_memory
foreach ($arg in $build_args) {
write-host `t $arg -ForegroundColor Cyan
}
if ( Test-Path $executable) {
$executable_pre_build_hash = get-filehash -path $executable -Algorithm MD5