WIP - VEFontCache: Major changes
* Add back proper batching (busted the rendering for it though..) * Some reogonzation of definitions and procedure args * CURRENTLY BROKEN: Something went wrong with the calculations for text positioning..
This commit is contained in:
parent
078d9c8447
commit
cb6053395c
@ -22,6 +22,7 @@ Note: freetype and harfbuzz could technically be gutted if the user removes thei
|
||||
* Macro defines have been coverted (mostly) to runtime parameters
|
||||
* Support for hot_reloading
|
||||
* Curve quality step interpolation for glyph rendering can be set on a per font basis.
|
||||
* All codepaths heavily changed (its faster)
|
||||
|
||||
## TODOs
|
||||
|
||||
|
@ -89,7 +89,13 @@ Frame_Buffer_Pass :: enum u32 {
|
||||
Target_Uncached = 4,
|
||||
}
|
||||
|
||||
Glyph_Draw_Buffer :: struct {
|
||||
Glyph_Batch_Cache :: struct {
|
||||
table : map[u32]b8,
|
||||
num : i32,
|
||||
cap : i32,
|
||||
}
|
||||
|
||||
Glyph_Draw_Buffer :: struct{
|
||||
over_sample : Vec2,
|
||||
batch : i32,
|
||||
width : i32,
|
||||
@ -100,6 +106,10 @@ Glyph_Draw_Buffer :: struct {
|
||||
clear_draw_list : Draw_List,
|
||||
draw_list : Draw_List,
|
||||
|
||||
// TODO(Ed): Get this working properly again.
|
||||
batch_cache : Glyph_Batch_Cache,
|
||||
shape_gen_scratch : [dynamic]Vertex,
|
||||
|
||||
glyph_pack : #soa[dynamic]Glyph_Pack_Entry,
|
||||
oversized : [dynamic]i32,
|
||||
to_cache : [dynamic]i32,
|
||||
@ -108,9 +118,7 @@ Glyph_Draw_Buffer :: struct {
|
||||
|
||||
blit_quad :: #force_inline proc ( draw_list : ^Draw_List, p0 : Vec2 = {0, 0}, p1 : Vec2 = {1, 1}, uv0 : Vec2 = {0, 0}, uv1 : Vec2 = {1, 1} )
|
||||
{
|
||||
profile(#procedure)
|
||||
// logf("Blitting: xy0: %0.2f, %0.2f xy1: %0.2f, %0.2f uv0: %0.2f, %0.2f uv1: %0.2f, %0.2f",
|
||||
// p0.x, p0.y, p1.x, p1.y, uv0.x, uv0.y, uv1.x, uv1.y);
|
||||
// profile(#procedure)
|
||||
v_offset := cast(u32) len(draw_list.vertices)
|
||||
|
||||
quadv : [4]Vertex = {
|
||||
@ -142,12 +150,14 @@ blit_quad :: #force_inline proc ( draw_list : ^Draw_List, p0 : Vec2 = {0, 0}, p1
|
||||
}
|
||||
|
||||
// Constructs a triangle fan to fill a shape using the provided path outside_point represents the center point of the fan.
|
||||
construct_filled_path :: #force_inline proc( draw_list : ^Draw_List, outside_point : Vec2, path : []Vertex,
|
||||
scale := Vec2 { 1, 1 },
|
||||
translate := Vec2 { 0, 0 }
|
||||
construct_filled_path :: #force_inline proc( draw_list : ^Draw_List,
|
||||
outside_point : Vec2,
|
||||
path : []Vertex,
|
||||
scale := Vec2 { 1, 1 },
|
||||
translate := Vec2 { 0, 0 }
|
||||
)
|
||||
{
|
||||
profile(#procedure)
|
||||
// profile(#procedure)
|
||||
v_offset := cast(u32) len(draw_list.vertices)
|
||||
for point in path {
|
||||
point := point
|
||||
@ -184,7 +194,6 @@ generate_glyph_pass_draw_list :: proc(draw_list : ^Draw_List, path : ^[dynamic]V
|
||||
)
|
||||
{
|
||||
profile(#procedure)
|
||||
|
||||
outside := Vec2{bounds.p0.x - 21, bounds.p0.y - 33}
|
||||
|
||||
draw := Draw_Call_Default
|
||||
@ -266,7 +275,6 @@ cache_glyph_to_atlas :: #force_no_inline proc (
|
||||
)
|
||||
{
|
||||
profile(#procedure)
|
||||
|
||||
batch_x := cast(f32) glyph_buf_Batch_x ^
|
||||
buffer_padding_scaled := glyph_padding * over_sample
|
||||
buffer_bounds_scale := (bounds_size_scaled) * over_sample
|
||||
@ -332,36 +340,41 @@ cache_glyph_to_atlas :: #force_no_inline proc (
|
||||
generate_glyph_pass_draw_list( draw_list, temp_path, glyph_shape, curve_quality, bounds, glyph_transform.scale, glyph_transform.pos )
|
||||
}
|
||||
|
||||
generate_shape_draw_list :: #force_no_inline proc( ctx : ^Context,
|
||||
entry : Entry,
|
||||
shaped : Shaped_Text,
|
||||
position, target_scale : Vec2,
|
||||
snap_width, snap_height : f32
|
||||
generate_shape_draw_list :: #force_no_inline proc( draw_list : ^Draw_List, shape : Shaped_Text,
|
||||
atlas : ^Atlas,
|
||||
glyph_buffer : ^Glyph_Draw_Buffer,
|
||||
|
||||
colour : Colour,
|
||||
entry : Entry,
|
||||
font_scale : f32,
|
||||
|
||||
target_position : Vec2,
|
||||
target_scale : Vec2,
|
||||
snap_width : f32,
|
||||
snap_height : f32
|
||||
) -> (cursor_pos : Vec2) #no_bounds_check
|
||||
{
|
||||
profile(#procedure)
|
||||
|
||||
colour := ctx.colour
|
||||
colour.a = 1.0 + ctx.alpha_scalar
|
||||
mark_glyph_seen :: #force_inline proc "contextless" ( cache : ^Glyph_Batch_Cache, lru_code : u32 ) {
|
||||
cache.table[lru_code] = true
|
||||
cache.num += 1
|
||||
}
|
||||
reset_batch :: #force_inline proc( cache : ^Glyph_Batch_Cache ) {
|
||||
clear_map( & cache.table )
|
||||
cache.num = 0
|
||||
}
|
||||
|
||||
atlas := & ctx.atlas
|
||||
glyph_buffer := & ctx.glyph_buffer
|
||||
draw_list := & ctx.draw_list
|
||||
atlas_glyph_pad := atlas.glyph_padding
|
||||
atlas_size := Vec2 { f32(atlas.width), f32(atlas.height) }
|
||||
glyph_buffer_size := Vec2 { f32(glyph_buffer.width), f32(glyph_buffer.height) }
|
||||
|
||||
profile_begin("soa prep")
|
||||
// Make sure the packs are large enough for the shape
|
||||
glyph_pack := & glyph_buffer.glyph_pack
|
||||
oversized := & glyph_buffer.oversized
|
||||
to_cache := & glyph_buffer.to_cache
|
||||
cached := & glyph_buffer.cached
|
||||
non_zero_resize_soa(glyph_pack, len(shaped.glyphs))
|
||||
clear(oversized)
|
||||
clear(to_cache)
|
||||
clear(cached)
|
||||
profile_end()
|
||||
non_zero_resize_soa(glyph_pack, len(shape.glyphs))
|
||||
|
||||
append_sub_pack :: #force_inline proc ( pack : ^[dynamic]i32, entry : i32 )
|
||||
{
|
||||
@ -374,7 +387,7 @@ generate_shape_draw_list :: #force_no_inline proc( ctx : ^Context,
|
||||
profile_begin("index")
|
||||
for & glyph, index in glyph_pack
|
||||
{
|
||||
glyph.index = shaped.glyphs[ index ]
|
||||
glyph.index = shape.glyphs[ index ]
|
||||
glyph.lru_code = font_glyph_lru_code(entry.id, glyph.index)
|
||||
}
|
||||
profile_end()
|
||||
@ -382,7 +395,7 @@ generate_shape_draw_list :: #force_no_inline proc( ctx : ^Context,
|
||||
profile_begin("translate")
|
||||
for & glyph, index in glyph_pack
|
||||
{
|
||||
glyph.position = position + (shaped.positions[index]) * target_scale
|
||||
glyph.position = target_position + (shape.positions[index]) * target_scale
|
||||
}
|
||||
profile_end()
|
||||
|
||||
@ -390,9 +403,9 @@ generate_shape_draw_list :: #force_no_inline proc( ctx : ^Context,
|
||||
for & glyph, index in glyph_pack
|
||||
{
|
||||
glyph.bounds = parser_get_bounds( entry.parser_info, glyph.index )
|
||||
glyph.bounds_scaled = { glyph.bounds.p0 * entry.size_scale, glyph.bounds.p1 * entry.size_scale }
|
||||
glyph.bounds_scaled = { glyph.bounds.p0 * font_scale, glyph.bounds.p1 * font_scale }
|
||||
glyph.bounds_size = glyph.bounds.p1 - glyph.bounds.p0
|
||||
glyph.bounds_size_scaled = glyph.bounds_size * entry.size_scale
|
||||
glyph.bounds_size_scaled = glyph.bounds_size * font_scale
|
||||
glyph.scale = glyph.bounds_size_scaled + atlas.glyph_padding
|
||||
}
|
||||
profile_end()
|
||||
@ -406,7 +419,11 @@ generate_shape_draw_list :: #force_no_inline proc( ctx : ^Context,
|
||||
}
|
||||
profile_end()
|
||||
|
||||
profile_begin("atlas slot resolution & to_cache/cached segregation")
|
||||
profile_begin("batching")
|
||||
clear(oversized)
|
||||
clear(to_cache)
|
||||
clear(cached)
|
||||
|
||||
for & glyph, index in glyph_pack
|
||||
{
|
||||
if glyph.region_kind == .None {
|
||||
@ -428,43 +445,106 @@ generate_shape_draw_list :: #force_no_inline proc( ctx : ^Context,
|
||||
region := atlas.regions[glyph.region_kind]
|
||||
glyph.atlas_index = lru_get( & region.state, glyph.lru_code )
|
||||
|
||||
to_cache_check:
|
||||
// Glyphs are prepared in batches based on the capacity of the batch cache.
|
||||
Prepare_For_Batch:
|
||||
{
|
||||
if ctx.temp_codepoint_seen_num <= i32(cap(ctx.temp_codepoint_seen))
|
||||
{
|
||||
if glyph.atlas_index == - 1
|
||||
{
|
||||
// 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
|
||||
}
|
||||
}
|
||||
// Determine if we hit the limit for this batch.
|
||||
if glyph_buffer.batch_cache.num >= glyph_buffer.batch_cache.cap do break Prepare_For_Batch
|
||||
|
||||
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
|
||||
if glyph.atlas_index == - 1
|
||||
{
|
||||
// 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_glyph := lru_get_next_evicted( region.state )
|
||||
found_take_slow_path, success := glyph_buffer.batch_cache.table[next_evict_glyph]
|
||||
assert(success != false)
|
||||
if (found_take_slow_path) {
|
||||
break Prepare_For_Batch
|
||||
}
|
||||
}
|
||||
|
||||
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_glyph_seen(& glyph_buffer.batch_cache, glyph.lru_code)
|
||||
continue
|
||||
}
|
||||
|
||||
profile("append cached")
|
||||
glyph.region_pos, glyph.region_size = atlas_region_bbox(region ^, glyph.atlas_index)
|
||||
append_sub_pack(cached, cast(i32) index)
|
||||
mark_glyph_seen(& glyph_buffer.batch_cache, glyph.lru_code)
|
||||
continue
|
||||
}
|
||||
|
||||
profile("append cached")
|
||||
glyph.region_pos, glyph.region_size = atlas_region_bbox(region ^, glyph.atlas_index)
|
||||
append_sub_pack(cached, cast(i32) index)
|
||||
mark_batch_codepoint_seen(ctx, glyph.lru_code)
|
||||
// Batch has been prepared for a set of glyphs time to generate glyphs.
|
||||
batch_generate_glyphs_draw_list( draw_list, glyph_pack, sub_slice(cached), sub_slice(to_cache), sub_slice(oversized),
|
||||
atlas,
|
||||
glyph_buffer,
|
||||
atlas_size,
|
||||
glyph_buffer_size,
|
||||
entry,
|
||||
colour,
|
||||
font_scale,
|
||||
target_scale
|
||||
)
|
||||
|
||||
reset_batch( & glyph_buffer.batch_cache)
|
||||
clear(oversized)
|
||||
clear(to_cache)
|
||||
clear(cached)
|
||||
}
|
||||
profile_end()
|
||||
|
||||
// Last batch pass
|
||||
batch_generate_glyphs_draw_list( draw_list, glyph_pack, sub_slice(cached), sub_slice(to_cache), sub_slice(oversized),
|
||||
atlas,
|
||||
glyph_buffer,
|
||||
atlas_size,
|
||||
glyph_buffer_size,
|
||||
entry,
|
||||
colour,
|
||||
font_scale,
|
||||
target_scale
|
||||
)
|
||||
|
||||
reset_batch( & glyph_buffer.batch_cache)
|
||||
clear(oversized)
|
||||
clear(to_cache)
|
||||
clear(cached)
|
||||
|
||||
cursor_pos = target_position + shape.end_cursor_pos * target_scale
|
||||
return
|
||||
}
|
||||
|
||||
batch_generate_glyphs_draw_list :: proc ( draw_list : ^Draw_List,
|
||||
glyph_pack : ^#soa[dynamic]Glyph_Pack_Entry,
|
||||
cached : []i32,
|
||||
to_cache : []i32,
|
||||
oversized : []i32,
|
||||
|
||||
atlas : ^Atlas,
|
||||
glyph_buffer : ^Glyph_Draw_Buffer,
|
||||
atlas_size : Vec2,
|
||||
glyph_buffer_size : Vec2,
|
||||
|
||||
entry : Entry,
|
||||
colour : Colour,
|
||||
font_scale : Vec2,
|
||||
target_scale : Vec2,
|
||||
)
|
||||
{
|
||||
profile(#procedure)
|
||||
|
||||
when ENABLE_DRAW_TYPE_VIS {
|
||||
colour := colour
|
||||
}
|
||||
|
||||
profile_begin("transform & quad compute")
|
||||
for id, index in sub_slice(cached)
|
||||
for id, index in cached
|
||||
{
|
||||
// Quad to for drawing atlas slot to target
|
||||
glyph := & glyph_pack[id]
|
||||
@ -475,7 +555,7 @@ generate_shape_draw_list :: #force_no_inline proc( ctx : ^Context,
|
||||
quad.src_pos = (glyph.region_pos)
|
||||
to_target_space( & quad.src_pos, & quad.src_scale, atlas_size )
|
||||
}
|
||||
for id, index in sub_slice(to_cache)
|
||||
for id, index in to_cache
|
||||
{
|
||||
glyph := & glyph_pack[id]
|
||||
|
||||
@ -489,17 +569,17 @@ generate_shape_draw_list :: #force_no_inline proc( ctx : ^Context,
|
||||
|
||||
// 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.scale = font_scale * glyph_buffer.over_sample
|
||||
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)
|
||||
for id, index in oversized
|
||||
{
|
||||
glyph := & glyph_pack[id]
|
||||
|
||||
// The glyph buffer space transform for generate_glyph_pass_draw_list
|
||||
transform := & glyph.draw_transform
|
||||
transform.scale = entry.size_scale * glyph.over_sample
|
||||
transform.scale = font_scale * glyph.over_sample
|
||||
transform.pos = -1 * glyph.bounds.p0 * transform.scale + vec2(atlas.glyph_padding)
|
||||
to_glyph_buffer_space( & transform.pos, & transform.scale, glyph_buffer_size )
|
||||
// Oversized will use a cleared glyph_buffer every time.
|
||||
@ -516,8 +596,6 @@ generate_shape_draw_list :: #force_no_inline proc( ctx : ^Context,
|
||||
}
|
||||
profile_end()
|
||||
|
||||
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)
|
||||
@ -540,13 +618,12 @@ generate_shape_draw_list :: #force_no_inline proc( ctx : ^Context,
|
||||
}
|
||||
|
||||
profile_begin("to_cache: caching to atlas")
|
||||
for id, index in sub_slice(to_cache) {
|
||||
for id, index in 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(to_cache)
|
||||
for id, index in to_cache
|
||||
{
|
||||
profile("glyph")
|
||||
when ENABLE_DRAW_TYPE_VIS {
|
||||
@ -561,8 +638,7 @@ generate_shape_draw_list :: #force_no_inline proc( ctx : ^Context,
|
||||
& glyph_buffer.draw_list,
|
||||
& glyph_buffer.clear_draw_list,
|
||||
& glyph_buffer.batch_x,
|
||||
|
||||
& ctx.temp_path,
|
||||
& glyph_buffer.shape_gen_scratch,
|
||||
|
||||
glyph.shape,
|
||||
glyph.bounds_scaled,
|
||||
@ -580,37 +656,33 @@ generate_shape_draw_list :: #force_no_inline proc( ctx : ^Context,
|
||||
)
|
||||
}
|
||||
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)
|
||||
for id, index in 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 )
|
||||
generate_cached_draw_list( draw_list, glyph_pack[:], to_cache, colour )
|
||||
|
||||
profile_begin("generate_cached_draw_list: to_cache")
|
||||
|
||||
when ENABLE_DRAW_TYPE_VIS {
|
||||
colour.r = 1.0
|
||||
colour.g = 1.0
|
||||
colour.b = 1.0
|
||||
}
|
||||
generate_cached_draw_list( draw_list, glyph_pack[:], sub_slice(cached), ctx.colour )
|
||||
reset_batch_codepoint_state( ctx )
|
||||
generate_cached_draw_list( draw_list, glyph_pack[:], cached, colour )
|
||||
profile_end()
|
||||
|
||||
flush_glyph_buffer_draw_list(draw_list, & glyph_buffer.draw_list, & glyph_buffer.clear_draw_list, & glyph_buffer.batch_x)
|
||||
|
||||
profile_begin("generate oversized glyphs draw_list")
|
||||
for id, index in sub_slice(oversized) {
|
||||
for id, index in 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)
|
||||
for id, index in oversized
|
||||
{
|
||||
flush_glyph_buffer_draw_list(draw_list, & glyph_buffer.draw_list, & glyph_buffer.clear_draw_list, & glyph_buffer.batch_x)
|
||||
|
||||
generate_glyph_pass_draw_list( draw_list, & ctx.temp_path,
|
||||
generate_glyph_pass_draw_list( draw_list, & glyph_buffer.shape_gen_scratch,
|
||||
glyph_pack[id].shape,
|
||||
entry.curve_quality,
|
||||
glyph_pack[id].bounds,
|
||||
@ -631,7 +703,7 @@ generate_shape_draw_list :: #force_no_inline proc( ctx : ^Context,
|
||||
{
|
||||
using draw_to_target
|
||||
pass = .Target_Uncached
|
||||
colour = ctx.colour
|
||||
colour = colour
|
||||
start_index = u32(len(draw_list.indices))
|
||||
|
||||
blit_quad( draw_list,
|
||||
@ -650,13 +722,8 @@ generate_shape_draw_list :: #force_no_inline proc( ctx : ^Context,
|
||||
}
|
||||
append( & draw_list.calls, ..calls[:] )
|
||||
}
|
||||
|
||||
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 oversized do parser_free_shape(entry.parser_info, glyph_pack[id].shape)
|
||||
profile_end()
|
||||
|
||||
cursor_pos = position + shaped.end_cursor_pos * target_scale
|
||||
return
|
||||
}
|
||||
|
||||
// Flush the content of the glyph_buffers draw lists to the main draw list
|
||||
|
@ -134,4 +134,3 @@ profile_end :: #force_inline proc "contextless" () {
|
||||
}
|
||||
|
||||
//#endregion("Proc overload mappings")
|
||||
|
||||
|
@ -6,14 +6,34 @@ import "core:math"
|
||||
|
||||
import core_log "core:log"
|
||||
|
||||
reload_array :: #force_inline proc( self : ^[dynamic]$Type, allocator : Allocator ) {
|
||||
raw := transmute( ^runtime.Raw_Dynamic_Array) self
|
||||
raw.allocator = allocator
|
||||
}
|
||||
|
||||
reload_array_soa :: #force_inline proc( self : ^#soa[dynamic]$Type, allocator : Allocator ) {
|
||||
raw := runtime.raw_soa_footer(self)
|
||||
raw.allocator = allocator
|
||||
}
|
||||
|
||||
reload_map :: #force_inline proc( self : ^map [$KeyType] $EntryType, allocator : Allocator ) {
|
||||
raw := transmute( ^runtime.Raw_Map) self
|
||||
raw.allocator = allocator
|
||||
}
|
||||
|
||||
font_glyph_lru_code :: #force_inline proc "contextless" ( font : Font_ID, glyph_index : Glyph ) -> (lru_code : u32) {
|
||||
lru_code = u32(glyph_index) + ( ( 0x10000 * u32(font) ) & 0xFFFF0000 )
|
||||
return
|
||||
}
|
||||
|
||||
djb8_hash_32 :: #force_inline proc "contextless" ( hash : ^u32, bytes : []byte ) { for value in bytes do (hash^) = (( (hash^) << 8) + (hash^) ) + u32(value) }
|
||||
djb8_hash :: #force_inline proc "contextless" ( hash : ^u64, bytes : []byte ) { for value in bytes do (hash^) = (( (hash^) << 8) + (hash^) ) + u64(value) }
|
||||
|
||||
Colour :: [4]f32
|
||||
Vec2 :: [2]f32
|
||||
Vec2i :: [2]i32
|
||||
Vec2_64 :: [2]f64
|
||||
|
||||
djb8_hash_32 :: #force_inline proc "contextless" ( hash : ^u32, bytes : []byte ) { for value in bytes do (hash^) = (( (hash^) << 8) + (hash^) ) + u32(value) }
|
||||
djb8_hash :: #force_inline proc "contextless" ( hash : ^u64, bytes : []byte ) { for value in bytes do (hash^) = (( (hash^) << 8) + (hash^) ) + u64(value) }
|
||||
|
||||
vec2_from_scalar :: #force_inline proc "contextless" ( scalar : f32 ) -> Vec2 { return { scalar, scalar }}
|
||||
vec2_64_from_vec2 :: #force_inline proc "contextless" ( v2 : Vec2 ) -> Vec2_64 { return { f64(v2.x), f64(v2.y) }}
|
||||
vec2_from_vec2i :: #force_inline proc "contextless" ( v2i : Vec2i ) -> Vec2 { return { f32(v2i.x), f32(v2i.y) }}
|
||||
@ -42,36 +62,6 @@ logf :: proc( fmt : string, args : ..any, level := core_log.Level.Info, loc :=
|
||||
core_log.logf( level, fmt, ..args, location = loc )
|
||||
}
|
||||
|
||||
reload_array :: proc( self : ^[dynamic]$Type, allocator : Allocator ) {
|
||||
raw := transmute( ^runtime.Raw_Dynamic_Array) self
|
||||
raw.allocator = allocator
|
||||
}
|
||||
|
||||
reload_array_soa :: proc( self : ^#soa[dynamic]$Type, allocator : Allocator ) {
|
||||
raw := runtime.raw_soa_footer(self)
|
||||
raw.allocator = allocator
|
||||
}
|
||||
|
||||
reload_map :: proc( self : ^map [$KeyType] $EntryType, allocator : Allocator ) {
|
||||
raw := transmute( ^runtime.Raw_Map) self
|
||||
raw.allocator = allocator
|
||||
}
|
||||
|
||||
font_glyph_lru_code :: #force_inline proc "contextless" ( font : Font_ID, glyph_index : Glyph ) -> (lru_code : u32) {
|
||||
lru_code = u32(glyph_index) + ( ( 0x10000 * u32(font) ) & 0xFFFF0000 )
|
||||
return
|
||||
}
|
||||
|
||||
mark_batch_codepoint_seen :: #force_inline proc "contextless" ( ctx : ^Context, lru_code : u32 ) {
|
||||
ctx.temp_codepoint_seen[lru_code] = true
|
||||
ctx.temp_codepoint_seen_num += 1
|
||||
}
|
||||
|
||||
reset_batch_codepoint_state :: #force_inline proc( ctx : ^Context ) {
|
||||
clear_map( & ctx.temp_codepoint_seen )
|
||||
ctx.temp_codepoint_seen_num = 0
|
||||
}
|
||||
|
||||
to_glyph_buffer_space :: #force_inline proc "contextless" ( #no_alias position, scale : ^Vec2, size : Vec2 )
|
||||
{
|
||||
pos := position^
|
||||
|
@ -274,8 +274,7 @@ parser_get_glyph_shape :: #force_inline proc ( font : Parser_Font_Info, glyph_in
|
||||
|
||||
parser_is_glyph_empty :: #force_inline proc "contextless" ( font : Parser_Font_Info, glyph_index : Glyph ) -> b32
|
||||
{
|
||||
|
||||
// switch font.kind
|
||||
// switch font.kind
|
||||
// {
|
||||
// case .Freetype:
|
||||
// error := freetype.load_glyph( font.freetype_info, cast(u32) glyph_index, { .No_Bitmap, .No_Hinting, .No_Scale } )
|
||||
|
@ -23,7 +23,7 @@ Shaped_Text_Cache :: struct {
|
||||
next_cache_id : i32,
|
||||
}
|
||||
|
||||
Shaper_Shape_Text_Uncached_Proc :: #type proc( ctx : ^Context, font : Font_ID, text_utf8 : string, entry : Entry, output : ^Shaped_Text )
|
||||
Shaper_Shape_Text_Uncached_Proc :: #type proc( ctx : ^Shaper_Context, entry : Entry, font_px_Size, font_scale : f32, text_utf8 : string, output : ^Shaped_Text )
|
||||
|
||||
Shaper_Kind :: enum {
|
||||
Naive = 0,
|
||||
@ -56,7 +56,7 @@ shaper_shutdown :: proc( ctx : ^Shaper_Context )
|
||||
}
|
||||
}
|
||||
|
||||
shaper_load_font :: proc( ctx : ^Shaper_Context, label : string, data : []byte, user_data : rawptr = nil ) -> (info : Shaper_Info)
|
||||
shaper_load_font :: #force_inline proc( ctx : ^Shaper_Context, label : string, data : []byte, user_data : rawptr = nil ) -> (info : Shaper_Info)
|
||||
{
|
||||
using info
|
||||
blob = harfbuzz.blob_create( raw_data(data), cast(c.uint) len(data), harfbuzz.Memory_Mode.READONLY, user_data, nil )
|
||||
@ -65,7 +65,7 @@ shaper_load_font :: proc( ctx : ^Shaper_Context, label : string, data : []byte,
|
||||
return
|
||||
}
|
||||
|
||||
shaper_unload_font :: proc( ctx : ^Shaper_Info )
|
||||
shaper_unload_font :: #force_inline proc( ctx : ^Shaper_Info )
|
||||
{
|
||||
using ctx
|
||||
if blob != nil do harfbuzz.font_destroy( font )
|
||||
@ -73,28 +73,37 @@ shaper_unload_font :: proc( ctx : ^Shaper_Info )
|
||||
if blob != nil do harfbuzz.blob_destroy( blob )
|
||||
}
|
||||
|
||||
shaper_shape_from_text :: #force_inline proc( ctx : ^Shaper_Context, parser_info : Parser_Font_Info, info : Shaper_Info, output :^Shaped_Text, text_utf8 : string,
|
||||
ascent, descent, line_gap : i32, size, size_scale : f32 )
|
||||
shaper_shape_harfbuzz :: #force_inline proc( ctx : ^Shaper_Context, text_utf8 : string, entry : Entry, font_px_Size, font_scale : f32, output :^Shaped_Text )
|
||||
{
|
||||
profile(#procedure)
|
||||
current_script := harfbuzz.Script.UNKNOWN
|
||||
hb_ucfunc := harfbuzz.unicode_funcs_get_default()
|
||||
harfbuzz.buffer_clear_contents( ctx.hb_buffer )
|
||||
assert( info.font != nil )
|
||||
|
||||
ascent := f32(ascent)
|
||||
descent := f32(descent)
|
||||
line_gap := f32(line_gap)
|
||||
|
||||
ascent := entry.ascent
|
||||
descent := entry.descent
|
||||
line_gap :=entry.line_gap
|
||||
|
||||
max_line_width := f32(0)
|
||||
line_count := 1
|
||||
line_height := ((ascent - descent + line_gap) * size_scale)
|
||||
line_height := ((ascent - descent + line_gap) * font_scale)
|
||||
|
||||
position : Vec2
|
||||
shape_run :: proc( parser_info : Parser_Font_Info, buffer : harfbuzz.Buffer, script : harfbuzz.Script, font : harfbuzz.Font, output : ^Shaped_Text,
|
||||
position : ^Vec2, max_line_width: ^f32, line_count: ^int,
|
||||
ascent, descent, line_gap, size, size_scale: f32,
|
||||
snap_shape_pos : b32, adv_snap_small_font_threshold : f32 )
|
||||
shape_run :: proc( output : ^Shaped_Text,
|
||||
entry : Entry,
|
||||
buffer : harfbuzz.Buffer,
|
||||
script : harfbuzz.Script,
|
||||
|
||||
position : ^Vec2,
|
||||
max_line_width : ^f32,
|
||||
line_count : ^int,
|
||||
|
||||
font_px_size : f32,
|
||||
font_scale : f32,
|
||||
|
||||
snap_shape_pos : b32,
|
||||
adv_snap_small_font_threshold : f32
|
||||
)
|
||||
{
|
||||
profile(#procedure)
|
||||
// Set script and direction. We use the system's default langauge.
|
||||
@ -105,14 +114,14 @@ shaper_shape_from_text :: #force_inline proc( ctx : ^Shaper_Context, parser_info
|
||||
|
||||
// Perform the actual shaping of this run using HarfBuzz.
|
||||
harfbuzz.buffer_set_content_type( buffer, harfbuzz.Buffer_Content_Type.UNICODE )
|
||||
harfbuzz.shape( font, buffer, nil, 0 )
|
||||
harfbuzz.shape( entry.shaper_info.font, buffer, nil, 0 )
|
||||
|
||||
// Loop over glyphs and append to output buffer.
|
||||
glyph_count : u32
|
||||
glyph_infos := harfbuzz.buffer_get_glyph_infos( buffer, & glyph_count )
|
||||
glyph_positions := harfbuzz.buffer_get_glyph_positions( buffer, & glyph_count )
|
||||
|
||||
line_height := (ascent - descent + line_gap) * size_scale
|
||||
line_height := (entry.ascent - entry.descent + entry.line_gap) * font_scale
|
||||
|
||||
for index : i32; index < i32(glyph_count); index += 1
|
||||
{
|
||||
@ -129,13 +138,13 @@ shaper_shape_from_text :: #force_inline proc( ctx : ^Shaper_Context, parser_info
|
||||
(line_count^) += 1
|
||||
continue
|
||||
}
|
||||
if abs( size ) <= adv_snap_small_font_threshold
|
||||
if abs( font_px_size ) <= adv_snap_small_font_threshold
|
||||
{
|
||||
(position^) = ceil( position^ )
|
||||
}
|
||||
|
||||
glyph_pos := position^
|
||||
offset := Vec2 { f32(hb_gposition.x_offset), f32(hb_gposition.y_offset) } * size_scale
|
||||
offset := Vec2 { f32(hb_gposition.x_offset), f32(hb_gposition.y_offset) } * font_scale
|
||||
glyph_pos += offset
|
||||
|
||||
if snap_shape_pos {
|
||||
@ -143,13 +152,13 @@ shaper_shape_from_text :: #force_inline proc( ctx : ^Shaper_Context, parser_info
|
||||
}
|
||||
|
||||
advance := Vec2 {
|
||||
f32(hb_gposition.x_advance) * size_scale,
|
||||
f32(hb_gposition.y_advance) * size_scale
|
||||
f32(hb_gposition.x_advance) * font_scale,
|
||||
f32(hb_gposition.y_advance) * font_scale
|
||||
}
|
||||
(position^) += advance
|
||||
(max_line_width^) = max(max_line_width^, position.x)
|
||||
|
||||
is_empty := parser_is_glyph_empty(parser_info, glyph_id)
|
||||
is_empty := parser_is_glyph_empty(entry.parser_info, glyph_id)
|
||||
if ! is_empty {
|
||||
append( & output.glyphs, glyph_id )
|
||||
append( & output.positions, glyph_pos)
|
||||
@ -181,61 +190,62 @@ shaper_shape_from_text :: #force_inline proc( ctx : ^Shaper_Context, parser_info
|
||||
}
|
||||
|
||||
// End current run since we've encountered a script change.
|
||||
shape_run( parser_info,
|
||||
ctx.hb_buffer, current_script, info.font, output,
|
||||
& position, & max_line_width, & line_count,
|
||||
ascent, descent, line_gap, size, size_scale,
|
||||
ctx.snap_glyph_position, ctx.adv_snap_small_font_threshold
|
||||
)
|
||||
shape_run( output,
|
||||
entry,
|
||||
ctx.hb_buffer,
|
||||
current_script,
|
||||
& position,
|
||||
& max_line_width,
|
||||
& line_count,
|
||||
font_px_Size,
|
||||
font_scale,
|
||||
ctx.snap_glyph_position,
|
||||
ctx.adv_snap_small_font_threshold
|
||||
)
|
||||
harfbuzz.buffer_add( ctx.hb_buffer, hb_codepoint, codepoint == '\n' ? 1 : 0 )
|
||||
current_script = script
|
||||
}
|
||||
|
||||
// End the last run if needed
|
||||
shape_run( parser_info,
|
||||
ctx.hb_buffer, current_script, info.font, output,
|
||||
& position, & max_line_width, & line_count,
|
||||
ascent, descent, line_gap, size, size_scale,
|
||||
ctx.snap_glyph_position, ctx.adv_snap_small_font_threshold
|
||||
)
|
||||
shape_run( output,
|
||||
entry,
|
||||
ctx.hb_buffer,
|
||||
current_script,
|
||||
& position,
|
||||
& max_line_width,
|
||||
& line_count,
|
||||
font_px_Size,
|
||||
font_scale,
|
||||
ctx.snap_glyph_position,
|
||||
ctx.adv_snap_small_font_threshold
|
||||
)
|
||||
|
||||
// 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 : ^Context, font : Font_ID, text_utf8 : string, entry : Entry, output : ^Shaped_Text )
|
||||
|
||||
shaper_shape_text_uncached_advanced :: #force_inline proc( ctx : ^Shaper_Context, entry : Entry, font_px_size : f32, font_scale : f32, text_utf8 : string, output : ^Shaped_Text )
|
||||
{
|
||||
profile(#procedure)
|
||||
assert( ctx != nil )
|
||||
assert( font >= 0 && int(font) < len(ctx.entries) )
|
||||
|
||||
clear( & output.glyphs )
|
||||
clear( & output.positions )
|
||||
|
||||
ascent_i32, descent_i32, line_gap_i32 := parser_get_font_vertical_metrics( entry.parser_info )
|
||||
ascent := f32(ascent_i32)
|
||||
descent := f32(descent_i32)
|
||||
line_gap := f32(line_gap_i32)
|
||||
line_height := (ascent - descent + line_gap) * entry.size_scale
|
||||
|
||||
shaper_shape_from_text( & ctx.shaper_ctx, entry.parser_info, entry.shaper_info, output, text_utf8, ascent_i32, descent_i32, line_gap_i32, entry.size, entry.size_scale )
|
||||
shaper_shape_harfbuzz( ctx, text_utf8, entry, font_px_size, font_scale, output )
|
||||
}
|
||||
|
||||
shaper_shape_from_text_latin :: #force_inline proc( ctx : ^Context, font : Font_ID, text_utf8 : string, entry : Entry, output : ^Shaped_Text )
|
||||
shaper_shape_text_latin :: #force_inline proc( ctx : ^Shaper_Context, entry : Entry, font_px_Size, font_scale : f32, text_utf8 : string, output : ^Shaped_Text )
|
||||
{
|
||||
profile(#procedure)
|
||||
assert( ctx != nil )
|
||||
assert( font >= 0 && int(font) < len(ctx.entries) )
|
||||
|
||||
clear( & output.glyphs )
|
||||
clear( & output.positions )
|
||||
|
||||
ascent_i32, descent_i32, line_gap_i32 := parser_get_font_vertical_metrics( entry.parser_info )
|
||||
ascent := f32(ascent_i32)
|
||||
descent := f32(descent_i32)
|
||||
line_gap := f32(line_gap_i32)
|
||||
line_height := (ascent - descent + line_gap) * entry.size_scale
|
||||
line_height := (entry.ascent - entry.descent + entry.line_gap) * font_scale
|
||||
|
||||
line_count : int = 1
|
||||
max_line_width : f32 = 0
|
||||
@ -246,7 +256,7 @@ shaper_shape_from_text_latin :: #force_inline proc( ctx : ^Context, font : Font_
|
||||
{
|
||||
if prev_codepoint > 0 {
|
||||
kern := parser_get_codepoint_kern_advance( entry.parser_info, prev_codepoint, codepoint )
|
||||
position.x += f32(kern) * entry.size_scale
|
||||
position.x += f32(kern) * font_scale
|
||||
}
|
||||
if codepoint == '\n'
|
||||
{
|
||||
@ -258,12 +268,12 @@ shaper_shape_from_text_latin :: #force_inline proc( ctx : ^Context, font : Font_
|
||||
prev_codepoint = rune(0)
|
||||
continue
|
||||
}
|
||||
if abs( entry.size ) <= ctx.shaper_ctx.adv_snap_small_font_threshold {
|
||||
if abs( font_px_Size ) <= ctx.adv_snap_small_font_threshold {
|
||||
position.x = ceil(position.x)
|
||||
}
|
||||
|
||||
glyph_index := parser_find_glyph_index( entry.parser_info, codepoint )
|
||||
is_glyph_empty := parser_is_glyph_empty( entry.parser_info, glyph_index )
|
||||
glyph_index := parser_find_glyph_index( entry.parser_info, codepoint )
|
||||
is_glyph_empty := parser_is_glyph_empty( entry.parser_info, glyph_index )
|
||||
if ! is_glyph_empty
|
||||
{
|
||||
append( & output.glyphs, glyph_index)
|
||||
@ -274,7 +284,7 @@ shaper_shape_from_text_latin :: #force_inline proc( ctx : ^Context, font : Font_
|
||||
}
|
||||
|
||||
advance, _ := parser_get_codepoint_horizontal_metrics( entry.parser_info, codepoint )
|
||||
position.x += f32(advance) * entry.size_scale
|
||||
position.x += f32(advance) * font_scale
|
||||
prev_codepoint = codepoint
|
||||
}
|
||||
|
||||
@ -285,7 +295,15 @@ shaper_shape_from_text_latin :: #force_inline proc( ctx : ^Context, font : Font_
|
||||
output.size.y = f32(line_count) * line_height
|
||||
}
|
||||
|
||||
shaper_shape_text_cached :: #force_inline proc( ctx : ^Context, font : Font_ID, text_utf8 : string, entry : Entry, shape_text_uncached : $Shaper_Shape_Text_Uncached_Proc ) -> (shaped_text : Shaped_Text)
|
||||
shaper_shape_text_cached :: #force_inline proc( text_utf8 : string,
|
||||
ctx : ^Shaper_Context,
|
||||
shape_cache : ^Shaped_Text_Cache,
|
||||
font : Font_ID,
|
||||
entry : Entry,
|
||||
font_px_size : f32,
|
||||
font_scale : f32,
|
||||
shape_text_uncached : $Shaper_Shape_Text_Uncached_Proc
|
||||
) -> (shaped_text : Shaped_Text)
|
||||
{
|
||||
profile(#procedure)
|
||||
font := font
|
||||
@ -296,8 +314,7 @@ shaper_shape_text_cached :: #force_inline proc( ctx : ^Context, font : Font_ID,
|
||||
shape_lru_code( & lru_code, font_bytes )
|
||||
shape_lru_code( & lru_code, text_bytes )
|
||||
|
||||
shape_cache := & ctx.shape_cache
|
||||
state := & ctx.shape_cache.state
|
||||
state := & shape_cache.state
|
||||
|
||||
shape_cache_idx := lru_get( state, lru_code )
|
||||
if shape_cache_idx == -1
|
||||
@ -319,7 +336,7 @@ shaper_shape_text_cached :: #force_inline proc( ctx : ^Context, font : Font_ID,
|
||||
}
|
||||
|
||||
storage_entry := & shape_cache.storage[ shape_cache_idx ]
|
||||
shape_text_uncached( ctx, font, text_utf8, entry, storage_entry )
|
||||
shape_text_uncached( ctx, entry, font_px_size, font_scale, text_utf8, storage_entry )
|
||||
|
||||
shaped_text = storage_entry ^
|
||||
return
|
||||
|
@ -8,9 +8,9 @@ package vefontcache
|
||||
import "base:runtime"
|
||||
|
||||
// White: Cached Hit, Red: Cache Miss, Yellow: Oversized
|
||||
ENABLE_DRAW_TYPE_VIS :: false
|
||||
ENABLE_DRAW_TYPE_VIS :: true
|
||||
// See: mappings.odin for profiling hookup
|
||||
DISABLE_PROFILING :: true
|
||||
DISABLE_PROFILING :: false
|
||||
|
||||
Font_ID :: distinct i32
|
||||
Glyph :: distinct i32
|
||||
@ -21,53 +21,52 @@ Entry :: struct {
|
||||
id : Font_ID,
|
||||
used : b32,
|
||||
curve_quality : f32,
|
||||
size : f32,
|
||||
size_scale : f32,
|
||||
|
||||
ascent : f32,
|
||||
descent : f32,
|
||||
line_gap : f32,
|
||||
}
|
||||
|
||||
Entry_Default :: Entry {
|
||||
id = 0,
|
||||
used = false,
|
||||
curve_quality = 2,
|
||||
|
||||
// TODO(Ed): Remove size information. Its not conceptually needed.
|
||||
// The size_scale can be computed only the fly when needed for a font.
|
||||
size = 24.0,
|
||||
size_scale = 1.0,
|
||||
curve_quality = 3,
|
||||
}
|
||||
|
||||
Context :: struct {
|
||||
backing : Allocator,
|
||||
|
||||
parser_ctx : Parser_Context,
|
||||
shaper_ctx : Shaper_Context,
|
||||
parser_ctx : Parser_Context, // Glyph parser state
|
||||
shaper_ctx : Shaper_Context, // Text shaper state
|
||||
|
||||
// The managed font instances
|
||||
entries : [dynamic]Entry,
|
||||
|
||||
temp_path : [dynamic]Vertex,
|
||||
temp_codepoint_seen : map[u32]b8,
|
||||
temp_codepoint_seen_num : i32,
|
||||
|
||||
snap_width : f32,
|
||||
snap_height : f32,
|
||||
|
||||
alpha_scalar : f32, // Will apply a multiplier to the colour's alpha which provides some sharpening of the edges.
|
||||
colour : Colour,
|
||||
cursor_pos : Vec2,
|
||||
glyph_buffer : Glyph_Draw_Buffer,
|
||||
atlas : Atlas,
|
||||
shape_cache : Shaped_Text_Cache,
|
||||
draw_list : Draw_List,
|
||||
|
||||
// Tracks the offsets for the current layer in a draw_list
|
||||
draw_layer : struct {
|
||||
vertices_offset : int,
|
||||
indices_offset : int,
|
||||
calls_offset : int,
|
||||
},
|
||||
|
||||
draw_list : Draw_List,
|
||||
atlas : Atlas,
|
||||
glyph_buffer : Glyph_Draw_Buffer,
|
||||
shape_cache : Shaped_Text_Cache,
|
||||
// Helps with hinting
|
||||
snap_width : f32,
|
||||
snap_height : f32,
|
||||
|
||||
colour : Colour, // Color used in draw interface
|
||||
cursor_pos : Vec2,
|
||||
alpha_scalar : f32, // Will apply a multiplier to the colour's alpha which provides some sharpening of the edges.
|
||||
// Used by draw interface to super-scale the text by
|
||||
// upscaling px_size with px_scalar and then down-scaling
|
||||
// the draw_list result by the same amount.
|
||||
px_scalar : f32,
|
||||
|
||||
default_curve_quality : i32,
|
||||
use_advanced_shaper : b32,
|
||||
|
||||
debug_print : b32,
|
||||
debug_print_verbose : b32,
|
||||
@ -115,25 +114,27 @@ Init_Atlas_Params_Default :: Init_Atlas_Params {
|
||||
}
|
||||
|
||||
Init_Glyph_Draw_Params :: struct {
|
||||
over_sample : Vec2,
|
||||
buffer_batch : u32,
|
||||
draw_padding : u32,
|
||||
over_sample : Vec2,
|
||||
draw_padding : u32,
|
||||
shape_gen_scratch_reserve : u32,
|
||||
buffer_batch : u32,
|
||||
buffer_batch_glyph_limit : u32, // How many glyphs can at maximimum be proccessed at once by batch_generate_glyphs_draw_list
|
||||
}
|
||||
|
||||
Init_Glyph_Draw_Params_Default :: Init_Glyph_Draw_Params {
|
||||
over_sample = Vec2 { 4, 4 },
|
||||
buffer_batch = 4,
|
||||
draw_padding = Init_Atlas_Params_Default.glyph_padding,
|
||||
over_sample = Vec2 { 4, 4 },
|
||||
draw_padding = Init_Atlas_Params_Default.glyph_padding,
|
||||
shape_gen_scratch_reserve = 10 * 1024,
|
||||
buffer_batch = 4,
|
||||
buffer_batch_glyph_limit = 512,
|
||||
}
|
||||
|
||||
Init_Shaper_Params :: struct {
|
||||
use_advanced_text_shaper : b32,
|
||||
snap_glyph_position : b32,
|
||||
adv_snap_small_font_threshold : u32,
|
||||
}
|
||||
|
||||
Init_Shaper_Params_Default :: Init_Shaper_Params {
|
||||
use_advanced_text_shaper = true,
|
||||
snap_glyph_position = true,
|
||||
adv_snap_small_font_threshold = 0,
|
||||
}
|
||||
@ -158,10 +159,10 @@ startup :: proc( ctx : ^Context, parser_kind : Parser_Kind = .STB_TrueType,
|
||||
shape_cache_params := Init_Shape_Cache_Params_Default,
|
||||
shaper_params := Init_Shaper_Params_Default,
|
||||
alpha_sharpen := 0.2,
|
||||
// Curve quality to use for a font when unspecified,
|
||||
// Affects step size for bezier curve passes in generate_glyph_pass_draw_list
|
||||
default_curve_quality : u32 = 3,
|
||||
entires_reserve : u32 = 256,
|
||||
temp_path_reserve : u32 = 10 * 1024,
|
||||
temp_codepoint_seen_reserve : u32 = 10 * 1024,
|
||||
)
|
||||
{
|
||||
assert( ctx != nil, "Must provide a valid context" )
|
||||
@ -172,7 +173,6 @@ startup :: proc( ctx : ^Context, parser_kind : Parser_Kind = .STB_TrueType,
|
||||
|
||||
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
|
||||
|
||||
@ -185,12 +185,6 @@ startup :: proc( ctx : ^Context, parser_kind : Parser_Kind = .STB_TrueType,
|
||||
entries, error = make( [dynamic]Entry, len = 0, cap = entires_reserve )
|
||||
assert(error == .None, "VEFontCache.init : Failed to allocate entries")
|
||||
|
||||
temp_path, error = make( [dynamic]Vertex, len = 0, cap = temp_path_reserve )
|
||||
assert(error == .None, "VEFontCache.init : Failed to allocate temp_path")
|
||||
|
||||
temp_codepoint_seen, error = make( map[u32]b8, uint(temp_codepoint_seen_reserve) )
|
||||
assert(error == .None, "VEFontCache.init : Failed to allocate temp_path")
|
||||
|
||||
draw_list.vertices, error = make( [dynamic]Vertex, len = 0, cap = 8 * Kilobyte )
|
||||
assert(error == .None, "VEFontCache.init : Failed to allocate draw_list.vertices")
|
||||
|
||||
@ -270,7 +264,7 @@ startup :: proc( ctx : ^Context, parser_kind : Parser_Kind = .STB_TrueType,
|
||||
assert( error == .None, "VEFontCache.init : Failed to allocate vertices array for draw_list" )
|
||||
}
|
||||
|
||||
// Note(From original author): We can actually go over VE_FONTCACHE_GLYPHDRAW_BUFFER_BATCH batches due to smart packing!
|
||||
Glyph_Buffer_Setup:
|
||||
{
|
||||
using glyph_buffer
|
||||
over_sample = glyph_draw_params.over_sample
|
||||
@ -297,6 +291,14 @@ startup :: proc( ctx : ^Context, parser_kind : Parser_Kind = .STB_TrueType,
|
||||
clear_draw_list.vertices, error = make( [dynamic]Vertex, len = 0, cap = glyph_draw_params.buffer_batch * 2 * 4 )
|
||||
assert( error == .None, "VEFontCache.init : Failed to allocate vertices array for clear_draw_list" )
|
||||
|
||||
shape_gen_scratch, error = make( [dynamic]Vertex, len = 0, cap = glyph_draw_params.buffer_batch_glyph_limit )
|
||||
assert(error == .None, "VEFontCache.init : Failed to allocate shape_gen_scratch")
|
||||
|
||||
batch_cache.cap = i32(glyph_draw_params.buffer_batch_glyph_limit)
|
||||
batch_cache.num = 0
|
||||
batch_cache.table, error = make( map[u32]b8, uint(glyph_draw_params.shape_gen_scratch_reserve) )
|
||||
assert(error == .None, "VEFontCache.init : Failed to allocate batch_cache")
|
||||
|
||||
glyph_pack,error = make_soa( #soa[dynamic]Glyph_Pack_Entry, length = 0, capacity = 1 * Kilobyte, allocator = context.temp_allocator )
|
||||
oversized, error = make( [dynamic]i32, len = 0, cap = 1 * Kilobyte, allocator = context.temp_allocator )
|
||||
to_cache, error = make( [dynamic]i32, len = 0, cap = 1 * Kilobyte, allocator = context.temp_allocator )
|
||||
@ -315,8 +317,6 @@ hot_reload :: proc( ctx : ^Context, allocator : Allocator )
|
||||
using ctx
|
||||
|
||||
reload_array( & entries, allocator )
|
||||
reload_array( & temp_path, allocator )
|
||||
reload_map( & ctx.temp_codepoint_seen, allocator )
|
||||
|
||||
reload_array( & draw_list.vertices, allocator)
|
||||
reload_array( & draw_list.indices, allocator )
|
||||
@ -349,10 +349,12 @@ hot_reload :: proc( ctx : ^Context, allocator : Allocator )
|
||||
reload_array( & glyph_buffer.to_cache, allocator )
|
||||
reload_array( & glyph_buffer.cached, allocator )
|
||||
|
||||
reload_array( & glyph_buffer.shape_gen_scratch, allocator )
|
||||
reload_map( & glyph_buffer.batch_cache.table, allocator )
|
||||
|
||||
reload_array( & shape_cache.storage, allocator )
|
||||
}
|
||||
|
||||
// ve_foncache_shutdown
|
||||
shutdown :: proc( ctx : ^Context )
|
||||
{
|
||||
assert( ctx != nil )
|
||||
@ -364,8 +366,6 @@ shutdown :: proc( ctx : ^Context )
|
||||
}
|
||||
|
||||
delete( entries )
|
||||
delete( temp_path )
|
||||
delete( temp_codepoint_seen )
|
||||
|
||||
delete( draw_list.vertices )
|
||||
delete( draw_list.indices )
|
||||
@ -398,11 +398,13 @@ shutdown :: proc( ctx : ^Context )
|
||||
delete( glyph_buffer.to_cache)
|
||||
delete( glyph_buffer.cached)
|
||||
|
||||
delete( glyph_buffer.shape_gen_scratch )
|
||||
delete( glyph_buffer.batch_cache.table )
|
||||
|
||||
shaper_shutdown( & shaper_ctx )
|
||||
parser_shutdown( & parser_ctx )
|
||||
}
|
||||
|
||||
// ve_fontcache_load
|
||||
load_font :: proc( ctx : ^Context, label : string, data : []byte, size_px : f32, glyph_curve_quality : u32 = 0 ) -> (font_id : Font_ID)
|
||||
{
|
||||
profile(#procedure)
|
||||
@ -426,22 +428,23 @@ load_font :: proc( ctx : ^Context, label : string, data : []byte, size_px : f32,
|
||||
|
||||
entry := & entries[ id ]
|
||||
{
|
||||
using entry
|
||||
used = true
|
||||
entry.used = true
|
||||
|
||||
profile_begin("calling loaders")
|
||||
parser_info = parser_load_font( & parser_ctx, label, data )
|
||||
shaper_info = shaper_load_font( & shaper_ctx, label, data )
|
||||
entry.parser_info = parser_load_font( & parser_ctx, label, data )
|
||||
entry.shaper_info = shaper_load_font( & shaper_ctx, label, data )
|
||||
profile_end()
|
||||
|
||||
size = size_px
|
||||
size_scale = parser_scale( parser_info, size )
|
||||
ascent, descent, line_gap := parser_get_font_vertical_metrics(entry.parser_info)
|
||||
entry.ascent = f32(ascent)
|
||||
entry.descent = f32(descent)
|
||||
entry.line_gap = f32(line_gap)
|
||||
|
||||
if glyph_curve_quality == 0 {
|
||||
curve_quality = f32(ctx.default_curve_quality)
|
||||
entry.curve_quality = f32(ctx.default_curve_quality)
|
||||
}
|
||||
else {
|
||||
curve_quality = f32(glyph_curve_quality)
|
||||
entry.curve_quality = f32(glyph_curve_quality)
|
||||
}
|
||||
}
|
||||
entry.id = Font_ID(id)
|
||||
@ -451,7 +454,6 @@ load_font :: proc( ctx : ^Context, label : string, data : []byte, size_px : f32,
|
||||
return
|
||||
}
|
||||
|
||||
// ve_fontcache_unload
|
||||
unload_font :: proc( ctx : ^Context, font : Font_ID )
|
||||
{
|
||||
assert( ctx != nil )
|
||||
@ -470,17 +472,17 @@ unload_font :: proc( ctx : ^Context, font : Font_ID )
|
||||
|
||||
//#region("drawing")
|
||||
|
||||
// ve_fontcache_configure_snap
|
||||
configure_snap :: #force_inline proc( ctx : ^Context, snap_width, snap_height : u32 ) {
|
||||
assert( ctx != nil )
|
||||
ctx.snap_width = f32(snap_width)
|
||||
ctx.snap_height = f32(snap_height)
|
||||
}
|
||||
|
||||
get_cursor_pos :: #force_inline proc( ctx : ^Context ) -> Vec2 { assert(ctx != nil); return ctx.cursor_pos }
|
||||
set_colour :: #force_inline proc( ctx : ^Context, colour : Colour ) { assert(ctx != nil); ctx.colour = colour }
|
||||
get_cursor_pos :: #force_inline proc( ctx : ^Context ) -> Vec2 { assert(ctx != nil); return ctx.cursor_pos }
|
||||
set_alpha_scalar :: #force_inline proc( ctx : ^Context, scalar : f32 ) { assert(ctx != nil); ctx.alpha_scalar = scalar }
|
||||
set_colour :: #force_inline proc( ctx : ^Context, colour : Colour ) { assert(ctx != nil); ctx.colour = colour }
|
||||
|
||||
draw_text :: #force_inline proc( ctx : ^Context, font : Font_ID, text_utf8 : string, position, scale : Vec2 ) -> b32
|
||||
draw_text :: #force_inline proc( ctx : ^Context, font : Font_ID, px_size : f32, position, scale : Vec2, text_utf8 : string ) -> b32
|
||||
{
|
||||
profile(#procedure)
|
||||
assert( ctx != nil )
|
||||
@ -493,14 +495,23 @@ draw_text :: #force_inline proc( ctx : ^Context, font : Font_ID, text_utf8 : str
|
||||
position.x = ceil(position.x * ctx.snap_width ) / ctx.snap_width
|
||||
position.y = ceil(position.y * ctx.snap_height) / ctx.snap_height
|
||||
|
||||
entry := ctx.entries[ font ]
|
||||
colour := ctx.colour
|
||||
colour.a = 1.0 + ctx.alpha_scalar
|
||||
|
||||
// TODO(Ed): Test this.
|
||||
// px_size_scalar :: 2
|
||||
// px_size := px_size * px_size_scalar
|
||||
// scale := scale / px_size_scalar
|
||||
|
||||
entry := ctx.entries[ font ]
|
||||
font_scale := parser_scale( entry.parser_info, px_size )
|
||||
shape := shaper_shape_text_cached( text_utf8, & ctx.shaper_ctx, & ctx.shape_cache, font, entry, px_size, font_scale, shaper_shape_text_uncached_advanced )
|
||||
ctx.cursor_pos = generate_shape_draw_list( & ctx.draw_list, shape, & ctx.atlas, & ctx.glyph_buffer, colour, entry, font_scale, position, scale, ctx.snap_width, ctx.snap_height )
|
||||
|
||||
shape := shaper_shape_text_cached( ctx, font, text_utf8, entry, shaper_shape_text_uncached_advanced )
|
||||
ctx.cursor_pos = generate_shape_draw_list( ctx, entry, shape, position, scale, ctx.snap_width, ctx.snap_height )
|
||||
return true
|
||||
}
|
||||
|
||||
draw_text_no_snap :: #force_inline proc( ctx : ^Context, font : Font_ID, text_utf8 : string, position, scale : Vec2 ) -> b32
|
||||
draw_text_no_snap :: #force_inline proc( ctx : ^Context, font : Font_ID, px_size : f32, position, scale : Vec2, text_utf8 : string ) -> b32
|
||||
{
|
||||
profile(#procedure)
|
||||
assert( ctx != nil )
|
||||
@ -509,14 +520,18 @@ draw_text_no_snap :: #force_inline proc( ctx : ^Context, font : Font_ID, text_ut
|
||||
|
||||
ctx.cursor_pos = {}
|
||||
|
||||
entry := ctx.entries[ font ]
|
||||
shape := shaper_shape_text_cached( ctx, font, text_utf8, entry, shaper_shape_text_uncached_advanced )
|
||||
ctx.cursor_pos = generate_shape_draw_list( ctx, entry, shape, position, scale, ctx.snap_width, ctx.snap_height )
|
||||
colour := ctx.colour
|
||||
colour.a = 1.0 + ctx.alpha_scalar
|
||||
|
||||
entry := ctx.entries[ font ]
|
||||
font_scale := parser_scale( entry.parser_info, px_size )
|
||||
shape := shaper_shape_text_cached( text_utf8, & ctx.shaper_ctx, & ctx.shape_cache, font, entry, px_size, font_scale, shaper_shape_text_latin )
|
||||
ctx.cursor_pos = generate_shape_draw_list( & ctx.draw_list, shape, & ctx.atlas, & ctx.glyph_buffer, colour, entry, font_scale, position, scale, ctx.snap_width, ctx.snap_height )
|
||||
return true
|
||||
}
|
||||
|
||||
// For high performance: Resolve the shape and track it to reduce iteration overhead
|
||||
draw_text_shape :: #force_inline proc( ctx : ^Context, font : Font_ID, shape : Shaped_Text, position, scale : Vec2 ) -> b32
|
||||
// Resolve the shape and track it to reduce iteration overhead
|
||||
draw_text_shape :: #force_inline proc( ctx : ^Context, font : Font_ID, px_size : f32, position, scale : Vec2, shape : Shaped_Text ) -> b32
|
||||
{
|
||||
profile(#procedure)
|
||||
assert( ctx != nil )
|
||||
@ -525,24 +540,31 @@ draw_text_shape :: #force_inline proc( ctx : ^Context, font : Font_ID, shape : S
|
||||
position.x = ceil(position.x * ctx.snap_width ) / ctx.snap_width
|
||||
position.y = ceil(position.y * ctx.snap_height) / ctx.snap_height
|
||||
|
||||
entry := ctx.entries[ font ]
|
||||
ctx.cursor_pos = generate_shape_draw_list( ctx, entry, shape, position, scale, ctx.snap_width, ctx.snap_height )
|
||||
colour := ctx.colour
|
||||
colour.a = 1.0 + ctx.alpha_scalar
|
||||
|
||||
entry := ctx.entries[ font ]
|
||||
font_scale := parser_scale( entry.parser_info, px_size )
|
||||
ctx.cursor_pos = generate_shape_draw_list( & ctx.draw_list, shape, & ctx.atlas, & ctx.glyph_buffer, colour, entry, font_scale, position, scale, ctx.snap_width, ctx.snap_height )
|
||||
return true
|
||||
}
|
||||
|
||||
// For high performance: Resolve the shape and track it to reduce iteration overhead
|
||||
draw_text_shape_no_snap :: #force_inline proc( ctx : ^Context, font : Font_ID, shape : Shaped_Text, position, scale : Vec2 ) -> b32
|
||||
// Resolve the shape and track it to reduce iteration overhead
|
||||
draw_text_shape_no_snap :: #force_inline proc( ctx : ^Context, font : Font_ID, px_size : f32, position, scale : Vec2, shape : Shaped_Text ) -> b32
|
||||
{
|
||||
profile(#procedure)
|
||||
assert( ctx != nil )
|
||||
assert( font >= 0 && int(font) < len(ctx.entries) )
|
||||
|
||||
colour := ctx.colour
|
||||
colour.a = 1.0 + ctx.alpha_scalar
|
||||
|
||||
entry := ctx.entries[ font ]
|
||||
ctx.cursor_pos = generate_shape_draw_list( ctx, entry, shape, position, scale, ctx.snap_width, ctx.snap_height )
|
||||
font_scale := parser_scale( entry.parser_info, px_size )
|
||||
ctx.cursor_pos = generate_shape_draw_list( & ctx.draw_list, shape, & ctx.atlas, & ctx.glyph_buffer, colour, entry, font_scale, position, scale, ctx.snap_width, ctx.snap_height )
|
||||
return true
|
||||
}
|
||||
|
||||
// ve_fontcache_Draw_List
|
||||
get_draw_list :: #force_inline proc( ctx : ^Context, optimize_before_returning := true ) -> ^Draw_List {
|
||||
assert( ctx != nil )
|
||||
if optimize_before_returning do optimize_draw_list( & ctx.draw_list, 0 )
|
||||
@ -558,7 +580,6 @@ get_draw_list_layer :: #force_inline proc( ctx : ^Context, optimize_before_retur
|
||||
return
|
||||
}
|
||||
|
||||
// ve_fontcache_flush_Draw_List
|
||||
flush_draw_list :: #force_inline proc( ctx : ^Context ) {
|
||||
assert( ctx != nil )
|
||||
using ctx
|
||||
@ -580,14 +601,15 @@ flush_draw_list_layer :: #force_inline proc( ctx : ^Context ) {
|
||||
|
||||
//#region("metrics")
|
||||
|
||||
measure_text_size :: #force_inline proc( ctx : ^Context, font : Font_ID, text_utf8 : string ) -> (measured : Vec2)
|
||||
measure_text_size :: #force_inline proc( ctx : ^Context, font : Font_ID, px_size : f32, text_utf8 : string ) -> (measured : Vec2)
|
||||
{
|
||||
// profile(#procedure)
|
||||
assert( ctx != nil )
|
||||
assert( font >= 0 && int(font) < len(ctx.entries) )
|
||||
|
||||
entry := ctx.entries[font]
|
||||
shaped := shaper_shape_text_cached(ctx, font, text_utf8, entry, shaper_shape_text_uncached_advanced )
|
||||
entry := ctx.entries[font]
|
||||
font_scale := parser_scale( entry.parser_info, px_size )
|
||||
shaped := shaper_shape_text_cached( text_utf8, & ctx.shaper_ctx, & ctx.shape_cache, font, entry, px_size, font_scale, shaper_shape_text_uncached_advanced )
|
||||
return shaped.size
|
||||
}
|
||||
|
||||
@ -597,11 +619,11 @@ get_font_vertical_metrics :: #force_inline proc ( ctx : ^Context, font : Font_ID
|
||||
assert( font >= 0 && int(font) < len(ctx.entries) )
|
||||
|
||||
entry := & ctx.entries[ font ]
|
||||
ascent_i32, descent_i32, line_gap_i32 := parser_get_font_vertical_metrics( entry.parser_info )
|
||||
// ascent_i32, descent_i32, line_gap_i32 := parser_get_font_vertical_metrics( entry.parser_info )
|
||||
|
||||
ascent = (f32(ascent_i32) * entry.size_scale)
|
||||
descent = (f32(descent_i32) * entry.size_scale)
|
||||
line_gap = (f32(line_gap_i32) * entry.size_scale)
|
||||
ascent = entry.ascent
|
||||
descent = entry.descent
|
||||
line_gap = entry.line_gap
|
||||
return
|
||||
}
|
||||
|
||||
@ -609,20 +631,22 @@ 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, text_utf8 : string, allocator := context.allocator ) -> Shaped_Text
|
||||
shape_text_latin :: #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 ]
|
||||
return shaper_shape_text_cached( ctx, font, text_utf8, entry, shaper_shape_from_text_latin )
|
||||
font_scale := parser_scale( entry.parser_info, px_size )
|
||||
return shaper_shape_text_cached( text_utf8, & ctx.shaper_ctx, & ctx.shape_cache, font, entry, px_size, font_scale, shaper_shape_text_latin )
|
||||
}
|
||||
|
||||
shape_text_advanced :: #force_inline proc( ctx : ^Context, font : Font_ID, text_utf8 : string ) -> Shaped_Text
|
||||
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 ]
|
||||
return shaper_shape_text_cached( ctx, font, text_utf8, entry, shaper_shape_text_uncached_advanced )
|
||||
font_scale := parser_scale( entry.parser_info, px_size )
|
||||
return shaper_shape_text_cached( text_utf8, & ctx.shaper_ctx, & ctx.shape_cache, font, entry, px_size, font_scale, shaper_shape_text_uncached_advanced )
|
||||
}
|
||||
|
||||
// User handled shaped text. Will not be cached
|
||||
|
@ -1,5 +1,9 @@
|
||||
package grime
|
||||
|
||||
djb8_hash_32 :: #force_inline proc "contextless" ( hash : ^u32, bytes : []byte ) {
|
||||
for value in bytes do (hash^) = (( (hash^) << 8) + (hash^) ) + u32(value)
|
||||
}
|
||||
|
||||
djb8_hash :: #force_inline proc "contextless" ( hash : ^u64, bytes : []byte ) {
|
||||
for value in bytes do (hash^) = (( (hash^) << 8) + (hash^) ) + u64(value)
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ set_profiler_module_context :: #force_inline proc "contextless" ( ctx : ^SpallPr
|
||||
Module_Context = ctx
|
||||
}
|
||||
|
||||
DISABLE_PROFILING :: false
|
||||
DISABLE_PROFILING :: true
|
||||
|
||||
@(deferred_none = profile_end, disabled = DISABLE_PROFILING)
|
||||
profile :: #force_inline proc "contextless" ( name : string, loc := #caller_location ) {
|
||||
|
@ -284,7 +284,7 @@ app_color_theme :: #force_inline proc "contextless" () -> AppColorTheme { retur
|
||||
debug_data :: #force_inline proc "contextless" () -> DebugData { return get_state().debug }
|
||||
get_frametime :: #force_inline proc "contextless" () -> FrameTime { return get_state().frametime }
|
||||
get_default_font :: #force_inline proc "contextless" () -> FontID { return get_state().default_font }
|
||||
get_input_state :: #force_inline proc "contextless" () -> InputState { return (get_state().input ^) }
|
||||
get_input_state :: #force_inline proc "contextless" () -> InputState { return (get_state().input ^) }
|
||||
|
||||
get_ui_context_mut :: #force_inline proc "contextless" () -> ^UI_State { return get_state().ui_context }
|
||||
set_ui_context :: #force_inline proc "contextless" ( ui : ^UI_State ) { get_state().ui_context = ui }
|
||||
|
@ -649,12 +649,10 @@ render_ui_via_box_list :: proc( box_list : []UI_RenderBoxInfo, text_list : []UI_
|
||||
text_enqueued = true
|
||||
|
||||
if cam != nil {
|
||||
// draw_text_shape_pos_extent_zoomed( entry.shape, font, entry.font_size, entry.position, cam_offset, screen_size, screen_size_norm, cam.zoom, entry.color )
|
||||
draw_text_string_pos_extent_zoomed( entry.text, font, entry.font_size, entry.position, cam_offset, screen_size, screen_size_norm, cam.zoom, entry.color )
|
||||
}
|
||||
else {
|
||||
draw_text_string_pos_extent( entry.text, font, entry.font_size, entry.position, entry.color )
|
||||
// draw_text_string_pos_extent( entry.shape, font, entry.font_size, entry.position, entry.color )
|
||||
}
|
||||
}
|
||||
|
||||
@ -914,7 +912,7 @@ draw_text_string_pos_norm :: #force_inline proc( text : string, id : FontID, fon
|
||||
screen_size_norm := Vec2{1 / width, 1 / height}
|
||||
|
||||
ve.set_colour( & font_provider_ctx.ve_ctx, color_norm )
|
||||
ve.draw_text( & font_provider_ctx.ve_ctx, ve_id, text, pos, screen_size_norm * scale * (1 / config.font_size_screen_scalar) )
|
||||
ve.draw_text( & font_provider_ctx.ve_ctx, ve_id, f32(resolved_size), pos, screen_size_norm * scale * (1 / config.font_size_screen_scalar), text )
|
||||
return
|
||||
}
|
||||
|
||||
@ -942,7 +940,7 @@ draw_text_shape_pos_norm :: #force_inline proc( shape : ShapedText, id : FontID,
|
||||
screen_size_norm := Vec2{1 / width, 1 / height}
|
||||
|
||||
ve.set_colour( & font_provider_ctx.ve_ctx, color_norm )
|
||||
ve.draw_text_shape( & font_provider_ctx.ve_ctx, ve_id, shape, pos, screen_size_norm * scale * (1 / config.font_size_screen_scalar) )
|
||||
ve.draw_text_shape( & font_provider_ctx.ve_ctx, ve_id, f32(resolved_size), pos, screen_size_norm * scale * (1 / config.font_size_screen_scalar), shape )
|
||||
return
|
||||
}
|
||||
|
||||
@ -960,12 +958,13 @@ draw_text_shape_pos_extent :: #force_inline proc( shape : ShapedText, id : FontI
|
||||
draw_text_string_pos_extent_zoomed :: #force_inline proc( text : string, id : FontID, size : f32, pos, cam_offset, screen_size, screen_size_norm : Vec2, zoom : f32, color := Color_White )
|
||||
{
|
||||
profile(#procedure)
|
||||
state := get_state(); using state // TODO(Ed): Remove usage of direct access to entire mutable state.
|
||||
// state := get_state(); using state // TODO(Ed): Remove usage of direct access to entire mutable state.
|
||||
config := app_config()
|
||||
|
||||
zoom_adjust_size := size * zoom
|
||||
|
||||
// Over-sample font-size for any render under a camera
|
||||
over_sample : f32 = f32(state.config.font_size_canvas_scalar)
|
||||
over_sample : f32 = f32(config.font_size_canvas_scalar)
|
||||
zoom_adjust_size *= over_sample
|
||||
|
||||
pos_offset := (pos + cam_offset)
|
||||
@ -973,11 +972,11 @@ draw_text_string_pos_extent_zoomed :: #force_inline proc( text : string, id : Fo
|
||||
normalized_pos := render_pos * screen_size_norm
|
||||
|
||||
ve_id, resolved_size := font_provider_resolve_draw_id( id, zoom_adjust_size )
|
||||
f32_resolved_size := f32(resolved_size)
|
||||
|
||||
text_scale : Vec2 = screen_size_norm
|
||||
// if config.cam_zoom_mode == .Smooth
|
||||
{
|
||||
f32_resolved_size := f32(resolved_size)
|
||||
diff_scalar := 1 + (zoom_adjust_size - f32_resolved_size) / f32_resolved_size
|
||||
text_scale = diff_scalar * screen_size_norm
|
||||
text_scale.x = clamp( text_scale.x, 0, screen_size.x )
|
||||
@ -988,8 +987,8 @@ draw_text_string_pos_extent_zoomed :: #force_inline proc( text : string, id : Fo
|
||||
text_scale /= over_sample
|
||||
|
||||
color_norm := normalize_rgba8(color)
|
||||
ve.set_colour( & font_provider_ctx.ve_ctx, color_norm )
|
||||
ve.draw_text( & font_provider_ctx.ve_ctx, ve_id, text, normalized_pos, text_scale )
|
||||
ve.set_colour( & get_state().font_provider_ctx.ve_ctx, color_norm )
|
||||
ve.draw_text( & get_state().font_provider_ctx.ve_ctx, ve_id, f32(resolved_size), normalized_pos, text_scale, text )
|
||||
}
|
||||
|
||||
draw_text_shape_pos_extent_zoomed :: #force_inline proc( shape : ShapedText, id : FontID, size : f32, pos, cam_offset, screen_size, screen_size_norm : Vec2, zoom : f32, color := Color_White )
|
||||
@ -1008,11 +1007,11 @@ draw_text_shape_pos_extent_zoomed :: #force_inline proc( shape : ShapedText, id
|
||||
normalized_pos := render_pos * screen_size_norm
|
||||
|
||||
ve_id, resolved_size := font_provider_resolve_draw_id( id, zoom_adjust_size )
|
||||
f32_resolved_size := f32(resolved_size)
|
||||
|
||||
text_scale : Vec2 = screen_size_norm
|
||||
// if config.cam_zoom_mode == .Smooth
|
||||
{
|
||||
f32_resolved_size := f32(resolved_size)
|
||||
diff_scalar := 1 + (zoom_adjust_size - f32_resolved_size) / f32_resolved_size
|
||||
text_scale = diff_scalar * screen_size_norm
|
||||
text_scale.x = clamp( text_scale.x, 0, screen_size.x )
|
||||
@ -1024,7 +1023,7 @@ draw_text_shape_pos_extent_zoomed :: #force_inline proc( shape : ShapedText, id
|
||||
|
||||
color_norm := normalize_rgba8(color)
|
||||
ve.set_colour( & font_provider_ctx.ve_ctx, color_norm )
|
||||
ve.draw_text_shape( & font_provider_ctx.ve_ctx, ve_id, shape, normalized_pos, text_scale )
|
||||
ve.draw_text_shape( & font_provider_ctx.ve_ctx, ve_id, f32_resolved_size, normalized_pos, text_scale, shape )
|
||||
}
|
||||
|
||||
// TODO(Ed): Eventually the workspace will need a viewport for drawing text
|
||||
|
@ -135,7 +135,7 @@ font_provider_resolve_draw_id :: #force_inline proc( id : FontID, size := Font_U
|
||||
measure_text_size :: #force_inline proc( text : string, font : FontID, font_size := Font_Use_Default_Size, spacing : f32 ) -> Vec2
|
||||
{
|
||||
ve_id, size := font_provider_resolve_draw_id( font, font_size )
|
||||
measured := ve.measure_text_size( & get_state().font_provider_ctx.ve_ctx, ve_id, text )
|
||||
measured := ve.measure_text_size( & get_state().font_provider_ctx.ve_ctx, ve_id, f32(size), text )
|
||||
return measured
|
||||
}
|
||||
|
||||
@ -149,13 +149,13 @@ get_font_vertical_metrics :: #force_inline proc ( font : FontID, font_size := Fo
|
||||
shape_text_cached_latin :: #force_inline proc( text : string, font : FontID, font_size := Font_Use_Default_Size, scalar : f32 ) -> ShapedText
|
||||
{
|
||||
ve_id, size := font_provider_resolve_draw_id( font, font_size * scalar )
|
||||
shape := ve.shape_text_latin( & get_state().font_provider_ctx.ve_ctx, ve_id, text )
|
||||
shape := ve.shape_text_latin( & get_state().font_provider_ctx.ve_ctx, ve_id, f32(size), text )
|
||||
return shape
|
||||
}
|
||||
|
||||
shape_text_cached :: #force_inline proc( text : string, font : FontID, font_size := Font_Use_Default_Size, scalar : f32 ) -> ShapedText
|
||||
{
|
||||
ve_id, size := font_provider_resolve_draw_id( font, font_size * scalar )
|
||||
shape := ve.shape_text_advanced( & get_state().font_provider_ctx.ve_ctx, ve_id, text )
|
||||
shape := ve.shape_text_advanced( & get_state().font_provider_ctx.ve_ctx, ve_id, f32(size), text )
|
||||
return shape
|
||||
}
|
||||
|
@ -201,10 +201,10 @@ push-location $path_root
|
||||
# $build_args += $flag_micro_architecture_native
|
||||
$build_args += $flag_use_separate_modules
|
||||
$build_args += $flag_thread_count + $CoreCount_Physical
|
||||
$build_args += $flag_optimize_none
|
||||
# $build_args += $flag_optimize_none
|
||||
# $build_args += $flag_optimize_minimal
|
||||
# $build_args += $flag_optimize_speed
|
||||
# $build_args += $falg_optimize_aggressive
|
||||
$build_args += $falg_optimize_aggressive
|
||||
$build_args += $flag_debug
|
||||
$build_args += $flag_pdb_name + $pdb
|
||||
$build_args += $flag_subsystem + 'windows'
|
||||
|
Loading…
Reference in New Issue
Block a user