Update vefontcache to latest
This commit is contained in:
parent
5c3bc243a4
commit
bb6e1f78d3
@ -1,4 +1,4 @@
|
||||
package VEFontCache
|
||||
package vefontcache
|
||||
|
||||
/*
|
||||
The choice was made to keep the LRU cache implementation as close to the original as possible.
|
||||
@ -54,7 +54,8 @@ pool_list_init :: proc( pool : ^PoolList, capacity : i32, dbg_name : string = ""
|
||||
}
|
||||
|
||||
pool_list_free :: proc( pool : ^PoolList ) {
|
||||
// TODO(Ed): Implement
|
||||
delete( pool.items)
|
||||
delete( pool.free_list)
|
||||
}
|
||||
|
||||
pool_list_reload :: proc( pool : ^PoolList, allocator : Allocator ) {
|
||||
@ -174,7 +175,8 @@ LRU_init :: proc( cache : ^LRU_Cache, capacity : i32, dbg_name : string = "" ) {
|
||||
}
|
||||
|
||||
LRU_free :: proc( cache : ^LRU_Cache ) {
|
||||
// TODO(Ed): Implement
|
||||
pool_list_free( & cache.key_queue )
|
||||
delete( cache.table )
|
||||
}
|
||||
|
||||
LRU_reload :: #force_inline proc( cache : ^LRU_Cache, allocator : Allocator ) {
|
||||
|
@ -6,43 +6,38 @@ Its original purpose was for use in game engines, however its rendeirng quality
|
||||
|
||||
See: [docs/Readme.md](docs/Readme.md) for the library's interface
|
||||
|
||||
## Building
|
||||
|
||||
See [scripts/Readme.md](scripts/Readme.md) for building examples or utilizing the provided backends.
|
||||
|
||||
Currently the scripts provided & the library itself where developed & tested on Windows. The library itself should not be limited to that OS platform however, just don't have the configuration setup for alternative platforms (yet).
|
||||
|
||||
The library depends on freetype, harfbuzz, & stb_truetype currently to build.
|
||||
Note: freetype and harfbuzz could technically be gutted if the user removes their definitions, however they have not been made into a conditional compilation option (yet).
|
||||
|
||||
## Changes from orignal
|
||||
|
||||
* Font Parser & Glyph shaper are abstracted to their own interface
|
||||
* Font face parser info encapsulated in parser_info struct.
|
||||
* ve_fontcache_loadfile not ported (ust use core:os or os2, then call load_font)
|
||||
* Macro defines have been coverted (mostly) to runtime parameters
|
||||
* Support for hot_reloading
|
||||
* Curve quality step granularity for glyph rendering can be set on a per font basis.
|
||||
|
||||
## TODOs
|
||||
|
||||
### Thirdparty support:
|
||||
|
||||
* Setup freetype, harfbuzz, depedency management within the library
|
||||
|
||||
### Documentation:
|
||||
|
||||
* Pureref outline of draw_text exectuion
|
||||
* Markdown general documentation
|
||||
|
||||
### Content:
|
||||
|
||||
* Port over the original demo utilizing sokol libraries instead
|
||||
* Provide a sokol_gfx backend package
|
||||
|
||||
### Additional Features:
|
||||
|
||||
* Support for freetype
|
||||
* Support for harfbuzz
|
||||
* Support for freetype (WIP, Currently a mess... and slow)
|
||||
* Add ability to conditionally compile dependencies (so that the user may not need to resolve those packages).
|
||||
* Ability to set a draw transform, viewport and projection
|
||||
* By default the library's position is in unsigned normalized render space
|
||||
* Could implement a similar design to sokol_gp's interface
|
||||
* Allow curve_quality to be set on a per-font basis
|
||||
|
||||
### Optimization:
|
||||
|
||||
* Check if its better to store the generated glyph vertices if they need to be re-cached or directly drawn.
|
||||
* Look into setting up multi-threading by giving each thread a context
|
||||
* There is a heavy performance bottleneck in iterating the text/shape/glyphs on the cpu (single-thread) vs the actual rendering
|
||||
* There is a heavy performance bottleneck in iterating the text/shape/glyphs on the cpu (single-thread) vs the actual rendering *(if doing thousands of drawing commands)*
|
||||
* draw_text can provide in the context a job list per thread for the user to thenk hookup to their own threading solution to handle.
|
||||
* Context would need to be segregated into staged data structures for each thread to utilize
|
||||
* Each should have their own?
|
||||
|
@ -1,4 +1,4 @@
|
||||
package VEFontCache
|
||||
package vefontcache
|
||||
|
||||
AtlasRegionKind :: enum u8 {
|
||||
None = 0x00,
|
||||
@ -86,8 +86,7 @@ atlas_bbox :: proc( atlas : ^Atlas, region : AtlasRegionKind, local_idx : i32 )
|
||||
return
|
||||
}
|
||||
|
||||
decide_codepoint_region :: proc(ctx : ^Context, entry : ^Entry, glyph_index : Glyph
|
||||
) -> (region_kind : AtlasRegionKind, region : ^AtlasRegion, over_sample : Vec2)
|
||||
decide_codepoint_region :: proc(ctx : ^Context, entry : ^Entry, glyph_index : Glyph ) -> (region_kind : AtlasRegionKind, region : ^AtlasRegion, over_sample : Vec2)
|
||||
{
|
||||
if parser_is_glyph_empty(&entry.parser_info, glyph_index) {
|
||||
return .None, nil, {}
|
||||
|
@ -39,11 +39,11 @@ You'll find this used immediately in draw_text it acts as a way to snap the posi
|
||||
|
||||
If snapping is not desired, set the snap_width and height before calling draw_text to 0.
|
||||
|
||||
## get_cursor_pos
|
||||
### get_cursor_pos
|
||||
|
||||
Will provide the current cursor_pos for the resulting text drawn.
|
||||
|
||||
## set_color
|
||||
### set_color
|
||||
|
||||
Sets the color to utilize on `DrawCall`s for FrameBuffer.Target or .Target_Uncached passes
|
||||
|
||||
@ -72,6 +72,6 @@ Will update the draw list layer with the latest offset based on the current leng
|
||||
|
||||
Provides a Vec2 the width and height occupied by the provided text string. The y is measured to be the the largest glyph box bounds height of the text. The width is derived from the `end_cursor_pos` field from a `ShapedText` entry.
|
||||
|
||||
## get_font_vertical_metrics
|
||||
### get_font_vertical_metrics
|
||||
|
||||
A wrapper for `parser_get_font_vertical_metrics`. Will provide the ascent, descent, and line_gap for a font entry.
|
||||
|
Binary file not shown.
@ -1,4 +1,7 @@
|
||||
package VEFontCache
|
||||
package vefontcache
|
||||
|
||||
import "thirdparty:freetype"
|
||||
import "core:slice"
|
||||
|
||||
Vertex :: struct {
|
||||
pos : Vec2,
|
||||
@ -29,6 +32,7 @@ DrawList :: struct {
|
||||
calls : [dynamic]DrawCall,
|
||||
}
|
||||
|
||||
// TODO(Ed): This was a rough translation of the raw values the orignal was using, need to give better names...
|
||||
FrameBufferPass :: enum u32 {
|
||||
None = 0,
|
||||
Glyph = 1,
|
||||
@ -84,6 +88,162 @@ blit_quad :: proc( draw_list : ^DrawList, p0 : Vec2 = {0, 0}, p1 : Vec2 = {1, 1}
|
||||
return
|
||||
}
|
||||
|
||||
// TODO(Ed): glyph caching cannot be handled in a 'font parser' abstraction. Just going to have explicit procedures to grab info neatly...
|
||||
cache_glyph_freetype :: proc(ctx: ^Context, font: FontID, glyph_index: Glyph, entry: ^Entry, bounds_0, bounds_1: Vec2, scale, translate: Vec2) -> b32
|
||||
{
|
||||
draw_filled_path_freetype :: proc( draw_list : ^DrawList, outside_point : Vec2, path : []Vertex,
|
||||
scale := Vec2 { 1, 1 },
|
||||
translate := Vec2 { 0, 0 },
|
||||
debug_print_verbose : b32 = false
|
||||
)
|
||||
{
|
||||
if debug_print_verbose {
|
||||
log("outline_path:")
|
||||
for point in path {
|
||||
vec := point.pos * scale + translate
|
||||
logf(" %0.2f %0.2f", vec.x, vec.y )
|
||||
}
|
||||
}
|
||||
|
||||
v_offset := cast(u32) len(draw_list.vertices)
|
||||
for point in path
|
||||
{
|
||||
transformed_point := Vertex {
|
||||
pos = point.pos * scale + translate,
|
||||
u = 0,
|
||||
v = 0
|
||||
}
|
||||
append( & draw_list.vertices, transformed_point )
|
||||
}
|
||||
|
||||
if len(path) > 2
|
||||
{
|
||||
indices := & draw_list.indices
|
||||
for index : u32 = 1; index < cast(u32) len(path) - 1; index += 1 {
|
||||
to_add := [3]u32 {
|
||||
v_offset,
|
||||
v_offset + index,
|
||||
v_offset + index + 1
|
||||
}
|
||||
append( indices, ..to_add[:] )
|
||||
}
|
||||
|
||||
// Close the path by connecting the last vertex to the first two
|
||||
to_add := [3]u32 {
|
||||
v_offset,
|
||||
v_offset + cast(u32)(len(path) - 1),
|
||||
v_offset + 1
|
||||
}
|
||||
append( indices, ..to_add[:] )
|
||||
}
|
||||
}
|
||||
|
||||
if glyph_index == Glyph(0) {
|
||||
return false
|
||||
}
|
||||
|
||||
face := entry.parser_info.freetype_info
|
||||
error := freetype.load_glyph(face, u32(glyph_index), {.No_Bitmap, .No_Scale})
|
||||
if error != .Ok {
|
||||
return false
|
||||
}
|
||||
|
||||
glyph := face.glyph
|
||||
if glyph.format != .Outline {
|
||||
return false
|
||||
}
|
||||
|
||||
outline := &glyph.outline
|
||||
if outline.n_points == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
draw := DrawCall_Default
|
||||
draw.pass = FrameBufferPass.Glyph
|
||||
draw.start_index = cast(u32) len(ctx.draw_list.indices)
|
||||
|
||||
contours := slice.from_ptr(cast( [^]i16) outline.contours, int(outline.n_contours))
|
||||
points := slice.from_ptr(cast( [^]freetype.Vector) outline.points, int(outline.n_points))
|
||||
tags := slice.from_ptr(cast( [^]u8) outline.tags, int(outline.n_points))
|
||||
|
||||
path := &ctx.temp_path
|
||||
clear(path)
|
||||
|
||||
outside := Vec2{ bounds_0.x - 21, bounds_0.y - 33 }
|
||||
|
||||
start_index: int = 0
|
||||
for contour_index in 0 ..< int(outline.n_contours)
|
||||
{
|
||||
end_index := int(contours[contour_index]) + 1
|
||||
prev_point: Vec2
|
||||
first_point: Vec2
|
||||
|
||||
for idx := start_index; idx < end_index; idx += 1
|
||||
{
|
||||
current_pos := Vec2 { f32( points[idx].x ), f32( points[idx].y ) }
|
||||
if ( tags[idx] & 1 ) == 0
|
||||
{
|
||||
// If current point is off-curve
|
||||
if (idx == start_index || (tags[ idx - 1 ] & 1) != 0)
|
||||
{
|
||||
// current is the first or following an on-curve point
|
||||
prev_point = current_pos
|
||||
}
|
||||
else
|
||||
{
|
||||
// current and previous are off-curve, calculate midpoint
|
||||
midpoint := (prev_point + current_pos) * 0.5
|
||||
append( path, Vertex { pos = midpoint } ) // Add midpoint as on-curve point
|
||||
if idx < end_index - 1
|
||||
{
|
||||
// perform interp from prev_point to current_pos via midpoint
|
||||
step := 1.0 / entry.curve_quality
|
||||
for alpha : f32 = 0.0; alpha <= 1.0; alpha += step
|
||||
{
|
||||
bezier_point := eval_point_on_bezier3( prev_point, midpoint, current_pos, alpha )
|
||||
append( path, Vertex{ pos = bezier_point } )
|
||||
}
|
||||
}
|
||||
|
||||
prev_point = current_pos
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if idx == start_index {
|
||||
first_point = current_pos
|
||||
}
|
||||
if prev_point != (Vec2{}) {
|
||||
// there was an off-curve point before this
|
||||
append(path, Vertex{ pos = prev_point}) // Ensure previous off-curve is handled
|
||||
}
|
||||
append(path, Vertex{ pos = current_pos})
|
||||
prev_point = {}
|
||||
}
|
||||
}
|
||||
|
||||
// ensure the contour is closed
|
||||
if path[0].pos != path[ len(path) - 1 ].pos {
|
||||
append(path, Vertex{pos = path[0].pos})
|
||||
}
|
||||
draw_filled_path(&ctx.draw_list, bounds_0, path[:], scale, translate, ctx.debug_print_verbose)
|
||||
clear(path)
|
||||
start_index = end_index
|
||||
}
|
||||
|
||||
if len(path) > 0 {
|
||||
draw_filled_path(&ctx.draw_list, outside, path[:], scale, translate, ctx.debug_print_verbose)
|
||||
}
|
||||
|
||||
draw.end_index = cast(u32) len(ctx.draw_list.indices)
|
||||
if draw.end_index > draw.start_index {
|
||||
append( & ctx.draw_list.calls, draw)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// TODO(Ed): Is it better to cache the glyph vertices for when it must be re-drawn (directly or two atlas)?
|
||||
cache_glyph :: proc(ctx : ^Context, font : FontID, glyph_index : Glyph, entry : ^Entry, bounds_0, bounds_1 : Vec2, scale, translate : Vec2) -> b32
|
||||
{
|
||||
// profile(#procedure)
|
||||
@ -91,6 +251,12 @@ cache_glyph :: proc(ctx : ^Context, font : FontID, glyph_index : Glyph, entry :
|
||||
return false
|
||||
}
|
||||
|
||||
// Glyph shape handling are not abstractable between freetype and stb_truetype
|
||||
if entry.parser_info.kind == .Freetype {
|
||||
result := cache_glyph_freetype( ctx, font, glyph_index, entry, bounds_0, bounds_1, scale, translate )
|
||||
return result
|
||||
}
|
||||
|
||||
shape, error := parser_get_glyph_shape(&entry.parser_info, glyph_index)
|
||||
assert(error == .None)
|
||||
if len(shape) == 0 {
|
||||
@ -150,7 +316,7 @@ cache_glyph :: proc(ctx : ^Context, font : FontID, glyph_index : Glyph, entry :
|
||||
|
||||
draw.end_index = u32(len(ctx.draw_list.indices))
|
||||
if draw.end_index > draw.start_index {
|
||||
append(&ctx.draw_list.calls, draw)
|
||||
append( & ctx.draw_list.calls, draw)
|
||||
}
|
||||
|
||||
parser_free_shape(&entry.parser_info, shape)
|
||||
@ -475,7 +641,7 @@ draw_text_batch :: proc(ctx: ^Context, entry: ^Entry, shaped: ^ShapedText,
|
||||
bounds_size := Vec2 { vbounds_1.x - vbounds_0.x, vbounds_1.y - vbounds_0.y }
|
||||
|
||||
shaped_position := shaped.positions[index]
|
||||
glyph_translate := position + shaped_position * scale
|
||||
glyph_translate := position + (shaped_position) * scale
|
||||
|
||||
if region_kind == .E
|
||||
{
|
||||
|
@ -1,4 +1,4 @@
|
||||
package VEFontCache
|
||||
package vefontcache
|
||||
|
||||
import "core:hash"
|
||||
fnv64a :: hash.fnv64a
|
||||
|
@ -1,4 +1,4 @@
|
||||
package VEFontCache
|
||||
package vefontcache
|
||||
|
||||
import "base:runtime"
|
||||
import "core:simd"
|
||||
@ -20,23 +20,23 @@ vec2i_from_vec2 :: #force_inline proc "contextless" ( v2 : Vec2 ) -> Vec2
|
||||
@(require_results) floor_vec2 :: proc "contextless" ( v : Vec2 ) -> Vec2 { return { floor_f32(v.x), floor_f32(v.y) } }
|
||||
|
||||
// This buffer is used below excluisvely to prevent any allocator recusion when verbose logging from allocators.
|
||||
// This means a single line is limited to 32k buffer (increase naturally if this SOMEHOW becomes a bottleneck...)
|
||||
// Logger_Allocator_Buffer : [32 * Kilobyte]u8
|
||||
// This means a single line is limited to 4k buffer
|
||||
// Logger_Allocator_Buffer : [4 * Kilobyte]u8
|
||||
|
||||
// log :: proc( msg : string, level := core_log.Level.Info, loc := #caller_location ) {
|
||||
// temp_arena : Arena; arena_init(& temp_arena, Logger_Allocator_Buffer[:])
|
||||
// context.allocator = arena_allocator(& temp_arena)
|
||||
// context.temp_allocator = arena_allocator(& temp_arena)
|
||||
// temp_arena : Arena; arena_init(& temp_arena, Logger_Allocator_Buffer[:])
|
||||
// context.allocator = arena_allocator(& temp_arena)
|
||||
// context.temp_allocator = arena_allocator(& temp_arena)
|
||||
|
||||
// core_log.log( level, msg, location = loc )
|
||||
// core_log.log( level, msg, location = loc )
|
||||
// }
|
||||
|
||||
// logf :: proc( fmt : string, args : ..any, level := core_log.Level.Info, loc := #caller_location ) {
|
||||
// temp_arena : Arena; arena_init(& temp_arena, Logger_Allocator_Buffer[:])
|
||||
// context.allocator = arena_allocator(& temp_arena)
|
||||
// context.temp_allocator = arena_allocator(& temp_arena)
|
||||
// temp_arena : Arena; arena_init(& temp_arena, Logger_Allocator_Buffer[:])
|
||||
// context.allocator = arena_allocator(& temp_arena)
|
||||
// context.temp_allocator = arena_allocator(& temp_arena)
|
||||
|
||||
// core_log.logf( level, fmt, ..args, location = loc )
|
||||
// core_log.logf( level, fmt, ..args, location = loc )
|
||||
// }
|
||||
|
||||
reload_array :: proc( self : ^[dynamic]$Type, allocator : Allocator ) {
|
||||
@ -121,7 +121,7 @@ textspace_x_form :: #force_inline proc "contextless" ( position, scale : ^Vec2,
|
||||
}
|
||||
}
|
||||
|
||||
Use_SIMD_For_Bezier_Ops :: true
|
||||
Use_SIMD_For_Bezier_Ops :: false
|
||||
|
||||
when ! Use_SIMD_For_Bezier_Ops
|
||||
{
|
||||
@ -130,10 +130,10 @@ when ! Use_SIMD_For_Bezier_Ops
|
||||
// ve_fontcache_eval_bezier (quadratic)
|
||||
eval_point_on_bezier3 :: #force_inline proc "contextless" ( p0, p1, p2 : Vec2, alpha : f32 ) -> Vec2
|
||||
{
|
||||
p0 := vec2_64(p0)
|
||||
p1 := vec2_64(p1)
|
||||
p2 := vec2_64(p2)
|
||||
alpha := f64(alpha)
|
||||
// p0 := vec2_64(p0)
|
||||
// p1 := vec2_64(p1)
|
||||
// p2 := vec2_64(p2)
|
||||
// alpha := f64(alpha)
|
||||
|
||||
weight_start := (1 - alpha) * (1 - alpha)
|
||||
weight_control := 2.0 * (1 - alpha) * alpha
|
||||
@ -152,11 +152,11 @@ when ! Use_SIMD_For_Bezier_Ops
|
||||
// ve_fontcache_eval_bezier (cubic)
|
||||
eval_point_on_bezier4 :: #force_inline proc "contextless" ( p0, p1, p2, p3 : Vec2, alpha : f32 ) -> Vec2
|
||||
{
|
||||
p0 := vec2_64(p0)
|
||||
p1 := vec2_64(p1)
|
||||
p2 := vec2_64(p2)
|
||||
p3 := vec2_64(p3)
|
||||
alpha := f64(alpha)
|
||||
// p0 := vec2_64(p0)
|
||||
// p1 := vec2_64(p1)
|
||||
// p2 := vec2_64(p2)
|
||||
// p3 := vec2_64(p3)
|
||||
// alpha := f64(alpha)
|
||||
|
||||
weight_start := (1 - alpha) * (1 - alpha) * (1 - alpha)
|
||||
weight_c_a := 3 * (1 - alpha) * (1 - alpha) * alpha
|
||||
@ -184,59 +184,6 @@ else
|
||||
return Vec2{ simd.extract(v, 0), simd.extract(v, 1) }
|
||||
}
|
||||
|
||||
vec2_add_simd :: #force_inline proc "contextless" (a, b: Vec2) -> Vec2 {
|
||||
simd_a := vec2_to_simd(a)
|
||||
simd_b := vec2_to_simd(b)
|
||||
result := simd.add(simd_a, simd_b)
|
||||
return simd_to_vec2(result)
|
||||
}
|
||||
|
||||
vec2_sub_simd :: #force_inline proc "contextless" (a, b: Vec2) -> Vec2 {
|
||||
simd_a := vec2_to_simd(a)
|
||||
simd_b := vec2_to_simd(b)
|
||||
result := simd.sub(simd_a, simd_b)
|
||||
return simd_to_vec2(result)
|
||||
}
|
||||
|
||||
vec2_mul_simd :: #force_inline proc "contextless" (a: Vec2, s: f32) -> Vec2 {
|
||||
simd_a := vec2_to_simd(a)
|
||||
simd_s := Vec2_SIMD{s, s, s, s}
|
||||
result := simd.mul(simd_a, simd_s)
|
||||
return simd_to_vec2(result)
|
||||
}
|
||||
|
||||
vec2_div_simd :: #force_inline proc "contextless" (a: Vec2, s: f32) -> Vec2 {
|
||||
simd_a := vec2_to_simd(a)
|
||||
simd_s := Vec2_SIMD{s, s, s, s}
|
||||
result := simd.div(simd_a, simd_s)
|
||||
return simd_to_vec2(result)
|
||||
}
|
||||
|
||||
vec2_dot_simd :: #force_inline proc "contextless" (a, b: Vec2) -> f32 {
|
||||
simd_a := vec2_to_simd(a)
|
||||
simd_b := vec2_to_simd(b)
|
||||
result := simd.mul(simd_a, simd_b)
|
||||
return simd.reduce_add_ordered(result)
|
||||
}
|
||||
|
||||
vec2_length_sqr_simd :: #force_inline proc "contextless" (a: Vec2) -> f32 {
|
||||
return vec2_dot_simd(a, a)
|
||||
}
|
||||
|
||||
vec2_length_simd :: #force_inline proc "contextless" (a: Vec2) -> f32 {
|
||||
return math.sqrt(vec2_length_sqr_simd(a))
|
||||
}
|
||||
|
||||
vec2_normalize_simd :: #force_inline proc "contextless" (a: Vec2) -> Vec2 {
|
||||
len := vec2_length_simd(a)
|
||||
if len > 0 {
|
||||
inv_len := 1.0 / len
|
||||
return vec2_mul_simd(a, inv_len)
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// SIMD-optimized version of eval_point_on_bezier3
|
||||
eval_point_on_bezier3 :: #force_inline proc "contextless" (p0, p1, p2: Vec2, alpha: f32) -> Vec2
|
||||
{
|
||||
simd_p0 := vec2_to_simd(p0)
|
||||
|
@ -1,4 +1,4 @@
|
||||
package VEFontCache
|
||||
package vefontcache
|
||||
|
||||
/*
|
||||
Notes:
|
||||
@ -12,6 +12,7 @@ STB_Truetype has macros for its allocation unfortuantely
|
||||
import "base:runtime"
|
||||
import "core:c"
|
||||
import "core:math"
|
||||
import "core:slice"
|
||||
import stbtt "vendor:stb/truetype"
|
||||
import freetype "thirdparty:freetype"
|
||||
|
||||
@ -52,13 +53,11 @@ ParserGlyphShape :: [dynamic]ParserGlyphVertex
|
||||
ParserContext :: struct {
|
||||
kind : ParserKind,
|
||||
ft_library : freetype.Library,
|
||||
|
||||
// fonts : HMapChained(ParserFontInfo),
|
||||
}
|
||||
|
||||
parser_init :: proc( ctx : ^ParserContext )
|
||||
parser_init :: proc( ctx : ^ParserContext, kind : ParserKind )
|
||||
{
|
||||
switch ctx.kind
|
||||
switch kind
|
||||
{
|
||||
case .Freetype:
|
||||
result := freetype.init_free_type( & ctx.ft_library )
|
||||
@ -68,9 +67,7 @@ parser_init :: proc( ctx : ^ParserContext )
|
||||
// Do nothing intentional
|
||||
}
|
||||
|
||||
// error : AllocatorError
|
||||
// ctx.fonts, error = make( HMapChained(ParserFontInfo), 256 )
|
||||
// assert( error == .None, "VEFontCache.parser_init: Failed to allocate fonts array" )
|
||||
ctx.kind = kind
|
||||
}
|
||||
|
||||
parser_shutdown :: proc( ctx : ^ParserContext ) {
|
||||
@ -79,13 +76,6 @@ parser_shutdown :: proc( ctx : ^ParserContext ) {
|
||||
|
||||
parser_load_font :: proc( ctx : ^ParserContext, label : string, data : []byte ) -> (font : ParserFontInfo)
|
||||
{
|
||||
// key := font_key_from_label(label)
|
||||
// font = get( ctx.fonts, key )
|
||||
// if font != nil do return
|
||||
|
||||
// error : AllocatorError
|
||||
// font, error = set( ctx.fonts, key, ParserFontInfo {} )
|
||||
// assert( error == .None, "VEFontCache.parser_load_font: Failed to set a new parser font info" )
|
||||
switch ctx.kind
|
||||
{
|
||||
case .Freetype:
|
||||
@ -99,6 +89,7 @@ parser_load_font :: proc( ctx : ^ParserContext, label : string, data : []byte )
|
||||
|
||||
font.label = label
|
||||
font.data = data
|
||||
font.kind = ctx.kind
|
||||
return
|
||||
}
|
||||
|
||||
@ -190,6 +181,10 @@ parser_get_font_vertical_metrics :: #force_inline proc "contextless" ( font : ^P
|
||||
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:
|
||||
stbtt.GetFontVMetrics( & font.stbtt_info, & ascent, & descent, & line_gap )
|
||||
@ -225,137 +220,8 @@ parser_get_glyph_shape :: proc( font : ^ParserFontInfo, glyph_index : Glyph ) ->
|
||||
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 {
|
||||
return
|
||||
}
|
||||
|
||||
glyph := font.freetype_info.glyph
|
||||
if glyph.format != .Outline {
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
convert freetype outline to stb_truetype shape
|
||||
|
||||
freetype docs: https://freetype.org/freetype2/docs/glyphs/glyphs-6.html
|
||||
|
||||
stb_truetype shape info:
|
||||
The shape is a series of contours. Each one starts with
|
||||
a STBTT_moveto, then consists of a series of mixed
|
||||
STBTT_lineto and STBTT_curveto segments. A lineto
|
||||
draws a line from previous endpoint to its x,y; a curveto
|
||||
draws a quadratic bezier from previous endpoint to
|
||||
its x,y, using cx,cy as the bezier control point.
|
||||
*/
|
||||
{
|
||||
FT_CURVE_TAG_CONIC :: 0x00
|
||||
FT_CURVE_TAG_ON :: 0x01
|
||||
FT_CURVE_TAG_CUBIC :: 0x02
|
||||
|
||||
vertices, error := make( [dynamic]ParserGlyphVertex, 1024 )
|
||||
assert( error == .None )
|
||||
|
||||
// TODO(Ed): This makes freetype second class I guess but VEFontCache doesn't have native support for freetype originally so....
|
||||
outline := & glyph.outline
|
||||
|
||||
contours := transmute( [^]u16) outline.contours
|
||||
points := transmute( [^]freetype.Vector) outline.points
|
||||
tags := transmute( [^]u8) outline.tags
|
||||
|
||||
// TODO(Ed): Review this, never tested before and its problably bad.
|
||||
for contour : i32 = 0; contour < i32(outline.n_contours); contour += 1
|
||||
{
|
||||
start := (contour == 0) ? 0 : i32(contours[ contour - 1 ] + 1)
|
||||
end := i32(contours[ contour ])
|
||||
|
||||
for index := start; index < i32(outline.n_points); index += 1
|
||||
{
|
||||
point := points[ index ]
|
||||
tag := tags[ index ]
|
||||
|
||||
if (tag & FT_CURVE_TAG_ON) != 0
|
||||
{
|
||||
if len(vertices) > 0 && !(vertices[len(vertices) - 1].type == .Move )
|
||||
{
|
||||
// Close the previous contour if needed
|
||||
append(& vertices, ParserGlyphVertex { type = .Line,
|
||||
x = i16(points[start].x), y = i16(points[start].y),
|
||||
contour_x0 = i16(0), contour_y0 = i16(0),
|
||||
contour_x1 = i16(0), contour_y1 = i16(0),
|
||||
padding = 0,
|
||||
})
|
||||
}
|
||||
|
||||
append(& vertices, ParserGlyphVertex { type = .Move,
|
||||
x = i16(point.x), y = i16(point.y),
|
||||
contour_x0 = i16(0), contour_y0 = i16(0),
|
||||
contour_x1 = i16(0), contour_y1 = i16(0),
|
||||
padding = 0,
|
||||
})
|
||||
}
|
||||
else if (tag & FT_CURVE_TAG_CUBIC) != 0
|
||||
{
|
||||
point1 := points[ index + 1 ]
|
||||
point2 := points[ index + 2 ]
|
||||
append(& vertices, ParserGlyphVertex { type = .Cubic,
|
||||
x = i16(point2.x), y = i16(point2.y),
|
||||
contour_x0 = i16(point.x), contour_y0 = i16(point.y),
|
||||
contour_x1 = i16(point1.x), contour_y1 = i16(point1.y),
|
||||
padding = 0,
|
||||
})
|
||||
index += 2
|
||||
}
|
||||
else if (tag & FT_CURVE_TAG_CONIC) != 0
|
||||
{
|
||||
// TODO(Ed): This is using a very dead simple algo to convert the conic to a cubic curve
|
||||
// not sure if we need something more sophisticaated
|
||||
point1 := points[ index + 1 ]
|
||||
|
||||
control_conv :: f32(0.5) // Conic to cubic control point distance
|
||||
to_float := f32(1.0 / 64.0)
|
||||
|
||||
fp := Vec2 { f32(point.x), f32(point.y) } * to_float
|
||||
fp1 := Vec2 { f32(point1.x), f32(point1.y) } * to_float
|
||||
|
||||
control1 := freetype.Vector {
|
||||
point.x + freetype.Pos( (fp1.x - fp.x) * control_conv * 64.0 ),
|
||||
point.y + freetype.Pos( (fp1.y - fp.y) * control_conv * 64.0 ),
|
||||
}
|
||||
control2 := freetype.Vector {
|
||||
point1.x + freetype.Pos( (fp.x - fp1.x) * control_conv * 64.0 ),
|
||||
point1.y + freetype.Pos( (fp.y - fp1.y) * control_conv * 64.0 ),
|
||||
}
|
||||
append(& vertices, ParserGlyphVertex { type = .Cubic,
|
||||
x = i16(point1.x), y = i16(point1.y),
|
||||
contour_x0 = i16(control1.x), contour_y0 = i16(control1.y),
|
||||
contour_x1 = i16(control2.x), contour_y1 = i16(control2.y),
|
||||
padding = 0,
|
||||
})
|
||||
index += 1
|
||||
}
|
||||
else
|
||||
{
|
||||
append(& vertices, ParserGlyphVertex { type = .Line,
|
||||
x = i16(point.x), y = i16(point.y),
|
||||
contour_x0 = i16(0), contour_y0 = i16(0),
|
||||
contour_x1 = i16(0), contour_y1 = i16(0),
|
||||
padding = 0,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Close contour
|
||||
append(& vertices, ParserGlyphVertex { type = .Line,
|
||||
x = i16(points[start].x), y = i16(points[start].y),
|
||||
contour_x0 = i16(0), contour_y0 = i16(0),
|
||||
contour_x1 = i16(0), contour_y1 = i16(0),
|
||||
padding = 0,
|
||||
})
|
||||
}
|
||||
|
||||
shape = vertices
|
||||
}
|
||||
// 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:
|
||||
stb_shape : [^]stbtt.vertex
|
||||
@ -447,48 +313,3 @@ parser_scale_for_mapping_em_to_pixels :: #force_inline proc "contextless" ( font
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
when false {
|
||||
parser_convert_conic_to_cubic_freetype :: proc( vertices : Array(ParserGlyphVertex), p0, p1, p2 : freetype.Vector, tolerance : f32 )
|
||||
{
|
||||
scratch : [Kilobyte * 4]u8
|
||||
scratch_arena : Arena; arena_init(& scratch_arena, scratch[:])
|
||||
|
||||
points, error := make( Array(freetype.Vector), 256, allocator = arena_allocator( &scratch_arena) )
|
||||
assert(error == .None)
|
||||
|
||||
append( & points, p0)
|
||||
append( & points, p1)
|
||||
append( & points, p2)
|
||||
|
||||
to_float : f32 = 1.0 / 64.0
|
||||
control_conv :: f32(2.0 / 3.0) // Conic to cubic control point distance
|
||||
|
||||
for ; points.num > 1; {
|
||||
p0 := points.data[0]
|
||||
p1 := points.data[1]
|
||||
p2 := points.data[2]
|
||||
|
||||
fp0 := Vec2{ f32(p0.x), f32(p0.y) } * to_float
|
||||
fp1 := Vec2{ f32(p1.x), f32(p1.y) } * to_float
|
||||
fp2 := Vec2{ f32(p2.x), f32(p2.y) } * to_float
|
||||
|
||||
delta_x := fp0.x - 2 * fp1.x + fp2.x;
|
||||
delta_y := fp0.y - 2 * fp1.y + fp2.y;
|
||||
distance := math.sqrt(delta_x * delta_x + delta_y * delta_y);
|
||||
|
||||
if distance <= tolerance
|
||||
{
|
||||
control1 := {
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
control2 := {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
package VEFontCache
|
||||
package vefontcache
|
||||
|
||||
ShapedText :: struct {
|
||||
glyphs : [dynamic]Glyph,
|
||||
@ -65,8 +65,6 @@ shape_text_uncached :: proc( ctx : ^Context, font : FontID, text_utf8 : string,
|
||||
assert( ctx != nil )
|
||||
assert( font >= 0 && int(font) < len(ctx.entries) )
|
||||
|
||||
use_full_text_shape := ctx.text_shape_adv
|
||||
|
||||
clear( & output.glyphs )
|
||||
clear( & output.positions )
|
||||
|
||||
@ -76,11 +74,9 @@ shape_text_uncached :: proc( ctx : ^Context, font : FontID, text_utf8 : string,
|
||||
line_gap := f32(line_gap_i32)
|
||||
line_height := (ascent - descent + line_gap) * entry.size_scale
|
||||
|
||||
if use_full_text_shape
|
||||
if ctx.text_shape_adv
|
||||
{
|
||||
// assert( entry.shaper_info != nil )
|
||||
shaper_shape_from_text( & ctx.shaper_ctx, & entry.shaper_info, output, text_utf8, ascent_i32, descent_i32, line_gap_i32, entry.size, entry.size_scale )
|
||||
// TODO(Ed): Need to be able to provide the text height as well
|
||||
return
|
||||
}
|
||||
else
|
||||
@ -106,7 +102,7 @@ shape_text_uncached :: proc( ctx : ^Context, font : FontID, text_utf8 : string,
|
||||
max_line_width = max(max_line_width, position.x)
|
||||
position.x = 0.0
|
||||
position.y -= line_height
|
||||
position.y = ceil(position.y)
|
||||
position.y = position.y
|
||||
prev_codepoint = rune(0)
|
||||
continue
|
||||
}
|
||||
@ -117,19 +113,22 @@ shape_text_uncached :: proc( ctx : ^Context, font : FontID, text_utf8 : string,
|
||||
append( & output.glyphs, parser_find_glyph_index( & entry.parser_info, codepoint ))
|
||||
advance, _ := parser_get_codepoint_horizontal_metrics( & entry.parser_info, codepoint )
|
||||
|
||||
if ctx.snap_shape_pos do position.x = ceil(position.x)
|
||||
|
||||
append( & output.positions, Vec2 {
|
||||
ctx.snap_shape_pos ? ceil(position.x) : position.x,
|
||||
position.x,
|
||||
position.y
|
||||
})
|
||||
|
||||
position.x += ctx.snap_shape_pos ? ceil(f32(advance) * entry.size_scale) : f32(advance) * entry.size_scale
|
||||
position.x += f32(advance) * entry.size_scale
|
||||
if ctx.snap_shape_pos do position.x = ceil(position.x)
|
||||
prev_codepoint = codepoint
|
||||
}
|
||||
|
||||
output.end_cursor_pos = position
|
||||
max_line_width = max(max_line_width, position.x)
|
||||
|
||||
output.size.x = ceil(max_line_width)
|
||||
output.size.x = max_line_width
|
||||
output.size.y = f32(line_count) * line_height
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
package VEFontCache
|
||||
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.
|
||||
*/
|
||||
@ -13,7 +13,6 @@ ShaperKind :: enum {
|
||||
|
||||
ShaperContext :: struct {
|
||||
hb_buffer : harfbuzz.Buffer,
|
||||
// infos : HMapChained(ShaperInfo),
|
||||
}
|
||||
|
||||
ShaperInfo :: struct {
|
||||
@ -26,10 +25,6 @@ shaper_init :: proc( ctx : ^ShaperContext )
|
||||
{
|
||||
ctx.hb_buffer = harfbuzz.buffer_create()
|
||||
assert( ctx.hb_buffer != nil, "VEFontCache.shaper_init: Failed to create harfbuzz buffer")
|
||||
|
||||
// error : AllocatorError
|
||||
// ctx.infos, error = make( HMapChained(ShaperInfo), 256 )
|
||||
// assert( error == .None, "VEFontCache.shaper_init: Failed to create shaper infos map" )
|
||||
}
|
||||
|
||||
shaper_shutdown :: proc( ctx : ^ShaperContext )
|
||||
@ -37,20 +32,10 @@ shaper_shutdown :: proc( ctx : ^ShaperContext )
|
||||
if ctx.hb_buffer != nil {
|
||||
harfbuzz.buffer_destroy( ctx.hb_buffer )
|
||||
}
|
||||
|
||||
// delete(& ctx.infos)
|
||||
}
|
||||
|
||||
shaper_load_font :: proc( ctx : ^ShaperContext, label : string, data : []byte, user_data : rawptr ) -> (info : ShaperInfo)
|
||||
{
|
||||
// key := font_key_from_label( label )
|
||||
// info = get( ctx.infos, key )
|
||||
// if info != nil do return
|
||||
|
||||
// error : AllocatorError
|
||||
// info, error = set( ctx.infos, key, ShaperInfo {} )
|
||||
// assert( error == .None, "VEFontCache.parser_load_font: Failed to set a new shaper info" )
|
||||
|
||||
using info
|
||||
blob = harfbuzz.blob_create( raw_data(data), cast(c.uint) len(data), harfbuzz.Memory_Mode.READONLY, user_data, nil )
|
||||
face = harfbuzz.face_create( blob, 0 )
|
||||
@ -79,10 +64,14 @@ shaper_shape_from_text :: proc( ctx : ^ShaperContext, info : ^ShaperInfo, output
|
||||
descent := f32(descent)
|
||||
line_gap := f32(line_gap)
|
||||
|
||||
max_line_width := f32(0)
|
||||
line_count := 1
|
||||
line_height := (ascent - descent + line_gap) * size_scale
|
||||
|
||||
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 )
|
||||
position, vertical_position, max_line_width: ^f32, line_count: ^int,
|
||||
ascent, descent, line_gap, size, size_scale: f32 )
|
||||
{
|
||||
// Set script and direction. We use the system's default langauge.
|
||||
// script = HB_SCRIPT_LATIN
|
||||
@ -91,6 +80,7 @@ shaper_shape_from_text :: proc( ctx : ^ShaperContext, info : ^ShaperInfo, output
|
||||
harfbuzz.buffer_set_language( buffer, harfbuzz.language_get_default() )
|
||||
|
||||
// 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 )
|
||||
|
||||
// Loop over glyphs and append to output buffer.
|
||||
@ -98,6 +88,8 @@ shaper_shape_from_text :: proc( ctx : ^ShaperContext, info : ^ShaperInfo, output
|
||||
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
|
||||
|
||||
for index : i32; index < i32(glyph_count); index += 1
|
||||
{
|
||||
hb_glyph := glyph_infos[ index ]
|
||||
@ -106,9 +98,11 @@ shaper_shape_from_text :: proc( ctx : ^ShaperContext, info : ^ShaperInfo, output
|
||||
|
||||
if hb_glyph.cluster > 0
|
||||
{
|
||||
(max_line_width^) = max( max_line_width^, position^ )
|
||||
(position^) = 0.0
|
||||
(vertical_position^) -= (ascent - descent + line_gap) * size_scale
|
||||
(vertical_position^) -= line_height
|
||||
(vertical_position^) = cast(f32) i32(vertical_position^ + 0.5)
|
||||
(line_count^) += 1
|
||||
continue
|
||||
}
|
||||
if abs( size ) <= Advance_Snap_Smallfont_Size
|
||||
@ -128,6 +122,7 @@ shaper_shape_from_text :: proc( ctx : ^ShaperContext, info : ^ShaperInfo, output
|
||||
|
||||
(position^) += f32(hb_gposition.x_advance) * size_scale
|
||||
(vertical_position^) += f32(hb_gposition.y_advance) * size_scale
|
||||
(max_line_width^) = max(max_line_width^, position^)
|
||||
}
|
||||
|
||||
output.end_cursor_pos.x = position^
|
||||
@ -141,25 +136,31 @@ shaper_shape_from_text :: proc( ctx : ^ShaperContext, info : ^ShaperInfo, output
|
||||
|
||||
for codepoint, byte_offset in text_utf8
|
||||
{
|
||||
script := harfbuzz.unicode_script( hb_ucfunc, cast(harfbuzz.Codepoint) codepoint )
|
||||
hb_codepoint := cast(harfbuzz.Codepoint) codepoint
|
||||
|
||||
script := harfbuzz.unicode_script( hb_ucfunc, hb_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 )
|
||||
if special_script || script == current_script || byte_offset == 0 {
|
||||
harfbuzz.buffer_add( ctx.hb_buffer, hb_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 )
|
||||
shape_run( ctx.hb_buffer, current_script, info.font, output, & position, & vertical_position, & max_line_width, & line_count, ascent, descent, line_gap, size, size_scale )
|
||||
harfbuzz.buffer_add( ctx.hb_buffer, hb_codepoint, codepoint == '\n' ? 1 : 0 )
|
||||
current_script = script
|
||||
}
|
||||
|
||||
// End the last run if needed
|
||||
shape_run( ctx.hb_buffer, current_script, info.font, output, & position, & vertical_position, ascent, descent, line_gap, size, size_scale )
|
||||
shape_run( ctx.hb_buffer, current_script, info.font, output, & position, & vertical_position, & max_line_width, & line_count, ascent, descent, line_gap, size, size_scale )
|
||||
|
||||
// Set the final size
|
||||
output.size.x = max_line_width
|
||||
output.size.y = f32(line_count) * line_height
|
||||
return
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ A port of (https://github.com/hypernewbie/VEFontCache) to Odin.
|
||||
|
||||
See: https://github.com/Ed94/VEFontCache-Odin
|
||||
*/
|
||||
package VEFontCache
|
||||
package vefontcache
|
||||
|
||||
import "base:runtime"
|
||||
|
||||
@ -32,7 +32,6 @@ Entry_Default :: Entry {
|
||||
Context :: struct {
|
||||
backing : Allocator,
|
||||
|
||||
parser_kind : ParserKind,
|
||||
parser_ctx : ParserContext,
|
||||
shaper_ctx : ShaperContext,
|
||||
|
||||
@ -45,6 +44,7 @@ Context :: struct {
|
||||
snap_width : f32,
|
||||
snap_height : f32,
|
||||
|
||||
|
||||
colour : Colour,
|
||||
cursor_pos : Vec2,
|
||||
|
||||
@ -67,7 +67,7 @@ Context :: struct {
|
||||
debug_print_verbose : b32,
|
||||
}
|
||||
|
||||
#region("lifetime")
|
||||
//#region("lifetime")
|
||||
|
||||
InitAtlasRegionParams :: struct {
|
||||
width : u32,
|
||||
@ -136,6 +136,7 @@ startup :: proc( ctx : ^Context, parser_kind : ParserKind,
|
||||
atlas_params := InitAtlasParams_Default,
|
||||
glyph_draw_params := InitGlyphDrawParams_Default,
|
||||
shape_cache_params := InitShapeCacheParams_Default,
|
||||
use_advanced_text_shaper : b32 = true,
|
||||
snap_shape_position : b32 = true,
|
||||
default_curve_quality : u32 = 3,
|
||||
entires_reserve : u32 = 512,
|
||||
@ -150,6 +151,7 @@ startup :: proc( ctx : ^Context, parser_kind : ParserKind,
|
||||
context.allocator = ctx.backing
|
||||
|
||||
snap_shape_pos = snap_shape_position
|
||||
text_shape_adv = use_advanced_text_shaper
|
||||
|
||||
if default_curve_quality == 0 {
|
||||
default_curve_quality = 3
|
||||
@ -264,7 +266,7 @@ startup :: proc( ctx : ^Context, parser_kind : ParserKind,
|
||||
assert( error == .None, "VEFontCache.init : Failed to allocate vertices array for clear_draw_list" )
|
||||
}
|
||||
|
||||
parser_init( & parser_ctx )
|
||||
parser_init( & parser_ctx, parser_kind )
|
||||
shaper_init( & shaper_ctx )
|
||||
}
|
||||
|
||||
@ -320,9 +322,38 @@ shutdown :: proc( ctx : ^Context )
|
||||
unload_font( ctx, entry.id )
|
||||
}
|
||||
|
||||
shaper_shutdown( & shaper_ctx )
|
||||
delete( entries )
|
||||
delete( temp_path )
|
||||
delete( temp_codepoint_seen )
|
||||
|
||||
// TODO(Ed): Finish implementing, there is quite a few resource not released here.
|
||||
delete( draw_list.vertices )
|
||||
delete( draw_list.indices )
|
||||
delete( draw_list.calls )
|
||||
|
||||
LRU_free( & atlas.region_a.state )
|
||||
LRU_free( & atlas.region_b.state )
|
||||
LRU_free( & atlas.region_c.state )
|
||||
LRU_free( & atlas.region_d.state )
|
||||
|
||||
for idx : i32 = 0; idx < i32(len(shape_cache.storage)); idx += 1 {
|
||||
stroage_entry := & shape_cache.storage[idx]
|
||||
using stroage_entry
|
||||
|
||||
delete( glyphs )
|
||||
delete( positions )
|
||||
}
|
||||
LRU_free( & shape_cache.state )
|
||||
|
||||
delete( glyph_buffer.draw_list.vertices )
|
||||
delete( glyph_buffer.draw_list.indices )
|
||||
delete( glyph_buffer.draw_list.calls )
|
||||
|
||||
delete( glyph_buffer.clear_draw_list.vertices )
|
||||
delete( glyph_buffer.clear_draw_list.indices )
|
||||
delete( glyph_buffer.clear_draw_list.calls )
|
||||
|
||||
shaper_shutdown( & shaper_ctx )
|
||||
parser_shutdown( & parser_ctx )
|
||||
}
|
||||
|
||||
// ve_fontcache_load
|
||||
@ -352,10 +383,7 @@ load_font :: proc( ctx : ^Context, label : string, data : []byte, size_px : f32,
|
||||
used = true
|
||||
|
||||
parser_info = parser_load_font( & parser_ctx, label, data )
|
||||
// assert( parser_info != nil, "VEFontCache.load_font: Failed to load font info from parser" )
|
||||
|
||||
shaper_info = shaper_load_font( & shaper_ctx, label, data, transmute(rawptr) id )
|
||||
// assert( shaper_info != nil, "VEFontCache.load_font: Failed to load font from shaper")
|
||||
|
||||
size = size_px
|
||||
size_scale = size_px < 0.0 ? \
|
||||
@ -391,9 +419,9 @@ unload_font :: proc( ctx : ^Context, font : FontID )
|
||||
shaper_unload_font( & entry.shaper_info )
|
||||
}
|
||||
|
||||
#endregion("lifetime")
|
||||
//#endregion("lifetime")
|
||||
|
||||
#region("drawing")
|
||||
//#region("drawing")
|
||||
|
||||
// ve_fontcache_configure_snap
|
||||
configure_snap :: #force_inline proc( ctx : ^Context, snap_width, snap_height : u32 ) {
|
||||
@ -407,7 +435,7 @@ set_colour :: #force_inline proc "contextless" ( ctx : ^Context, colour : Co
|
||||
|
||||
draw_text :: proc( ctx : ^Context, font : FontID, text_utf8 : string, position, scale : Vec2 ) -> b32
|
||||
{
|
||||
// profile(#procedure)
|
||||
profile(#procedure)
|
||||
assert( ctx != nil )
|
||||
assert( font >= 0 && int(font) < len(ctx.entries) )
|
||||
|
||||
@ -469,9 +497,9 @@ flush_draw_list_layer :: proc( ctx : ^Context ) {
|
||||
draw_layer.calls_offset = len(draw_list.calls)
|
||||
}
|
||||
|
||||
#endregion("drawing")
|
||||
//#endregion("drawing")
|
||||
|
||||
#region("metrics")
|
||||
//#region("metrics")
|
||||
|
||||
measure_text_size :: proc( ctx : ^Context, font : FontID, text_utf8 : string ) -> (measured : Vec2)
|
||||
{
|
||||
@ -498,4 +526,4 @@ get_font_vertical_metrics :: #force_inline proc ( ctx : ^Context, font : FontID
|
||||
return
|
||||
}
|
||||
|
||||
#endregion("metrics")
|
||||
//#endregion("metrics")
|
||||
|
Loading…
Reference in New Issue
Block a user