WIP: attempt to improve text rendering

This commit is contained in:
2024-12-29 10:20:06 -05:00
parent 292d1b58b5
commit 7eab6f9a7f
16 changed files with 145 additions and 69 deletions

View File

@ -27,7 +27,8 @@ Atlas :: struct {
width : 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_b : Atlas_Region,
@ -98,8 +99,8 @@ decide_codepoint_region :: proc(ctx : ^Context, entry : ^Entry, glyph_index : Gl
glyph_buffer := & ctx.glyph_buffer
glyph_padding := f32( atlas.glyph_padding ) * 2
bounds_width_scaled := i32(bounds_width * entry.size_scale + glyph_padding)
bounds_height_scaled := i32(bounds_height * 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 * atlas.glyph_over_scalar + glyph_padding)
// Use a lookup table for faster region selection
region_lookup := [4]struct { kind: Atlas_Region_Kind, region: ^Atlas_Region } {

View File

@ -174,9 +174,9 @@ cache_glyph_freetype :: proc(ctx: ^Context, font: Font_ID, glyph_index: Glyph, e
start_index: int = 0
for contour_index in 0 ..< int(outline.n_contours)
{
end_index := int(contours[contour_index]) + 1
prev_point: Vec2
first_point: Vec2
end_index := int(contours[contour_index]) + 1
prev_point : Vec2
first_point : Vec2
for idx := start_index; idx < end_index; idx += 1
{
@ -766,7 +766,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)
assert(draw_list != nil)

View File

@ -76,7 +76,7 @@ shape_text_uncached :: proc( ctx : ^Context, font : Font_ID, text_utf8 : string,
if ctx.text_shape_adv
{
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, ctx.snap_shape_pos, & entry.shaper_info, output, text_utf8, ascent_i32, descent_i32, line_gap_i32, entry.size, entry.size_scale )
return
}
else
@ -107,21 +107,18 @@ shape_text_uncached :: proc( ctx : ^Context, font : Font_ID, text_utf8 : string,
continue
}
if abs( entry.size ) <= ADVANCE_SNAP_SMALLFONT_SIZE {
position.x = position.x
position.x = ceil(position.x)
}
append( & output.glyphs, parser_find_glyph_index( & 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 {
position.x,
position.y
floor(position.x),
floor(position.y)
})
position.x += f32(advance) * entry.size_scale
if ctx.snap_shape_pos do position.x = ceil(position.x)
prev_codepoint = codepoint
}

View File

@ -13,6 +13,9 @@ Shaper_Kind :: enum {
Shaper_Context :: struct {
hb_buffer : harfbuzz.Buffer,
snap_glyph_pos : b32,
adv_snap_small_font_threshold : u32,
}
Shaper_Info :: struct {
@ -51,7 +54,7 @@ shaper_unload_font :: proc( ctx : ^Shaper_Info )
if blob != nil do harfbuzz.blob_destroy( blob )
}
shaper_shape_from_text :: proc( ctx : ^Shaper_Context, info : ^Shaper_Info, output :^Shaped_Text, text_utf8 : string,
shaper_shape_from_text :: proc( ctx : ^Shaper_Context, snap_shape_pos : b32, info : ^Shaper_Info, output :^Shaped_Text, text_utf8 : string,
ascent, descent, line_gap : i32, size, size_scale : f32 )
{
// profile(#procedure)
@ -71,7 +74,8 @@ shaper_shape_from_text :: proc( ctx : ^Shaper_Context, info : ^Shaper_Info, outp
position, vertical_position : f32
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,
ascent, descent, line_gap, size, size_scale: f32 )
ascent, descent, line_gap, size, size_scale: f32,
snap_shape_pos : b32 )
{
// Set script and direction. We use the system's default langauge.
// script = HB_SCRIPT_LATIN
@ -101,7 +105,7 @@ shaper_shape_from_text :: proc( ctx : ^Shaper_Context, info : ^Shaper_Info, outp
(max_line_width^) = max( max_line_width^, position^ )
(position^) = 0.0
(vertical_position^) -= line_height
(vertical_position^) = cast(f32) i32(vertical_position^ + 0.5)
(vertical_position^) = vertical_position^
(line_count^) += 1
continue
}
@ -112,13 +116,18 @@ shaper_shape_from_text :: proc( ctx : ^Shaper_Context, info : ^Shaper_Info, outp
append( & output.glyphs, glyph_id )
pos := position^
pos := position^
v_pos := vertical_position^
offset_x := f32(hb_gposition.x_offset) * size_scale
offset_y := f32(hb_gposition.y_offset) * size_scale
append( & output.positions, Vec2 { cast(f32) i32( pos + offset_x + 0.5 ),
v_pos + offset_y,
})
pos += offset_x
v_pos += offset_y
if snap_shape_pos {
pos = floor(pos)
v_pos = floor(v_pos)
}
append( & output.positions, Vec2 {pos, v_pos})
(position^) += f32(hb_gposition.x_advance) * size_scale
(vertical_position^) += f32(hb_gposition.y_advance) * size_scale
@ -151,13 +160,13 @@ shaper_shape_from_text :: proc( ctx : ^Shaper_Context, info : ^Shaper_Info, outp
}
// 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, snap_shape_pos )
harfbuzz.buffer_add( ctx.hb_buffer, hb_codepoint, codepoint == '\n' ? 1 : 0 )
current_script = script
}
// 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, snap_shape_pos )
// Set the final size
output.size.x = max_line_width

View File

@ -7,7 +7,7 @@ package vefontcache
import "base:runtime"
ADVANCE_SNAP_SMALLFONT_SIZE :: 0
ADVANCE_SNAP_SMALLFONT_SIZE :: 10
Font_ID :: distinct i64
Glyph :: distinct i32
@ -44,7 +44,6 @@ Context :: struct {
snap_width : f32,
snap_height : f32,
colour : Colour,
cursor_pos : Vec2,
@ -75,9 +74,10 @@ Init_Atlas_Region_Params :: struct {
}
Init_Atlas_Params :: struct {
width : u32,
height : u32,
glyph_padding : u32,
width : u32,
height : 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_b : Init_Atlas_Region_Params,
@ -86,9 +86,10 @@ Init_Atlas_Params :: struct {
}
Init_Atlas_Params_Default :: Init_Atlas_Params {
width = 4096,
height = 2048,
glyph_padding = 4,
width = 4096,
height = 2048,
glyph_padding = 1,
glyph_over_scalar = 1,
region_a = {
width = 32,
@ -115,7 +116,7 @@ Init_Glyph_Draw_Params :: struct {
}
Init_Glyph_Draw_Params_Default :: Init_Glyph_Draw_Params {
over_sample = { 16, 16 },
over_sample = { 4, 4 },
buffer_batch = 4,
draw_padding = Init_Atlas_Params_Default.glyph_padding,
}
@ -138,7 +139,7 @@ startup :: proc( ctx : ^Context, parser_kind : Parser_Kind = .STB_TrueType,
shape_cache_params := Init_Shape_Cache_Params_Default,
use_advanced_text_shaper : b32 = true,
snap_shape_position : b32 = true,
default_curve_quality : u32 = 6,
default_curve_quality : u32 = 3,
entires_reserve : u32 = 512,
temp_path_reserve : u32 = 1024,
temp_codepoint_seen_reserve : u32 = 2048,
@ -202,9 +203,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_d, atlas_params, atlas_params.region_d, { 2, 1}, 256 )
atlas.width = i32(atlas_params.width)
atlas.height = i32(atlas_params.height)
atlas.glyph_padding = i32(atlas_params.glyph_padding)
atlas.width = i32(atlas_params.width)
atlas.height = i32(atlas_params.height)
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_b.offset.x = 0
@ -385,10 +387,8 @@ load_font :: proc( ctx : ^Context, label : string, data : []byte, size_px : f32,
parser_info = parser_load_font( & parser_ctx, label, data )
shaper_info = shaper_load_font( & shaper_ctx, label, data, transmute(rawptr) id )
size = size_px
size_scale = size_px < 0.0 ? \
parser_scale_for_pixel_height( & parser_info, -size_px ) \
: parser_scale_for_mapping_em_to_pixels( & parser_info, size_px )
size = size_px
size_scale = parser_scale( & parser_info, size )
if glyph_curve_quality == 0 {
curve_quality = f32(ctx.default_curve_quality)