diff --git a/examples/sokol_demo/sokol_demo.odin b/examples/sokol_demo/sokol_demo.odin index 4c8b372..9b6eacf 100644 --- a/examples/sokol_demo/sokol_demo.odin +++ b/examples/sokol_demo/sokol_demo.odin @@ -58,6 +58,7 @@ Font_ID :: struct { Font_Entry :: struct { path_file : string, + data : []byte, default_size : i32, ve_id : ve.Font_ID, } @@ -126,6 +127,7 @@ font_load :: proc(path_file : string, error : ve.Load_Font_Error def.path_file = path_file 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 ) assert(error == .None) @@ -164,7 +166,8 @@ draw_text :: proc( content : string, font : Font_ID, pos : Vec2, size : f32 = 0. pos, scale, zoom, - content + content, + // ve.shaper_shape_text_latin, ) return } @@ -258,7 +261,6 @@ init :: proc "c" () 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_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_mono = font_load(path_ubuntu_mono, 21.0, "UbuntuMono") 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) c := [5]u8{} 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 { 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{} 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) } } diff --git a/thirdparty/stb/lib/stb_truetype.lib b/thirdparty/stb/lib/stb_truetype.lib index 4f1ea6c..4217b43 100644 Binary files a/thirdparty/stb/lib/stb_truetype.lib and b/thirdparty/stb/lib/stb_truetype.lib differ diff --git a/vefontcache/draw.odin b/vefontcache/draw.odin index 189e308..a85f1b6 100644 --- a/vefontcache/draw.odin +++ b/vefontcache/draw.odin @@ -592,12 +592,12 @@ batch_generate_glyphs_draw_list :: proc ( draw_list : ^Draw_List, colour.g = 1.0 colour.b = 0.0 } - for pack_id, index in oversized { - error : Allocator_Error - glyph_pack[pack_id].shape, error = parser_get_glyph_shape(entry.parser_info, shape.glyph[pack_id]) - assert(error == .None) - assert(glyph_pack[pack_id].shape != nil) - } + // for pack_id, index in oversized { + // error : Allocator_Error + // glyph_pack[pack_id].shape, error = parser_get_glyph_shape(entry.parser_info, shape.glyph[pack_id]) + // assert(error == .None) + // assert(glyph_pack[pack_id].shape != nil) + // } for id, index in oversized { 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.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, glyph_pack[id].shape, @@ -616,6 +621,9 @@ batch_generate_glyphs_draw_list :: proc ( draw_list : ^Draw_List, 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 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) - for pack_id, index in oversized { - assert(glyph_pack[pack_id].shape != nil) - parser_free_shape(entry.parser_info, glyph_pack[pack_id].shape) - } + // for pack_id, index in oversized { + // assert(glyph_pack[pack_id].shape != nil) + // parser_free_shape(entry.parser_info, glyph_pack[pack_id].shape) + // } } profile_end() @@ -666,12 +674,12 @@ batch_generate_glyphs_draw_list :: proc ( draw_list : ^Draw_List, profile_begin("to_cache: caching to atlas") if len(to_cache) > 0 { - for pack_id, index in to_cache { - error : Allocator_Error - glyph_pack[pack_id].shape, error = parser_get_glyph_shape(entry.parser_info, shape.glyph[pack_id]) - assert(error == .None) - assert(glyph_pack[pack_id].shape != nil) - } + // for pack_id, index in to_cache { + // error : Allocator_Error + // glyph_pack[pack_id].shape, error = parser_get_glyph_shape(entry.parser_info, shape.glyph[pack_id]) + // assert(error == .None) + // assert(glyph_pack[pack_id].shape != nil) + // } 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.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) 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.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) - for pack_id, index in to_cache { - assert(glyph_pack[pack_id].shape != nil) - parser_free_shape(entry.parser_info, glyph_pack[pack_id].shape) - } + // for pack_id, index in to_cache { + // assert(glyph_pack[pack_id].shape != nil) + // parser_free_shape(entry.parser_info, glyph_pack[pack_id].shape) + // } profile_begin("gen_cached_draw_list: to_cache") when ENABLE_DRAW_TYPE_VISUALIZATION { diff --git a/vefontcache/shaper.odin b/vefontcache/shaper.odin index e342f8e..dc6d51e 100644 --- a/vefontcache/shaper.odin +++ b/vefontcache/shaper.odin @@ -107,9 +107,22 @@ shaper_unload_font :: #force_inline proc( info : ^Shaper_Info ) // Recommended shaper. Very performant. // TODO(Ed): Would be nice to properly support vertical shaping, right now its strictly just horizontal... @(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) + assert( ctx != nil ) + + clear( & output.glyph ) + clear( & output.position ) + current_script := harfbuzz.Script.UNKNOWN hb_ucfunc := harfbuzz.unicode_funcs_get_default() harfbuzz.buffer_clear_contents( ctx.hb_buffer ) @@ -142,8 +155,6 @@ shaper_shape_harfbuzz :: proc( ctx : ^Shaper_Context, text_utf8 : string, entry ) { 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_direction( buffer, harfbuzz.script_get_horizontal_direction( script )) 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 + last_cluster := u32(0) for index : i32; index < i32(glyph_count); index += 1 { - hb_glyph := glyph_infos[ index ] + hb_glyph := glyph_infos [ index ] hb_gposition := glyph_positions[ index ] - glyph := cast(Glyph) hb_glyph.codepoint + glyph := cast(Glyph) hb_glyph.codepoint 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 = floor(position.y) (line_count^) += 1 + + last_cluster = hb_glyph.cluster continue } 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) is_empty := parser_is_glyph_empty(entry.parser_info, glyph) - if ! is_empty { + if ! is_empty && glyph != 0 { append( & output.glyph, glyph ) append( & output.position, glyph_pos) } @@ -235,7 +249,7 @@ shaper_shape_harfbuzz :: proc( ctx : ^Shaper_Context, text_utf8 : string, entry & position, & max_line_width, & line_count, - font_px_Size, + font_px_size, font_scale, ctx.snap_glyph_position, ctx.adv_snap_small_font_threshold @@ -252,7 +266,7 @@ shaper_shape_harfbuzz :: proc( ctx : ^Shaper_Context, text_utf8 : string, entry & position, & max_line_width, & line_count, - font_px_Size, + font_px_size, font_scale, ctx.snap_glyph_position, 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 output.size.x = max_line_width 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. 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 ) } profile_end() + return } // Basic western alphabet based shaping. Not that much faster than harfbuzz if at all. diff --git a/vefontcache/vefontcache.odin b/vefontcache/vefontcache.odin index 76826de..3e93885 100644 --- a/vefontcache/vefontcache.odin +++ b/vefontcache/vefontcache.odin @@ -428,9 +428,18 @@ hot_reload :: proc( ctx : ^Context, allocator : Allocator ) reload_array( & draw_list.indices, allocator) reload_array( & draw_list.calls, allocator) + parser_reload(& ctx.parser_ctx, allocator) + // 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 { - + 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, 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) @@ -970,7 +986,7 @@ draw_text_view_space :: proc(ctx : ^Context, scale : Vec2, zoom : f32, 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) @@ -1111,7 +1127,7 @@ absolute_scale := peek(stack.scale ) * scale */ // @(optimization_mode = "favor_size") 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) @@ -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 -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) assert( ctx != nil ) @@ -1237,7 +1255,7 @@ measure_text_size :: #force_inline proc( ctx : ^Context, font : Font_ID, px_size entry, target_px_size, target_font_scale, - shaper_shape_text_uncached_advanced + shaper_proc ) return shaped.size * target_scale } @@ -1261,7 +1279,9 @@ get_font_vertical_metrics :: #force_inline proc ( ctx : ^Context, font : Font_ID //#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) assert( len(text_utf8) > 0 ) @@ -1279,34 +1299,15 @@ shape_text_latin :: #force_inline proc( ctx : ^Context, font : Font_ID, px_size entry, target_px_size, 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 -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) 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_font_scale := parser_scale( entry.parser_info, target_px_size ) - shaper_shape_text_latin(& 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, + shaper_proc(& ctx.shaper_ctx, ctx.atlas, vec2(ctx.glyph_buffer.size), entry,