VEFontchange: codepath changes and bugfixing

This commit is contained in:
Edward R. Gonzalez 2025-01-03 13:06:43 -05:00
parent 8c7b3e2058
commit c4c3bba20c
7 changed files with 421 additions and 378 deletions

View File

@ -13,29 +13,29 @@ Atlas_Region_Kind :: enum u8 {
Atlas_Region :: struct {
state : LRU_Cache,
width : i32,
height : i32,
size : Vec2i,
capacity : Vec2i,
offset : Vec2i,
width : i32,
height : i32,
next_idx : i32,
}
Atlas :: struct {
width : i32,
height : i32,
glyph_padding : f32, // 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,
region_c : Atlas_Region,
region_d : Atlas_Region,
regions : [5] ^Atlas_Region,
glyph_padding : f32, // 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.
width : i32,
height : i32,
}
atlas_region_bbox :: proc( region : Atlas_Region, local_idx : i32 ) -> (position, size: Vec2)

View File

@ -28,7 +28,7 @@ Glyph_Draw_Quad :: struct {
src_scale : Vec2,
}
Glyph_Pack_Entry :: struct {
Glyph_Pack_Entry :: struct #packed {
position : Vec2,
index : Glyph,
@ -53,6 +53,7 @@ Glyph_Pack_Entry :: struct {
draw_quad : Glyph_Draw_Quad,
draw_atlas_quad : Glyph_Draw_Quad,
draw_quad_clear : Glyph_Draw_Quad,
// shape_id : i32,
}
@ -177,26 +178,21 @@ construct_filled_path :: #force_inline proc( draw_list : ^Draw_List, outside_poi
}
}
generate_glyph_pass_draw_list :: #force_inline proc(ctx : ^Context,
// glyph_id : Glyph,
// parser_info : Parser_Font_Info,
glyph_shape : Parser_Glyph_Shape,
curve_quality : f32,
bounds : Range2,
generate_glyph_pass_draw_list :: proc(draw_list : ^Draw_List, path : ^[dynamic]Vertex,
glyph_shape : Parser_Glyph_Shape,
curve_quality : f32,
bounds : Range2,
scale, translate : Vec2
) -> b32
)
{
profile(#procedure)
// glyph_shape, error := parser_get_glyph_shape( parser_info, glyph_id )
outside := Vec2{bounds.p0.x - 21, bounds.p0.y - 33}
draw := Draw_Call_Default
draw.pass = Frame_Buffer_Pass.Glyph
draw.start_index = u32(len(ctx.draw_list.indices))
draw.start_index = u32(len(draw_list.indices))
path := &ctx.temp_path
clear(path)
step := 1.0 / curve_quality
@ -204,7 +200,7 @@ generate_glyph_pass_draw_list :: #force_inline proc(ctx : ^Context,
{
case .Move:
if len(path) > 0 {
construct_filled_path(&ctx.draw_list, outside, path[:], scale, translate)
construct_filled_path( draw_list, outside, path[:], scale, translate)
clear(path)
}
fallthrough
@ -235,63 +231,50 @@ generate_glyph_pass_draw_list :: #force_inline proc(ctx : ^Context,
append( path, Vertex { pos = eval_point_on_bezier4(p0, p1, p2, p3, alpha) } )
}
case:
// case:
// assert(false, "WTF")
}
if len(path) > 0 {
construct_filled_path(&ctx.draw_list, outside, path[:], scale, translate)
construct_filled_path(draw_list, outside, path[:], scale, translate)
}
draw.end_index = u32(len(ctx.draw_list.indices))
draw.end_index = u32(len(draw_list.indices))
if draw.end_index > draw.start_index {
append( & ctx.draw_list.calls, draw)
append( & draw_list.calls, draw)
}
// parser_free_shape(parser_info, glyph_shape)
return true
}
cache_glyph_to_atlas :: #force_no_inline proc ( ctx : ^Context,
cache_glyph_to_atlas :: #force_no_inline proc (
#no_alias draw_list,
glyph_buf_draw_list, glyph_buf_clear_list : ^Draw_List,
glyph_buf_Batch_x : ^i32,
glyph_buf_draw_list,
glyph_buf_clear_list : ^Draw_List,
glyph_buf_Batch_x : ^i32,
temp_path : ^[dynamic]Vertex,
glyph_shape : Parser_Glyph_Shape,
bounds : Range2,
bounds_size_scaled : Vec2,
atlas_size : Vec2,
glyph_padding : f32,
glyph_buffer_size : Vec2,
over_sample : Vec2,
glyph_padding : f32,
buf_transform : Transform,
atlas_size : Vec2,
buf_transform : Transform,
glyph_shape : Parser_Glyph_Shape,
bounds : Range2, // -> generate_glyph_pass_draw_list
bounds_size : Vec2,
region_pos : Vec2,
region_size : Vec2,
lru_code : u64,
atlas_index : i32,
entry : Entry,
// region_kind : Atlas_Region_Kind,
// region : ^Atlas_Region,
over_sample : Vec2,
// glyph_id : Glyph,
// parser_info : Parser_Font_Info,
region_pos : Vec2,
region_size : Vec2,
curve_quality : f32,
)
{
profile(#procedure)
batch_x := cast(f32) glyph_buf_Batch_x ^
glyph_buffer_pad := over_sample.x * glyph_padding
batch_x := cast(f32) glyph_buf_Batch_x ^
buffer_padding_scaled := glyph_padding * over_sample
buffer_bounds_scale := bounds_size_scaled * over_sample
// Allocate a glyph glyph render target region (FBO)
buffer_x_allocation := bounds_size.x * buf_transform.scale.x + glyph_buffer_pad + 2.0
buffer_x_allocation := buffer_bounds_scale.x + buffer_padding_scaled.x + 2.0
// If we exceed the region availbe to draw on the buffer, flush the calls to reset the buffer
if i32(batch_x + buffer_x_allocation) >= i32(glyph_buffer_size.x) {
@ -299,16 +282,15 @@ cache_glyph_to_atlas :: #force_no_inline proc ( ctx : ^Context,
batch_x = cast(f32) glyph_buf_Batch_x ^
}
region_pos := region_pos
region_pos := region_pos
dst_glyph_position := region_pos
dst_glyph_size := ceil(bounds_size * entry.size_scale) + glyph_padding
dst_size := (region_size)
dst_glyph_size := bounds_size_scaled + glyph_padding
dst_size := region_size
to_screen_space( & dst_glyph_position, & dst_glyph_size, atlas_size )
to_screen_space( & region_pos, & dst_size, atlas_size )
src_position := Vec2 { batch_x, 0 }
src_size := (bounds_size * buf_transform.scale + over_sample * glyph_padding)
src_size := (buffer_bounds_scale + buffer_padding_scaled)
to_text_space( & src_position, & src_size, glyph_buffer_size )
clear_target_region : Draw_Call
@ -342,25 +324,18 @@ cache_glyph_to_atlas :: #force_no_inline proc ( ctx : ^Context,
append( & glyph_buf_clear_list.calls, clear_target_region )
append( & glyph_buf_draw_list.calls, blit_to_atlas )
screen_space_translate := buf_transform.pos
screen_space_scale := buf_transform.scale
screen_space_translate.x = (buf_transform.pos.x + batch_x)
glyph_buf_Batch_x^ += i32(buffer_x_allocation)
to_screen_space( & screen_space_translate, & screen_space_scale, glyph_buffer_size )
// The glyph buffer space transform for generate_glyph_pass_draw_list
glyph_transform := buf_transform
glyph_transform.pos.x += batch_x
(glyph_buf_Batch_x ^) += i32(buffer_x_allocation)
to_screen_space( & glyph_transform.pos, & glyph_transform.scale, glyph_buffer_size )
// Render glyph to glyph render target (FBO)
generate_glyph_pass_draw_list( ctx,
// glyph_id,
// parser_info,
glyph_shape,
entry.curve_quality, bounds, screen_space_scale, screen_space_translate )
generate_glyph_pass_draw_list( draw_list, temp_path, glyph_shape, curve_quality, bounds, glyph_transform.scale, glyph_transform.pos )
}
generate_oversized_draw_list :: #force_no_inline proc( ctx : ^Context,
generate_oversized_draw_list :: #force_no_inline proc( draw_list : ^Draw_List, temp_path : ^[dynamic]Vertex,
colour : Colour,
curve_quality : f32,
glyph_shape : Parser_Glyph_Shape,
bounds : Range2,
@ -370,27 +345,19 @@ generate_oversized_draw_list :: #force_no_inline proc( ctx : ^Context,
{
profile(#procedure)
generate_glyph_pass_draw_list( ctx,
glyph_shape,
curve_quality,
bounds,
glyph_pass_transform.scale,
glyph_pass_transform.pos
)
calls : [2]Draw_Call
draw_to_target := & calls[0]
{
using draw_to_target
pass = .Target_Uncached
colour = ctx.colour
start_index = u32(len(ctx.draw_list.indices))
colour = colour
start_index = u32(len(draw_list.indices))
blit_quad( & ctx.draw_list,
blit_quad( draw_list,
target_quad.dst_pos, target_quad.dst_pos + target_quad.dst_scale,
target_quad.src_pos, target_quad.src_pos + target_quad.src_scale )
end_index = u32(len(ctx.draw_list.indices))
end_index = u32(len(draw_list.indices))
}
clear_glyph_update := & calls[1]
{
@ -400,7 +367,15 @@ generate_oversized_draw_list :: #force_no_inline proc( ctx : ^Context,
clear_glyph_update.end_index = 0
clear_glyph_update.clear_before_draw = true
}
append( & ctx.draw_list.calls, ..calls[:] )
append( & draw_list.calls, ..calls[:] )
generate_glyph_pass_draw_list( draw_list, temp_path,
glyph_shape,
curve_quality,
bounds,
glyph_pass_transform.scale,
glyph_pass_transform.pos
)
}
generate_shape_draw_list :: #force_no_inline proc( ctx : ^Context,
@ -481,48 +456,57 @@ generate_shape_draw_list :: #force_no_inline proc( ctx : ^Context,
for & glyph, index in glyph_pack
{
glyph.region_kind = atlas_decide_region( atlas ^, glyph_buffer_size, glyph.bounds_size_scaled )
glyph.over_sample = glyph_buffer.over_sample
// glyph.over_sample = glyph_buffer.over_sample
}
profile_end()
profile_begin("atlas slot resolution & to_cache/cached segregation")
for & glyph, index in glyph_pack
{
if glyph.region_kind == .None {
assert(false, "FAILED TO ASSGIN REGION")
continue
}
if glyph.region_kind == .E
{
glyph.over_sample = \
glyph.bounds_size_scaled.x <= glyph_buffer_size.x / 2 &&
glyph.bounds_size_scaled.y <= glyph_buffer_size.y / 2 ? \
{2.0, 2.0} \
: {1.0, 1.0}
glyph.bounds_size_scaled.x <= glyph_buffer_size.x / 2 &&
glyph.bounds_size_scaled.y <= glyph_buffer_size.y / 2 ? \
{2.0, 2.0} \
: {1.0, 1.0}
append_sub_pack(oversized, cast(i32) index)
continue
}
glyph.over_sample = glyph_buffer.over_sample
region := atlas.regions[glyph.region_kind]
glyph.atlas_index = lru_get( & region.state, glyph.lru_code )
if ctx.temp_codepoint_seen_num <= i32(cap(ctx.temp_codepoint_seen))
to_cache_check:
{
if glyph.atlas_index == - 1
if ctx.temp_codepoint_seen_num <= i32(cap(ctx.temp_codepoint_seen))
{
// Check to see if we reached capacity for the atlas
if region.next_idx > region.state.capacity
if glyph.atlas_index == - 1
{
// We will evict LRU. We must predict which LRU will get evicted, and if it's something we've seen then we need to take slowpath and flush batch.
next_evict_codepoint := lru_get_next_evicted( region.state )
found, success := ctx.temp_codepoint_seen[next_evict_codepoint]
assert(success != false)
if (found) {
continue
// Check to see if we reached capacity for the atlas
if region.next_idx > region.state.capacity
{
// We will evict LRU. We must predict which LRU will get evicted, and if it's something we've seen then we need to take slowpath and flush batch.
next_evict_codepoint := lru_get_next_evicted( region.state )
found, success := ctx.temp_codepoint_seen[next_evict_codepoint]
assert(success != false)
if (found) {
break to_cache_check
}
}
}
profile("append to_cache")
glyph.atlas_index = atlas_reserve_slot(region, glyph.lru_code)
glyph.region_pos, glyph.region_size = atlas_region_bbox(region ^, glyph.atlas_index)
append_sub_pack(to_cache, cast(i32) index)
continue
profile("append to_cache")
glyph.atlas_index = atlas_reserve_slot(region, glyph.lru_code)
glyph.region_pos, glyph.region_size = atlas_region_bbox(region ^, glyph.atlas_index)
append_sub_pack(to_cache, cast(i32) index)
mark_batch_codepoint_seen(ctx, glyph.lru_code)
continue
}
}
}
@ -533,18 +517,8 @@ generate_shape_draw_list :: #force_no_inline proc( ctx : ^Context,
}
profile_end()
profile_begin("font parser shape generation")
for id, index in sub_slice(to_cache) {
error : Allocator_Error
glyph_pack[id].shape, error = parser_get_glyph_shape(entry.parser_info, glyph_pack[id].index)
assert(error == .None)
}
for id, index in sub_slice(oversized) {
error : Allocator_Error
glyph_pack[id].shape, error = parser_get_glyph_shape(entry.parser_info, glyph_pack[id].index)
assert(error == .None)
}
profile_end()
// ctx.colour = {}
ctx.colour.a *= 1.0 + ctx.alpha_scalar
profile_begin("transform & quad compute")
for id, index in sub_slice(cached)
@ -561,6 +535,8 @@ generate_shape_draw_list :: #force_no_inline proc( ctx : ^Context,
for id, index in sub_slice(to_cache)
{
glyph := & glyph_pack[id]
// Quad to for drawing atlas slot to target
quad := & glyph.draw_quad
quad.dst_pos = glyph.position + glyph.bounds_scaled.p0 * target_scale
quad.dst_scale = glyph.scale * target_scale
@ -568,71 +544,41 @@ generate_shape_draw_list :: #force_no_inline proc( ctx : ^Context,
quad.src_pos = glyph.region_pos
to_text_space( & quad.src_pos, & quad.src_scale, atlas_size )
// The glyph buffer space transform for generate_glyph_pass_draw_list
transform := & glyph.draw_transform
transform.scale = entry.size_scale * glyph_buffer.over_sample
transform.pos = -1 * glyph_pack[id].bounds.p0 * transform.scale + atlas.glyph_padding
transform.pos = -1 * glyph.bounds.p0 * transform.scale + atlas.glyph_padding
// Unlike with oversized, this cannot be finished here as its final value is dependent on glyph_buffer.batch_x allocation
}
for id, index in sub_slice(oversized)
{
// The glyph buffer space transform for generate_glyph_pass_draw_list
glyph := & glyph_pack[id]
transform := glyph.draw_transform
transform.scale = entry.size_scale * glyph_buffer.over_sample
transform.pos = -1 * glyph_pack[id].bounds.p0 * transform.scale + atlas.glyph_padding
to_screen_space( & transform.pos, & transform.scale, glyph_buffer_size )
glyph_padding := glyph_buffer.draw_padding
// Quad to draw during target pass
quad := & glyph.draw_quad
quad.dst_pos = glyph.position + glyph.bounds_scaled.p0 * target_scale - glyph_padding * target_scale
quad.dst_scale = glyph.scale + glyph_padding * target_scale
quad.src_scale = glyph.bounds_size_scaled * glyph.over_sample
quad.src_pos = glyph.position
to_text_space( & quad.src_pos, & quad.src_scale, atlas_size )
to_text_space( & quad.src_pos, & quad.src_scale, atlas_size )
}
profile_end()
profile_begin("to_cache: caching to atlas")
for id, index in sub_slice(to_cache)
{
glyph := glyph_pack[id]
cache_glyph_to_atlas( ctx,
draw_list,
& glyph_buffer.draw_list,
& glyph_buffer.clear_draw_list,
& glyph_buffer.batch_x,
atlas.glyph_padding,
glyph_buffer_size,
atlas_size,
glyph.draw_transform,
glyph.shape,
glyph.bounds,
glyph.bounds_size,
glyph.region_pos,
glyph.region_size,
glyph.lru_code,
glyph.atlas_index,
entry,
glyph.over_sample,
// glyph.index,
// entry.parser_info,
)
mark_batch_codepoint_seen(ctx, glyph.lru_code)
}
reset_batch_codepoint_state( ctx )
flush_glyph_buffer_draw_list(draw_list, & glyph_buffer.draw_list, & glyph_buffer.clear_draw_list, & glyph_buffer.batch_x)
profile_end()
// The glyph buffer space transform for generate_glyph_pass_draw_list
transform := glyph.draw_transform
transform.scale = entry.size_scale * glyph.over_sample
transform.pos = -1 * glyph.bounds.p0 * transform.scale + vec2(atlas.glyph_padding)
to_screen_space( & transform.pos, & transform.scale, glyph_buffer_size )
// Oversized will use a cleared glyph_buffer every time.
glyph_padding := vec2(glyph_buffer.draw_padding)
target_scale := target_scale
// target_scale = Vec2{ 1, 1}
// Quad to draw during target pass, every
quad := & glyph.draw_quad
quad.dst_pos = glyph.position + glyph.bounds_scaled.p0 * target_scale - glyph_padding * target_scale
quad.dst_scale = (glyph.bounds_size_scaled + glyph_padding) * target_scale
quad.src_pos = {}
quad.src_scale = glyph.bounds_size_scaled * glyph.over_sample + glyph_padding
to_text_space( & quad.src_pos, & quad.src_scale, glyph_buffer_size )
dummy := 1
dummy += 1
}
profile_end()
generate_cached_draw_list :: #force_inline proc (draw_list : ^Draw_List, glyph_pack : #soa[]Glyph_Pack_Entry, sub_pack : []i32, colour : Colour )
{
profile(#procedure)
@ -653,17 +599,115 @@ generate_shape_draw_list :: #force_no_inline proc( ctx : ^Context,
append(& draw_list.calls, call)
}
}
generate_cached_draw_list( draw_list, glyph_pack[:], sub_slice(to_cache), ctx.colour )
generate_cached_draw_list( draw_list, glyph_pack[:], sub_slice(cached), ctx.colour )
reset_batch_codepoint_state( ctx )
profile_begin("to_cache: caching to atlas")
// profile_begin("font parser shape generation")
for id, index in sub_slice(to_cache) {
error : Allocator_Error
glyph_pack[id].shape, error = parser_get_glyph_shape(entry.parser_info, glyph_pack[id].index)
assert(error == .None)
}
// profile_end()
for id, index in sub_slice(to_cache)
{
profile("glyph")
ctx.colour.r = 0.80
ctx.colour.g = 0.25
ctx.colour.b = 0.25
glyph := glyph_pack[id]
cache_glyph_to_atlas(
draw_list,
& glyph_buffer.draw_list,
& glyph_buffer.clear_draw_list,
& glyph_buffer.batch_x,
& ctx.temp_path,
glyph.shape,
glyph.bounds,
glyph.bounds_size_scaled,
atlas_size,
glyph_buffer_size,
glyph.over_sample,
atlas.glyph_padding,
glyph.draw_transform,
glyph.region_pos,
glyph.region_size,
entry.curve_quality,
)
// call := Draw_Call_Default
// call.pass = .Target
// call.colour = ctx.colour
// profile("glyph")
// call.start_index = u32(len(draw_list.indices))
// quad := glyph_pack[id].draw_quad
// blit_quad(draw_list,
// quad.dst_pos, quad.dst_pos + quad.dst_scale,
// quad.src_pos, quad.src_pos + quad.src_scale
// )
// call.end_index = u32(len(draw_list.indices))
// append(& draw_list.calls, call)
}
flush_glyph_buffer_draw_list(draw_list, & glyph_buffer.draw_list, & glyph_buffer.clear_draw_list, & glyph_buffer.batch_x)
for id, index in sub_slice(to_cache) do parser_free_shape(entry.parser_info, glyph_pack[id].shape)
profile_end()
generate_cached_draw_list( draw_list, glyph_pack[:], sub_slice(to_cache), ctx.colour )
profile_begin("generate_cached_draw_list: to_cache")
ctx.colour.r = 1.0
ctx.colour.g = 1.0
ctx.colour.b = 1.0
// flush_glyph_buffer_draw_list(draw_list, & glyph_buffer.draw_list, & glyph_buffer.clear_draw_list, & glyph_buffer.batch_x)
// generate_cached_draw_list( draw_list, glyph_pack[:], sub_slice(cached), ctx.colour )
for id, index in sub_slice(cached)
{
call := Draw_Call_Default
call.pass = .Target
call.colour = ctx.colour
profile("glyph")
call.start_index = u32(len(draw_list.indices))
quad := glyph_pack[id].draw_quad
blit_quad(draw_list,
quad.dst_pos, quad.dst_pos + quad.dst_scale,
quad.src_pos, quad.src_pos + quad.src_scale
)
call.end_index = u32(len(draw_list.indices))
append(& draw_list.calls, call)
}
reset_batch_codepoint_state( ctx )
profile_end()
profile_begin("generate oversized glyphs draw_list")
for id, index in sub_slice(oversized) {
error : Allocator_Error
glyph_pack[id].shape, error = parser_get_glyph_shape(entry.parser_info, glyph_pack[id].index)
assert(error == .None)
}
for id, index in sub_slice(oversized)
{
ctx.colour.r = 0.0
ctx.colour.g = 0.0
ctx.colour.b = 1.0
flush_glyph_buffer_draw_list(draw_list, & glyph_buffer.draw_list, & glyph_buffer.clear_draw_list, & glyph_buffer.batch_x)
glyph := glyph_pack[id]
generate_oversized_draw_list(ctx,
generate_oversized_draw_list(
draw_list,
& ctx.temp_path,
ctx.colour,
entry.curve_quality,
glyph.shape,
glyph.bounds,
@ -673,12 +717,10 @@ generate_shape_draw_list :: #force_no_inline proc( ctx : ^Context,
}
profile_end()
reset_batch_codepoint_state( ctx )
flush_glyph_buffer_draw_list(draw_list, & glyph_buffer.draw_list, & glyph_buffer.clear_draw_list, & glyph_buffer.batch_x)
// flush_glyph_buffer_draw_list(draw_list, & glyph_buffer.draw_list, & glyph_buffer.clear_draw_list, & glyph_buffer.batch_x)
profile_begin("font parser shape cleanup")
for id, index in sub_slice(oversized) do parser_free_shape(entry.parser_info, glyph_pack[id].shape)
for id, index in sub_slice(to_cache) do parser_free_shape(entry.parser_info, glyph_pack[id].shape)
profile_end()
cursor_pos = position + shaped.end_cursor_pos * target_scale

View File

@ -36,8 +36,6 @@ import "core:mem"
arena_init :: mem.arena_init
import "core:slice"
//#region("Proc overload mappings")
append :: proc {
@ -51,15 +49,15 @@ append_soa :: proc {
}
ceil :: proc {
math.ceil_f16,
math.ceil_f16le,
math.ceil_f16be,
math.ceil_f32,
math.ceil_f32le,
math.ceil_f32be,
math.ceil_f64,
math.ceil_f64le,
math.ceil_f64be,
ceil_f16,
ceil_f16le,
ceil_f16be,
ceil_f32,
ceil_f32le,
ceil_f32be,
ceil_f64,
ceil_f64le,
ceil_f64be,
ceil_vec2,
}
@ -70,15 +68,15 @@ clear :: proc {
}
floor :: proc {
math.floor_f16,
math.floor_f16le,
math.floor_f16be,
math.floor_f32,
math.floor_f32le,
math.floor_f32be,
math.floor_f64,
math.floor_f64le,
math.floor_f64be,
floor_f16,
floor_f16le,
floor_f16be,
floor_f32,
floor_f32le,
floor_f32be,
floor_f64,
floor_f64le,
floor_f64be,
floor_vec2,
}

View File

@ -89,7 +89,7 @@ to_screen_space :: #force_inline proc "contextless" ( #no_alias position, scale
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) }
(position^) = { f32(pos_64.x), f32(pos_64.y) }
(scale^) = { f32(scale_64.x), f32(scale_64.y) }
}
else
@ -98,7 +98,7 @@ to_screen_space :: #force_inline proc "contextless" ( #no_alias position, scale
scale_32 := scale^
quotient : Vec2 = 1.0 / size
pos = pos * quotient * 2.0 - 1.0
pos = pos * quotient * 2.0 - 1.0
scale_32 = scale_32 * quotient * 2.0
(position^) = pos
@ -117,7 +117,7 @@ to_text_space :: #force_inline proc "contextless" ( #no_alias position, scale :
pos_64 *= quotient
scale_64 *= quotient
(position^) = { f32(pos_64.x), f32(pos_64.y) }
(position^) = { f32(pos_64.x), f32(pos_64.y) }
(scale^) = { f32(scale_64.x), f32(scale_64.y) }
}
else

View File

@ -14,7 +14,7 @@ import "core:c"
import "core:math"
import "core:slice"
import stbtt "vendor:stb/truetype"
import freetype "thirdparty:freetype"
// import freetype "thirdparty:freetype"
Parser_Kind :: enum u32 {
STB_TrueType,
@ -26,7 +26,7 @@ Parser_Font_Info :: struct {
kind : Parser_Kind,
using _ : struct #raw_union {
stbtt_info : stbtt.fontinfo,
freetype_info : freetype.Face
// freetype_info : freetype.Face
},
data : []byte,
}
@ -52,20 +52,20 @@ Parser_Glyph_Shape :: [dynamic]Parser_Glyph_Vertex
Parser_Context :: struct {
kind : Parser_Kind,
ft_library : freetype.Library,
// ft_library : freetype.Library,
}
parser_init :: proc( ctx : ^Parser_Context, kind : Parser_Kind )
{
switch kind
{
case .Freetype:
result := freetype.init_free_type( & ctx.ft_library )
assert( result == freetype.Error.Ok, "VEFontCache.parser_init: Failed to initialize freetype" )
// switch kind
// {
// case .Freetype:
// result := freetype.init_free_type( & ctx.ft_library )
// assert( result == freetype.Error.Ok, "VEFontCache.parser_init: Failed to initialize freetype" )
case .STB_TrueType:
// case .STB_TrueType:
// Do nothing intentional
}
// }
ctx.kind = kind
}
@ -76,22 +76,22 @@ parser_shutdown :: proc( ctx : ^Parser_Context ) {
parser_load_font :: proc( ctx : ^Parser_Context, label : string, data : []byte ) -> (font : Parser_Font_Info)
{
switch ctx.kind
{
case .Freetype:
when ODIN_OS == .Windows {
error := freetype.new_memory_face( ctx.ft_library, raw_data(data), cast(i32) len(data), 0, & font.freetype_info )
if error != .Ok do return
}
else when ODIN_OS == .Linux {
error := freetype.new_memory_face( ctx.ft_library, raw_data(data), cast(i64) len(data), 0, & font.freetype_info )
if error != .Ok do return
}
// switch ctx.kind
// {
// case .Freetype:
// when ODIN_OS == .Windows {
// error := freetype.new_memory_face( ctx.ft_library, raw_data(data), cast(i32) len(data), 0, & font.freetype_info )
// if error != .Ok do return
// }
// else when ODIN_OS == .Linux {
// error := freetype.new_memory_face( ctx.ft_library, raw_data(data), cast(i64) len(data), 0, & font.freetype_info )
// if error != .Ok do return
// }
case .STB_TrueType:
// case .STB_TrueType:
success := stbtt.InitFont( & font.stbtt_info, raw_data(data), 0 )
if ! success do return
}
// }
font.label = label
font.data = data
@ -101,122 +101,122 @@ parser_load_font :: proc( ctx : ^Parser_Context, label : string, data : []byte )
parser_unload_font :: proc( font : ^Parser_Font_Info )
{
switch font.kind {
case .Freetype:
error := freetype.done_face( font.freetype_info )
assert( error == .Ok, "VEFontCache.parser_unload_font: Failed to unload freetype face" )
// switch font.kind {
// case .Freetype:
// error := freetype.done_face( font.freetype_info )
// assert( error == .Ok, "VEFontCache.parser_unload_font: Failed to unload freetype face" )
case .STB_TrueType:
// case .STB_TrueType:
// Do Nothing
}
// }
}
parser_find_glyph_index :: #force_inline proc "contextless" ( font : Parser_Font_Info, codepoint : rune ) -> (glyph_index : Glyph)
{
profile(#procedure)
switch font.kind
{
case .Freetype:
when ODIN_OS == .Windows {
glyph_index = transmute(Glyph) freetype.get_char_index( font.freetype_info, transmute(u32) codepoint )
}
else when ODIN_OS == .Linux {
glyph_index = transmute(Glyph) freetype.get_char_index( font.freetype_info, cast(u64) codepoint )
}
return
// switch font.kind
// {
// case .Freetype:
// when ODIN_OS == .Windows {
// glyph_index = transmute(Glyph) freetype.get_char_index( font.freetype_info, transmute(u32) codepoint )
// }
// else when ODIN_OS == .Linux {
// glyph_index = transmute(Glyph) freetype.get_char_index( font.freetype_info, cast(u64) codepoint )
// }
// return
case .STB_TrueType:
// case .STB_TrueType:
glyph_index = transmute(Glyph) stbtt.FindGlyphIndex( font.stbtt_info, codepoint )
return
}
return Glyph(-1)
// }
// return Glyph(-1)
}
parser_free_shape :: #force_inline proc( font : Parser_Font_Info, shape : Parser_Glyph_Shape )
{
switch font.kind
{
case .Freetype:
delete(shape)
// switch font.kind
// {
// case .Freetype:
// delete(shape)
case .STB_TrueType:
// case .STB_TrueType:
stbtt.FreeShape( font.stbtt_info, transmute( [^]stbtt.vertex) raw_data(shape) )
}
// }
}
parser_get_codepoint_horizontal_metrics :: #force_inline proc "contextless" ( font : Parser_Font_Info, codepoint : rune ) -> ( advance, to_left_side_glyph : i32 )
{
switch font.kind
{
case .Freetype:
glyph_index : Glyph
when ODIN_OS == .Windows {
glyph_index = transmute(Glyph) freetype.get_char_index( font.freetype_info, transmute(u32) codepoint )
}
else when ODIN_OS == .Linux {
glyph_index = transmute(Glyph) freetype.get_char_index( font.freetype_info, cast(u64) codepoint )
}
// switch font.kind
// {
// case .Freetype:
// glyph_index : Glyph
// when ODIN_OS == .Windows {
// glyph_index = transmute(Glyph) freetype.get_char_index( font.freetype_info, transmute(u32) codepoint )
// }
// else when ODIN_OS == .Linux {
// glyph_index = transmute(Glyph) freetype.get_char_index( font.freetype_info, cast(u64) codepoint )
// }
if glyph_index != 0
{
freetype.load_glyph( font.freetype_info, c.uint(codepoint), { .No_Bitmap, .No_Hinting, .No_Scale } )
advance = i32(font.freetype_info.glyph.advance.x) >> 6
to_left_side_glyph = i32(font.freetype_info.glyph.metrics.hori_bearing_x) >> 6
}
else
{
advance = 0
to_left_side_glyph = 0
}
// if glyph_index != 0
// {
// freetype.load_glyph( font.freetype_info, c.uint(codepoint), { .No_Bitmap, .No_Hinting, .No_Scale } )
// advance = i32(font.freetype_info.glyph.advance.x) >> 6
// to_left_side_glyph = i32(font.freetype_info.glyph.metrics.hori_bearing_x) >> 6
// }
// else
// {
// advance = 0
// to_left_side_glyph = 0
// }
case .STB_TrueType:
// case .STB_TrueType:
stbtt.GetCodepointHMetrics( font.stbtt_info, codepoint, & advance, & to_left_side_glyph )
}
// }
return
}
parser_get_codepoint_kern_advance :: #force_inline proc "contextless" ( font : Parser_Font_Info, prev_codepoint, codepoint : rune ) -> i32
{
switch font.kind
{
case .Freetype:
prev_glyph_index : Glyph
glyph_index : Glyph
when ODIN_OS == .Windows {
prev_glyph_index = transmute(Glyph) freetype.get_char_index( font.freetype_info, transmute(u32) prev_codepoint )
glyph_index = transmute(Glyph) freetype.get_char_index( font.freetype_info, transmute(u32) codepoint )
}
else when ODIN_OS == .Linux {
prev_glyph_index = transmute(Glyph) freetype.get_char_index( font.freetype_info, cast(u64) prev_codepoint )
glyph_index = transmute(Glyph) freetype.get_char_index( font.freetype_info, cast(u64) codepoint )
}
// switch font.kind
// {
// case .Freetype:
// prev_glyph_index : Glyph
// glyph_index : Glyph
// when ODIN_OS == .Windows {
// prev_glyph_index = transmute(Glyph) freetype.get_char_index( font.freetype_info, transmute(u32) prev_codepoint )
// glyph_index = transmute(Glyph) freetype.get_char_index( font.freetype_info, transmute(u32) codepoint )
// }
// else when ODIN_OS == .Linux {
// prev_glyph_index = transmute(Glyph) freetype.get_char_index( font.freetype_info, cast(u64) prev_codepoint )
// glyph_index = transmute(Glyph) freetype.get_char_index( font.freetype_info, cast(u64) codepoint )
// }
if prev_glyph_index != 0 && glyph_index != 0
{
kerning : freetype.Vector
font.freetype_info.driver.clazz.get_kerning( font.freetype_info, transmute(u32) prev_codepoint, transmute(u32) codepoint, & kerning )
}
// if prev_glyph_index != 0 && glyph_index != 0
// {
// kerning : freetype.Vector
// font.freetype_info.driver.clazz.get_kerning( font.freetype_info, transmute(u32) prev_codepoint, transmute(u32) codepoint, & kerning )
// }
case .STB_TrueType:
// case .STB_TrueType:
kern := stbtt.GetCodepointKernAdvance( font.stbtt_info, prev_codepoint, codepoint )
return kern
}
return -1
// }
// return -1
}
parser_get_font_vertical_metrics :: #force_inline proc "contextless" ( font : Parser_Font_Info ) -> (ascent, descent, line_gap : i32 )
{
switch font.kind
{
case .Freetype:
info := font.freetype_info
ascent = i32(info.ascender)
descent = i32(info.descender)
line_gap = i32(info.height) - (ascent - descent)
// switch font.kind
// {
// case .Freetype:
// info := font.freetype_info
// ascent = i32(info.ascender)
// descent = i32(info.descender)
// line_gap = i32(info.height) - (ascent - descent)
case .STB_TrueType:
// case .STB_TrueType:
stbtt.GetFontVMetrics( font.stbtt_info, & ascent, & descent, & line_gap )
}
// }
return
}
@ -250,13 +250,13 @@ parser_get_bounds :: #force_inline proc "contextless" ( font : Parser_Font_Info,
parser_get_glyph_shape :: #force_inline proc ( font : Parser_Font_Info, glyph_index : Glyph ) -> (shape : Parser_Glyph_Shape, error : Allocator_Error)
{
switch font.kind
{
case .Freetype:
// TODO(Ed): Don't do this, going a completely different route for handling shapes.
// This abstraction fails to be time-saving or performant.
// switch font.kind
// {
// case .Freetype:
// // TODO(Ed): Don't do this, going a completely different route for handling shapes.
// // This abstraction fails to be time-saving or performant.
case .STB_TrueType:
// case .STB_TrueType:
stb_shape : [^]stbtt.vertex
nverts := stbtt.GetGlyphShape( font.stbtt_info, cast(i32) glyph_index, & stb_shape )
@ -266,33 +266,33 @@ parser_get_glyph_shape :: #force_inline proc ( font : Parser_Font_Info, glyph_in
shape_raw.cap = int(nverts)
shape_raw.allocator = runtime.nil_allocator()
error = Allocator_Error.None
return
}
// return
// }
return
}
parser_is_glyph_empty :: #force_inline proc "contextless" ( font : Parser_Font_Info, glyph_index : Glyph ) -> b32
{
switch font.kind
{
case .Freetype:
error := freetype.load_glyph( font.freetype_info, cast(u32) glyph_index, { .No_Bitmap, .No_Hinting, .No_Scale } )
if error == .Ok
{
if font.freetype_info.glyph.format == .Outline {
return font.freetype_info.glyph.outline.n_points == 0
}
else if font.freetype_info.glyph.format == .Bitmap {
return font.freetype_info.glyph.bitmap.width == 0 && font.freetype_info.glyph.bitmap.rows == 0;
}
}
return false
// switch font.kind
// {
// case .Freetype:
// error := freetype.load_glyph( font.freetype_info, cast(u32) glyph_index, { .No_Bitmap, .No_Hinting, .No_Scale } )
// if error == .Ok
// {
// if font.freetype_info.glyph.format == .Outline {
// return font.freetype_info.glyph.outline.n_points == 0
// }
// else if font.freetype_info.glyph.format == .Bitmap {
// return font.freetype_info.glyph.bitmap.width == 0 && font.freetype_info.glyph.bitmap.rows == 0;
// }
// }
// return false
case .STB_TrueType:
// case .STB_TrueType:
return stbtt.IsGlyphEmpty( font.stbtt_info, cast(c.int) glyph_index )
}
return false
// }
// return false
}
parser_scale :: #force_inline proc "contextless" ( font : Parser_Font_Info, size : f32 ) -> f32
@ -306,43 +306,43 @@ parser_scale :: #force_inline proc "contextless" ( font : Parser_Font_Info, size
parser_scale_for_pixel_height :: #force_inline proc "contextless" ( font : Parser_Font_Info, size : f32 ) -> f32
{
switch font.kind {
case .Freetype:
freetype.set_pixel_sizes( font.freetype_info, 0, cast(u32) size )
size_scale := size / cast(f32)font.freetype_info.units_per_em
return size_scale
// switch font.kind {
// case .Freetype:
// freetype.set_pixel_sizes( font.freetype_info, 0, cast(u32) size )
// size_scale := size / cast(f32)font.freetype_info.units_per_em
// return size_scale
case.STB_TrueType:
// case.STB_TrueType:
return stbtt.ScaleForPixelHeight( font.stbtt_info, size )
}
return 0
// }
// return 0
}
parser_scale_for_mapping_em_to_pixels :: #force_inline proc "contextless" ( font : Parser_Font_Info, size : f32 ) -> f32
{
switch font.kind {
case .Freetype:
Inches_To_CM :: cast(f32) 2.54
Points_Per_CM :: cast(f32) 28.3465
CM_Per_Point :: cast(f32) 1.0 / DPT_DPCM
CM_Per_Pixel :: cast(f32) 1.0 / DPT_PPCM
DPT_DPCM :: cast(f32) 72.0 * Inches_To_CM // 182.88 points/dots per cm
DPT_PPCM :: cast(f32) 96.0 * Inches_To_CM // 243.84 pixels per cm
DPT_DPI :: cast(f32) 72.0
// switch font.kind {
// case .Freetype:
// Inches_To_CM :: cast(f32) 2.54
// Points_Per_CM :: cast(f32) 28.3465
// CM_Per_Point :: cast(f32) 1.0 / DPT_DPCM
// CM_Per_Pixel :: cast(f32) 1.0 / DPT_PPCM
// DPT_DPCM :: cast(f32) 72.0 * Inches_To_CM // 182.88 points/dots per cm
// DPT_PPCM :: cast(f32) 96.0 * Inches_To_CM // 243.84 pixels per cm
// DPT_DPI :: cast(f32) 72.0
// TODO(Ed): Don't assume the dots or pixels per inch.
system_dpi :: DPT_DPI
// // TODO(Ed): Don't assume the dots or pixels per inch.
// system_dpi :: DPT_DPI
FT_Font_Size_Point_Unit :: 1.0 / 64.0
FT_Point_10 :: 64.0
// FT_Font_Size_Point_Unit :: 1.0 / 64.0
// 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 := f32(f64(size) / cast(f64) font.freetype_info.units_per_em)
return size_scale
// 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 := f32(f64(size) / cast(f64) font.freetype_info.units_per_em)
// return size_scale
case .STB_TrueType:
// case .STB_TrueType:
return stbtt.ScaleForMappingEmToPixels( font.stbtt_info, size )
}
return 0
// }
// return 0
}

View File

@ -1,13 +1,12 @@
package vefontcache
Shaped_Text :: struct {
font : Font_ID,
entry : ^Entry,
glyphs : [dynamic]Glyph,
positions : [dynamic]Vec2,
end_cursor_pos : Vec2,
size : Vec2,
entry : ^Entry,
font : Font_ID,
}
Shaped_Text_Cache :: struct {

View File

@ -46,8 +46,9 @@ Context :: struct {
snap_width : f32,
snap_height : f32,
colour : Colour,
cursor_pos : Vec2,
alpha_scalar : f32, // Will apply a multiplier to the colour's alpha which provides some sharpening of the edges.
colour : Colour,
cursor_pos : Vec2,
draw_layer : struct {
vertices_offset : int,
@ -116,7 +117,7 @@ Init_Glyph_Draw_Params :: struct {
Init_Glyph_Draw_Params_Default :: Init_Glyph_Draw_Params {
over_sample = Vec2 { 4, 4 },
buffer_batch = 8,
buffer_batch = 4,
draw_padding = Init_Atlas_Params_Default.glyph_padding,
}
@ -151,10 +152,11 @@ startup :: proc( ctx : ^Context, parser_kind : Parser_Kind = .STB_TrueType,
glyph_draw_params := Init_Glyph_Draw_Params_Default,
shape_cache_params := Init_Shape_Cache_Params_Default,
shaper_params := Init_Shaper_Params_Default,
default_curve_quality : u32 = 6,
alpha_sharpen := 0.2,
default_curve_quality : u32 = 3,
entires_reserve : u32 = 256,
temp_path_reserve : u32 = 1024,
temp_codepoint_seen_reserve : u32 = 1024,
temp_path_reserve : u32 = 10 * 1024,
temp_codepoint_seen_reserve : u32 = 10 * 1024,
)
{
assert( ctx != nil, "Must provide a valid context" )
@ -163,12 +165,14 @@ startup :: proc( ctx : ^Context, parser_kind : Parser_Kind = .STB_TrueType,
ctx.backing = allocator
context.allocator = ctx.backing
ctx.colour = { 1, 1, 1, 1 }
use_advanced_shaper = shaper_params.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 {
default_curve_quality = 6
default_curve_quality = 3
}
ctx.default_curve_quality = default_curve_quality
@ -267,7 +271,7 @@ startup :: proc( ctx : ^Context, parser_kind : Parser_Kind = .STB_TrueType,
over_sample = glyph_draw_params.over_sample
batch = cast(i32) glyph_draw_params.buffer_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) //* (batch / 2)
draw_padding = cast(f32) glyph_draw_params.draw_padding
draw_list.calls, error = make( [dynamic]Draw_Call, len = 0, cap = glyph_draw_params.buffer_batch * 2 )