Fixed shaper bug, removed shaper_shape_text_uncached_advanced (intergrated into harfbuzz)

This commit is contained in:
2025-01-11 00:51:39 -05:00
parent c64f8132dc
commit 48927fd008
5 changed files with 99 additions and 107 deletions

View File

@@ -58,6 +58,7 @@ Font_ID :: struct {
Font_Entry :: struct { Font_Entry :: struct {
path_file : string, path_file : string,
data : []byte,
default_size : i32, default_size : i32,
ve_id : ve.Font_ID, ve_id : ve.Font_ID,
} }
@@ -126,6 +127,7 @@ font_load :: proc(path_file : string,
error : ve.Load_Font_Error error : ve.Load_Font_Error
def.path_file = path_file def.path_file = path_file
def.default_size = default_size def.default_size = default_size
def.data = font_data
def.ve_id, error = ve.load_font( & demo_ctx.ve_ctx, desired_id, font_data, curve_quality ) def.ve_id, error = ve.load_font( & demo_ctx.ve_ctx, desired_id, font_data, curve_quality )
assert(error == .None) assert(error == .None)
@@ -164,7 +166,8 @@ draw_text :: proc( content : string, font : Font_ID, pos : Vec2, size : f32 = 0.
pos, pos,
scale, scale,
zoom, zoom,
content content,
// ve.shaper_shape_text_latin,
) )
return return
} }
@@ -258,7 +261,6 @@ init :: proc "c" ()
path_firacode := strings.concatenate({ PATH_FONTS, "FiraCode-Regular.ttf" }) path_firacode := strings.concatenate({ PATH_FONTS, "FiraCode-Regular.ttf" })
demo_ctx.font_logo = font_load(path_sawarabi_mincho, 150.0, "SawarabiMincho", 18 ) demo_ctx.font_logo = font_load(path_sawarabi_mincho, 150.0, "SawarabiMincho", 18 )
// demo_ctx.font_title = font_load(path_open_sans, 92.0, "OpenSans", 6 )
demo_ctx.font_print = font_load(path_noto_sans_jp, 19.0, "NotoSansJP") demo_ctx.font_print = font_load(path_noto_sans_jp, 19.0, "NotoSansJP")
demo_ctx.font_mono = font_load(path_ubuntu_mono, 21.0, "UbuntuMono") demo_ctx.font_mono = font_load(path_ubuntu_mono, 21.0, "UbuntuMono")
demo_ctx.font_small = font_load(path_roboto, 10.0, "Roboto") demo_ctx.font_small = font_load(path_roboto, 10.0, "Roboto")
@@ -644,14 +646,14 @@ etiam dignissim diam quis enim. Convallis convallis tellus id interdum.`
posy := current_scroll - (section_start + 0.66 + f32(y) * 0.052) posy := current_scroll - (section_start + 0.66 + f32(y) * 0.052)
c := [5]u8{} c := [5]u8{}
codepoint_to_utf8(c[:], grid2[ y * GRID2_W + x ]) codepoint_to_utf8(c[:], grid2[ y * GRID2_W + x ])
draw_text(string( c[:] ), demo_ctx.font_demo_chinese, { posx, posy }, size = 54) draw_text(string( c[:] ), demo_ctx.font_demo_grid2, { posx, posy }, size = 54)
} }
for y in 0 ..< GRID3_H do for x in 0 ..< GRID3_W { for y in 0 ..< GRID3_H do for x in 0 ..< GRID3_W {
posx := 0.45 + f32(x) * 0.02 posx := 0.45 + f32(x) * 0.02
posy := current_scroll - (section_start + 0.64 + f32(y) * 0.034) posy := current_scroll - (section_start + 0.64 + f32(y) * 0.04)
c := [5]u8{} c := [5]u8{}
codepoint_to_utf8( c[:], grid3[ y * GRID3_W + x ]) codepoint_to_utf8( c[:], grid3[ y * GRID3_W + x ])
draw_text(string( c[:] ), demo_ctx.font_demo_serif, { posx, posy }, size = 44) draw_text(string( c[:] ), demo_ctx.font_demo_grid3, { posx, posy }, size = 44)
} }
} }

Binary file not shown.

View File

@@ -592,12 +592,12 @@ batch_generate_glyphs_draw_list :: proc ( draw_list : ^Draw_List,
colour.g = 1.0 colour.g = 1.0
colour.b = 0.0 colour.b = 0.0
} }
for pack_id, index in oversized { // for pack_id, index in oversized {
error : Allocator_Error // error : Allocator_Error
glyph_pack[pack_id].shape, error = parser_get_glyph_shape(entry.parser_info, shape.glyph[pack_id]) // glyph_pack[pack_id].shape, error = parser_get_glyph_shape(entry.parser_info, shape.glyph[pack_id])
assert(error == .None) // assert(error == .None)
assert(glyph_pack[pack_id].shape != nil) // assert(glyph_pack[pack_id].shape != nil)
} // }
for id, index in oversized for id, index in oversized
{ {
glyph := & glyph_pack[id] glyph := & glyph_pack[id]
@@ -607,6 +607,11 @@ batch_generate_glyphs_draw_list :: proc ( draw_list : ^Draw_List,
& glyph_buffer.clear_draw_list, & glyph_buffer.clear_draw_list,
& glyph_buffer.allocated_x & glyph_buffer.allocated_x
) )
error : Allocator_Error
glyph.shape, error = parser_get_glyph_shape(entry.parser_info, shape.glyph[id])
assert(error == .None)
assert(glyph.shape != nil)
generate_glyph_pass_draw_list( draw_list, & glyph_buffer.shape_gen_scratch, generate_glyph_pass_draw_list( draw_list, & glyph_buffer.shape_gen_scratch,
glyph_pack[id].shape, glyph_pack[id].shape,
@@ -616,6 +621,9 @@ batch_generate_glyphs_draw_list :: proc ( draw_list : ^Draw_List,
glyph_pack[id].draw_transform.scale glyph_pack[id].draw_transform.scale
) )
assert(glyph.shape != nil)
parser_free_shape(entry.parser_info, glyph.shape)
target_quad := & glyph_pack[id].draw_quad target_quad := & glyph_pack[id].draw_quad
draw_to_target : Draw_Call draw_to_target : Draw_Call
@@ -634,10 +642,10 @@ batch_generate_glyphs_draw_list :: proc ( draw_list : ^Draw_List,
} }
flush_glyph_buffer_draw_list(draw_list, & glyph_buffer.draw_list, & glyph_buffer.clear_draw_list, & glyph_buffer.allocated_x) flush_glyph_buffer_draw_list(draw_list, & glyph_buffer.draw_list, & glyph_buffer.clear_draw_list, & glyph_buffer.allocated_x)
for pack_id, index in oversized { // for pack_id, index in oversized {
assert(glyph_pack[pack_id].shape != nil) // assert(glyph_pack[pack_id].shape != nil)
parser_free_shape(entry.parser_info, glyph_pack[pack_id].shape) // parser_free_shape(entry.parser_info, glyph_pack[pack_id].shape)
} // }
} }
profile_end() profile_end()
@@ -666,12 +674,12 @@ batch_generate_glyphs_draw_list :: proc ( draw_list : ^Draw_List,
profile_begin("to_cache: caching to atlas") profile_begin("to_cache: caching to atlas")
if len(to_cache) > 0 if len(to_cache) > 0
{ {
for pack_id, index in to_cache { // for pack_id, index in to_cache {
error : Allocator_Error // error : Allocator_Error
glyph_pack[pack_id].shape, error = parser_get_glyph_shape(entry.parser_info, shape.glyph[pack_id]) // glyph_pack[pack_id].shape, error = parser_get_glyph_shape(entry.parser_info, shape.glyph[pack_id])
assert(error == .None) // assert(error == .None)
assert(glyph_pack[pack_id].shape != nil) // assert(glyph_pack[pack_id].shape != nil)
} // }
for id, index in to_cache for id, index in to_cache
{ {
@@ -724,6 +732,11 @@ batch_generate_glyphs_draw_list :: proc ( draw_list : ^Draw_List,
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 )
error : Allocator_Error
glyph.shape, error = parser_get_glyph_shape(entry.parser_info, shape.glyph[id])
assert(error == .None)
assert(glyph.shape != nil)
// Render glyph to glyph render target (FBO) // Render glyph to glyph render target (FBO)
generate_glyph_pass_draw_list( draw_list, & glyph_buffer.shape_gen_scratch, generate_glyph_pass_draw_list( draw_list, & glyph_buffer.shape_gen_scratch,
@@ -733,13 +746,16 @@ batch_generate_glyphs_draw_list :: proc ( draw_list : ^Draw_List,
glyph.draw_transform.pos, glyph.draw_transform.pos,
glyph.draw_transform.scale glyph.draw_transform.scale
) )
assert(glyph.shape != nil)
parser_free_shape(entry.parser_info, glyph.shape)
} }
flush_glyph_buffer_draw_list(draw_list, & glyph_buffer.draw_list, & glyph_buffer.clear_draw_list, & glyph_buffer.allocated_x) flush_glyph_buffer_draw_list(draw_list, & glyph_buffer.draw_list, & glyph_buffer.clear_draw_list, & glyph_buffer.allocated_x)
for pack_id, index in to_cache { // for pack_id, index in to_cache {
assert(glyph_pack[pack_id].shape != nil) // assert(glyph_pack[pack_id].shape != nil)
parser_free_shape(entry.parser_info, glyph_pack[pack_id].shape) // parser_free_shape(entry.parser_info, glyph_pack[pack_id].shape)
} // }
profile_begin("gen_cached_draw_list: to_cache") profile_begin("gen_cached_draw_list: to_cache")
when ENABLE_DRAW_TYPE_VISUALIZATION { when ENABLE_DRAW_TYPE_VISUALIZATION {

View File

@@ -107,9 +107,22 @@ shaper_unload_font :: #force_inline proc( info : ^Shaper_Info )
// Recommended shaper. Very performant. // Recommended shaper. Very performant.
// TODO(Ed): Would be nice to properly support vertical shaping, right now its strictly just horizontal... // TODO(Ed): Would be nice to properly support vertical shaping, right now its strictly just horizontal...
@(optimization_mode="favor_size") @(optimization_mode="favor_size")
shaper_shape_harfbuzz :: proc( ctx : ^Shaper_Context, text_utf8 : string, entry : Entry, font_px_Size, font_scale : f32, output :^Shaped_Text ) shaper_shape_harfbuzz :: proc( ctx : ^Shaper_Context,
atlas : Atlas,
glyph_buffer_size : Vec2,
entry : Entry,
font_px_size : f32,
font_scale : f32,
text_utf8 : string,
output : ^Shaped_Text
)
{ {
profile(#procedure) profile(#procedure)
assert( ctx != nil )
clear( & output.glyph )
clear( & output.position )
current_script := harfbuzz.Script.UNKNOWN current_script := harfbuzz.Script.UNKNOWN
hb_ucfunc := harfbuzz.unicode_funcs_get_default() hb_ucfunc := harfbuzz.unicode_funcs_get_default()
harfbuzz.buffer_clear_contents( ctx.hb_buffer ) harfbuzz.buffer_clear_contents( ctx.hb_buffer )
@@ -142,8 +155,6 @@ shaper_shape_harfbuzz :: proc( ctx : ^Shaper_Context, text_utf8 : string, entry
) )
{ {
profile(#procedure) profile(#procedure)
// Set script and direction. We use the system's default langauge.
// script = HB_SCRIPT_LATIN
harfbuzz.buffer_set_script ( buffer, script ) harfbuzz.buffer_set_script ( buffer, script )
harfbuzz.buffer_set_direction( buffer, harfbuzz.script_get_horizontal_direction( script )) harfbuzz.buffer_set_direction( buffer, harfbuzz.script_get_horizontal_direction( script ))
harfbuzz.buffer_set_language ( buffer, harfbuzz.language_get_default() ) harfbuzz.buffer_set_language ( buffer, harfbuzz.language_get_default() )
@@ -159,11 +170,12 @@ shaper_shape_harfbuzz :: proc( ctx : ^Shaper_Context, text_utf8 : string, entry
line_height := (entry.ascent - entry.descent + entry.line_gap) * font_scale line_height := (entry.ascent - entry.descent + entry.line_gap) * font_scale
last_cluster := u32(0)
for index : i32; index < i32(glyph_count); index += 1 for index : i32; index < i32(glyph_count); index += 1
{ {
hb_glyph := glyph_infos[ index ] hb_glyph := glyph_infos [ index ]
hb_gposition := glyph_positions[ index ] hb_gposition := glyph_positions[ index ]
glyph := cast(Glyph) hb_glyph.codepoint glyph := cast(Glyph) hb_glyph.codepoint
if hb_glyph.cluster > 0 if hb_glyph.cluster > 0
{ {
@@ -172,6 +184,8 @@ shaper_shape_harfbuzz :: proc( ctx : ^Shaper_Context, text_utf8 : string, entry
position.y -= line_height position.y -= line_height
position.y = floor(position.y) position.y = floor(position.y)
(line_count^) += 1 (line_count^) += 1
last_cluster = hb_glyph.cluster
continue continue
} }
if abs( font_px_size ) <= adv_snap_small_font_threshold { if abs( font_px_size ) <= adv_snap_small_font_threshold {
@@ -194,7 +208,7 @@ shaper_shape_harfbuzz :: proc( ctx : ^Shaper_Context, text_utf8 : string, entry
(max_line_width^) = max(max_line_width^, position.x) (max_line_width^) = max(max_line_width^, position.x)
is_empty := parser_is_glyph_empty(entry.parser_info, glyph) is_empty := parser_is_glyph_empty(entry.parser_info, glyph)
if ! is_empty { if ! is_empty && glyph != 0 {
append( & output.glyph, glyph ) append( & output.glyph, glyph )
append( & output.position, glyph_pos) append( & output.position, glyph_pos)
} }
@@ -235,7 +249,7 @@ shaper_shape_harfbuzz :: proc( ctx : ^Shaper_Context, text_utf8 : string, entry
& position, & position,
& max_line_width, & max_line_width,
& line_count, & line_count,
font_px_Size, font_px_size,
font_scale, font_scale,
ctx.snap_glyph_position, ctx.snap_glyph_position,
ctx.adv_snap_small_font_threshold ctx.adv_snap_small_font_threshold
@@ -252,7 +266,7 @@ shaper_shape_harfbuzz :: proc( ctx : ^Shaper_Context, text_utf8 : string, entry
& position, & position,
& max_line_width, & max_line_width,
& line_count, & line_count,
font_px_Size, font_px_size,
font_scale, font_scale,
ctx.snap_glyph_position, ctx.snap_glyph_position,
ctx.adv_snap_small_font_threshold ctx.adv_snap_small_font_threshold
@@ -261,27 +275,7 @@ shaper_shape_harfbuzz :: proc( ctx : ^Shaper_Context, text_utf8 : string, entry
// Set the final size // Set the final size
output.size.x = max_line_width output.size.x = max_line_width
output.size.y = f32(line_count) * line_height output.size.y = f32(line_count) * line_height
return
}
shaper_shape_text_uncached_advanced :: #force_inline proc( ctx : ^Shaper_Context,
atlas : Atlas,
glyph_buffer_size : Vec2,
entry : Entry,
font_px_size : f32,
font_scale : f32,
text_utf8 : string,
output : ^Shaped_Text
)
{
profile(#procedure)
assert( ctx != nil )
clear( & output.glyph )
clear( & output.position )
shaper_shape_harfbuzz( ctx, text_utf8, entry, font_px_size, font_scale, output )
// Resolve each glyphs: bounds, atlas lru, and the atlas region as we have everything we need now. // Resolve each glyphs: bounds, atlas lru, and the atlas region as we have everything we need now.
resize( & output.atlas_lru_code, len(output.glyph) ) resize( & output.atlas_lru_code, len(output.glyph) )
@@ -302,6 +296,7 @@ shaper_shape_text_uncached_advanced :: #force_inline proc( ctx : ^Shaper_Context
output.region_kind[index] = atlas_decide_region( atlas, glyph_buffer_size, bounds_size_scaled ) output.region_kind[index] = atlas_decide_region( atlas, glyph_buffer_size, bounds_size_scaled )
} }
profile_end() profile_end()
return
} }
// Basic western alphabet based shaping. Not that much faster than harfbuzz if at all. // Basic western alphabet based shaping. Not that much faster than harfbuzz if at all.

View File

@@ -428,9 +428,18 @@ hot_reload :: proc( ctx : ^Context, allocator : Allocator )
reload_array( & draw_list.indices, allocator) reload_array( & draw_list.indices, allocator)
reload_array( & draw_list.calls, allocator) reload_array( & draw_list.calls, allocator)
parser_reload(& ctx.parser_ctx, allocator)
// Scope Stack // Scope Stack
{ {
stack := & ctx.stack
reload_array(& stack.font, allocator)
reload_array(& stack.font_size, allocator)
reload_array(& stack.colour, allocator)
reload_array(& stack.view, allocator)
reload_array(& stack.position, allocator)
reload_array(& stack.scale, allocator)
reload_array(& stack.zoom, allocator)
} }
} }
@@ -497,7 +506,14 @@ shutdown :: proc( ctx : ^Context )
// Scope Stack // Scope Stack
{ {
stack := & ctx.stack
delete(stack.font)
delete(stack.font_size)
delete(stack.colour)
delete(stack.view)
delete(stack.position)
delete(stack.scale)
delete(stack.zoom)
} }
} }
@@ -833,7 +849,7 @@ draw_text_normalized_space :: proc( ctx : ^Context,
position : Vec2, position : Vec2,
scale : Vec2, scale : Vec2,
text_utf8 : string, text_utf8 : string,
shaper_proc : $Shaper_Shape_Text_Uncached_Proc = shaper_shape_text_uncached_advanced shaper_proc : $Shaper_Shape_Text_Uncached_Proc = shaper_shape_harfbuzz
) )
{ {
profile(#procedure) profile(#procedure)
@@ -970,7 +986,7 @@ draw_text_view_space :: proc(ctx : ^Context,
scale : Vec2, scale : Vec2,
zoom : f32, zoom : f32,
text_utf8 : string, text_utf8 : string,
shaper_proc : $Shaper_Shape_Text_Uncached_Proc = shaper_shape_text_uncached_advanced shaper_proc : $Shaper_Shape_Text_Uncached_Proc = shaper_shape_harfbuzz
) )
{ {
profile(#procedure) profile(#procedure)
@@ -1111,7 +1127,7 @@ absolute_scale := peek(stack.scale ) * scale
*/ */
// @(optimization_mode = "favor_size") // @(optimization_mode = "favor_size")
draw_text :: proc( ctx : ^Context, position, scale : Vec2, text_utf8 : string, draw_text :: proc( ctx : ^Context, position, scale : Vec2, text_utf8 : string,
shaper_proc : $Shaper_Shape_Text_Uncached_Proc = shaper_shape_text_uncached_advanced shaper_proc : $Shaper_Shape_Text_Uncached_Proc = shaper_shape_harfbuzz
) )
{ {
profile(#procedure) profile(#procedure)
@@ -1216,7 +1232,9 @@ measure_shape_size :: #force_inline proc( ctx : ^Context, shape : Shaped_Text )
} }
// Don't use this if you already have the shape instead use measure_shape_size // Don't use this if you already have the shape instead use measure_shape_size
measure_text_size :: #force_inline proc( ctx : ^Context, font : Font_ID, px_size : f32, text_utf8 : string ) -> (measured : Vec2) measure_text_size :: #force_inline proc( ctx : ^Context, font : Font_ID, px_size : f32, text_utf8 : string,
shaper_proc : $Shaper_Shape_Text_Uncached_Proc = shaper_shape_harfbuzz
) -> (measured : Vec2)
{ {
// profile(#procedure) // profile(#procedure)
assert( ctx != nil ) assert( ctx != nil )
@@ -1237,7 +1255,7 @@ measure_text_size :: #force_inline proc( ctx : ^Context, font : Font_ID, px_size
entry, entry,
target_px_size, target_px_size,
target_font_scale, target_font_scale,
shaper_shape_text_uncached_advanced shaper_proc
) )
return shaped.size * target_scale return shaped.size * target_scale
} }
@@ -1261,7 +1279,9 @@ get_font_vertical_metrics :: #force_inline proc ( ctx : ^Context, font : Font_ID
//#region("shaping") //#region("shaping")
shape_text_latin :: #force_inline proc( ctx : ^Context, font : Font_ID, px_size : f32, text_utf8 : string ) -> Shaped_Text shape_text :: #force_inline proc( ctx : ^Context, font : Font_ID, px_size : f32, text_utf8 : string,
shaper_proc : $Shaper_Shape_Text_Uncached_Proc = shaper_shape_harfbuzz
) -> Shaped_Text
{ {
profile(#procedure) profile(#procedure)
assert( len(text_utf8) > 0 ) assert( len(text_utf8) > 0 )
@@ -1279,34 +1299,15 @@ shape_text_latin :: #force_inline proc( ctx : ^Context, font : Font_ID, px_size
entry, entry,
target_px_size, target_px_size,
target_font_scale, target_font_scale,
shaper_shape_text_latin shaper_proc
) )
} }
shape_text_advanced :: #force_inline proc( ctx : ^Context, font : Font_ID, px_size : f32, text_utf8 : string ) -> Shaped_Text
{
profile(#procedure)
assert( len(text_utf8) > 0 )
entry := ctx.entries[ font ]
target_px_size := px_size * ctx.px_scalar
target_font_scale := parser_scale( entry.parser_info, target_px_size )
return shaper_shape_text_cached( text_utf8,
& ctx.shaper_ctx,
& ctx.shape_cache,
ctx.atlas,
vec2(ctx.glyph_buffer.size),
font,
entry,
target_px_size,
target_font_scale,
shaper_shape_text_uncached_advanced
)
}
// User handled shaped text. Will not be cached // User handled shaped text. Will not be cached
shape_text_latin_uncached :: #force_inline proc( ctx : ^Context, font : Font_ID, px_size: f32, text_utf8 : string, shape : ^Shaped_Text ) shape_text_uncached :: #force_inline proc( ctx : ^Context, font : Font_ID, px_size: f32, text_utf8 : string, shape : ^Shaped_Text,
shaper_proc : $Shaper_Shape_Text_Uncached_Proc = shaper_shape_harfbuzz
)
{ {
profile(#procedure) profile(#procedure)
assert( len(text_utf8) > 0 ) assert( len(text_utf8) > 0 )
@@ -1315,29 +1316,7 @@ shape_text_latin_uncached :: #force_inline proc( ctx : ^Context, font : Font_ID,
target_px_size := px_size * ctx.px_scalar target_px_size := px_size * ctx.px_scalar
target_font_scale := parser_scale( entry.parser_info, target_px_size ) target_font_scale := parser_scale( entry.parser_info, target_px_size )
shaper_shape_text_latin(& ctx.shaper_ctx, shaper_proc(& ctx.shaper_ctx,
ctx.atlas,
vec2(ctx.glyph_buffer.size),
entry,
target_px_size,
target_font_scale,
text_utf8,
shape
)
return
}
// User handled shaped text. Will not be cached
shape_text_advanced_uncached :: #force_inline proc( ctx : ^Context, font : Font_ID, px_size: f32, text_utf8 : string, shape : ^Shaped_Text )
{
profile(#procedure)
assert( len(text_utf8) > 0 )
entry := ctx.entries[ font ]
target_px_size := px_size * ctx.px_scalar
target_font_scale := parser_scale( entry.parser_info, target_px_size )
shaper_shape_text_uncached_advanced(& ctx.shaper_ctx,
ctx.atlas, ctx.atlas,
vec2(ctx.glyph_buffer.size), vec2(ctx.glyph_buffer.size),
entry, entry,