progress on VEFontCache port
This commit is contained in:
parent
991e7a81c0
commit
d469fd53e8
@ -15,6 +15,8 @@ Changes:
|
||||
*/
|
||||
package VEFontCache
|
||||
|
||||
Advance_Snap_Smallfont_Size :: 12
|
||||
|
||||
FontID :: distinct i64
|
||||
Glyph :: distinct i32
|
||||
|
||||
@ -53,10 +55,6 @@ Entry :: struct {
|
||||
shaper_info : ^ShaperInfo,
|
||||
id : FontID,
|
||||
used : b32,
|
||||
|
||||
// Note(Ed) : Not sure how I feel about the size specification here
|
||||
// I rather have different size glyphs for a font on demand (necessary for the canvas UI)
|
||||
// Might be mis-understaning how this cache works...
|
||||
size : f32,
|
||||
size_scale : f32,
|
||||
}
|
||||
@ -94,7 +92,8 @@ Context :: struct {
|
||||
curve_quality : u32,
|
||||
text_shape_adv : b32,
|
||||
|
||||
debug_print_verbose : b32
|
||||
debug_print : b32,
|
||||
debug_print_verbose : b32,
|
||||
}
|
||||
|
||||
get_cursor_pos :: proc( ctx : ^Context ) -> Vec2 { return ctx.cursor_pos }
|
||||
@ -114,6 +113,54 @@ font_key_from_label :: #force_inline proc( label : string ) -> u64 {
|
||||
return hash
|
||||
}
|
||||
|
||||
// ve_fontcache_configure_snap
|
||||
configure_snap :: proc( ctx : ^Context, snap_width, snap_height : u32 ) {
|
||||
assert( ctx != nil )
|
||||
ctx.snap_width = snap_width
|
||||
ctx.snap_height = snap_height
|
||||
}
|
||||
|
||||
// For a provided alpha value,
|
||||
// allows the function to calculate the position of a point along the curve at any given fraction of its total length
|
||||
// ve_fontcache_eval_bezier (quadratic)
|
||||
eval_point_on_bezier3 :: proc( p0, p1, p2 : Vec2, alpha : f32 ) -> Vec2
|
||||
{
|
||||
starting_point := p0 * (1 - alpha) * (1 - alpha)
|
||||
control_point := p1 * 2.0 * (1 - alpha)
|
||||
end_point := p2 * alpha * alpha
|
||||
|
||||
point := starting_point + control_point + end_point
|
||||
return point
|
||||
}
|
||||
|
||||
// For a provided alpha value,
|
||||
// allows the function to calculate the position of a point along the curve at any given fraction of its total length
|
||||
// ve_fontcache_eval_bezier (cubic)
|
||||
eval_point_on_bezier4 :: proc( p0, p1, p2, p3 : Vec2, alpha : f32 ) -> Vec2
|
||||
{
|
||||
start_point := p0 * (1 - alpha) * (1 - alpha) * (1 - alpha)
|
||||
control_a := p1 * 3 * (1 - alpha) * (1 - alpha) * alpha
|
||||
control_b := p2 * 3 * (1 - alpha) * alpha * alpha
|
||||
end_point := p3 * alpha * alpha * alpha
|
||||
|
||||
point := start_point + control_a + control_b + end_point
|
||||
return point
|
||||
}
|
||||
|
||||
screenspace_x_form :: proc( position, scale : ^Vec2, width, height : f32 ) {
|
||||
scale.x = (scale.x / width ) * 2.0
|
||||
scale.y = (scale.y / height) * 2.0
|
||||
position.x = position.x * (2.0 / width) - 1.0
|
||||
position.y = position.y * (2.0 / width) - 1.0
|
||||
}
|
||||
|
||||
textspace_x_form :: proc( position, scale : ^Vec2, width, height : f32 ) {
|
||||
position.x /= width
|
||||
position.y /= height
|
||||
scale.x /= width
|
||||
scale.y /= height
|
||||
}
|
||||
|
||||
InitAtlasRegionParams :: struct {
|
||||
width : u32,
|
||||
height : u32,
|
||||
@ -182,7 +229,6 @@ init :: proc( ctx : ^Context,
|
||||
glyph_draw_params := InitGlyphDrawParams_Default,
|
||||
shape_cache_params := InitShapeCacheParams_Default,
|
||||
curve_quality : u32 = 6,
|
||||
advance_snap_smallfont_size : u32 = 12,
|
||||
entires_reserve : u32 = Kilobyte,
|
||||
temp_path_reserve : u32 = Kilobyte,
|
||||
temp_codepoint_seen_reserve : u32 = 512,
|
||||
@ -358,40 +404,6 @@ unload_font :: proc( ctx : ^Context, font : FontID )
|
||||
shaper_unload_font( entry.shaper_info )
|
||||
}
|
||||
|
||||
// ve_fontcache_configure_snap
|
||||
configure_snap :: proc( ctx : ^Context, snap_width, snap_height : u32 ) {
|
||||
assert( ctx != nil )
|
||||
ctx.snap_width = snap_width
|
||||
ctx.snap_height = snap_height
|
||||
}
|
||||
|
||||
// For a provided alpha value,
|
||||
// allows the function to calculate the position of a point along the curve at any given fraction of its total length
|
||||
// ve_fontcache_eval_bezier (quadratic)
|
||||
eval_point_on_bezier3 :: proc( p0, p1, p2 : Vec2, alpha : f32 ) -> Vec2
|
||||
{
|
||||
starting_point := p0 * (1 - alpha) * (1 - alpha)
|
||||
control_point := p1 * 2.0 * (1 - alpha)
|
||||
end_point := p2 * alpha * alpha
|
||||
|
||||
point := starting_point + control_point + end_point
|
||||
return point
|
||||
}
|
||||
|
||||
// For a provided alpha value,
|
||||
// allows the function to calculate the position of a point along the curve at any given fraction of its total length
|
||||
// ve_fontcache_eval_bezier (cubic)
|
||||
eval_point_on_bezier4 :: proc( p0, p1, p2, p3 : Vec2, alpha : f32 ) -> Vec2
|
||||
{
|
||||
start_point := p0 * (1 - alpha) * (1 - alpha) * (1 - alpha)
|
||||
control_a := p1 * 3 * (1 - alpha) * (1 - alpha) * alpha
|
||||
control_b := p2 * 3 * (1 - alpha) * alpha * alpha
|
||||
end_point := p3 * alpha * alpha * alpha
|
||||
|
||||
point := start_point + control_a + control_b + end_point
|
||||
return point
|
||||
}
|
||||
|
||||
cache_glyph :: proc( ctx : ^Context, font : FontID, glyph_index : Glyph, scale, translate : Vec2 ) -> b32
|
||||
{
|
||||
assert( ctx != nil )
|
||||
@ -513,20 +525,6 @@ cache_glyph :: proc( ctx : ^Context, font : FontID, glyph_index : Glyph, scale,
|
||||
return false
|
||||
}
|
||||
|
||||
screenspace_x_form :: proc( position, scale : ^Vec2, width, height : f32 ) {
|
||||
scale.x = (scale.x / width ) * 2.0
|
||||
scale.y = (scale.y / height) * 2.0
|
||||
position.x = position.x * (2.0 / width) - 1.0
|
||||
position.y = position.y * (2.0 / width) - 1.0
|
||||
}
|
||||
|
||||
textspace_x_form :: proc( position, scale : ^Vec2, width, height : f32 ) {
|
||||
position.x /= width
|
||||
position.y /= height
|
||||
scale.x /= width
|
||||
scale.y /= height
|
||||
}
|
||||
|
||||
cache_glyph_to_atlas :: proc( ctx : ^Context, font : FontID, glyph_index : Glyph )
|
||||
{
|
||||
assert( ctx != nil )
|
||||
@ -541,12 +539,83 @@ cache_glyph_to_atlas :: proc( ctx : ^Context, font : FontID, glyph_index : Glyph
|
||||
bounds_width := bounds_1.x - bounds_0.x
|
||||
bounds_height := bounds_1.y - bounds_0.y
|
||||
|
||||
region, state, next_idx, over_sample := decide_codepoint_region( ctx, entry, glyph_index )
|
||||
region_kind, region, over_sample := decide_codepoint_region( ctx, entry, glyph_index )
|
||||
|
||||
// E region is special case and not cached to atlas.
|
||||
if region == .None || region == .E do return
|
||||
if region_kind == .None || region_kind == .E do return
|
||||
|
||||
// Grab an atlas LRU cache slot.
|
||||
lru_code := font_glyph_lru_code( font, glyph_index )
|
||||
atlas_index := LRU_get( & region.state, lru_code )
|
||||
if atlas_index == -1
|
||||
{
|
||||
if region.next_idx < region.state.capacity
|
||||
{
|
||||
evicted := LRU_put( & region.state, lru_code, i32(region.next_idx) )
|
||||
atlas_index = i32(region.next_idx)
|
||||
region.next_idx += 1
|
||||
assert( evicted == lru_code )
|
||||
}
|
||||
else
|
||||
{
|
||||
next_evict_codepoint := LRU_get_next_evicted( & region.state )
|
||||
assert( next_evict_codepoint != 0xFFFFFFFFFFFFFFFF )
|
||||
|
||||
atlas_index = LRU_peek( & region.state, next_evict_codepoint )
|
||||
assert( atlas_index != -1 )
|
||||
|
||||
evicted := LRU_put( & region.state, lru_code, atlas_index )
|
||||
assert( evicted == next_evict_codepoint )
|
||||
}
|
||||
|
||||
assert( LRU_get( & region.state, lru_code ) != - 1 )
|
||||
}
|
||||
|
||||
if ctx.debug_print
|
||||
{
|
||||
@static debug_total_cached : i32 = 0
|
||||
logf("glyph %v%v( %v ) caching to atlas region %v at idx %d. %d total glyphs cached.\n", i32(glyph_index), rune(glyph_index), cast(rune) region_kind, atlas_index, debug_total_cached)
|
||||
debug_total_cached += 1
|
||||
}
|
||||
|
||||
// Draw oversized glyph to update FBO
|
||||
glyph_draw_scale := over_sample * entry.size_scale
|
||||
glyph_draw_translate := Vec2 { f32(bounds_0.x), f32(bounds_0.y) } * glyph_draw_scale + Vec2{ f32(ctx.atlas.glyph_padding), f32(ctx.atlas.glyph_padding) }
|
||||
glyph_draw_translate.x = cast(f32) (i32(glyph_draw_translate.x + 0.9999999))
|
||||
glyph_draw_translate.y = cast(f32) (i32(glyph_draw_translate.y + 0.9999999))
|
||||
|
||||
// Allocate a glyph_update_FBO region
|
||||
// gwidth_scaled_px =
|
||||
|
||||
// Calculate the src and destination regions
|
||||
|
||||
// Advance glyph_update_batch_x and calculate final glyph drawing transform
|
||||
|
||||
// Queue up clear on target region on atlas
|
||||
|
||||
// Queue up a blit from glyph_update_FBO to the atlas
|
||||
|
||||
// Render glyph to glyph_update_FBO
|
||||
// cache_glyph( )
|
||||
}
|
||||
|
||||
directly_draw_massive_glyph :: proc( ctx : ^Context, entry : ^Entry, glyph : Glyph, bounds_0 : Vec2i, bounds_width, bounds_height : u32, over_sample, position, scale : Vec2 )
|
||||
{
|
||||
flush_glyph_buffer_to_atlas( ctx )
|
||||
|
||||
glyph_draw_scale := over_sample * entry.size_scale
|
||||
glyph_draw_translate := - Vec2{ f32(bounds_0.x), f32(bounds_0.y)} * glyph_draw_scale + Vec2{ f32(ctx.atlas.glyph_padding), f32(ctx.atlas.glyph_padding) }
|
||||
screenspace_x_form( & glyph_draw_translate, & glyph_draw_scale, f32(ctx.atlas.buffer_width), f32(ctx.atlas.buffer_height) )
|
||||
|
||||
cache_glyph( ctx, entry.id, glyph, glyph_draw_scale, glyph_draw_translate )
|
||||
|
||||
// Figure out the source rect.
|
||||
|
||||
// Figure out the destination rect.
|
||||
|
||||
// Add the glyph drawcall.
|
||||
|
||||
// Clear glyph_update_FBO.
|
||||
}
|
||||
|
||||
is_empty :: proc( ctx : ^Context, entry : ^Entry, glyph_index : Glyph ) -> b32
|
||||
@ -597,12 +666,36 @@ shape_text_cached :: proc( ctx : ^Context, font : FontID, text_utf8 : string ) -
|
||||
|
||||
LRU_put( state, hash, shape_cache_idx )
|
||||
}
|
||||
|
||||
shape_text_uncached( ctx, font, & shape_cache.storage.data[ shape_cache_idx ], text_utf8 )
|
||||
}
|
||||
|
||||
return & shape_cache.storage.data[ shape_cache_idx ]
|
||||
}
|
||||
|
||||
shape_text_uncached :: proc()
|
||||
shape_text_uncached :: proc( ctx : ^Context, font : FontID, output : ^ShapedText, text_utf8 : string )
|
||||
{
|
||||
assert( ctx != nil )
|
||||
assert( font >= 0 && font < FontID(ctx.entries.num) )
|
||||
|
||||
use_full_text_shape := ctx.text_shape_adv
|
||||
entry := & ctx.entries.data[ font ]
|
||||
|
||||
clear( output.glyphs )
|
||||
clear( output.positions )
|
||||
|
||||
ascent, descent, line_gap := parser_get_font_vertical_metrics( entry.parser_info )
|
||||
|
||||
if use_full_text_shape
|
||||
{
|
||||
assert( entry.shaper_info != nil )
|
||||
|
||||
shaper_shape_from_text( & ctx.shaper_ctx, entry.shaper_info, output, text_utf8, ascent, descent, line_gap, entry.size, entry.size_scale )
|
||||
return
|
||||
}
|
||||
|
||||
// We use our own fallback dumbass text shaping.
|
||||
// WARNING: PLEASE USE HARFBUZZ. GOOD TEXT SHAPING IS IMPORTANT FOR INTERNATIONALISATION.
|
||||
|
||||
|
||||
}
|
||||
|
@ -85,22 +85,22 @@ can_batch_glyph :: proc( ctx : ^Context, font : FontID, entry : ^Entry, glyph_in
|
||||
|
||||
// Decide which atlas to target
|
||||
assert( glyph_index != -1 )
|
||||
region, state, next_index, over_sample := decide_codepoint_region( ctx, entry, glyph_index )
|
||||
region_kind, region, over_sample := decide_codepoint_region( ctx, entry, glyph_index )
|
||||
|
||||
// E region can't batch
|
||||
if region == .E || region == .None do return false
|
||||
if ctx.temp_codepoint_seen_num > 1024 do return false
|
||||
if region_kind == .E || region_kind == .None do return false
|
||||
if ctx.temp_codepoint_seen_num > 1024 do return false
|
||||
// Note(Ed): Why 1024?
|
||||
|
||||
// Is this glyph cached?
|
||||
// lru_code := u64(glyph_index) + ( ( 0x100000000 * u64(font) ) & 0xFFFFFFFF00000000 )
|
||||
lru_code := font_glyph_lru_code(font, glyph_index)
|
||||
atlas_index := LRU_get( state, lru_code )
|
||||
atlas_index := LRU_get( & region.state, lru_code )
|
||||
if atlas_index == - 1
|
||||
{
|
||||
if (next_index^) >= u32(state.capacity) {
|
||||
if region.next_idx >= u32( 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( state )
|
||||
next_evict_codepoint := LRU_get_next_evicted( & region.state )
|
||||
seen := get( ctx.temp_codepoint_seen, next_evict_codepoint )
|
||||
assert(seen != nil)
|
||||
|
||||
@ -112,17 +112,17 @@ can_batch_glyph :: proc( ctx : ^Context, font : FontID, entry : ^Entry, glyph_in
|
||||
cache_glyph_to_atlas( ctx, font, glyph_index )
|
||||
}
|
||||
|
||||
assert( LRU_get( state, lru_code ) != 1 )
|
||||
assert( LRU_get( & region.state, lru_code ) != 1 )
|
||||
set( ctx.temp_codepoint_seen, lru_code, true )
|
||||
ctx.temp_codepoint_seen_num += 1
|
||||
return true
|
||||
}
|
||||
|
||||
decide_codepoint_region :: proc( ctx : ^Context, entry : ^Entry, glyph_index : Glyph
|
||||
) -> (region : AtlasRegionKind, state : ^LRU_Cache, next_idx : ^u32, over_sample : Vec2)
|
||||
) -> (region_kind : AtlasRegionKind, region : ^AtlasRegion, over_sample : Vec2)
|
||||
{
|
||||
if parser_is_glyph_empty( entry.parser_info, glyph_index ) {
|
||||
region = .None
|
||||
region_kind = .None
|
||||
}
|
||||
|
||||
bounds_0, bounds_1 := parser_get_glyph_box( entry.parser_info, glyph_index )
|
||||
@ -137,37 +137,32 @@ decide_codepoint_region :: proc( ctx : ^Context, entry : ^Entry, glyph_index : G
|
||||
if bounds_width_scaled <= atlas.region_a.width && bounds_height_scaled <= atlas.region_a.height
|
||||
{
|
||||
// Region A for small glyphs. These are good for things such as punctuation.
|
||||
region = .A
|
||||
state = & atlas.region_a.state
|
||||
next_idx = & atlas.region_a.next_idx
|
||||
region_kind = .A
|
||||
region = & atlas.region_a
|
||||
}
|
||||
else if bounds_width_scaled <= atlas.region_b.width && bounds_height_scaled <= atlas.region_b.height
|
||||
{
|
||||
// Region B for tall glyphs. These are good for things such as european alphabets.
|
||||
region = .B
|
||||
state = & atlas.region_b.state
|
||||
next_idx = & atlas.region_b.next_idx
|
||||
region_kind = .B
|
||||
region = & atlas.region_b
|
||||
}
|
||||
else if bounds_width_scaled <= atlas.region_c.width && bounds_height_scaled <= atlas.region_c.height
|
||||
{
|
||||
// Region C for big glyphs. These are good for things such as asian typography.
|
||||
region = .C
|
||||
state = & atlas.region_c.state
|
||||
next_idx = & atlas.region_c.next_idx
|
||||
region_kind = .C
|
||||
region = & atlas.region_c
|
||||
}
|
||||
else if bounds_width_scaled <= atlas.region_d.width && bounds_height_scaled <= atlas.region_d.height
|
||||
{
|
||||
// Region D for huge glyphs. These are good for things such as titles and 4k.
|
||||
region = .D
|
||||
state = & atlas.region_d.state
|
||||
next_idx = & atlas.region_d.next_idx
|
||||
region_kind = .D
|
||||
region = & atlas.region_d
|
||||
}
|
||||
else if bounds_width_scaled <= atlas.buffer_width && bounds_height_scaled <= atlas.buffer_height
|
||||
{
|
||||
// Region 'E' for massive glyphs. These are rendered uncached and un-oversampled.
|
||||
region = .E
|
||||
state = nil
|
||||
next_idx = nil
|
||||
region_kind = .E
|
||||
region = nil
|
||||
if bounds_width_scaled <= atlas.buffer_width / 2 && bounds_height_scaled <= atlas.buffer_height / 2 {
|
||||
over_sample = { 2.0, 2.0 }
|
||||
}
|
||||
@ -177,11 +172,10 @@ decide_codepoint_region :: proc( ctx : ^Context, entry : ^Entry, glyph_index : G
|
||||
return
|
||||
}
|
||||
else {
|
||||
region = .None
|
||||
region_kind = .None
|
||||
return
|
||||
}
|
||||
|
||||
assert(state != nil)
|
||||
assert(next_idx != nil)
|
||||
assert(region != nil)
|
||||
return
|
||||
}
|
||||
|
@ -89,11 +89,6 @@ clear_draw_list :: proc( draw_list : ^DrawList ) {
|
||||
clear( draw_list.vertices )
|
||||
}
|
||||
|
||||
directly_draw_massive_glyph :: proc( ctx : ^Context, entry : ^Entry, glyph : Glyph, bounds_0 : Vec2i, bounds_width, bounds_height : u32, over_sample, position, scale : Vec2 )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
draw_cached_glyph :: proc( ctx : ^Context, entry : ^Entry, glyph_index : Glyph, position, scale : Vec2 ) -> b32
|
||||
{
|
||||
// Glyph not in current font
|
||||
@ -106,10 +101,10 @@ draw_cached_glyph :: proc( ctx : ^Context, entry : ^Entry, glyph_index : Glyph,
|
||||
bounds_height := bounds_1.y - bounds_0.y
|
||||
|
||||
// Decide which atlas to target
|
||||
region, state, next_idx, over_sample := decide_codepoint_region( ctx, entry, glyph_index )
|
||||
region_kind, region, over_sample := decide_codepoint_region( ctx, entry, glyph_index )
|
||||
|
||||
// E region is special case and not cached to atlas
|
||||
if region == .E
|
||||
if region_kind == .E
|
||||
{
|
||||
directly_draw_massive_glyph( ctx, entry, glyph_index, bounds_0, bounds_width, bounds_height, over_sample, position, scale )
|
||||
return true
|
||||
@ -118,7 +113,7 @@ draw_cached_glyph :: proc( ctx : ^Context, entry : ^Entry, glyph_index : Glyph,
|
||||
// Is this codepoint cached?
|
||||
// lru_code := u64(glyph_index) + ( ( 0x100000000 * u64(entry.id) ) & 0xFFFFFFFF00000000 )
|
||||
lru_code := font_glyph_lru_code(entry.id, glyph_index)
|
||||
atlas_index := LRU_get( state, lru_code )
|
||||
atlas_index := LRU_get( & region.state, lru_code )
|
||||
if atlas_index == - 1 {
|
||||
return false
|
||||
}
|
||||
@ -126,7 +121,7 @@ draw_cached_glyph :: proc( ctx : ^Context, entry : ^Entry, glyph_index : Glyph,
|
||||
atlas := & ctx.atlas
|
||||
|
||||
// Figure out the source bounding box in the atlas texture
|
||||
position, width, height := atlas_bbox( atlas, region, u32(atlas_index) )
|
||||
position, width, height := atlas_bbox( atlas, region_kind, u32(atlas_index) )
|
||||
|
||||
glyph_position := position
|
||||
glyph_width := f32(bounds_width) * entry.size_scale
|
||||
|
@ -106,6 +106,18 @@ parser_unload_font :: proc( font : ^ParserFontInfo )
|
||||
}
|
||||
}
|
||||
|
||||
parser_get_font_vertical_metrics :: proc( font : ^ParserFontInfo ) -> (ascent, descent, line_gap : i32 )
|
||||
{
|
||||
switch font.kind
|
||||
{
|
||||
case .Freetype:
|
||||
|
||||
case .STB_TrueType:
|
||||
stbtt.GetFontVMetrics( & font.stbtt_info, & ascent, & descent, & line_gap )
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
parser_scale_for_pixel_height :: #force_inline proc( font : ^ParserFontInfo, size : f32 ) -> f32
|
||||
{
|
||||
switch font.kind {
|
||||
|
@ -1,6 +1,10 @@
|
||||
package VEFontCache
|
||||
/*
|
||||
Note(Ed): The only reason I didn't directly use harfbuzz is because hamza exists and seems to be under active development as an alternative.
|
||||
*/
|
||||
|
||||
import "core:c"
|
||||
import "core:math"
|
||||
import "thirdparty:harfbuzz"
|
||||
|
||||
ShaperKind :: enum {
|
||||
@ -56,3 +60,100 @@ shaper_unload_font :: proc( ctx : ^ShaperInfo )
|
||||
if face != nil do harfbuzz.face_destroy( face )
|
||||
if blob != nil do harfbuzz.blob_destroy( blob )
|
||||
}
|
||||
|
||||
shaper_shape_from_text :: proc( ctx : ^ShaperContext, info : ^ShaperInfo, output :^ShapedText, text_utf8 : string,
|
||||
ascent, descent, line_gap : i32, size, size_scale : f32 )
|
||||
{
|
||||
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)
|
||||
|
||||
position, vertical_position : f32
|
||||
shape_run :: proc( buffer : harfbuzz.Buffer, script : harfbuzz.Script, font : harfbuzz.Font, output : ^ShapedText,
|
||||
position, vertical_position : ^f32,
|
||||
ascent, descent, line_gap, size, size_scale : f32 )
|
||||
{
|
||||
// Set script and direction. We use the system's default langauge.
|
||||
// script = HB_SCRIPT_LATIN
|
||||
harfbuzz.buffer_set_script( buffer, script )
|
||||
harfbuzz.buffer_set_direction( buffer, harfbuzz.script_get_horizontal_direction( script ))
|
||||
harfbuzz.set_language( buffer, harfbuzz.language_get_default() )
|
||||
|
||||
// Perform the actual shaping of this run using HarfBuzz.
|
||||
harfbuzz.shape( 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 )
|
||||
|
||||
for index : i32; index < i32(glyph_count); index += 1
|
||||
{
|
||||
hb_glyph := glyph_infos[ index ]
|
||||
hb_gposition := glyph_positions[ index ]
|
||||
glyph_id := cast(Glyph) hb_glyph.codepoint
|
||||
|
||||
if hb_glyph.cluster > 0
|
||||
{
|
||||
(position^) = 0.0
|
||||
(vertical_position^) -= (ascent - descent + line_gap) * size_scale
|
||||
(vertical_position^) = cast(f32) i32(vertical_position^ + 0.5)
|
||||
continue
|
||||
}
|
||||
if math.abs( size ) <= Advance_Snap_Smallfont_Size
|
||||
{
|
||||
(position^) = math.ceil( position^ )
|
||||
}
|
||||
|
||||
append( & output.glyphs, glyph_id )
|
||||
|
||||
pos := position^
|
||||
v_pos := vertical_position^
|
||||
offset_x := f32(hb_gposition.x_offset) * size_scale
|
||||
offset_y := f32(hb_gposition.y_offset) * size_scale
|
||||
append( & output.positions, Vec2 { cast(f32) i32( pos + offset_x + 0.5 ),
|
||||
v_pos + offset_y,
|
||||
})
|
||||
|
||||
(position^) += f32(hb_gposition.x_advance) * size_scale
|
||||
(vertical_position^) += f32(hb_gposition.y_advance) * size_scale
|
||||
}
|
||||
|
||||
output.end_cursor_pos.x = position^
|
||||
output.end_cursor_pos.y = vertical_position^
|
||||
harfbuzz.buffer_clear_contents( buffer )
|
||||
}
|
||||
|
||||
// Note(Original Author):
|
||||
// We first start with simple bidi and run logic.
|
||||
// True CTL is pretty hard and we don't fully support that; patches welcome!
|
||||
|
||||
for codepoint, byte_offset in text_utf8
|
||||
{
|
||||
script := harfbuzz.unicode_script( hb_ucfunc, cast(harfbuzz.Codepoint) codepoint )
|
||||
|
||||
// Can we continue the current run?
|
||||
ScriptKind :: harfbuzz.Script
|
||||
|
||||
special_script : b32 = script == ScriptKind.UNKNOWN || script == ScriptKind.INHERITED || script == ScriptKind.COMMON
|
||||
if special_script || script == current_script {
|
||||
harfbuzz.buffer_add( ctx.hb_buffer, cast(harfbuzz.Codepoint) codepoint, codepoint == '\n' ? 1 : 0 )
|
||||
current_script = special_script ? current_script : script
|
||||
continue
|
||||
}
|
||||
|
||||
// End current run since we've encountered a script change.
|
||||
shape_run( ctx.hb_buffer, current_script, info.font, output, & position, & vertical_position, ascent, descent, line_gap, size, size_scale )
|
||||
harfbuzz.buffer_add( ctx.hb_buffer, cast(harfbuzz.Codepoint) codepoint, codepoint == '\n' ? 1 : 0 )
|
||||
current_script = script
|
||||
}
|
||||
|
||||
// End the last run if needed
|
||||
shape_run( ctx.hb_buffer, current_script, info.font, output, & position, & vertical_position, ascent, descent, line_gap, size, size_scale )
|
||||
return
|
||||
}
|
||||
|
2
thirdparty/harfbuzz
vendored
2
thirdparty/harfbuzz
vendored
@ -1 +1 @@
|
||||
Subproject commit 5112039d627bf5f6a683b7aca5bb360e53570d97
|
||||
Subproject commit d3a08d9fa487dfbc0a1801e86a57f28614d7a308
|
Loading…
Reference in New Issue
Block a user