Various improvements to VEFontCache's font rendering

This commit is contained in:
Edward R. Gonzalez 2024-06-25 14:28:59 -04:00
parent 1fe741034d
commit b5f9687927
4 changed files with 100 additions and 59 deletions

View File

@ -17,11 +17,13 @@ import "core:mem"
Advance_Snap_Smallfont_Size :: 12
Colour :: [4]f32
Vec2 :: [2]f32
Vec2i :: [2]i32
Colour :: [4]f32
Vec2 :: [2]f32
Vec2i :: [2]i32
Vec2_64 :: [2]f64
vec2_from_scalar :: proc( scalar : f32 ) -> Vec2 { return { scalar, scalar } }
vec2_from_scalar :: #force_inline proc( scalar : f32 ) -> Vec2 { return { scalar, scalar } }
vec2_64_from_vec2 :: #force_inline proc( v2 : Vec2 ) -> Vec2_64 { return { f64(v2.x), f64(v2.y) }}
FontID :: distinct i64
Glyph :: distinct i32
@ -98,7 +100,7 @@ InitAtlasParams :: struct {
InitAtlasParams_Default :: InitAtlasParams {
width = 4096,
height = 2048,
glyph_padding = 1,
glyph_padding = 2,
region_a = {
width = 32,
@ -125,9 +127,10 @@ InitGlyphDrawParams :: struct {
}
InitGlyphDrawParams_Default :: InitGlyphDrawParams {
over_sample = { 8, 8 },
over_sample = { 4, 4 },
buffer_batch = 4,
draw_padding = InitAtlasParams_Default.glyph_padding,
// draw_padding = InitAtlasParams_Default.glyph_padding,
}
InitShapeCacheParams :: struct {
@ -159,7 +162,7 @@ init :: proc( ctx : ^Context, parser_kind : ParserKind,
context.allocator = ctx.backing
if curve_quality == 0 {
curve_quality = 6
curve_quality = 12
}
ctx.curve_quality = curve_quality
@ -525,8 +528,8 @@ cache_glyph_to_atlas :: proc( ctx : ^Context, font : FontID, glyph_index : Glyph
// Get hb_font text metrics. These are unscaled!
bounds_0, bounds_1 := parser_get_glyph_box( & entry.parser_info, glyph_index )
bounds_width := bounds_1.x - bounds_0.x
bounds_height := bounds_1.y - bounds_0.y
bounds_width := f32(bounds_1.x - bounds_0.x)
bounds_height := f32(bounds_1.y - bounds_0.y)
region_kind, region, over_sample := decide_codepoint_region( ctx, entry, glyph_index )
@ -560,8 +563,12 @@ cache_glyph_to_atlas :: proc( ctx : ^Context, font : FontID, glyph_index : Glyph
assert( LRU_get( & region.state, lru_code ) != - 1 )
}
atlas := & ctx.atlas
glyph_padding := cast(f32) atlas.glyph_padding
atlas := & ctx.atlas
atlas_width := f32(atlas.width)
atlas_height := f32(atlas.height)
glyph_buffer_width := f32(atlas.buffer_width)
glyph_buffer_height := f32(atlas.buffer_height)
glyph_padding := cast(f32) atlas.glyph_padding
if ctx.debug_print
{
@ -572,42 +579,41 @@ cache_glyph_to_atlas :: proc( ctx : ^Context, font : FontID, glyph_index : Glyph
// Draw oversized glyph to update FBO
glyph_draw_scale := over_sample * entry.size_scale
glyph_draw_translate := Vec2 { -f32(bounds_0.x), -f32(bounds_0.y) } * glyph_draw_scale + Vec2{ glyph_padding, glyph_padding }
glyph_draw_translate := -1 * Vec2 { f32(bounds_0.x), f32(bounds_0.y) } * 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
gwidth_scaled_px := i32( f32(bounds_width) * f32(glyph_draw_scale.x) + 1.0 ) + i32(2 * over_sample.x * glyph_padding)
gwidth_scaled_px := i32( bounds_width * glyph_draw_scale.x + 1.0 ) + i32(over_sample.x * glyph_padding)
if i32(atlas.update_batch_x + gwidth_scaled_px) >= i32(atlas.buffer_width) {
flush_glyph_buffer_to_atlas( ctx )
}
// Calculate the src and destination regions
dst_position, dst_width, dst_height := atlas_bbox( atlas, region_kind, atlas_index )
dst_glyph_position := dst_position + { glyph_padding, glyph_padding }
dst_glyph_width := f32(bounds_width) * entry.size_scale
dst_glyph_height := f32(bounds_height) * entry.size_scale
dst_glyph_position -= { glyph_padding, glyph_padding }
dst_glyph_width += 2 * glyph_padding
dst_glyph_height += 2 * glyph_padding
dst_glyph_position := dst_position
dst_glyph_width := bounds_width * entry.size_scale
dst_glyph_height := bounds_height * entry.size_scale
dst_glyph_width += glyph_padding
dst_glyph_height += glyph_padding
dst_size := Vec2 { dst_width, dst_height }
dst_glyph_size := Vec2 { dst_glyph_width, dst_glyph_height }
screenspace_x_form( & dst_glyph_position, & dst_glyph_size, f32(atlas.width), f32(atlas.height) )
screenspace_x_form( & dst_position, & dst_size, f32(atlas.width), f32(atlas.height) )
screenspace_x_form( & dst_glyph_position, & dst_glyph_size, atlas_width, atlas_height )
screenspace_x_form( & dst_position, & dst_size, atlas_width, atlas_height )
src_position := Vec2 { f32(atlas.update_batch_x), 0 }
src_size := Vec2 {
f32(bounds_width) * glyph_draw_scale.x,
f32(bounds_height) * glyph_draw_scale.y,
bounds_width * glyph_draw_scale.x,
bounds_height * glyph_draw_scale.y,
}
src_size += 2 * over_sample * glyph_padding
textspace_x_form( & src_position, & src_size, f32(atlas.buffer_width), f32(atlas.buffer_height) )
src_size += over_sample * glyph_padding
textspace_x_form( & src_position, & src_size, glyph_buffer_width, glyph_buffer_height )
// Advance glyph_update_batch_x and calculate final glyph drawing transform
glyph_draw_translate.x += f32(atlas.update_batch_x)
atlas.update_batch_x += gwidth_scaled_px
screenspace_x_form( & glyph_draw_translate, & glyph_draw_scale, f32(atlas.buffer_width), f32(atlas.buffer_height))
screenspace_x_form( & glyph_draw_translate, & glyph_draw_scale, glyph_buffer_width, glyph_buffer_height )
call : DrawCall
{
@ -652,7 +658,7 @@ measure_text_size :: proc( ctx : ^Context, font : FontID, text_utf8 : string ) -
atlas := ctx.atlas
shaped := shape_text_cached( ctx, font, text_utf8 )
entry := & ctx.entries.data[ font ]
padding := 2 * cast(f32) atlas.glyph_padding
padding := cast(f32) atlas.glyph_padding
for index : i32 = 0; index < i32(shaped.glyphs.num); index += 1
{
@ -666,14 +672,14 @@ measure_text_size :: proc( ctx : ^Context, font : FontID, text_utf8 : string ) -
// region_kind, region, over_sample := decide_codepoint_region( ctx, entry, glyph_index )
glyph_size := Vec2 {
f32(bounds_width) * entry.size_scale + padding,
f32(bounds_height) * entry.size_scale + padding,
f32(bounds_width) * entry.size_scale, //+ padding,
f32(bounds_height) * entry.size_scale, //+ padding,
}
dummy_position : Vec2
measured.y = max(measured.y, glyph_size.y)
}
measured.x = shaped.end_cursor_pos.x - padding
measured.x = shaped.end_cursor_pos.x
return measured
}

View File

@ -1,5 +1,7 @@
package VEFontCache
import "core:math"
DrawCall :: struct {
pass : FrameBufferPass,
start_index : u32,
@ -123,8 +125,8 @@ directly_draw_massive_glyph :: proc( ctx : ^Context, entry : ^Entry, glyph : Gly
dst := position + scale * bounds_scaled
dst_width := scale.x * glyph_dst_width
dst_height := scale.y * glyph_dst_height
dst.x -= scale.x * f32(ctx.atlas.glyph_padding)
dst.y -= scale.y * f32(ctx.atlas.glyph_padding)
dst.x -= scale.x * f32(ctx.atlas.draw_padding)
dst.y -= scale.y * f32(ctx.atlas.draw_padding)
dst_size := Vec2{ dst_width, dst_height }
glyph_size := Vec2 { glyph_width, glyph_height }
@ -158,8 +160,8 @@ draw_cached_glyph :: proc( ctx : ^Context, entry : ^Entry, glyph_index : Glyph,
bounds_0, bounds_1 := parser_get_glyph_box( & entry.parser_info, glyph_index )
bounds_width := bounds_1.x - bounds_0.x
bounds_height := bounds_1.y - bounds_0.y
bounds_width := f32(bounds_1.x - bounds_0.x)
bounds_height := f32(bounds_1.y - bounds_0.y)
// Decide which atlas to target
region_kind, region, over_sample := decide_codepoint_region( ctx, entry, glyph_index )
@ -167,7 +169,7 @@ draw_cached_glyph :: proc( ctx : ^Context, entry : ^Entry, glyph_index : Glyph,
// E region is special case and not cached to atlas
if region_kind == .E
{
directly_draw_massive_glyph( ctx, entry, glyph_index, bounds_0, bounds_width, bounds_height, over_sample, position, scale )
directly_draw_massive_glyph( ctx, entry, glyph_index, bounds_0, cast(i32) bounds_width, cast(i32) bounds_height, over_sample, position, scale )
return true
}
@ -180,32 +182,36 @@ draw_cached_glyph :: proc( ctx : ^Context, entry : ^Entry, glyph_index : Glyph,
}
atlas := & ctx.atlas
atlas_width := f32(atlas.width)
atlas_height := f32(atlas.height)
glyph_padding := f32(atlas.glyph_padding)
// Figure out the source bounding box in the atlas texture
atlas_position, atlas_width, atlas_height := atlas_bbox( atlas, region_kind, atlas_index )
glyph_atlas_position, glyph_atlas_width, glyph_atlas_height := atlas_bbox( atlas, region_kind, atlas_index )
glyph_position := atlas_position //* {1, 2}
glyph_width := f32(bounds_width) * entry.size_scale
glyph_height := f32(bounds_height) * entry.size_scale
glyph_width := bounds_width * entry.size_scale
glyph_height := bounds_height * entry.size_scale
glyph_width += 2 * f32(atlas.glyph_padding)
glyph_height += 2 * f32(atlas.glyph_padding)
glyph_width += glyph_padding
glyph_height += glyph_padding
glyph_scale := Vec2 { glyph_width, glyph_height }
bounds_0_scaled := Vec2{ f32(bounds_0.x), f32(bounds_0.y) } * entry.size_scale - { 0.5, 0.5 }
bounds_0_scaled := Vec2{ f32(bounds_0.x), f32(bounds_0.y) } * entry.size_scale //- { 0.5, 0.5 }
bounds_0_scaled = {
cast(f32) cast(i32) bounds_0_scaled.x,
cast(f32) cast(i32) bounds_0_scaled.y,
math.ceil(bounds_0_scaled.x),
math.ceil(bounds_0_scaled.y),
}
// dst := position * scale * bounds_0_scaled
dst := Vec2 {
position.x + scale.x * bounds_0_scaled.x,
position.y + scale.y * bounds_0_scaled.y,
position.x + bounds_0_scaled.x * scale.x,
position.y + bounds_0_scaled.y * scale.y,
}
dst_width := scale.x * glyph_width
dst_height := scale.y * glyph_height
dst -= scale * { f32(atlas.glyph_padding), f32(atlas.glyph_padding) }
dst -= scale * glyph_padding
dst_scale := Vec2 { dst_width, dst_height }
textspace_x_form( & glyph_position, & glyph_scale, f32(atlas.width), f32(atlas.height) )
textspace_x_form( & glyph_atlas_position, & glyph_scale, atlas_width, atlas_height )
// Add the glyph drawcall
call := DrawCall_Default
@ -215,7 +221,7 @@ draw_cached_glyph :: proc( ctx : ^Context, entry : ^Entry, glyph_index : Glyph,
colour = ctx.colour
start_index = cast(u32) ctx.draw_list.indices.num
blit_quad( & ctx.draw_list, dst, dst + dst_scale, glyph_position, glyph_position + glyph_scale )
blit_quad( & ctx.draw_list, dst, dst + dst_scale, glyph_atlas_position, glyph_atlas_position + glyph_scale )
end_index = cast(u32) ctx.draw_list.indices.num
}
append( & ctx.draw_list.calls, call )

View File

@ -31,6 +31,11 @@ shape_lru_hash :: #force_inline proc( label : string ) -> u64 {
// ve_fontcache_eval_bezier (quadratic)
eval_point_on_bezier3 :: proc( p0, p1, p2 : Vec2, alpha : f32 ) -> Vec2
{
p0 := vec2_64_from_vec2(p0)
p1 := vec2_64_from_vec2(p1)
p2 := vec2_64_from_vec2(p2)
alpha := f64(alpha)
weight_start := (1 - alpha) * (1 - alpha)
weight_control := 2.0 * (1 - alpha) * alpha
weight_end := alpha * alpha
@ -40,7 +45,7 @@ eval_point_on_bezier3 :: proc( p0, p1, p2 : Vec2, alpha : f32 ) -> Vec2
end_point := p2 * weight_end
point := starting_point + control_point + end_point
return point
return { f32(point.x), f32(point.y) }
}
// For a provided alpha value,
@ -48,6 +53,12 @@ eval_point_on_bezier3 :: proc( p0, p1, p2 : Vec2, alpha : f32 ) -> Vec2
// ve_fontcache_eval_bezier (cubic)
eval_point_on_bezier4 :: proc( p0, p1, p2, p3 : Vec2, alpha : f32 ) -> Vec2
{
p0 := vec2_64_from_vec2(p0)
p1 := vec2_64_from_vec2(p1)
p2 := vec2_64_from_vec2(p2)
p3 := vec2_64_from_vec2(p3)
alpha := f64(alpha)
weight_start := (1 - alpha) * (1 - alpha) * (1 - alpha)
weight_c_a := 3 * (1 - alpha) * (1 - alpha) * alpha
weight_c_b := 3 * (1 - alpha) * alpha * alpha
@ -59,7 +70,7 @@ eval_point_on_bezier4 :: proc( p0, p1, p2, p3 : Vec2, alpha : f32 ) -> Vec2
end_point := p3 * weight_end
point := start_point + control_a + control_b + end_point
return point
return { f32(point.x), f32(point.y) }
}
reset_batch_codepoint_state :: proc( ctx : ^Context ) {
@ -68,13 +79,31 @@ reset_batch_codepoint_state :: proc( ctx : ^Context ) {
}
screenspace_x_form :: proc( position, scale : ^Vec2, width, height : f32 ) {
quotient := 1.0 / Vec2 { width, height }
(position^) = (position^) * quotient * 2.0 - 1.0
(scale^) = (scale^) * quotient * 2.0
pos_64 := vec2_64_from_vec2(position^)
scale_64 := vec2_64_from_vec2(scale^)
width := f64(width)
height := f64(height)
quotient : Vec2_64 = 1.0 / { width, height }
// quotient : Vec2 = 1.0 / { width, height }
pos_64 = pos_64 * quotient * 2.0 - 1.0
scale_64 = scale_64 * quotient * 2.0
(position^) = { f32(pos_64.x), f32(pos_64.y) }
(scale^) = { f32(scale_64.x), f32(scale_64.y) }
}
textspace_x_form :: proc( position, scale : ^Vec2, width, height : f32 ) {
quotient := 1.0 / Vec2 { width, height }
(position^) *= quotient
(scale^) *= quotient
pos_64 := vec2_64_from_vec2(position^)
scale_64 := vec2_64_from_vec2(scale^)
width := f64(width)
height := f64(height)
quotient : Vec2_64 = 1.0 / { width, height }
// quotient : Vec2 = 1.0 / { width, height }
pos_64 *= quotient
scale_64 *= quotient
(position^) = { f32(pos_64.x), f32(pos_64.y) }
(scale^) = { f32(scale_64.x), f32(scale_64.y) }
}

View File

@ -438,8 +438,8 @@ parser_scale_for_mapping_em_to_pixels :: proc( font : ^ParserFontInfo, size : f3
FT_Point_10 :: 64.0
points_per_em := (size / system_dpi ) * DPT_DPI
freetype.set_char_size( font.freetype_info, 0, cast(freetype.F26Dot6) (f32(points_per_em) * FT_Point_10), cast(u32) DPT_DPI, cast(u32) DPT_DPI )
size_scale := size / cast(f32) font.freetype_info.units_per_em;
freetype.set_char_size( font.freetype_info, 0, cast(freetype.F26Dot6) f32(points_per_em * FT_Point_10), cast(u32) DPT_DPI, cast(u32) DPT_DPI )
size_scale := f32(f64(size) / cast(f64) font.freetype_info.units_per_em)
return size_scale
case .STB_TrueType: