mirror of
https://github.com/Ed94/VEFontCache-Odin.git
synced 2025-08-06 06:52:44 -07:00
General improvements (text and features)
* Added clear_atlas_region_caches & clear_shape_cache to VEFontCache (Usage Example: On hot-reloads to force refresh the caches if tuning the library) * Made glyph_draw's over_sample a vec2 for initialization (incase user wants to do some float value multiple of 4x4) * ADVANCE_SNAP_SMALLFONT_SIZE made a runtime option: Shaper_Context.adv_snap_small_font_threshold * Some improvement to text hinting and general rendering of text * Better defaults for initialization of the library
This commit is contained in:
@@ -271,7 +271,7 @@ init :: proc "c" ()
|
|||||||
case .DUMMY: fmt.println(">> using dummy backend")
|
case .DUMMY: fmt.println(">> using dummy backend")
|
||||||
}
|
}
|
||||||
|
|
||||||
ve.startup( & demo_ctx.ve_ctx, .STB_TrueType, allocator = context.allocator, snap_shape_position = false, use_advanced_text_shaper = true )
|
ve.startup( & demo_ctx.ve_ctx, .STB_TrueType, allocator = context.allocator )
|
||||||
ve_sokol.setup_gfx_objects( & demo_ctx.render_ctx, & demo_ctx.ve_ctx, vert_cap = 1024 * 1024, index_cap = 1024 * 1024 )
|
ve_sokol.setup_gfx_objects( & demo_ctx.render_ctx, & demo_ctx.ve_ctx, vert_cap = 1024 * 1024, index_cap = 1024 * 1024 )
|
||||||
|
|
||||||
error : mem.Allocator_Error
|
error : mem.Allocator_Error
|
||||||
|
@@ -63,6 +63,26 @@ pool_list_reload :: proc( pool : ^Pool_List, allocator : Allocator ) {
|
|||||||
reload_array( & pool.free_list, allocator )
|
reload_array( & pool.free_list, allocator )
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pool_list_clear :: proc( pool: ^Pool_List ) {
|
||||||
|
using pool
|
||||||
|
clear(& items)
|
||||||
|
clear(& free_list)
|
||||||
|
resize( & pool.items, cap(pool.items) )
|
||||||
|
resize( & pool.free_list, cap(pool.free_list) )
|
||||||
|
|
||||||
|
for id in 0 ..< capacity {
|
||||||
|
free_list[id] = i32(id)
|
||||||
|
items[id] = {
|
||||||
|
prev = -1,
|
||||||
|
next = -1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
front = -1
|
||||||
|
back = -1
|
||||||
|
size = 0
|
||||||
|
}
|
||||||
|
|
||||||
pool_list_push_front :: proc( pool : ^Pool_List, value : Pool_ListValue )
|
pool_list_push_front :: proc( pool : ^Pool_List, value : Pool_ListValue )
|
||||||
{
|
{
|
||||||
using pool
|
using pool
|
||||||
@@ -186,6 +206,12 @@ lru_reload :: #force_inline proc( cache : ^LRU_Cache, allocator : Allocator ) {
|
|||||||
pool_list_reload( & cache.key_queue, allocator )
|
pool_list_reload( & cache.key_queue, allocator )
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lru_clear :: proc ( cache : ^LRU_Cache ) {
|
||||||
|
pool_list_clear( & cache.key_queue )
|
||||||
|
clear(& cache.table)
|
||||||
|
cache.num = 0
|
||||||
|
}
|
||||||
|
|
||||||
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]
|
link, success := cache.table[key]
|
||||||
return link, success
|
return link, success
|
||||||
|
@@ -27,7 +27,8 @@ Atlas :: struct {
|
|||||||
width : i32,
|
width : i32,
|
||||||
height : i32,
|
height : i32,
|
||||||
|
|
||||||
glyph_padding : i32,
|
glyph_padding : i32, // Padding to add to bounds_<width/height>_scaled for choosing which atlas region.
|
||||||
|
glyph_over_scalar : f32, // Scalar to apply to bounds_<width/height>_scaled for choosing which atlas region.
|
||||||
|
|
||||||
region_a : Atlas_Region,
|
region_a : Atlas_Region,
|
||||||
region_b : Atlas_Region,
|
region_b : Atlas_Region,
|
||||||
@@ -98,8 +99,8 @@ decide_codepoint_region :: proc(ctx : ^Context, entry : ^Entry, glyph_index : Gl
|
|||||||
glyph_buffer := & ctx.glyph_buffer
|
glyph_buffer := & ctx.glyph_buffer
|
||||||
glyph_padding := f32( atlas.glyph_padding ) * 2
|
glyph_padding := f32( atlas.glyph_padding ) * 2
|
||||||
|
|
||||||
bounds_width_scaled := i32(bounds_width * entry.size_scale + glyph_padding)
|
bounds_width_scaled := i32(bounds_width * entry.size_scale * atlas.glyph_over_scalar + glyph_padding)
|
||||||
bounds_height_scaled := i32(bounds_height * entry.size_scale + glyph_padding)
|
bounds_height_scaled := i32(bounds_height * entry.size_scale * atlas.glyph_over_scalar + glyph_padding)
|
||||||
|
|
||||||
// Use a lookup table for faster region selection
|
// Use a lookup table for faster region selection
|
||||||
region_lookup := [4]struct { kind: Atlas_Region_Kind, region: ^Atlas_Region } {
|
region_lookup := [4]struct { kind: Atlas_Region_Kind, region: ^Atlas_Region } {
|
||||||
@@ -118,7 +119,7 @@ decide_codepoint_region :: proc(ctx : ^Context, entry : ^Entry, glyph_index : Gl
|
|||||||
over_sample = \
|
over_sample = \
|
||||||
bounds_width_scaled <= glyph_buffer.width / 2 &&
|
bounds_width_scaled <= glyph_buffer.width / 2 &&
|
||||||
bounds_height_scaled <= glyph_buffer.height / 2 ? \
|
bounds_height_scaled <= glyph_buffer.height / 2 ? \
|
||||||
{2.0, 2.0} \
|
{2.0, 2.0} \
|
||||||
: {1.0, 1.0}
|
: {1.0, 1.0}
|
||||||
return .E, nil, over_sample
|
return .E, nil, over_sample
|
||||||
}
|
}
|
||||||
|
@@ -174,9 +174,9 @@ cache_glyph_freetype :: proc(ctx: ^Context, font: Font_ID, glyph_index: Glyph, e
|
|||||||
start_index: int = 0
|
start_index: int = 0
|
||||||
for contour_index in 0 ..< int(outline.n_contours)
|
for contour_index in 0 ..< int(outline.n_contours)
|
||||||
{
|
{
|
||||||
end_index := int(contours[contour_index]) + 1
|
end_index := int(contours[contour_index]) + 1
|
||||||
prev_point: Vec2
|
prev_point : Vec2
|
||||||
first_point: Vec2
|
first_point : Vec2
|
||||||
|
|
||||||
for idx := start_index; idx < end_index; idx += 1
|
for idx := start_index; idx < end_index; idx += 1
|
||||||
{
|
{
|
||||||
@@ -272,6 +272,7 @@ cache_glyph :: proc(ctx : ^Context, font : Font_ID, glyph_index : Glyph, entry :
|
|||||||
path := &ctx.temp_path
|
path := &ctx.temp_path
|
||||||
clear(path)
|
clear(path)
|
||||||
|
|
||||||
|
step := 1.0 / entry.curve_quality
|
||||||
for edge in shape do #partial switch edge.type
|
for edge in shape do #partial switch edge.type
|
||||||
{
|
{
|
||||||
case .Move:
|
case .Move:
|
||||||
@@ -290,7 +291,6 @@ cache_glyph :: proc(ctx : ^Context, font : Font_ID, glyph_index : Glyph, entry :
|
|||||||
p1 := Vec2{ f32(edge.contour_x0), f32(edge.contour_y0) }
|
p1 := Vec2{ f32(edge.contour_x0), f32(edge.contour_y0) }
|
||||||
p2 := Vec2{ f32(edge.x), f32(edge.y) }
|
p2 := Vec2{ f32(edge.x), f32(edge.y) }
|
||||||
|
|
||||||
step := 1.0 / entry.curve_quality
|
|
||||||
for index : f32 = 1; index <= entry.curve_quality; index += 1 {
|
for index : f32 = 1; index <= entry.curve_quality; index += 1 {
|
||||||
alpha := index * step
|
alpha := index * step
|
||||||
append( path, Vertex { pos = eval_point_on_bezier3(p0, p1, p2, alpha) } )
|
append( path, Vertex { pos = eval_point_on_bezier3(p0, p1, p2, alpha) } )
|
||||||
@@ -303,7 +303,6 @@ cache_glyph :: proc(ctx : ^Context, font : Font_ID, glyph_index : Glyph, entry :
|
|||||||
p2 := Vec2{ f32(edge.contour_x1), f32(edge.contour_y1) }
|
p2 := Vec2{ f32(edge.contour_x1), f32(edge.contour_y1) }
|
||||||
p3 := Vec2{ f32(edge.x), f32(edge.y) }
|
p3 := Vec2{ f32(edge.x), f32(edge.y) }
|
||||||
|
|
||||||
step := 1.0 / entry.curve_quality
|
|
||||||
for index : f32 = 1; index <= entry.curve_quality; index += 1 {
|
for index : f32 = 1; index <= entry.curve_quality; index += 1 {
|
||||||
alpha := index * step
|
alpha := index * step
|
||||||
append( path, Vertex { pos = eval_point_on_bezier4(p0, p1, p2, p3, alpha) } )
|
append( path, Vertex { pos = eval_point_on_bezier4(p0, p1, p2, p3, alpha) } )
|
||||||
@@ -342,10 +341,9 @@ cache_glyph_to_atlas :: proc( ctx : ^Context,
|
|||||||
|
|
||||||
// Get hb_font text metrics. These are unscaled!
|
// Get hb_font text metrics. These are unscaled!
|
||||||
bounds_0, bounds_1 := parser_get_glyph_box( & entry.parser_info, glyph_index )
|
bounds_0, bounds_1 := parser_get_glyph_box( & entry.parser_info, glyph_index )
|
||||||
bounds_size := Vec2 {
|
vbounds_0 := vec2(bounds_0)
|
||||||
f32(bounds_1.x - bounds_0.x),
|
vbounds_1 := vec2(bounds_1)
|
||||||
f32(bounds_1.y - bounds_0.y)
|
bounds_size := vbounds_1 - vbounds_0
|
||||||
}
|
|
||||||
|
|
||||||
// E region is special case and not cached to atlas.
|
// E region is special case and not cached to atlas.
|
||||||
if region_kind == .None || region_kind == .E do return
|
if region_kind == .None || region_kind == .E do return
|
||||||
@@ -390,33 +388,31 @@ cache_glyph_to_atlas :: proc( ctx : ^Context,
|
|||||||
debug_total_cached += 1
|
debug_total_cached += 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw oversized glyph to update FBO
|
// Draw oversized glyph to glyph render target (FBO)
|
||||||
glyph_draw_scale := over_sample * entry.size_scale
|
glyph_draw_scale := over_sample * entry.size_scale
|
||||||
glyph_draw_translate := -1 * vec2(bounds_0) * glyph_draw_scale + vec2( glyph_padding )
|
glyph_draw_translate := -1 * vbounds_0 * glyph_draw_scale + vec2( glyph_padding )
|
||||||
glyph_draw_translate.x = cast(f32) (i32(glyph_draw_translate.x + 0.9999999))
|
|
||||||
glyph_draw_translate.y = cast(f32) (i32(glyph_draw_translate.y + 0.9999999))
|
|
||||||
|
|
||||||
// Allocate a glyph_update_FBO region
|
// Allocate a glyph glyph render target region (FBO)
|
||||||
gwidth_scaled_px := bounds_size.x * glyph_draw_scale.x + 1.0 + over_sample.x * glyph_padding
|
gwidth_scaled_px := bounds_size.x * glyph_draw_scale.x + over_sample.x * glyph_padding + 1.0
|
||||||
if i32(f32(glyph_buffer.batch_x) + gwidth_scaled_px) >= i32(glyph_buffer.width) {
|
if i32(f32(glyph_buffer.batch_x) + gwidth_scaled_px) >= i32(glyph_buffer.width) {
|
||||||
flush_glyph_buffer_to_atlas( ctx )
|
flush_glyph_buffer_to_atlas( ctx )
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate the src and destination regions
|
// Calculate the src and destination regions
|
||||||
slot_position, slot_szie := atlas_bbox( atlas, region_kind, atlas_index )
|
slot_position, slot_size := atlas_bbox( atlas, region_kind, atlas_index )
|
||||||
|
|
||||||
dst_glyph_position := slot_position
|
dst_glyph_position := slot_position
|
||||||
dst_glyph_size := bounds_size * entry.size_scale + glyph_padding
|
dst_glyph_size := (bounds_size * entry.size_scale + glyph_padding)
|
||||||
dst_size := slot_szie
|
dst_size := (slot_size)
|
||||||
screenspace_x_form( & dst_glyph_position, & dst_glyph_size, atlas_size )
|
screenspace_x_form( & dst_glyph_position, & dst_glyph_size, atlas_size )
|
||||||
screenspace_x_form( & slot_position, & dst_size, atlas_size )
|
screenspace_x_form( & slot_position, & dst_size, atlas_size )
|
||||||
|
|
||||||
src_position := Vec2 { f32(glyph_buffer.batch_x), 0 }
|
src_position := Vec2 { f32(glyph_buffer.batch_x), 0 }
|
||||||
src_size := bounds_size * glyph_draw_scale + over_sample * glyph_padding
|
src_size := (bounds_size * glyph_draw_scale + over_sample * glyph_padding)
|
||||||
textspace_x_form( & src_position, & src_size, glyph_buffer_size )
|
textspace_x_form( & src_position, & src_size, glyph_buffer_size )
|
||||||
|
|
||||||
// Advance glyph_update_batch_x and calculate final glyph drawing transform
|
// Advance glyph_update_batch_x and calculate final glyph drawing transform
|
||||||
glyph_draw_translate.x += f32(glyph_buffer.batch_x)
|
glyph_draw_translate.x = (glyph_draw_translate.x + f32(glyph_buffer.batch_x))
|
||||||
glyph_buffer.batch_x += i32(gwidth_scaled_px)
|
glyph_buffer.batch_x += i32(gwidth_scaled_px)
|
||||||
screenspace_x_form( & glyph_draw_translate, & glyph_draw_scale, glyph_buffer_size )
|
screenspace_x_form( & glyph_draw_translate, & glyph_draw_scale, glyph_buffer_size )
|
||||||
|
|
||||||
@@ -451,8 +447,8 @@ cache_glyph_to_atlas :: proc( ctx : ^Context,
|
|||||||
append( & glyph_buffer.clear_draw_list.calls, clear_target_region )
|
append( & glyph_buffer.clear_draw_list.calls, clear_target_region )
|
||||||
append( & glyph_buffer.draw_list.calls, blit_to_atlas )
|
append( & glyph_buffer.draw_list.calls, blit_to_atlas )
|
||||||
|
|
||||||
// Render glyph to glyph_update_FBO
|
// Render glyph to glyph render target (FBO)
|
||||||
cache_glyph( ctx, font, glyph_index, entry, vec2(bounds_0), vec2(bounds_1), glyph_draw_scale, glyph_draw_translate )
|
cache_glyph( ctx, font, glyph_index, entry, vbounds_0, vbounds_1, glyph_draw_scale, glyph_draw_translate )
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the glyuph is found in the atlas, nothing occurs, otherwise, the glyph call is setup to catch it to the atlas
|
// If the glyuph is found in the atlas, nothing occurs, otherwise, the glyph call is setup to catch it to the atlas
|
||||||
@@ -525,17 +521,14 @@ directly_draw_massive_glyph :: proc( ctx : ^Context,
|
|||||||
|
|
||||||
// Figure out the source rect.
|
// Figure out the source rect.
|
||||||
glyph_position := Vec2 {}
|
glyph_position := Vec2 {}
|
||||||
glyph_size := vec2(glyph_padding_dbl)
|
glyph_size := vec2(glyph_padding)
|
||||||
glyph_dst_size := glyph_size + bounds_scaled
|
glyph_dst_size := glyph_size + bounds_scaled
|
||||||
glyph_size += bounds_scaled * over_sample
|
glyph_size += bounds_scaled * over_sample
|
||||||
|
|
||||||
// Figure out the destination rect.
|
// Figure out the destination rect.
|
||||||
bounds_0_scaled := Vec2 {
|
bounds_0_scaled := (bounds_0 * entry.size_scale)
|
||||||
cast(f32) i32(bounds_0.x * entry.size_scale - 0.5),
|
dst := position + scale * bounds_0_scaled - glyph_padding * scale
|
||||||
cast(f32) i32(bounds_0.y * entry.size_scale - 0.5),
|
dst_size := glyph_dst_size * scale
|
||||||
}
|
|
||||||
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 )
|
textspace_x_form( & glyph_position, & glyph_size, glyph_buffer_size )
|
||||||
|
|
||||||
// Add the glyph Draw_Call.
|
// Add the glyph Draw_Call.
|
||||||
@@ -557,7 +550,7 @@ directly_draw_massive_glyph :: proc( ctx : ^Context,
|
|||||||
|
|
||||||
clear_glyph_update := & calls[1]
|
clear_glyph_update := & calls[1]
|
||||||
{
|
{
|
||||||
// Clear glyph_update_FBO.
|
// Clear glyph render target (FBO)
|
||||||
clear_glyph_update.pass = .Glyph
|
clear_glyph_update.pass = .Glyph
|
||||||
clear_glyph_update.start_index = 0
|
clear_glyph_update.start_index = 0
|
||||||
clear_glyph_update.end_index = 0
|
clear_glyph_update.end_index = 0
|
||||||
@@ -638,7 +631,7 @@ draw_text_batch :: proc(ctx: ^Context, entry: ^Entry, shaped: ^Shaped_Text,
|
|||||||
bounds_0, bounds_1 := parser_get_glyph_box( & entry.parser_info, glyph_index )
|
bounds_0, bounds_1 := parser_get_glyph_box( & entry.parser_info, glyph_index )
|
||||||
vbounds_0 := vec2(bounds_0)
|
vbounds_0 := vec2(bounds_0)
|
||||||
vbounds_1 := vec2(bounds_1)
|
vbounds_1 := vec2(bounds_1)
|
||||||
bounds_size := Vec2 { vbounds_1.x - vbounds_0.x, vbounds_1.y - vbounds_0.y }
|
bounds_size := vbounds_1 - vbounds_0
|
||||||
|
|
||||||
shaped_position := shaped.positions[index]
|
shaped_position := shaped.positions[index]
|
||||||
glyph_translate := position + (shaped_position) * scale
|
glyph_translate := position + (shaped_position) * scale
|
||||||
@@ -652,10 +645,10 @@ draw_text_batch :: proc(ctx: ^Context, entry: ^Entry, shaped: ^Shaped_Text,
|
|||||||
}
|
}
|
||||||
else if atlas_index != -1
|
else if atlas_index != -1
|
||||||
{
|
{
|
||||||
// Draw cacxhed glyph
|
// Draw cached glyph
|
||||||
slot_position, _ := atlas_bbox( atlas, region_kind, atlas_index )
|
slot_position, _ := atlas_bbox( atlas, region_kind, atlas_index )
|
||||||
glyph_scale := bounds_size * entry.size_scale + glyph_padding
|
glyph_scale := bounds_size * entry.size_scale + glyph_padding
|
||||||
bounds_0_scaled := ceil(vbounds_0 * entry.size_scale)
|
bounds_0_scaled := ceil(vbounds_0 * entry.size_scale - 0.5 )
|
||||||
dst := glyph_translate + bounds_0_scaled * scale
|
dst := glyph_translate + bounds_0_scaled * scale
|
||||||
dst_scale := glyph_scale * scale
|
dst_scale := glyph_scale * scale
|
||||||
textspace_x_form( & slot_position, & glyph_scale, atlas_size )
|
textspace_x_form( & slot_position, & glyph_scale, atlas_size )
|
||||||
@@ -726,7 +719,7 @@ flush_glyph_buffer_to_atlas :: proc( ctx : ^Context )
|
|||||||
clear_draw_list( & ctx.glyph_buffer.draw_list )
|
clear_draw_list( & ctx.glyph_buffer.draw_list )
|
||||||
clear_draw_list( & ctx.glyph_buffer.clear_draw_list )
|
clear_draw_list( & ctx.glyph_buffer.clear_draw_list )
|
||||||
|
|
||||||
// Clear glyph_update_FBO
|
// Clear glyph render target (FBO)
|
||||||
if ctx.glyph_buffer.batch_x != 0
|
if ctx.glyph_buffer.batch_x != 0
|
||||||
{
|
{
|
||||||
call := Draw_Call_Default
|
call := Draw_Call_Default
|
||||||
@@ -766,7 +759,8 @@ merge_draw_list :: proc( dst, src : ^Draw_List )
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
optimize_draw_list :: proc(draw_list: ^Draw_List, call_offset: int) {
|
optimize_draw_list :: proc(draw_list: ^Draw_List, call_offset: int)
|
||||||
|
{
|
||||||
// profile(#procedure)
|
// profile(#procedure)
|
||||||
assert(draw_list != nil)
|
assert(draw_list != nil)
|
||||||
|
|
||||||
|
@@ -33,6 +33,7 @@ import "core:mem"
|
|||||||
Arena :: mem.Arena
|
Arena :: mem.Arena
|
||||||
arena_allocator :: mem.arena_allocator
|
arena_allocator :: mem.arena_allocator
|
||||||
arena_init :: mem.arena_init
|
arena_init :: mem.arena_init
|
||||||
|
import "core:slice"
|
||||||
|
|
||||||
//#region("Proc overload mappings")
|
//#region("Proc overload mappings")
|
||||||
|
|
||||||
@@ -58,6 +59,7 @@ ceil :: proc {
|
|||||||
|
|
||||||
clear :: proc {
|
clear :: proc {
|
||||||
clear_dynamic_array,
|
clear_dynamic_array,
|
||||||
|
clear_map,
|
||||||
}
|
}
|
||||||
|
|
||||||
floor :: proc {
|
floor :: proc {
|
||||||
@@ -74,6 +76,10 @@ floor :: proc {
|
|||||||
floor_vec2,
|
floor_vec2,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fill :: proc {
|
||||||
|
slice.fill,
|
||||||
|
}
|
||||||
|
|
||||||
make :: proc {
|
make :: proc {
|
||||||
make_dynamic_array,
|
make_dynamic_array,
|
||||||
make_dynamic_array_len,
|
make_dynamic_array_len,
|
||||||
|
@@ -74,7 +74,7 @@ shape_text_uncached :: proc( ctx : ^Context, font : Font_ID, text_utf8 : string,
|
|||||||
line_gap := f32(line_gap_i32)
|
line_gap := f32(line_gap_i32)
|
||||||
line_height := (ascent - descent + line_gap) * entry.size_scale
|
line_height := (ascent - descent + line_gap) * entry.size_scale
|
||||||
|
|
||||||
if ctx.text_shape_adv
|
if ctx.use_advanced_shaper
|
||||||
{
|
{
|
||||||
shaper_shape_from_text( & ctx.shaper_ctx, & entry.shaper_info, output, text_utf8, ascent_i32, descent_i32, line_gap_i32, entry.size, entry.size_scale )
|
shaper_shape_from_text( & ctx.shaper_ctx, & entry.shaper_info, output, text_utf8, ascent_i32, descent_i32, line_gap_i32, entry.size, entry.size_scale )
|
||||||
return
|
return
|
||||||
@@ -106,22 +106,19 @@ shape_text_uncached :: proc( ctx : ^Context, font : Font_ID, text_utf8 : string,
|
|||||||
prev_codepoint = rune(0)
|
prev_codepoint = rune(0)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if abs( entry.size ) <= ADVANCE_SNAP_SMALLFONT_SIZE {
|
if abs( entry.size ) <= ctx.shaper_ctx.adv_snap_small_font_threshold {
|
||||||
position.x = position.x
|
position.x = ceil(position.x)
|
||||||
}
|
}
|
||||||
|
|
||||||
append( & output.glyphs, parser_find_glyph_index( & entry.parser_info, codepoint ))
|
append( & output.glyphs, parser_find_glyph_index( & entry.parser_info, codepoint ))
|
||||||
advance, _ := parser_get_codepoint_horizontal_metrics( & entry.parser_info, codepoint )
|
advance, _ := parser_get_codepoint_horizontal_metrics( & entry.parser_info, codepoint )
|
||||||
|
|
||||||
if ctx.snap_shape_pos do position.x = ceil(position.x)
|
|
||||||
|
|
||||||
append( & output.positions, Vec2 {
|
append( & output.positions, Vec2 {
|
||||||
position.x,
|
ceil(position.x),
|
||||||
position.y
|
ceil(position.y)
|
||||||
})
|
})
|
||||||
|
|
||||||
position.x += f32(advance) * entry.size_scale
|
position.x += f32(advance) * entry.size_scale
|
||||||
if ctx.snap_shape_pos do position.x = ceil(position.x)
|
|
||||||
prev_codepoint = codepoint
|
prev_codepoint = codepoint
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -13,6 +13,9 @@ Shaper_Kind :: enum {
|
|||||||
|
|
||||||
Shaper_Context :: struct {
|
Shaper_Context :: struct {
|
||||||
hb_buffer : harfbuzz.Buffer,
|
hb_buffer : harfbuzz.Buffer,
|
||||||
|
|
||||||
|
snap_glyph_position : b32,
|
||||||
|
adv_snap_small_font_threshold : f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
Shaper_Info :: struct {
|
Shaper_Info :: struct {
|
||||||
@@ -66,12 +69,13 @@ shaper_shape_from_text :: proc( ctx : ^Shaper_Context, info : ^Shaper_Info, outp
|
|||||||
|
|
||||||
max_line_width := f32(0)
|
max_line_width := f32(0)
|
||||||
line_count := 1
|
line_count := 1
|
||||||
line_height := (ascent - descent + line_gap) * size_scale
|
line_height := ((ascent - descent + line_gap) * size_scale)
|
||||||
|
|
||||||
position, vertical_position : f32
|
position, vertical_position : f32
|
||||||
shape_run :: proc( buffer : harfbuzz.Buffer, script : harfbuzz.Script, font : harfbuzz.Font, output : ^Shaped_Text,
|
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,
|
position, vertical_position, max_line_width: ^f32, line_count: ^int,
|
||||||
ascent, descent, line_gap, size, size_scale: f32 )
|
ascent, descent, line_gap, size, size_scale: f32,
|
||||||
|
snap_shape_pos : b32, adv_snap_small_font_threshold : f32 )
|
||||||
{
|
{
|
||||||
// Set script and direction. We use the system's default langauge.
|
// Set script and direction. We use the system's default langauge.
|
||||||
// script = HB_SCRIPT_LATIN
|
// script = HB_SCRIPT_LATIN
|
||||||
@@ -101,11 +105,11 @@ shaper_shape_from_text :: proc( ctx : ^Shaper_Context, info : ^Shaper_Info, outp
|
|||||||
(max_line_width^) = max( max_line_width^, position^ )
|
(max_line_width^) = max( max_line_width^, position^ )
|
||||||
(position^) = 0.0
|
(position^) = 0.0
|
||||||
(vertical_position^) -= line_height
|
(vertical_position^) -= line_height
|
||||||
(vertical_position^) = cast(f32) i32(vertical_position^ + 0.5)
|
(vertical_position^) = floor(vertical_position^ + 0.5)
|
||||||
(line_count^) += 1
|
(line_count^) += 1
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if abs( size ) <= ADVANCE_SNAP_SMALLFONT_SIZE
|
if abs( size ) <= adv_snap_small_font_threshold
|
||||||
{
|
{
|
||||||
(position^) = ceil( position^ )
|
(position^) = ceil( position^ )
|
||||||
}
|
}
|
||||||
@@ -116,9 +120,14 @@ shaper_shape_from_text :: proc( ctx : ^Shaper_Context, info : ^Shaper_Info, outp
|
|||||||
v_pos := vertical_position^
|
v_pos := vertical_position^
|
||||||
offset_x := f32(hb_gposition.x_offset) * size_scale
|
offset_x := f32(hb_gposition.x_offset) * size_scale
|
||||||
offset_y := f32(hb_gposition.y_offset) * size_scale
|
offset_y := f32(hb_gposition.y_offset) * size_scale
|
||||||
append( & output.positions, Vec2 { cast(f32) i32( pos + offset_x + 0.5 ),
|
pos += offset_x
|
||||||
v_pos + offset_y,
|
v_pos += offset_y
|
||||||
})
|
|
||||||
|
if snap_shape_pos {
|
||||||
|
pos = ceil(pos)
|
||||||
|
v_pos = ceil(v_pos)
|
||||||
|
}
|
||||||
|
append( & output.positions, Vec2 {pos, v_pos})
|
||||||
|
|
||||||
(position^) += f32(hb_gposition.x_advance) * size_scale
|
(position^) += f32(hb_gposition.x_advance) * size_scale
|
||||||
(vertical_position^) += f32(hb_gposition.y_advance) * size_scale
|
(vertical_position^) += f32(hb_gposition.y_advance) * size_scale
|
||||||
@@ -151,13 +160,23 @@ shaper_shape_from_text :: proc( ctx : ^Shaper_Context, info : ^Shaper_Info, outp
|
|||||||
}
|
}
|
||||||
|
|
||||||
// End current run since we've encountered a script change.
|
// End current run since we've encountered a script change.
|
||||||
shape_run( ctx.hb_buffer, current_script, info.font, output, & position, & vertical_position, & max_line_width, & line_count, ascent, descent, line_gap, size, size_scale )
|
shape_run(
|
||||||
|
ctx.hb_buffer, current_script, info.font, output,
|
||||||
|
& position, & vertical_position, & max_line_width, & line_count,
|
||||||
|
ascent, descent, line_gap, size, size_scale,
|
||||||
|
ctx.snap_glyph_position, ctx.adv_snap_small_font_threshold
|
||||||
|
)
|
||||||
harfbuzz.buffer_add( ctx.hb_buffer, hb_codepoint, codepoint == '\n' ? 1 : 0 )
|
harfbuzz.buffer_add( ctx.hb_buffer, hb_codepoint, codepoint == '\n' ? 1 : 0 )
|
||||||
current_script = script
|
current_script = script
|
||||||
}
|
}
|
||||||
|
|
||||||
// End the last run if needed
|
// End the last run if needed
|
||||||
shape_run( ctx.hb_buffer, current_script, info.font, output, & position, & vertical_position, & max_line_width, & line_count, ascent, descent, line_gap, size, size_scale )
|
shape_run(
|
||||||
|
ctx.hb_buffer, current_script, info.font, output,
|
||||||
|
& position, & vertical_position, & max_line_width, & line_count,
|
||||||
|
ascent, descent, line_gap, size, size_scale,
|
||||||
|
ctx.snap_glyph_position, ctx.adv_snap_small_font_threshold
|
||||||
|
)
|
||||||
|
|
||||||
// Set the final size
|
// Set the final size
|
||||||
output.size.x = max_line_width
|
output.size.x = max_line_width
|
||||||
|
@@ -7,8 +7,6 @@ package vefontcache
|
|||||||
|
|
||||||
import "base:runtime"
|
import "base:runtime"
|
||||||
|
|
||||||
ADVANCE_SNAP_SMALLFONT_SIZE :: 0
|
|
||||||
|
|
||||||
Font_ID :: distinct i64
|
Font_ID :: distinct i64
|
||||||
Glyph :: distinct i32
|
Glyph :: distinct i32
|
||||||
|
|
||||||
@@ -44,7 +42,6 @@ Context :: struct {
|
|||||||
snap_width : f32,
|
snap_width : f32,
|
||||||
snap_height : f32,
|
snap_height : f32,
|
||||||
|
|
||||||
|
|
||||||
colour : Colour,
|
colour : Colour,
|
||||||
cursor_pos : Vec2,
|
cursor_pos : Vec2,
|
||||||
|
|
||||||
@@ -60,14 +57,12 @@ Context :: struct {
|
|||||||
shape_cache : Shaped_Text_Cache,
|
shape_cache : Shaped_Text_Cache,
|
||||||
|
|
||||||
default_curve_quality : i32,
|
default_curve_quality : i32,
|
||||||
text_shape_adv : b32,
|
use_advanced_shaper : b32,
|
||||||
snap_shape_pos : b32,
|
|
||||||
|
|
||||||
debug_print : b32,
|
debug_print : b32,
|
||||||
debug_print_verbose : b32,
|
debug_print_verbose : b32,
|
||||||
}
|
}
|
||||||
|
|
||||||
//#region("lifetime")
|
|
||||||
|
|
||||||
Init_Atlas_Region_Params :: struct {
|
Init_Atlas_Region_Params :: struct {
|
||||||
width : u32,
|
width : u32,
|
||||||
@@ -75,9 +70,10 @@ Init_Atlas_Region_Params :: struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Init_Atlas_Params :: struct {
|
Init_Atlas_Params :: struct {
|
||||||
width : u32,
|
width : u32,
|
||||||
height : u32,
|
height : u32,
|
||||||
glyph_padding : u32,
|
glyph_padding : u32, // Padding to add to bounds_<width/height>_scaled for choosing which atlas region.
|
||||||
|
glyph_over_scalar : f32, // Scalar to apply to bounds_<width/height>_scaled for choosing which atlas region.
|
||||||
|
|
||||||
region_a : Init_Atlas_Region_Params,
|
region_a : Init_Atlas_Region_Params,
|
||||||
region_b : Init_Atlas_Region_Params,
|
region_b : Init_Atlas_Region_Params,
|
||||||
@@ -86,9 +82,10 @@ Init_Atlas_Params :: struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Init_Atlas_Params_Default :: Init_Atlas_Params {
|
Init_Atlas_Params_Default :: Init_Atlas_Params {
|
||||||
width = 4096,
|
width = 4096,
|
||||||
height = 2048,
|
height = 2048,
|
||||||
glyph_padding = 4,
|
glyph_padding = 1,
|
||||||
|
glyph_over_scalar = 1,
|
||||||
|
|
||||||
region_a = {
|
region_a = {
|
||||||
width = 32,
|
width = 32,
|
||||||
@@ -109,17 +106,29 @@ Init_Atlas_Params_Default :: Init_Atlas_Params {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Init_Glyph_Draw_Params :: struct {
|
Init_Glyph_Draw_Params :: struct {
|
||||||
over_sample : Vec2i,
|
over_sample : Vec2,
|
||||||
buffer_batch : u32,
|
buffer_batch : u32,
|
||||||
draw_padding : u32,
|
draw_padding : u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
Init_Glyph_Draw_Params_Default :: Init_Glyph_Draw_Params {
|
Init_Glyph_Draw_Params_Default :: Init_Glyph_Draw_Params {
|
||||||
over_sample = { 8, 8 },
|
over_sample = Vec2 { 4, 4 },
|
||||||
buffer_batch = 4,
|
buffer_batch = 4,
|
||||||
draw_padding = Init_Atlas_Params_Default.glyph_padding,
|
draw_padding = Init_Atlas_Params_Default.glyph_padding,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Init_Shaper_Params :: struct {
|
||||||
|
use_advanced_text_shaper : b32,
|
||||||
|
snap_glyph_position : b32,
|
||||||
|
adv_snap_small_font_threshold : u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
Init_Shaper_Params_Default :: Init_Shaper_Params {
|
||||||
|
use_advanced_text_shaper = true,
|
||||||
|
snap_glyph_position = true,
|
||||||
|
adv_snap_small_font_threshold = 0,
|
||||||
|
}
|
||||||
|
|
||||||
Init_Shape_Cache_Params :: struct {
|
Init_Shape_Cache_Params :: struct {
|
||||||
capacity : u32,
|
capacity : u32,
|
||||||
reserve_length : u32,
|
reserve_length : u32,
|
||||||
@@ -130,14 +139,15 @@ Init_Shape_Cache_Params_Default :: Init_Shape_Cache_Params {
|
|||||||
reserve_length = 256,
|
reserve_length = 256,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//#region("lifetime")
|
||||||
|
|
||||||
// ve_fontcache_init
|
// ve_fontcache_init
|
||||||
startup :: proc( ctx : ^Context, parser_kind : Parser_Kind = .STB_TrueType,
|
startup :: proc( ctx : ^Context, parser_kind : Parser_Kind = .STB_TrueType,
|
||||||
allocator := context.allocator,
|
allocator := context.allocator,
|
||||||
atlas_params := Init_Atlas_Params_Default,
|
atlas_params := Init_Atlas_Params_Default,
|
||||||
glyph_draw_params := Init_Glyph_Draw_Params_Default,
|
glyph_draw_params := Init_Glyph_Draw_Params_Default,
|
||||||
shape_cache_params := Init_Shape_Cache_Params_Default,
|
shape_cache_params := Init_Shape_Cache_Params_Default,
|
||||||
use_advanced_text_shaper : b32 = true,
|
shaper_params := Init_Shaper_Params_Default,
|
||||||
snap_shape_position : b32 = true,
|
|
||||||
default_curve_quality : u32 = 3,
|
default_curve_quality : u32 = 3,
|
||||||
entires_reserve : u32 = 512,
|
entires_reserve : u32 = 512,
|
||||||
temp_path_reserve : u32 = 1024,
|
temp_path_reserve : u32 = 1024,
|
||||||
@@ -150,8 +160,9 @@ startup :: proc( ctx : ^Context, parser_kind : Parser_Kind = .STB_TrueType,
|
|||||||
ctx.backing = allocator
|
ctx.backing = allocator
|
||||||
context.allocator = ctx.backing
|
context.allocator = ctx.backing
|
||||||
|
|
||||||
snap_shape_pos = snap_shape_position
|
use_advanced_shaper = shaper_params.use_advanced_text_shaper
|
||||||
text_shape_adv = use_advanced_text_shaper
|
shaper_ctx.adv_snap_small_font_threshold = f32(shaper_params.adv_snap_small_font_threshold)
|
||||||
|
shaper_ctx.snap_glyph_position = shaper_params.snap_glyph_position
|
||||||
|
|
||||||
if default_curve_quality == 0 {
|
if default_curve_quality == 0 {
|
||||||
default_curve_quality = 3
|
default_curve_quality = 3
|
||||||
@@ -202,9 +213,10 @@ startup :: proc( ctx : ^Context, parser_kind : Parser_Kind = .STB_TrueType,
|
|||||||
init_atlas_region( & atlas.region_c, atlas_params, atlas_params.region_c, { 4, 1}, 512 )
|
init_atlas_region( & atlas.region_c, atlas_params, atlas_params.region_c, { 4, 1}, 512 )
|
||||||
init_atlas_region( & atlas.region_d, atlas_params, atlas_params.region_d, { 2, 1}, 256 )
|
init_atlas_region( & atlas.region_d, atlas_params, atlas_params.region_d, { 2, 1}, 256 )
|
||||||
|
|
||||||
atlas.width = i32(atlas_params.width)
|
atlas.width = i32(atlas_params.width)
|
||||||
atlas.height = i32(atlas_params.height)
|
atlas.height = i32(atlas_params.height)
|
||||||
atlas.glyph_padding = i32(atlas_params.glyph_padding)
|
atlas.glyph_padding = i32(atlas_params.glyph_padding)
|
||||||
|
atlas.glyph_over_scalar = atlas_params.glyph_over_scalar
|
||||||
|
|
||||||
atlas.region_a.offset = {0, 0}
|
atlas.region_a.offset = {0, 0}
|
||||||
atlas.region_b.offset.x = 0
|
atlas.region_b.offset.x = 0
|
||||||
@@ -241,7 +253,7 @@ startup :: proc( ctx : ^Context, parser_kind : Parser_Kind = .STB_TrueType,
|
|||||||
// Note(From original author): We can actually go over VE_FONTCACHE_GLYPHDRAW_BUFFER_BATCH batches due to smart packing!
|
// Note(From original author): We can actually go over VE_FONTCACHE_GLYPHDRAW_BUFFER_BATCH batches due to smart packing!
|
||||||
{
|
{
|
||||||
using glyph_buffer
|
using glyph_buffer
|
||||||
over_sample = vec2(glyph_draw_params.over_sample)
|
over_sample = glyph_draw_params.over_sample
|
||||||
batch = cast(i32) glyph_draw_params.buffer_batch
|
batch = cast(i32) glyph_draw_params.buffer_batch
|
||||||
width = atlas.region_d.width * i32(over_sample.x) * batch
|
width = atlas.region_d.width * i32(over_sample.x) * batch
|
||||||
height = atlas.region_d.height * i32(over_sample.y)
|
height = atlas.region_d.height * i32(over_sample.y)
|
||||||
@@ -308,7 +320,6 @@ hot_reload :: proc( ctx : ^Context, allocator : Allocator )
|
|||||||
reload_array( & glyph_buffer.clear_draw_list.vertices, allocator )
|
reload_array( & glyph_buffer.clear_draw_list.vertices, allocator )
|
||||||
|
|
||||||
reload_array( & shape_cache.storage, allocator )
|
reload_array( & shape_cache.storage, allocator )
|
||||||
lru_reload( & shape_cache.state, allocator )
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ve_foncache_shutdown
|
// ve_foncache_shutdown
|
||||||
@@ -385,10 +396,8 @@ load_font :: proc( ctx : ^Context, label : string, data : []byte, size_px : f32,
|
|||||||
parser_info = parser_load_font( & parser_ctx, label, data )
|
parser_info = parser_load_font( & parser_ctx, label, data )
|
||||||
shaper_info = shaper_load_font( & shaper_ctx, label, data, transmute(rawptr) id )
|
shaper_info = shaper_load_font( & shaper_ctx, label, data, transmute(rawptr) id )
|
||||||
|
|
||||||
size = size_px
|
size = size_px
|
||||||
size_scale = size_px < 0.0 ? \
|
size_scale = parser_scale( & parser_info, size )
|
||||||
parser_scale_for_pixel_height( & parser_info, -size_px ) \
|
|
||||||
: parser_scale_for_mapping_em_to_pixels( & parser_info, size_px )
|
|
||||||
|
|
||||||
if glyph_curve_quality == 0 {
|
if glyph_curve_quality == 0 {
|
||||||
curve_quality = f32(ctx.default_curve_quality)
|
curve_quality = f32(ctx.default_curve_quality)
|
||||||
@@ -442,8 +451,8 @@ draw_text :: proc( ctx : ^Context, font : Font_ID, text_utf8 : string, position,
|
|||||||
ctx.cursor_pos = {}
|
ctx.cursor_pos = {}
|
||||||
|
|
||||||
position := position
|
position := position
|
||||||
if ctx.snap_width > 0 do position.x = cast(f32) cast(u32) (position.x * ctx.snap_width + 0.5) / ctx.snap_width
|
if ctx.snap_width > 0 do position.x = ceil(position.x * ctx.snap_width ) / ctx.snap_width
|
||||||
if ctx.snap_height > 0 do position.y = cast(f32) cast(u32) (position.y * ctx.snap_height + 0.5) / ctx.snap_height
|
if ctx.snap_height > 0 do position.y = ceil(position.y * ctx.snap_height) / ctx.snap_height
|
||||||
|
|
||||||
entry := & ctx.entries[ font ]
|
entry := & ctx.entries[ font ]
|
||||||
|
|
||||||
@@ -520,10 +529,44 @@ get_font_vertical_metrics :: #force_inline proc ( ctx : ^Context, font : Font_ID
|
|||||||
entry := & ctx.entries[ font ]
|
entry := & ctx.entries[ font ]
|
||||||
ascent_i32, descent_i32, line_gap_i32 := parser_get_font_vertical_metrics( & entry.parser_info )
|
ascent_i32, descent_i32, line_gap_i32 := parser_get_font_vertical_metrics( & entry.parser_info )
|
||||||
|
|
||||||
ascent = ceil(f32(ascent_i32) * entry.size_scale)
|
ascent = (f32(ascent_i32) * entry.size_scale)
|
||||||
descent = ceil(f32(descent_i32) * entry.size_scale)
|
descent = (f32(descent_i32) * entry.size_scale)
|
||||||
line_gap = ceil(f32(line_gap_i32) * entry.size_scale)
|
line_gap = (f32(line_gap_i32) * entry.size_scale)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
//#endregion("metrics")
|
//#endregion("metrics")
|
||||||
|
|
||||||
|
// Can be used with hot-reload
|
||||||
|
clear_atlas_region_caches :: proc(ctx : ^Context)
|
||||||
|
{
|
||||||
|
lru_clear(& ctx.atlas.region_a.state)
|
||||||
|
lru_clear(& ctx.atlas.region_b.state)
|
||||||
|
lru_clear(& ctx.atlas.region_c.state)
|
||||||
|
lru_clear(& ctx.atlas.region_d.state)
|
||||||
|
|
||||||
|
ctx.atlas.region_a.next_idx = 0
|
||||||
|
ctx.atlas.region_b.next_idx = 0
|
||||||
|
ctx.atlas.region_c.next_idx = 0
|
||||||
|
ctx.atlas.region_d.next_idx = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Can be used with hot-reload
|
||||||
|
clear_shape_cache :: proc (ctx : ^Context)
|
||||||
|
{
|
||||||
|
using ctx
|
||||||
|
lru_clear(& shape_cache.state)
|
||||||
|
for idx : i32 = 0; idx < cast(i32) cap(shape_cache.storage); idx += 1
|
||||||
|
{
|
||||||
|
stroage_entry := & shape_cache.storage[idx]
|
||||||
|
using stroage_entry
|
||||||
|
end_cursor_pos = {}
|
||||||
|
size = {}
|
||||||
|
clear(& glyphs)
|
||||||
|
clear(& positions)
|
||||||
|
clear(& draw_list.calls)
|
||||||
|
clear(& draw_list.indices)
|
||||||
|
clear(& draw_list.vertices)
|
||||||
|
}
|
||||||
|
ctx.shape_cache.next_cache_id = 0
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user