update vefontcache to latest
This commit is contained in:
parent
08fff0f6fa
commit
109d2b0ff1
@ -6,33 +6,33 @@ The choice was made to keep the LRU cache implementation as close to the origina
|
||||
|
||||
import "base:runtime"
|
||||
|
||||
PoolListIter :: i32
|
||||
PoolListValue :: u64
|
||||
Pool_ListIter :: i32
|
||||
Pool_ListValue :: u64
|
||||
|
||||
PoolListItem :: struct {
|
||||
prev : PoolListIter,
|
||||
next : PoolListIter,
|
||||
value : PoolListValue,
|
||||
Pool_List_Item :: struct {
|
||||
prev : Pool_ListIter,
|
||||
next : Pool_ListIter,
|
||||
value : Pool_ListValue,
|
||||
}
|
||||
|
||||
PoolList :: struct {
|
||||
items : [dynamic]PoolListItem,
|
||||
free_list : [dynamic]PoolListIter,
|
||||
front : PoolListIter,
|
||||
back : PoolListIter,
|
||||
Pool_List :: struct {
|
||||
items : [dynamic]Pool_List_Item,
|
||||
free_list : [dynamic]Pool_ListIter,
|
||||
front : Pool_ListIter,
|
||||
back : Pool_ListIter,
|
||||
size : i32,
|
||||
capacity : i32,
|
||||
dbg_name : string,
|
||||
}
|
||||
|
||||
pool_list_init :: proc( pool : ^PoolList, capacity : i32, dbg_name : string = "" )
|
||||
pool_list_init :: proc( pool : ^Pool_List, capacity : i32, dbg_name : string = "" )
|
||||
{
|
||||
error : AllocatorError
|
||||
pool.items, error = make( [dynamic]PoolListItem, int(capacity) )
|
||||
error : Allocator_Error
|
||||
pool.items, error = make( [dynamic]Pool_List_Item, int(capacity) )
|
||||
assert( error == .None, "VEFontCache.pool_list_init : Failed to allocate items array")
|
||||
resize( & pool.items, capacity )
|
||||
|
||||
pool.free_list, error = make( [dynamic]PoolListIter, len = 0, cap = int(capacity) )
|
||||
pool.free_list, error = make( [dynamic]Pool_ListIter, len = 0, cap = int(capacity) )
|
||||
assert( error == .None, "VEFontCache.pool_list_init : Failed to allocate free_list array")
|
||||
resize( & pool.free_list, capacity )
|
||||
|
||||
@ -53,17 +53,17 @@ pool_list_init :: proc( pool : ^PoolList, capacity : i32, dbg_name : string = ""
|
||||
back = -1
|
||||
}
|
||||
|
||||
pool_list_free :: proc( pool : ^PoolList ) {
|
||||
pool_list_free :: proc( pool : ^Pool_List ) {
|
||||
delete( pool.items)
|
||||
delete( pool.free_list)
|
||||
}
|
||||
|
||||
pool_list_reload :: proc( pool : ^PoolList, allocator : Allocator ) {
|
||||
pool_list_reload :: proc( pool : ^Pool_List, allocator : Allocator ) {
|
||||
reload_array( & pool.items, allocator )
|
||||
reload_array( & pool.free_list, allocator )
|
||||
}
|
||||
|
||||
pool_list_push_front :: proc( pool : ^PoolList, value : PoolListValue )
|
||||
pool_list_push_front :: proc( pool : ^Pool_List, value : Pool_ListValue )
|
||||
{
|
||||
using pool
|
||||
if size >= capacity do return
|
||||
@ -90,7 +90,7 @@ pool_list_push_front :: proc( pool : ^PoolList, value : PoolListValue )
|
||||
size += 1
|
||||
}
|
||||
|
||||
pool_list_erase :: proc( pool : ^PoolList, iter : PoolListIter )
|
||||
pool_list_erase :: proc( pool : ^Pool_List, iter : Pool_ListIter )
|
||||
{
|
||||
using pool
|
||||
if size <= 0 do return
|
||||
@ -119,7 +119,7 @@ pool_list_erase :: proc( pool : ^PoolList, iter : PoolListIter )
|
||||
}
|
||||
}
|
||||
|
||||
pool_list_move_to_front :: #force_inline proc( pool : ^PoolList, iter : PoolListIter )
|
||||
pool_list_move_to_front :: #force_inline proc( pool : ^Pool_List, iter : Pool_ListIter )
|
||||
{
|
||||
using pool
|
||||
|
||||
@ -136,13 +136,13 @@ pool_list_move_to_front :: #force_inline proc( pool : ^PoolList, iter : PoolList
|
||||
front = iter
|
||||
}
|
||||
|
||||
pool_list_peek_back :: #force_inline proc ( pool : ^PoolList ) -> PoolListValue {
|
||||
pool_list_peek_back :: #force_inline proc ( pool : ^Pool_List ) -> Pool_ListValue {
|
||||
assert( pool.back != - 1 )
|
||||
value := pool.items[ pool.back ].value
|
||||
return value
|
||||
}
|
||||
|
||||
pool_list_pop_back :: #force_inline proc( pool : ^PoolList ) -> PoolListValue {
|
||||
pool_list_pop_back :: #force_inline proc( pool : ^Pool_List ) -> Pool_ListValue {
|
||||
if pool.size <= 0 do return 0
|
||||
assert( pool.back != -1 )
|
||||
|
||||
@ -153,8 +153,10 @@ pool_list_pop_back :: #force_inline proc( pool : ^PoolList ) -> PoolListValue {
|
||||
|
||||
LRU_Link :: struct {
|
||||
pad_top : u64,
|
||||
|
||||
value : i32,
|
||||
ptr : PoolListIter,
|
||||
ptr : Pool_ListIter,
|
||||
|
||||
pad_bottom : u64,
|
||||
}
|
||||
|
||||
@ -162,34 +164,34 @@ LRU_Cache :: struct {
|
||||
capacity : i32,
|
||||
num : i32,
|
||||
table : map[u64]LRU_Link,
|
||||
key_queue : PoolList,
|
||||
key_queue : Pool_List,
|
||||
}
|
||||
|
||||
LRU_init :: proc( cache : ^LRU_Cache, capacity : i32, dbg_name : string = "" ) {
|
||||
error : AllocatorError
|
||||
lru_init :: proc( cache : ^LRU_Cache, capacity : i32, dbg_name : string = "" ) {
|
||||
error : Allocator_Error
|
||||
cache.capacity = capacity
|
||||
cache.table, error = make( map[u64]LRU_Link, uint(capacity) )
|
||||
assert( error == .None, "VEFontCache.LRU_init : Failed to allocate cache's table")
|
||||
assert( error == .None, "VEFontCache.lru_init : Failed to allocate cache's table")
|
||||
|
||||
pool_list_init( & cache.key_queue, capacity, dbg_name = dbg_name )
|
||||
}
|
||||
|
||||
LRU_free :: proc( cache : ^LRU_Cache ) {
|
||||
lru_free :: proc( cache : ^LRU_Cache ) {
|
||||
pool_list_free( & cache.key_queue )
|
||||
delete( cache.table )
|
||||
}
|
||||
|
||||
LRU_reload :: #force_inline proc( cache : ^LRU_Cache, allocator : Allocator ) {
|
||||
lru_reload :: #force_inline proc( cache : ^LRU_Cache, allocator : Allocator ) {
|
||||
reload_map( & cache.table, allocator )
|
||||
pool_list_reload( & cache.key_queue, allocator )
|
||||
}
|
||||
|
||||
LRU_find :: #force_inline proc "contextless" ( cache : ^LRU_Cache, key : u64, must_find := false ) -> (LRU_Link, bool) {
|
||||
lru_find :: #force_inline proc "contextless" ( cache : ^LRU_Cache, key : u64, must_find := false ) -> (LRU_Link, bool) {
|
||||
link, success := cache.table[key]
|
||||
return link, success
|
||||
}
|
||||
|
||||
LRU_get :: #force_inline proc( cache: ^LRU_Cache, key : u64 ) -> i32 {
|
||||
lru_get :: #force_inline proc( cache: ^LRU_Cache, key : u64 ) -> i32 {
|
||||
if link, ok := &cache.table[ key ]; ok {
|
||||
pool_list_move_to_front(&cache.key_queue, link.ptr)
|
||||
return link.value
|
||||
@ -197,7 +199,7 @@ LRU_get :: #force_inline proc( cache: ^LRU_Cache, key : u64 ) -> i32 {
|
||||
return -1
|
||||
}
|
||||
|
||||
LRU_get_next_evicted :: #force_inline proc ( cache : ^LRU_Cache ) -> u64 {
|
||||
lru_get_next_evicted :: #force_inline proc ( cache : ^LRU_Cache ) -> u64 {
|
||||
if cache.key_queue.size >= cache.capacity {
|
||||
evict := pool_list_peek_back( & cache.key_queue )
|
||||
return evict
|
||||
@ -205,15 +207,15 @@ LRU_get_next_evicted :: #force_inline proc ( cache : ^LRU_Cache ) -> u64 {
|
||||
return 0xFFFFFFFFFFFFFFFF
|
||||
}
|
||||
|
||||
LRU_peek :: #force_inline proc ( cache : ^LRU_Cache, key : u64, must_find := false ) -> i32 {
|
||||
iter, success := LRU_find( cache, key, must_find )
|
||||
lru_peek :: #force_inline proc ( cache : ^LRU_Cache, key : u64, must_find := false ) -> i32 {
|
||||
iter, success := lru_find( cache, key, must_find )
|
||||
if success == false {
|
||||
return -1
|
||||
}
|
||||
return iter.value
|
||||
}
|
||||
|
||||
LRU_put :: #force_inline proc( cache : ^LRU_Cache, key : u64, value : i32 ) -> u64
|
||||
lru_put :: #force_inline proc( cache : ^LRU_Cache, key : u64, value : i32 ) -> u64
|
||||
{
|
||||
if link, ok := & cache.table[ key ]; ok {
|
||||
pool_list_move_to_front( & cache.key_queue, link.ptr )
|
||||
@ -237,8 +239,8 @@ LRU_put :: #force_inline proc( cache : ^LRU_Cache, key : u64, value : i32 ) -> u
|
||||
return evict
|
||||
}
|
||||
|
||||
LRU_refresh :: proc( cache : ^LRU_Cache, key : u64 ) {
|
||||
link, success := LRU_find( cache, key )
|
||||
lru_refresh :: proc( cache : ^LRU_Cache, key : u64 ) {
|
||||
link, success := lru_find( cache, key )
|
||||
pool_list_erase( & cache.key_queue, link.ptr )
|
||||
pool_list_push_front( & cache.key_queue, key )
|
||||
link.ptr = cache.key_queue.front
|
||||
|
@ -4,24 +4,24 @@ This is a port of the [VEFontCache](https://github.com/hypernewbie/VEFontCache)
|
||||
|
||||
Its original purpose was for use in game engines, however its rendeirng quality and performance is more than adequate for many other applications.
|
||||
|
||||
See: [docs/Readme.md](docs/Readme.md) for the library's interface
|
||||
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).
|
||||
Currently the scripts provided & the library itself were developed & tested on Windows. There are bash scripts for building on linux (they build on WSL but need additional testing).
|
||||
|
||||
The library depends on freetype, harfbuzz, & stb_truetype currently to build.
|
||||
The library depends on freetype, harfbuzz, & stb_truetype 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 Parser & Glyph shaper are abstracted to their own warpper interface
|
||||
* 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.
|
||||
* Curve quality step interpolation for glyph rendering can be set on a per font basis.
|
||||
|
||||
## TODOs
|
||||
|
||||
@ -35,18 +35,12 @@ Note: freetype and harfbuzz could technically be gutted if the user removes thei
|
||||
|
||||
### Optimization:
|
||||
|
||||
* Check if its better to store the generated glyph vertices if they need to be re-cached or directly drawn.
|
||||
* Check if its better to store the glyph vertices if they need to be re-cached to atlas 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 *(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?
|
||||
* draw_list
|
||||
* draw_layer
|
||||
* atlas.next_idx
|
||||
* glyph_draw_buffer
|
||||
* shape_cache
|
||||
* This would need to converge to the singlar draw_list on a per layer basis (then user reqeusts a draw_list layer there could a yield to wait for the jobs to finish); if the interface expects the user to issue the commands single-threaded unless, we just assume the user is going to feed the gpu the commands & data through separate threads as well (not ideal ux).
|
||||
* This would need to converge to the singlar draw_list on a per layer basis. The interface expects the user to issue commands single-threaded unless, its assumed the user is going to feed the gpu the commands & data through separate threads as well (not ideal ux).
|
||||
* How the contexts are given jobs should be left up to the user (can recommend a screen quadrant based approach in demo examples)
|
||||
|
||||
Failed Attempts:
|
||||
|
@ -1,6 +1,6 @@
|
||||
package vefontcache
|
||||
|
||||
AtlasRegionKind :: enum u8 {
|
||||
Atlas_Region_Kind :: enum u8 {
|
||||
None = 0x00,
|
||||
A = 0x41,
|
||||
B = 0x42,
|
||||
@ -10,7 +10,7 @@ AtlasRegionKind :: enum u8 {
|
||||
Ignore = 0xFF, // ve_fontcache_cache_glyph_to_atlas uses a -1 value in clear draw call
|
||||
}
|
||||
|
||||
AtlasRegion :: struct {
|
||||
Atlas_Region :: struct {
|
||||
state : LRU_Cache,
|
||||
|
||||
width : i32,
|
||||
@ -29,13 +29,13 @@ Atlas :: struct {
|
||||
|
||||
glyph_padding : i32,
|
||||
|
||||
region_a : AtlasRegion,
|
||||
region_b : AtlasRegion,
|
||||
region_c : AtlasRegion,
|
||||
region_d : AtlasRegion,
|
||||
region_a : Atlas_Region,
|
||||
region_b : Atlas_Region,
|
||||
region_c : Atlas_Region,
|
||||
region_d : Atlas_Region,
|
||||
}
|
||||
|
||||
atlas_bbox :: proc( atlas : ^Atlas, region : AtlasRegionKind, local_idx : i32 ) -> (position, size: Vec2)
|
||||
atlas_bbox :: proc( atlas : ^Atlas, region : Atlas_Region_Kind, local_idx : i32 ) -> (position, size: Vec2)
|
||||
{
|
||||
switch region
|
||||
{
|
||||
@ -79,14 +79,12 @@ atlas_bbox :: proc( atlas : ^Atlas, region : AtlasRegionKind, local_idx : i32 )
|
||||
position.x += f32(atlas.region_d.offset.x)
|
||||
position.y += f32(atlas.region_d.offset.y)
|
||||
|
||||
case .Ignore: fallthrough
|
||||
case .None: fallthrough
|
||||
case .E:
|
||||
case .Ignore, .None, .E:
|
||||
}
|
||||
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 : Atlas_Region_Kind, region : ^Atlas_Region, over_sample : Vec2)
|
||||
{
|
||||
if parser_is_glyph_empty(&entry.parser_info, glyph_index) {
|
||||
return .None, nil, {}
|
||||
@ -104,11 +102,11 @@ decide_codepoint_region :: proc(ctx : ^Context, entry : ^Entry, glyph_index : Gl
|
||||
bounds_height_scaled := i32(bounds_height * entry.size_scale + glyph_padding)
|
||||
|
||||
// Use a lookup table for faster region selection
|
||||
region_lookup := [4]struct { kind: AtlasRegionKind, region: ^AtlasRegion } {
|
||||
{ .A, & atlas.region_a },
|
||||
{ .B, & atlas.region_b },
|
||||
{ .C, & atlas.region_c },
|
||||
{ .D, & atlas.region_d },
|
||||
region_lookup := [4]struct { kind: Atlas_Region_Kind, region: ^Atlas_Region } {
|
||||
{ .A, & atlas.region_a },
|
||||
{ .B, & atlas.region_b },
|
||||
{ .C, & atlas.region_c },
|
||||
{ .D, & atlas.region_d },
|
||||
}
|
||||
|
||||
for region in region_lookup do if bounds_width_scaled <= region.region.width && bounds_height_scaled <= region.region.height {
|
||||
|
@ -3,7 +3,9 @@
|
||||
Notes
|
||||
---
|
||||
|
||||
Freetype implementation supports specifying a FT_Memory handle which is a pointer to a FT_MemoryRect. This can be used to define an allocator for the parser. Currently this library does not wrap this interface (yet). If using freetype its recommend to update `parser_init` with the necessary changes to wrap the context's backing allocator for freetype to utilize.
|
||||
The freetype setup is not finished. Specifically due to cache_glyph_freetype not parsing the glyph outline data structure properly.
|
||||
|
||||
Freetype supports specifying a FT_Memory handle which is a pointer to a FT_MemoryRect. This can be used to define an allocator for the parser. Currently this library does not wrap this interface (yet). If using freetype its recommend to update `parser_init` with the necessary changes to wrap the context's backing allocator for freetype to utilize.
|
||||
|
||||
```c
|
||||
struct FT_MemoryRec_
|
||||
@ -15,19 +17,19 @@ Freetype implementation supports specifying a FT_Memory handle which is a pointe
|
||||
};
|
||||
```
|
||||
|
||||
This library (seems) to perform best if the text commands are fed in 'whitespace aware chunks', where instead of feeding it entire blobs of text, the user identfies the "words" in the text and feeding the visible and whitespce chunks derived from this to draw_text as separate calls. This improves the caching of the text shapes. The downside is there has to be a time where the text is parsed into tokens beforehand so that the this iteration does not have to occur continously.
|
||||
This library (seems) to perform best if the text commands are fed in 'whitespace aware chunks', where instead of feeding it entire blobs of text, the user identfies "words" in the text and feeding the visible and whitespce chunks derived from this to draw_text as separate calls. It improves the caching of the text shapes. The downside is there has to be a time where the text is parsed into tokens beforehand so that the this iteration does not have to occur continously.
|
||||
|
||||
### startup
|
||||
|
||||
Initializes a provided context.
|
||||
|
||||
There are a large amount of parameters to tune the library instance to the user's preference. By default, keep in mind the library defaults to utilize stb_truetype as the font parser and harfbuzz (soon...) for the shaper.
|
||||
There are a large amount of parameters to tune the library instance to the user's preference. By default, keep in mind the library defaults to utilize stb_truetype as the font parser and harfbuzz for the shaper.
|
||||
|
||||
Much of the data structures within the context struct are not fixed-capacity allocations so make sure that the backing allocator utilized can handle it.
|
||||
Much of the data structures within the context struct are not fixed-capacity allocations so make sure that the backing allocator can handle it.
|
||||
|
||||
### hot_reload
|
||||
|
||||
The library supports being used in a dynamically loaded module. If this occurs simply make sure to call this procedure with a reference to the backing allocator provided during startup as all dynamic containers tend to lose a proper reference to the allocator's procedure.
|
||||
The library supports being used in a dynamically loaded module. If its hot-reloaded simply make sure to call this procedure with a reference to the backing allocator provided during startup as all dynamic containers tend to lose a proper reference to the allocator's procedure.
|
||||
|
||||
### shutdown
|
||||
|
||||
@ -45,7 +47,7 @@ Will provide the current cursor_pos for the resulting text drawn.
|
||||
|
||||
### set_color
|
||||
|
||||
Sets the color to utilize on `DrawCall`s for FrameBuffer.Target or .Target_Uncached passes
|
||||
Sets the color to utilize on `Draw_Call`s for FrameBuffer.Target or .Target_Uncached passes
|
||||
|
||||
### get_draw_list
|
||||
|
||||
@ -55,8 +57,8 @@ By default, if get_draw_list is called, it will first call `optimize_draw_list`
|
||||
### get_draw_list_layer
|
||||
|
||||
Get the enqueued draw_list for the current "layer".
|
||||
A layer is considered the slice of the drawlist's content from the last call to `flush_draw_list_layer` onward.
|
||||
By default, if get_draw_list_layer is called, it will first call `optimize_draw_list` for the user to optimize the slice (exlusively) of the draw list's draw calls. If this is undesired, make sure to pass `optimize_before_returning = false` in the arguments.
|
||||
A layer is considered the slice of the `Draw_List`'s content from the last call to `flush_draw_list_layer` onward.
|
||||
By default, if `get_draw_list_layer` is called, it will first call `optimize_draw_list` for the user to optimize the slice (exlusively) of the draw list's draw calls. If this is undesired, make sure to pass `optimize_before_returning = false` in the arguments.
|
||||
|
||||
The draw layer offsets are cleared with `flush_draw_list`
|
||||
|
||||
@ -70,7 +72,7 @@ Will update the draw list layer with the latest offset based on the current leng
|
||||
|
||||
### measure_text_size
|
||||
|
||||
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.
|
||||
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 `Shaped_Text` entry.
|
||||
|
||||
### get_font_vertical_metrics
|
||||
|
||||
|
@ -8,16 +8,16 @@ Vertex :: struct {
|
||||
u, v : f32,
|
||||
}
|
||||
|
||||
DrawCall :: struct {
|
||||
pass : FrameBufferPass,
|
||||
Draw_Call :: struct {
|
||||
pass : Frame_Buffer_Pass,
|
||||
start_index : u32,
|
||||
end_index : u32,
|
||||
clear_before_draw : b32,
|
||||
region : AtlasRegionKind,
|
||||
region : Atlas_Region_Kind,
|
||||
colour : Colour,
|
||||
}
|
||||
|
||||
DrawCall_Default :: DrawCall {
|
||||
Draw_Call_Default :: Draw_Call {
|
||||
pass = .None,
|
||||
start_index = 0,
|
||||
end_index = 0,
|
||||
@ -26,14 +26,14 @@ DrawCall_Default :: DrawCall {
|
||||
colour = { 1.0, 1.0, 1.0, 1.0 }
|
||||
}
|
||||
|
||||
DrawList :: struct {
|
||||
Draw_List :: struct {
|
||||
vertices : [dynamic]Vertex,
|
||||
indices : [dynamic]u32,
|
||||
calls : [dynamic]DrawCall,
|
||||
calls : [dynamic]Draw_Call,
|
||||
}
|
||||
|
||||
// TODO(Ed): This was a rough translation of the raw values the orignal was using, need to give better names...
|
||||
FrameBufferPass :: enum u32 {
|
||||
Frame_Buffer_Pass :: enum u32 {
|
||||
None = 0,
|
||||
Glyph = 1,
|
||||
Atlas = 2,
|
||||
@ -41,7 +41,7 @@ FrameBufferPass :: enum u32 {
|
||||
Target_Uncached = 4,
|
||||
}
|
||||
|
||||
GlyphDrawBuffer :: struct {
|
||||
Glyph_Draw_Buffer :: struct {
|
||||
over_sample : Vec2,
|
||||
batch : i32,
|
||||
width : i32,
|
||||
@ -49,11 +49,11 @@ GlyphDrawBuffer :: struct {
|
||||
draw_padding : i32,
|
||||
|
||||
batch_x : i32,
|
||||
clear_draw_list : DrawList,
|
||||
draw_list : DrawList,
|
||||
clear_draw_list : Draw_List,
|
||||
draw_list : Draw_List,
|
||||
}
|
||||
|
||||
blit_quad :: proc( draw_list : ^DrawList, p0 : Vec2 = {0, 0}, p1 : Vec2 = {1, 1}, uv0 : Vec2 = {0, 0}, uv1 : Vec2 = {1, 1} )
|
||||
blit_quad :: 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",
|
||||
@ -89,9 +89,9 @@ blit_quad :: proc( draw_list : ^DrawList, p0 : Vec2 = {0, 0}, p1 : Vec2 = {1, 1}
|
||||
}
|
||||
|
||||
// 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
|
||||
cache_glyph_freetype :: proc(ctx: ^Context, font: Font_ID, 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,
|
||||
draw_filled_path_freetype :: proc( draw_list : ^Draw_List, outside_point : Vec2, path : []Vertex,
|
||||
scale := Vec2 { 1, 1 },
|
||||
translate := Vec2 { 0, 0 },
|
||||
debug_print_verbose : b32 = false
|
||||
@ -106,7 +106,7 @@ cache_glyph_freetype :: proc(ctx: ^Context, font: FontID, glyph_index: Glyph, en
|
||||
}
|
||||
|
||||
v_offset := cast(u32) len(draw_list.vertices)
|
||||
for point in path
|
||||
for point in path
|
||||
{
|
||||
transformed_point := Vertex {
|
||||
pos = point.pos * scale + translate,
|
||||
@ -130,9 +130,9 @@ cache_glyph_freetype :: proc(ctx: ^Context, font: FontID, glyph_index: Glyph, en
|
||||
|
||||
// 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
|
||||
v_offset,
|
||||
v_offset + cast(u32)(len(path) - 1),
|
||||
v_offset + 1
|
||||
}
|
||||
append( indices, ..to_add[:] )
|
||||
}
|
||||
@ -158,8 +158,8 @@ cache_glyph_freetype :: proc(ctx: ^Context, font: FontID, glyph_index: Glyph, en
|
||||
return false
|
||||
}
|
||||
|
||||
draw := DrawCall_Default
|
||||
draw.pass = FrameBufferPass.Glyph
|
||||
draw := Draw_Call_Default
|
||||
draw.pass = Frame_Buffer_Pass.Glyph
|
||||
draw.start_index = cast(u32) len(ctx.draw_list.indices)
|
||||
|
||||
contours := slice.from_ptr(cast( [^]i16) outline.contours, int(outline.n_contours))
|
||||
@ -178,57 +178,57 @@ cache_glyph_freetype :: proc(ctx: ^Context, font: FontID, glyph_index: Glyph, en
|
||||
prev_point: Vec2
|
||||
first_point: Vec2
|
||||
|
||||
for idx := start_index; idx < end_index; idx += 1
|
||||
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
|
||||
{
|
||||
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)
|
||||
{
|
||||
// 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
|
||||
}
|
||||
// current is the first or following an on-curve point
|
||||
prev_point = current_pos
|
||||
}
|
||||
else
|
||||
{
|
||||
if idx == start_index {
|
||||
first_point = current_pos
|
||||
// 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 } )
|
||||
}
|
||||
}
|
||||
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 = {}
|
||||
|
||||
prev_point = current_pos
|
||||
}
|
||||
}
|
||||
|
||||
// ensure the contour is closed
|
||||
if path[0].pos != path[ len(path) - 1 ].pos {
|
||||
append(path, Vertex{pos = path[0].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 = {}
|
||||
}
|
||||
draw_filled_path(&ctx.draw_list, bounds_0, path[:], scale, translate, ctx.debug_print_verbose)
|
||||
clear(path)
|
||||
start_index = end_index
|
||||
}
|
||||
|
||||
// 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 {
|
||||
@ -244,7 +244,7 @@ cache_glyph_freetype :: proc(ctx: ^Context, font: FontID, glyph_index: Glyph, en
|
||||
}
|
||||
|
||||
// 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
|
||||
cache_glyph :: proc(ctx : ^Context, font : Font_ID, glyph_index : Glyph, entry : ^Entry, bounds_0, bounds_1 : Vec2, scale, translate : Vec2) -> b32
|
||||
{
|
||||
// profile(#procedure)
|
||||
if glyph_index == Glyph(0) {
|
||||
@ -265,8 +265,8 @@ cache_glyph :: proc(ctx : ^Context, font : FontID, glyph_index : Glyph, entry :
|
||||
|
||||
outside := Vec2{bounds_0.x - 21, bounds_0.y - 33}
|
||||
|
||||
draw := DrawCall_Default
|
||||
draw.pass = FrameBufferPass.Glyph
|
||||
draw := Draw_Call_Default
|
||||
draw.pass = Frame_Buffer_Pass.Glyph
|
||||
draw.start_index = u32(len(ctx.draw_list.indices))
|
||||
|
||||
path := &ctx.temp_path
|
||||
@ -276,8 +276,8 @@ cache_glyph :: proc(ctx : ^Context, font : FontID, glyph_index : Glyph, entry :
|
||||
{
|
||||
case .Move:
|
||||
if len(path) > 0 {
|
||||
draw_filled_path(&ctx.draw_list, outside, path[:], scale, translate, ctx.debug_print_verbose)
|
||||
clear(path)
|
||||
draw_filled_path(&ctx.draw_list, outside, path[:], scale, translate, ctx.debug_print_verbose)
|
||||
clear(path)
|
||||
}
|
||||
fallthrough
|
||||
|
||||
@ -329,13 +329,13 @@ cache_glyph :: proc(ctx : ^Context, font : FontID, glyph_index : Glyph, entry :
|
||||
* draw_text_shape : Glyph
|
||||
*/
|
||||
cache_glyph_to_atlas :: proc( ctx : ^Context,
|
||||
font : FontID,
|
||||
font : Font_ID,
|
||||
glyph_index : Glyph,
|
||||
lru_code : u64,
|
||||
atlas_index : i32,
|
||||
entry : ^Entry,
|
||||
region_kind : AtlasRegionKind,
|
||||
region : ^AtlasRegion,
|
||||
region_kind : Atlas_Region_Kind,
|
||||
region : ^Atlas_Region,
|
||||
over_sample : Vec2 )
|
||||
{
|
||||
// profile(#procedure)
|
||||
@ -356,24 +356,24 @@ cache_glyph_to_atlas :: proc( ctx : ^Context,
|
||||
{
|
||||
if region.next_idx < region.state.capacity
|
||||
{
|
||||
evicted := LRU_put( & region.state, lru_code, i32(region.next_idx) )
|
||||
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 )
|
||||
next_evict_codepoint := lru_get_next_evicted( & region.state )
|
||||
assert( next_evict_codepoint != 0xFFFFFFFFFFFFFFFF )
|
||||
|
||||
atlas_index = LRU_peek( & region.state, next_evict_codepoint, must_find = true )
|
||||
atlas_index = lru_peek( & region.state, next_evict_codepoint, must_find = true )
|
||||
assert( atlas_index != -1 )
|
||||
|
||||
evicted := LRU_put( & region.state, lru_code, atlas_index )
|
||||
evicted := lru_put( & region.state, lru_code, atlas_index )
|
||||
assert( evicted == next_evict_codepoint )
|
||||
}
|
||||
|
||||
assert( LRU_get( & region.state, lru_code ) != - 1 )
|
||||
assert( lru_get( & region.state, lru_code ) != - 1 )
|
||||
}
|
||||
|
||||
atlas := & ctx.atlas
|
||||
@ -420,7 +420,7 @@ cache_glyph_to_atlas :: proc( ctx : ^Context,
|
||||
glyph_buffer.batch_x += i32(gwidth_scaled_px)
|
||||
screenspace_x_form( & glyph_draw_translate, & glyph_draw_scale, glyph_buffer_size )
|
||||
|
||||
clear_target_region : DrawCall
|
||||
clear_target_region : Draw_Call
|
||||
{
|
||||
using clear_target_region
|
||||
pass = .Atlas
|
||||
@ -434,7 +434,7 @@ cache_glyph_to_atlas :: proc( ctx : ^Context,
|
||||
end_index = cast(u32) len(glyph_buffer.clear_draw_list.indices)
|
||||
}
|
||||
|
||||
blit_to_atlas : DrawCall
|
||||
blit_to_atlas : Draw_Call
|
||||
{
|
||||
using blit_to_atlas
|
||||
pass = .Atlas
|
||||
@ -456,11 +456,11 @@ cache_glyph_to_atlas :: proc( ctx : ^Context,
|
||||
}
|
||||
|
||||
// If the glyuph is found in the atlas, nothing occurs, otherwise, the glyph call is setup to catch it to the atlas
|
||||
check_glyph_in_atlas :: #force_inline proc( ctx : ^Context, font : FontID, entry : ^Entry, glyph_index : Glyph,
|
||||
check_glyph_in_atlas :: #force_inline proc( ctx : ^Context, font : Font_ID, entry : ^Entry, glyph_index : Glyph,
|
||||
lru_code : u64,
|
||||
atlas_index : i32,
|
||||
region_kind : AtlasRegionKind,
|
||||
region : ^AtlasRegion,
|
||||
region_kind : Atlas_Region_Kind,
|
||||
region : ^Atlas_Region,
|
||||
over_sample : Vec2
|
||||
) -> b32
|
||||
{
|
||||
@ -476,7 +476,7 @@ check_glyph_in_atlas :: #force_inline proc( ctx : ^Context, font : FontID, entry
|
||||
{
|
||||
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 )
|
||||
next_evict_codepoint := lru_get_next_evicted( & region.state )
|
||||
seen, success := ctx.temp_codepoint_seen[next_evict_codepoint]
|
||||
assert(success != false)
|
||||
|
||||
@ -488,13 +488,13 @@ check_glyph_in_atlas :: #force_inline proc( ctx : ^Context, font : FontID, entry
|
||||
cache_glyph_to_atlas( ctx, font, glyph_index, lru_code, atlas_index, entry, region_kind, region, over_sample )
|
||||
}
|
||||
|
||||
assert( LRU_get( & region.state, lru_code ) != -1 )
|
||||
assert( lru_get( & region.state, lru_code ) != -1 )
|
||||
mark_batch_codepoint_seen( ctx, lru_code)
|
||||
return true
|
||||
}
|
||||
|
||||
// ve_fontcache_clear_drawlist
|
||||
clear_draw_list :: #force_inline proc ( draw_list : ^DrawList ) {
|
||||
// ve_fontcache_clear_Draw_List
|
||||
clear_draw_list :: #force_inline proc ( draw_list : ^Draw_List ) {
|
||||
clear( & draw_list.calls )
|
||||
clear( & draw_list.indices )
|
||||
clear( & draw_list.vertices )
|
||||
@ -538,8 +538,8 @@ directly_draw_massive_glyph :: proc( ctx : ^Context,
|
||||
dst_size := glyph_dst_size * scale
|
||||
textspace_x_form( & glyph_position, & glyph_size, glyph_buffer_size )
|
||||
|
||||
// Add the glyph drawcall.
|
||||
calls : [2]DrawCall
|
||||
// Add the glyph Draw_Call.
|
||||
calls : [2]Draw_Call
|
||||
|
||||
draw_to_target := & calls[0]
|
||||
{
|
||||
@ -549,8 +549,8 @@ directly_draw_massive_glyph :: proc( ctx : ^Context,
|
||||
start_index = u32(len(ctx.draw_list.indices))
|
||||
|
||||
blit_quad( & ctx.draw_list,
|
||||
dst, dst + dst_size,
|
||||
glyph_position, glyph_position + glyph_size )
|
||||
dst, dst + dst_size,
|
||||
glyph_position, glyph_position + glyph_size )
|
||||
|
||||
end_index = u32(len(ctx.draw_list.indices))
|
||||
}
|
||||
@ -570,9 +570,9 @@ directly_draw_massive_glyph :: proc( ctx : ^Context,
|
||||
// outside_point represents the center point of the fan.
|
||||
//
|
||||
// Note(Original Author):
|
||||
// WARNING: doesn't actually append drawcall; caller is responsible for actually appending the drawcall.
|
||||
// WARNING: doesn't actually append Draw_Call; caller is responsible for actually appending the Draw_Call.
|
||||
// ve_fontcache_draw_filled_path
|
||||
draw_filled_path :: proc( draw_list : ^DrawList, outside_point : Vec2, path : []Vertex,
|
||||
draw_filled_path :: proc( draw_list : ^Draw_List, outside_point : Vec2, path : []Vertex,
|
||||
scale := Vec2 { 1, 1 },
|
||||
translate := Vec2 { 0, 0 },
|
||||
debug_print_verbose : b32 = false
|
||||
@ -615,7 +615,7 @@ draw_filled_path :: proc( draw_list : ^DrawList, outside_point : Vec2, path : []
|
||||
}
|
||||
}
|
||||
|
||||
draw_text_batch :: proc(ctx: ^Context, entry: ^Entry, shaped: ^ShapedText,
|
||||
draw_text_batch :: proc(ctx: ^Context, entry: ^Entry, shaped: ^Shaped_Text,
|
||||
batch_start_idx, batch_end_idx : i32,
|
||||
position, scale : Vec2,
|
||||
snap_width, snap_height : f32 )
|
||||
@ -634,7 +634,7 @@ draw_text_batch :: proc(ctx: ^Context, entry: ^Entry, shaped: ^ShapedText,
|
||||
|
||||
region_kind, region, over_sample := decide_codepoint_region( ctx, entry, glyph_index )
|
||||
lru_code := font_glyph_lru_code( entry.id, glyph_index )
|
||||
atlas_index := region_kind != .E ? LRU_get( & region.state, lru_code ) : -1
|
||||
atlas_index := region_kind != .E ? lru_get( & region.state, lru_code ) : -1
|
||||
bounds_0, bounds_1 := parser_get_glyph_box( & entry.parser_info, glyph_index )
|
||||
vbounds_0 := vec2(bounds_0)
|
||||
vbounds_1 := vec2(bounds_1)
|
||||
@ -660,7 +660,7 @@ draw_text_batch :: proc(ctx: ^Context, entry: ^Entry, shaped: ^ShapedText,
|
||||
dst_scale := glyph_scale * scale
|
||||
textspace_x_form( & slot_position, & glyph_scale, atlas_size )
|
||||
|
||||
call := DrawCall_Default
|
||||
call := Draw_Call_Default
|
||||
call.pass = .Target
|
||||
call.colour = ctx.colour
|
||||
call.start_index = u32(len(ctx.draw_list.indices))
|
||||
@ -677,9 +677,9 @@ draw_text_batch :: proc(ctx: ^Context, entry: ^Entry, shaped: ^ShapedText,
|
||||
|
||||
// Helper for draw_text, all raw text content should be confirmed to be either formatting or visible shapes before getting cached.
|
||||
draw_text_shape :: proc( ctx : ^Context,
|
||||
font : FontID,
|
||||
font : Font_ID,
|
||||
entry : ^Entry,
|
||||
shaped : ^ShapedText,
|
||||
shaped : ^Shaped_Text,
|
||||
position, scale : Vec2,
|
||||
snap_width, snap_height : f32
|
||||
) -> (cursor_pos : Vec2)
|
||||
@ -695,7 +695,7 @@ draw_text_shape :: proc( ctx : ^Context,
|
||||
lru_code := font_glyph_lru_code(entry.id, glyph_index)
|
||||
atlas_index := cast(i32) -1
|
||||
|
||||
if region_kind != .E do atlas_index = LRU_get( & region.state, lru_code )
|
||||
if region_kind != .E do atlas_index = lru_get( & region.state, lru_code )
|
||||
if check_glyph_in_atlas( ctx, font, entry, glyph_index, lru_code, atlas_index, region_kind, region, over_sample ) do continue
|
||||
|
||||
// We can no longer directly append the shape as it has missing glyphs in the atlas
|
||||
@ -720,7 +720,7 @@ draw_text_shape :: proc( ctx : ^Context,
|
||||
flush_glyph_buffer_to_atlas :: proc( ctx : ^Context )
|
||||
{
|
||||
// profile(#procedure)
|
||||
// Flush drawcalls to draw list
|
||||
// Flush Draw_Calls to draw list
|
||||
merge_draw_list( & ctx.draw_list, & ctx.glyph_buffer.clear_draw_list )
|
||||
merge_draw_list( & ctx.draw_list, & ctx.glyph_buffer.draw_list)
|
||||
clear_draw_list( & ctx.glyph_buffer.draw_list )
|
||||
@ -729,7 +729,7 @@ flush_glyph_buffer_to_atlas :: proc( ctx : ^Context )
|
||||
// Clear glyph_update_FBO
|
||||
if ctx.glyph_buffer.batch_x != 0
|
||||
{
|
||||
call := DrawCall_Default
|
||||
call := Draw_Call_Default
|
||||
call.pass = .Glyph
|
||||
call.start_index = 0
|
||||
call.end_index = 0
|
||||
@ -739,11 +739,11 @@ flush_glyph_buffer_to_atlas :: proc( ctx : ^Context )
|
||||
}
|
||||
}
|
||||
|
||||
// ve_fontcache_merge_drawlist
|
||||
merge_draw_list :: proc( dst, src : ^DrawList )
|
||||
// ve_fontcache_merge_Draw_List
|
||||
merge_draw_list :: proc( dst, src : ^Draw_List )
|
||||
{
|
||||
// profile(#procedure)
|
||||
error : AllocatorError
|
||||
error : Allocator_Error
|
||||
|
||||
v_offset := cast(u32) len( dst.vertices )
|
||||
num_appended : int
|
||||
@ -766,11 +766,11 @@ merge_draw_list :: proc( dst, src : ^DrawList )
|
||||
}
|
||||
}
|
||||
|
||||
optimize_draw_list :: proc(draw_list: ^DrawList, call_offset: int) {
|
||||
optimize_draw_list :: proc(draw_list: ^Draw_List, call_offset: int) {
|
||||
// profile(#procedure)
|
||||
assert(draw_list != nil)
|
||||
|
||||
can_merge_draw_calls :: #force_inline proc "contextless" ( a, b : ^DrawCall ) -> bool {
|
||||
can_merge_draw_calls :: #force_inline proc "contextless" ( a, b : ^Draw_Call ) -> bool {
|
||||
result := \
|
||||
a.pass == b.pass &&
|
||||
a.end_index == b.start_index &&
|
||||
|
@ -27,16 +27,12 @@ import "core:mem"
|
||||
Kilobyte :: mem.Kilobyte
|
||||
slice_ptr :: mem.slice_ptr
|
||||
|
||||
Allocator :: mem.Allocator
|
||||
AllocatorError :: mem.Allocator_Error
|
||||
Allocator :: mem.Allocator
|
||||
Allocator_Error :: mem.Allocator_Error
|
||||
|
||||
Arena :: mem.Arena
|
||||
arena_allocator :: mem.arena_allocator
|
||||
arena_init :: mem.arena_init
|
||||
import "codebase:grime"
|
||||
log :: grime.log
|
||||
logf :: grime.logf
|
||||
profile :: grime.profile
|
||||
|
||||
//#region("Proc overload mappings")
|
||||
|
||||
|
@ -4,7 +4,7 @@ import "base:runtime"
|
||||
import "core:simd"
|
||||
import "core:math"
|
||||
|
||||
// import core_log "core:log"
|
||||
import core_log "core:log"
|
||||
|
||||
Colour :: [4]f32
|
||||
Vec2 :: [2]f32
|
||||
@ -23,21 +23,21 @@ vec2i_from_vec2 :: #force_inline proc "contextless" ( v2 : Vec2 ) -> Vec2
|
||||
// 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 ) {
|
||||
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)
|
||||
|
||||
// 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 ) {
|
||||
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)
|
||||
|
||||
// core_log.logf( level, fmt, ..args, location = loc )
|
||||
// }
|
||||
core_log.logf( level, fmt, ..args, location = loc )
|
||||
}
|
||||
|
||||
reload_array :: proc( self : ^[dynamic]$Type, allocator : Allocator ) {
|
||||
raw := transmute( ^runtime.Raw_Dynamic_Array) self
|
||||
@ -49,7 +49,7 @@ reload_map :: proc( self : ^map [$KeyType] $EntryType, allocator : Allocator ) {
|
||||
raw.allocator = allocator
|
||||
}
|
||||
|
||||
font_glyph_lru_code :: #force_inline proc "contextless" ( font : FontID, glyph_index : Glyph ) -> (lru_code : u64) {
|
||||
font_glyph_lru_code :: #force_inline proc "contextless" ( font : Font_ID, glyph_index : Glyph ) -> (lru_code : u64) {
|
||||
lru_code = u64(glyph_index) + ( ( 0x100000000 * u64(font) ) & 0xFFFFFFFF00000000 )
|
||||
return
|
||||
}
|
||||
@ -71,9 +71,11 @@ reset_batch_codepoint_state :: #force_inline proc( ctx : ^Context ) {
|
||||
ctx.temp_codepoint_seen_num = 0
|
||||
}
|
||||
|
||||
USE_F64_PRECISION_ON_X_FORM_OPS :: false
|
||||
|
||||
screenspace_x_form :: #force_inline proc "contextless" ( position, scale : ^Vec2, size : Vec2 )
|
||||
{
|
||||
if true
|
||||
when USE_F64_PRECISION_ON_X_FORM_OPS
|
||||
{
|
||||
pos_64 := vec2_64_from_vec2(position^)
|
||||
scale_64 := vec2_64_from_vec2(scale^)
|
||||
@ -101,7 +103,7 @@ screenspace_x_form :: #force_inline proc "contextless" ( position, scale : ^Vec2
|
||||
|
||||
textspace_x_form :: #force_inline proc "contextless" ( position, scale : ^Vec2, size : Vec2 )
|
||||
{
|
||||
if true
|
||||
when USE_F64_PRECISION_ON_X_FORM_OPS
|
||||
{
|
||||
pos_64 := vec2_64_from_vec2(position^)
|
||||
scale_64 := vec2_64_from_vec2(scale^)
|
||||
@ -121,9 +123,9 @@ textspace_x_form :: #force_inline proc "contextless" ( position, scale : ^Vec2,
|
||||
}
|
||||
}
|
||||
|
||||
Use_SIMD_For_Bezier_Ops :: false
|
||||
USE_MANUAL_SIMD_FOR_BEZIER_OPS :: false
|
||||
|
||||
when ! Use_SIMD_For_Bezier_Ops
|
||||
when ! USE_MANUAL_SIMD_FOR_BEZIER_OPS
|
||||
{
|
||||
// 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
|
||||
|
@ -16,14 +16,14 @@ import "core:slice"
|
||||
import stbtt "vendor:stb/truetype"
|
||||
import freetype "thirdparty:freetype"
|
||||
|
||||
ParserKind :: enum u32 {
|
||||
Parser_Kind :: enum u32 {
|
||||
STB_TrueType,
|
||||
Freetype,
|
||||
}
|
||||
|
||||
ParserFontInfo :: struct {
|
||||
Parser_Font_Info :: struct {
|
||||
label : string,
|
||||
kind : ParserKind,
|
||||
kind : Parser_Kind,
|
||||
using _ : struct #raw_union {
|
||||
stbtt_info : stbtt.fontinfo,
|
||||
freetype_info : freetype.Face
|
||||
@ -31,7 +31,7 @@ ParserFontInfo :: struct {
|
||||
data : []byte,
|
||||
}
|
||||
|
||||
GlyphVertType :: enum u8 {
|
||||
Glyph_Vert_Type :: enum u8 {
|
||||
None,
|
||||
Move = 1,
|
||||
Line,
|
||||
@ -40,22 +40,22 @@ GlyphVertType :: enum u8 {
|
||||
}
|
||||
|
||||
// Based directly off of stb_truetype's vertex
|
||||
ParserGlyphVertex :: struct {
|
||||
Parser_Glyph_Vertex :: struct {
|
||||
x, y : i16,
|
||||
contour_x0, contour_y0 : i16,
|
||||
contour_x1, contour_y1 : i16,
|
||||
type : GlyphVertType,
|
||||
type : Glyph_Vert_Type,
|
||||
padding : u8,
|
||||
}
|
||||
// A shape can be a dynamic array free_type or an opaque set of data handled by stb_truetype
|
||||
ParserGlyphShape :: [dynamic]ParserGlyphVertex
|
||||
Parser_Glyph_Shape :: [dynamic]Parser_Glyph_Vertex
|
||||
|
||||
ParserContext :: struct {
|
||||
kind : ParserKind,
|
||||
Parser_Context :: struct {
|
||||
kind : Parser_Kind,
|
||||
ft_library : freetype.Library,
|
||||
}
|
||||
|
||||
parser_init :: proc( ctx : ^ParserContext, kind : ParserKind )
|
||||
parser_init :: proc( ctx : ^Parser_Context, kind : Parser_Kind )
|
||||
{
|
||||
switch kind
|
||||
{
|
||||
@ -70,17 +70,23 @@ parser_init :: proc( ctx : ^ParserContext, kind : ParserKind )
|
||||
ctx.kind = kind
|
||||
}
|
||||
|
||||
parser_shutdown :: proc( ctx : ^ParserContext ) {
|
||||
parser_shutdown :: proc( ctx : ^Parser_Context ) {
|
||||
// TODO(Ed): Implement
|
||||
}
|
||||
|
||||
parser_load_font :: proc( ctx : ^ParserContext, label : string, data : []byte ) -> (font : ParserFontInfo)
|
||||
parser_load_font :: proc( ctx : ^Parser_Context, label : string, data : []byte ) -> (font : Parser_Font_Info)
|
||||
{
|
||||
switch ctx.kind
|
||||
{
|
||||
case .Freetype:
|
||||
error := freetype.new_memory_face( ctx.ft_library, raw_data(data), cast(i32) len(data), 0, & font.freetype_info )
|
||||
if error != .Ok do return
|
||||
when ODIN_OS == .Windows {
|
||||
error := freetype.new_memory_face( ctx.ft_library, raw_data(data), cast(i32) len(data), 0, & font.freetype_info )
|
||||
if error != .Ok do return
|
||||
}
|
||||
else when ODIN_OS == .Linux {
|
||||
error := freetype.new_memory_face( ctx.ft_library, raw_data(data), cast(i64) len(data), 0, & font.freetype_info )
|
||||
if error != .Ok do return
|
||||
}
|
||||
|
||||
case .STB_TrueType:
|
||||
success := stbtt.InitFont( & font.stbtt_info, raw_data(data), 0 )
|
||||
@ -93,7 +99,7 @@ parser_load_font :: proc( ctx : ^ParserContext, label : string, data : []byte )
|
||||
return
|
||||
}
|
||||
|
||||
parser_unload_font :: proc( font : ^ParserFontInfo )
|
||||
parser_unload_font :: proc( font : ^Parser_Font_Info )
|
||||
{
|
||||
switch font.kind {
|
||||
case .Freetype:
|
||||
@ -105,12 +111,17 @@ parser_unload_font :: proc( font : ^ParserFontInfo )
|
||||
}
|
||||
}
|
||||
|
||||
parser_find_glyph_index :: #force_inline proc "contextless" ( font : ^ParserFontInfo, codepoint : rune ) -> (glyph_index : Glyph)
|
||||
parser_find_glyph_index :: #force_inline proc "contextless" ( font : ^Parser_Font_Info, codepoint : rune ) -> (glyph_index : Glyph)
|
||||
{
|
||||
switch font.kind
|
||||
{
|
||||
case .Freetype:
|
||||
glyph_index = transmute(Glyph) freetype.get_char_index( font.freetype_info, transmute(u32) codepoint )
|
||||
when ODIN_OS == .Windows {
|
||||
glyph_index = transmute(Glyph) freetype.get_char_index( font.freetype_info, transmute(u32) codepoint )
|
||||
}
|
||||
else when ODIN_OS == .Linux {
|
||||
glyph_index = transmute(Glyph) freetype.get_char_index( font.freetype_info, cast(u64) codepoint )
|
||||
}
|
||||
return
|
||||
|
||||
case .STB_TrueType:
|
||||
@ -120,7 +131,7 @@ parser_find_glyph_index :: #force_inline proc "contextless" ( font : ^ParserFont
|
||||
return Glyph(-1)
|
||||
}
|
||||
|
||||
parser_free_shape :: proc( font : ^ParserFontInfo, shape : ParserGlyphShape )
|
||||
parser_free_shape :: proc( font : ^Parser_Font_Info, shape : Parser_Glyph_Shape )
|
||||
{
|
||||
switch font.kind
|
||||
{
|
||||
@ -132,12 +143,19 @@ parser_free_shape :: proc( font : ^ParserFontInfo, shape : ParserGlyphShape )
|
||||
}
|
||||
}
|
||||
|
||||
parser_get_codepoint_horizontal_metrics :: #force_inline proc "contextless" ( font : ^ParserFontInfo, codepoint : rune ) -> ( advance, to_left_side_glyph : i32 )
|
||||
parser_get_codepoint_horizontal_metrics :: #force_inline proc "contextless" ( font : ^Parser_Font_Info, codepoint : rune ) -> ( advance, to_left_side_glyph : i32 )
|
||||
{
|
||||
switch font.kind
|
||||
{
|
||||
case .Freetype:
|
||||
glyph_index := transmute(Glyph) freetype.get_char_index( font.freetype_info, transmute(u32) codepoint )
|
||||
glyph_index : Glyph
|
||||
when ODIN_OS == .Windows {
|
||||
glyph_index = transmute(Glyph) freetype.get_char_index( font.freetype_info, transmute(u32) codepoint )
|
||||
}
|
||||
else when ODIN_OS == .Linux {
|
||||
glyph_index = transmute(Glyph) freetype.get_char_index( font.freetype_info, cast(u64) codepoint )
|
||||
}
|
||||
|
||||
if glyph_index != 0
|
||||
{
|
||||
freetype.load_glyph( font.freetype_info, c.uint(codepoint), { .No_Bitmap, .No_Hinting, .No_Scale } )
|
||||
@ -156,13 +174,22 @@ parser_get_codepoint_horizontal_metrics :: #force_inline proc "contextless" ( fo
|
||||
return
|
||||
}
|
||||
|
||||
parser_get_codepoint_kern_advance :: #force_inline proc "contextless" ( font : ^ParserFontInfo, prev_codepoint, codepoint : rune ) -> i32
|
||||
parser_get_codepoint_kern_advance :: #force_inline proc "contextless" ( font : ^Parser_Font_Info, prev_codepoint, codepoint : rune ) -> i32
|
||||
{
|
||||
switch font.kind
|
||||
{
|
||||
case .Freetype:
|
||||
prev_glyph_index := transmute(Glyph) freetype.get_char_index( font.freetype_info, transmute(u32) prev_codepoint )
|
||||
glyph_index := transmute(Glyph) freetype.get_char_index( font.freetype_info, transmute(u32) codepoint )
|
||||
prev_glyph_index : Glyph
|
||||
glyph_index : Glyph
|
||||
when ODIN_OS == .Windows {
|
||||
prev_glyph_index = transmute(Glyph) freetype.get_char_index( font.freetype_info, transmute(u32) prev_codepoint )
|
||||
glyph_index = transmute(Glyph) freetype.get_char_index( font.freetype_info, transmute(u32) codepoint )
|
||||
}
|
||||
else when ODIN_OS == .Linux {
|
||||
prev_glyph_index = transmute(Glyph) freetype.get_char_index( font.freetype_info, cast(u64) prev_codepoint )
|
||||
glyph_index = transmute(Glyph) freetype.get_char_index( font.freetype_info, cast(u64) codepoint )
|
||||
}
|
||||
|
||||
if prev_glyph_index != 0 && glyph_index != 0
|
||||
{
|
||||
kerning : freetype.Vector
|
||||
@ -176,7 +203,7 @@ parser_get_codepoint_kern_advance :: #force_inline proc "contextless" ( font : ^
|
||||
return -1
|
||||
}
|
||||
|
||||
parser_get_font_vertical_metrics :: #force_inline proc "contextless" ( font : ^ParserFontInfo ) -> (ascent, descent, line_gap : i32 )
|
||||
parser_get_font_vertical_metrics :: #force_inline proc "contextless" ( font : ^Parser_Font_Info ) -> (ascent, descent, line_gap : i32 )
|
||||
{
|
||||
switch font.kind
|
||||
{
|
||||
@ -192,7 +219,7 @@ parser_get_font_vertical_metrics :: #force_inline proc "contextless" ( font : ^P
|
||||
return
|
||||
}
|
||||
|
||||
parser_get_glyph_box :: #force_inline proc ( font : ^ParserFontInfo, glyph_index : Glyph ) -> (bounds_0, bounds_1 : Vec2i)
|
||||
parser_get_glyph_box :: #force_inline proc ( font : ^Parser_Font_Info, glyph_index : Glyph ) -> (bounds_0, bounds_1 : Vec2i)
|
||||
{
|
||||
switch font.kind
|
||||
{
|
||||
@ -215,7 +242,7 @@ parser_get_glyph_box :: #force_inline proc ( font : ^ParserFontInfo, glyph_index
|
||||
return
|
||||
}
|
||||
|
||||
parser_get_glyph_shape :: proc( font : ^ParserFontInfo, glyph_index : Glyph ) -> (shape : ParserGlyphShape, error : AllocatorError)
|
||||
parser_get_glyph_shape :: proc( font : ^Parser_Font_Info, glyph_index : Glyph ) -> (shape : Parser_Glyph_Shape, error : Allocator_Error)
|
||||
{
|
||||
switch font.kind
|
||||
{
|
||||
@ -232,14 +259,14 @@ parser_get_glyph_shape :: proc( font : ^ParserFontInfo, glyph_index : Glyph ) ->
|
||||
shape_raw.len = int(nverts)
|
||||
shape_raw.cap = int(nverts)
|
||||
shape_raw.allocator = runtime.nil_allocator()
|
||||
error = AllocatorError.None
|
||||
error = Allocator_Error.None
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
parser_is_glyph_empty :: #force_inline proc "contextless" ( font : ^ParserFontInfo, glyph_index : Glyph ) -> b32
|
||||
parser_is_glyph_empty :: #force_inline proc "contextless" ( font : ^Parser_Font_Info, glyph_index : Glyph ) -> b32
|
||||
{
|
||||
switch font.kind
|
||||
{
|
||||
@ -262,7 +289,7 @@ parser_is_glyph_empty :: #force_inline proc "contextless" ( font : ^ParserFontIn
|
||||
return false
|
||||
}
|
||||
|
||||
parser_scale :: #force_inline proc "contextless" ( font : ^ParserFontInfo, size : f32 ) -> f32
|
||||
parser_scale :: #force_inline proc "contextless" ( font : ^Parser_Font_Info, size : f32 ) -> f32
|
||||
{
|
||||
size_scale := size < 0.0 ? \
|
||||
parser_scale_for_pixel_height( font, -size ) \
|
||||
@ -271,7 +298,7 @@ parser_scale :: #force_inline proc "contextless" ( font : ^ParserFontInfo, size
|
||||
return size_scale
|
||||
}
|
||||
|
||||
parser_scale_for_pixel_height :: #force_inline proc "contextless" ( font : ^ParserFontInfo, size : f32 ) -> f32
|
||||
parser_scale_for_pixel_height :: #force_inline proc "contextless" ( font : ^Parser_Font_Info, size : f32 ) -> f32
|
||||
{
|
||||
switch font.kind {
|
||||
case .Freetype:
|
||||
@ -285,7 +312,7 @@ parser_scale_for_pixel_height :: #force_inline proc "contextless" ( font : ^Pars
|
||||
return 0
|
||||
}
|
||||
|
||||
parser_scale_for_mapping_em_to_pixels :: #force_inline proc "contextless" ( font : ^ParserFontInfo, size : f32 ) -> f32
|
||||
parser_scale_for_mapping_em_to_pixels :: #force_inline proc "contextless" ( font : ^Parser_Font_Info, size : f32 ) -> f32
|
||||
{
|
||||
switch font.kind {
|
||||
case .Freetype:
|
||||
|
@ -1,14 +1,14 @@
|
||||
package vefontcache
|
||||
|
||||
ShapedText :: struct {
|
||||
Shaped_Text :: struct {
|
||||
glyphs : [dynamic]Glyph,
|
||||
positions : [dynamic]Vec2,
|
||||
end_cursor_pos : Vec2,
|
||||
size : Vec2,
|
||||
}
|
||||
|
||||
ShapedTextCache :: struct {
|
||||
storage : [dynamic]ShapedText,
|
||||
Shaped_Text_Cache :: struct {
|
||||
storage : [dynamic]Shaped_Text,
|
||||
state : LRU_Cache,
|
||||
next_cache_id : i32,
|
||||
}
|
||||
@ -19,11 +19,11 @@ shape_lru_hash :: #force_inline proc "contextless" ( hash : ^u64, bytes : []byte
|
||||
}
|
||||
}
|
||||
|
||||
shape_text_cached :: proc( ctx : ^Context, font : FontID, text_utf8 : string, entry : ^Entry ) -> ^ShapedText
|
||||
shape_text_cached :: proc( ctx : ^Context, font : Font_ID, text_utf8 : string, entry : ^Entry ) -> ^Shaped_Text
|
||||
{
|
||||
// profile(#procedure)
|
||||
font := font
|
||||
font_bytes := slice_ptr( transmute(^byte) & font, size_of(FontID) )
|
||||
font_bytes := slice_ptr( transmute(^byte) & font, size_of(Font_ID) )
|
||||
text_bytes := transmute( []byte) text_utf8
|
||||
|
||||
lru_code : u64
|
||||
@ -33,23 +33,23 @@ shape_text_cached :: proc( ctx : ^Context, font : FontID, text_utf8 : string, en
|
||||
shape_cache := & ctx.shape_cache
|
||||
state := & ctx.shape_cache.state
|
||||
|
||||
shape_cache_idx := LRU_get( state, lru_code )
|
||||
shape_cache_idx := lru_get( state, lru_code )
|
||||
if shape_cache_idx == -1
|
||||
{
|
||||
if shape_cache.next_cache_id < i32(state.capacity) {
|
||||
shape_cache_idx = shape_cache.next_cache_id
|
||||
shape_cache.next_cache_id += 1
|
||||
evicted := LRU_put( state, lru_code, shape_cache_idx )
|
||||
evicted := lru_put( state, lru_code, shape_cache_idx )
|
||||
}
|
||||
else
|
||||
{
|
||||
next_evict_idx := LRU_get_next_evicted( state )
|
||||
next_evict_idx := lru_get_next_evicted( state )
|
||||
assert( next_evict_idx != 0xFFFFFFFFFFFFFFFF )
|
||||
|
||||
shape_cache_idx = LRU_peek( state, next_evict_idx, must_find = true )
|
||||
shape_cache_idx = lru_peek( state, next_evict_idx, must_find = true )
|
||||
assert( shape_cache_idx != - 1 )
|
||||
|
||||
LRU_put( state, lru_code, shape_cache_idx )
|
||||
lru_put( state, lru_code, shape_cache_idx )
|
||||
}
|
||||
|
||||
shape_entry := & shape_cache.storage[ shape_cache_idx ]
|
||||
@ -59,7 +59,7 @@ shape_text_cached :: proc( ctx : ^Context, font : FontID, text_utf8 : string, en
|
||||
return & shape_cache.storage[ shape_cache_idx ]
|
||||
}
|
||||
|
||||
shape_text_uncached :: proc( ctx : ^Context, font : FontID, text_utf8 : string, entry : ^Entry, output : ^ShapedText )
|
||||
shape_text_uncached :: proc( ctx : ^Context, font : Font_ID, text_utf8 : string, entry : ^Entry, output : ^Shaped_Text )
|
||||
{
|
||||
// profile(#procedure)
|
||||
assert( ctx != nil )
|
||||
@ -106,7 +106,7 @@ shape_text_uncached :: proc( ctx : ^Context, font : FontID, text_utf8 : string,
|
||||
prev_codepoint = rune(0)
|
||||
continue
|
||||
}
|
||||
if abs( entry.size ) <= Advance_Snap_Smallfont_Size {
|
||||
if abs( entry.size ) <= ADVANCE_SNAP_SMALLFONT_SIZE {
|
||||
position.x = position.x
|
||||
}
|
||||
|
||||
|
@ -6,35 +6,35 @@ Note(Ed): The only reason I didn't directly use harfbuzz is because hamza exists
|
||||
import "core:c"
|
||||
import "thirdparty:harfbuzz"
|
||||
|
||||
ShaperKind :: enum {
|
||||
Shaper_Kind :: enum {
|
||||
Naive = 0,
|
||||
Harfbuzz = 1,
|
||||
}
|
||||
|
||||
ShaperContext :: struct {
|
||||
Shaper_Context :: struct {
|
||||
hb_buffer : harfbuzz.Buffer,
|
||||
}
|
||||
|
||||
ShaperInfo :: struct {
|
||||
Shaper_Info :: struct {
|
||||
blob : harfbuzz.Blob,
|
||||
face : harfbuzz.Face,
|
||||
font : harfbuzz.Font,
|
||||
}
|
||||
|
||||
shaper_init :: proc( ctx : ^ShaperContext )
|
||||
shaper_init :: proc( ctx : ^Shaper_Context )
|
||||
{
|
||||
ctx.hb_buffer = harfbuzz.buffer_create()
|
||||
assert( ctx.hb_buffer != nil, "VEFontCache.shaper_init: Failed to create harfbuzz buffer")
|
||||
}
|
||||
|
||||
shaper_shutdown :: proc( ctx : ^ShaperContext )
|
||||
shaper_shutdown :: proc( ctx : ^Shaper_Context )
|
||||
{
|
||||
if ctx.hb_buffer != nil {
|
||||
harfbuzz.buffer_destroy( ctx.hb_buffer )
|
||||
}
|
||||
}
|
||||
|
||||
shaper_load_font :: proc( ctx : ^ShaperContext, label : string, data : []byte, user_data : rawptr ) -> (info : ShaperInfo)
|
||||
shaper_load_font :: proc( ctx : ^Shaper_Context, label : string, data : []byte, user_data : rawptr ) -> (info : Shaper_Info)
|
||||
{
|
||||
using info
|
||||
blob = harfbuzz.blob_create( raw_data(data), cast(c.uint) len(data), harfbuzz.Memory_Mode.READONLY, user_data, nil )
|
||||
@ -43,7 +43,7 @@ shaper_load_font :: proc( ctx : ^ShaperContext, label : string, data : []byte, u
|
||||
return
|
||||
}
|
||||
|
||||
shaper_unload_font :: proc( ctx : ^ShaperInfo )
|
||||
shaper_unload_font :: proc( ctx : ^Shaper_Info )
|
||||
{
|
||||
using ctx
|
||||
if blob != nil do harfbuzz.font_destroy( font )
|
||||
@ -51,7 +51,7 @@ shaper_unload_font :: proc( ctx : ^ShaperInfo )
|
||||
if blob != nil do harfbuzz.blob_destroy( blob )
|
||||
}
|
||||
|
||||
shaper_shape_from_text :: proc( ctx : ^ShaperContext, info : ^ShaperInfo, output :^ShapedText, text_utf8 : string,
|
||||
shaper_shape_from_text :: proc( ctx : ^Shaper_Context, info : ^Shaper_Info, output :^Shaped_Text, text_utf8 : string,
|
||||
ascent, descent, line_gap : i32, size, size_scale : f32 )
|
||||
{
|
||||
// profile(#procedure)
|
||||
@ -69,7 +69,7 @@ shaper_shape_from_text :: proc( ctx : ^ShaperContext, info : ^ShaperInfo, output
|
||||
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,
|
||||
shape_run :: proc( buffer : harfbuzz.Buffer, script : harfbuzz.Script, font : harfbuzz.Font, output : ^Shaped_Text,
|
||||
position, vertical_position, max_line_width: ^f32, line_count: ^int,
|
||||
ascent, descent, line_gap, size, size_scale: f32 )
|
||||
{
|
||||
@ -105,7 +105,7 @@ shaper_shape_from_text :: proc( ctx : ^ShaperContext, info : ^ShaperInfo, output
|
||||
(line_count^) += 1
|
||||
continue
|
||||
}
|
||||
if abs( size ) <= Advance_Snap_Smallfont_Size
|
||||
if abs( size ) <= ADVANCE_SNAP_SMALLFONT_SIZE
|
||||
{
|
||||
(position^) = ceil( position^ )
|
||||
}
|
||||
|
@ -7,15 +7,15 @@ package vefontcache
|
||||
|
||||
import "base:runtime"
|
||||
|
||||
Advance_Snap_Smallfont_Size :: 0
|
||||
ADVANCE_SNAP_SMALLFONT_SIZE :: 0
|
||||
|
||||
FontID :: distinct i64
|
||||
Font_ID :: distinct i64
|
||||
Glyph :: distinct i32
|
||||
|
||||
Entry :: struct {
|
||||
parser_info : ParserFontInfo,
|
||||
shaper_info : ShaperInfo,
|
||||
id : FontID,
|
||||
parser_info : Parser_Font_Info,
|
||||
shaper_info : Shaper_Info,
|
||||
id : Font_ID,
|
||||
used : b32,
|
||||
curve_quality : f32,
|
||||
size : f32,
|
||||
@ -32,8 +32,8 @@ Entry_Default :: Entry {
|
||||
Context :: struct {
|
||||
backing : Allocator,
|
||||
|
||||
parser_ctx : ParserContext,
|
||||
shaper_ctx : ShaperContext,
|
||||
parser_ctx : Parser_Context,
|
||||
shaper_ctx : Shaper_Context,
|
||||
|
||||
entries : [dynamic]Entry,
|
||||
|
||||
@ -54,10 +54,10 @@ Context :: struct {
|
||||
calls_offset : int,
|
||||
},
|
||||
|
||||
draw_list : DrawList,
|
||||
draw_list : Draw_List,
|
||||
atlas : Atlas,
|
||||
glyph_buffer : GlyphDrawBuffer,
|
||||
shape_cache : ShapedTextCache,
|
||||
glyph_buffer : Glyph_Draw_Buffer,
|
||||
shape_cache : Shaped_Text_Cache,
|
||||
|
||||
default_curve_quality : i32,
|
||||
text_shape_adv : b32,
|
||||
@ -69,23 +69,23 @@ Context :: struct {
|
||||
|
||||
//#region("lifetime")
|
||||
|
||||
InitAtlasRegionParams :: struct {
|
||||
Init_Atlas_Region_Params :: struct {
|
||||
width : u32,
|
||||
height : u32,
|
||||
}
|
||||
|
||||
InitAtlasParams :: struct {
|
||||
width : u32,
|
||||
height : u32,
|
||||
glyph_padding : u32,
|
||||
Init_Atlas_Params :: struct {
|
||||
width : u32,
|
||||
height : u32,
|
||||
glyph_padding : u32,
|
||||
|
||||
region_a : InitAtlasRegionParams,
|
||||
region_b : InitAtlasRegionParams,
|
||||
region_c : InitAtlasRegionParams,
|
||||
region_d : InitAtlasRegionParams,
|
||||
region_a : Init_Atlas_Region_Params,
|
||||
region_b : Init_Atlas_Region_Params,
|
||||
region_c : Init_Atlas_Region_Params,
|
||||
region_d : Init_Atlas_Region_Params,
|
||||
}
|
||||
|
||||
InitAtlasParams_Default :: InitAtlasParams {
|
||||
Init_Atlas_Params_Default :: Init_Atlas_Params {
|
||||
width = 4096,
|
||||
height = 2048,
|
||||
glyph_padding = 4,
|
||||
@ -108,34 +108,34 @@ InitAtlasParams_Default :: InitAtlasParams {
|
||||
}
|
||||
}
|
||||
|
||||
InitGlyphDrawParams :: struct {
|
||||
over_sample : Vec2i,
|
||||
buffer_batch : u32,
|
||||
draw_padding : u32,
|
||||
Init_Glyph_Draw_Params :: struct {
|
||||
over_sample : Vec2i,
|
||||
buffer_batch : u32,
|
||||
draw_padding : u32,
|
||||
}
|
||||
|
||||
InitGlyphDrawParams_Default :: InitGlyphDrawParams {
|
||||
Init_Glyph_Draw_Params_Default :: Init_Glyph_Draw_Params {
|
||||
over_sample = { 8, 8 },
|
||||
buffer_batch = 4,
|
||||
draw_padding = InitAtlasParams_Default.glyph_padding,
|
||||
draw_padding = Init_Atlas_Params_Default.glyph_padding,
|
||||
}
|
||||
|
||||
InitShapeCacheParams :: struct {
|
||||
Init_Shape_Cache_Params :: struct {
|
||||
capacity : u32,
|
||||
reserve_length : u32,
|
||||
}
|
||||
|
||||
InitShapeCacheParams_Default :: InitShapeCacheParams {
|
||||
capacity = 8 * 1024,
|
||||
reserve_length = 256,
|
||||
Init_Shape_Cache_Params_Default :: Init_Shape_Cache_Params {
|
||||
capacity = 8 * 1024,
|
||||
reserve_length = 256,
|
||||
}
|
||||
|
||||
// ve_fontcache_init
|
||||
startup :: proc( ctx : ^Context, parser_kind : ParserKind,
|
||||
startup :: proc( ctx : ^Context, parser_kind : Parser_Kind = .STB_TrueType,
|
||||
allocator := context.allocator,
|
||||
atlas_params := InitAtlasParams_Default,
|
||||
glyph_draw_params := InitGlyphDrawParams_Default,
|
||||
shape_cache_params := InitShapeCacheParams_Default,
|
||||
atlas_params := Init_Atlas_Params_Default,
|
||||
glyph_draw_params := Init_Glyph_Draw_Params_Default,
|
||||
shape_cache_params := Init_Shape_Cache_Params_Default,
|
||||
use_advanced_text_shaper : b32 = true,
|
||||
snap_shape_position : b32 = true,
|
||||
default_curve_quality : u32 = 3,
|
||||
@ -158,7 +158,7 @@ startup :: proc( ctx : ^Context, parser_kind : ParserKind,
|
||||
}
|
||||
ctx.default_curve_quality = default_curve_quality
|
||||
|
||||
error : AllocatorError
|
||||
error : Allocator_Error
|
||||
entries, error = make( [dynamic]Entry, len = 0, cap = entires_reserve )
|
||||
assert(error == .None, "VEFontCache.init : Failed to allocate entries")
|
||||
|
||||
@ -174,10 +174,10 @@ startup :: proc( ctx : ^Context, parser_kind : ParserKind,
|
||||
draw_list.indices, error = make( [dynamic]u32, len = 0, cap = 8 * Kilobyte )
|
||||
assert(error == .None, "VEFontCache.init : Failed to allocate draw_list.indices")
|
||||
|
||||
draw_list.calls, error = make( [dynamic]DrawCall, len = 0, cap = 512 )
|
||||
draw_list.calls, error = make( [dynamic]Draw_Call, len = 0, cap = 512 )
|
||||
assert(error == .None, "VEFontCache.init : Failed to allocate draw_list.calls")
|
||||
|
||||
init_atlas_region :: proc( region : ^AtlasRegion, params : InitAtlasParams, region_params : InitAtlasRegionParams, factor : Vec2i, expected_cap : i32 )
|
||||
init_atlas_region :: proc( region : ^Atlas_Region, params : Init_Atlas_Params, region_params : Init_Atlas_Region_Params, factor : Vec2i, expected_cap : i32 )
|
||||
{
|
||||
using region
|
||||
|
||||
@ -194,8 +194,8 @@ startup :: proc( ctx : ^Context, parser_kind : ParserKind,
|
||||
}
|
||||
assert( capacity.x * capacity.y == expected_cap )
|
||||
|
||||
error : AllocatorError
|
||||
LRU_init( & state, capacity.x * capacity.y )
|
||||
error : Allocator_Error
|
||||
lru_init( & state, capacity.x * capacity.y )
|
||||
}
|
||||
init_atlas_region( & atlas.region_a, atlas_params, atlas_params.region_a, { 4, 2}, 1024 )
|
||||
init_atlas_region( & atlas.region_b, atlas_params, atlas_params.region_b, { 4, 2}, 512 )
|
||||
@ -214,9 +214,9 @@ startup :: proc( ctx : ^Context, parser_kind : ParserKind,
|
||||
atlas.region_d.offset.x = atlas.width / 2
|
||||
atlas.region_d.offset.y = 0
|
||||
|
||||
LRU_init( & shape_cache.state, i32(shape_cache_params.capacity) )
|
||||
lru_init( & shape_cache.state, i32(shape_cache_params.capacity) )
|
||||
|
||||
shape_cache.storage, error = make( [dynamic]ShapedText, shape_cache_params.capacity )
|
||||
shape_cache.storage, error = make( [dynamic]Shaped_Text, shape_cache_params.capacity )
|
||||
assert(error == .None, "VEFontCache.init : Failed to allocate shape_cache.storage")
|
||||
|
||||
for idx : u32 = 0; idx < shape_cache_params.capacity; idx += 1 {
|
||||
@ -228,7 +228,7 @@ startup :: proc( ctx : ^Context, parser_kind : ParserKind,
|
||||
positions, error = make( [dynamic]Vec2, len = 0, cap = shape_cache_params.reserve_length )
|
||||
assert( error == .None, "VEFontCache.init : Failed to allocate positions array for shape cache storage" )
|
||||
|
||||
draw_list.calls, error = make( [dynamic]DrawCall, len = 0, cap = glyph_draw_params.buffer_batch * 2 )
|
||||
draw_list.calls, error = make( [dynamic]Draw_Call, len = 0, cap = glyph_draw_params.buffer_batch * 2 )
|
||||
assert( error == .None, "VEFontCache.init : Failed to allocate calls for draw_list" )
|
||||
|
||||
draw_list.indices, error = make( [dynamic]u32, len = 0, cap = glyph_draw_params.buffer_batch * 2 * 6 )
|
||||
@ -247,7 +247,7 @@ startup :: proc( ctx : ^Context, parser_kind : ParserKind,
|
||||
height = atlas.region_d.height * i32(over_sample.y)
|
||||
draw_padding = cast(i32) glyph_draw_params.draw_padding
|
||||
|
||||
draw_list.calls, error = make( [dynamic]DrawCall, len = 0, cap = glyph_draw_params.buffer_batch * 2 )
|
||||
draw_list.calls, error = make( [dynamic]Draw_Call, len = 0, cap = glyph_draw_params.buffer_batch * 2 )
|
||||
assert( error == .None, "VEFontCache.init : Failed to allocate calls for draw_list" )
|
||||
|
||||
draw_list.indices, error = make( [dynamic]u32, len = 0, cap = glyph_draw_params.buffer_batch * 2 * 6 )
|
||||
@ -256,7 +256,7 @@ startup :: proc( ctx : ^Context, parser_kind : ParserKind,
|
||||
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 draw_list" )
|
||||
|
||||
clear_draw_list.calls, error = make( [dynamic]DrawCall, len = 0, cap = glyph_draw_params.buffer_batch * 2 )
|
||||
clear_draw_list.calls, error = make( [dynamic]Draw_Call, len = 0, cap = glyph_draw_params.buffer_batch * 2 )
|
||||
assert( error == .None, "VEFontCache.init : Failed to allocate calls for calls for clear_draw_list" )
|
||||
|
||||
clear_draw_list.indices, error = make( [dynamic]u32, len = 0, cap = glyph_draw_params.buffer_batch * 2 * 4 )
|
||||
@ -285,12 +285,12 @@ hot_reload :: proc( ctx : ^Context, allocator : Allocator )
|
||||
reload_array( & draw_list.indices, allocator )
|
||||
reload_array( & draw_list.calls, allocator )
|
||||
|
||||
LRU_reload( & atlas.region_a.state, allocator)
|
||||
LRU_reload( & atlas.region_b.state, allocator)
|
||||
LRU_reload( & atlas.region_c.state, allocator)
|
||||
LRU_reload( & atlas.region_d.state, allocator)
|
||||
lru_reload( & atlas.region_a.state, allocator)
|
||||
lru_reload( & atlas.region_b.state, allocator)
|
||||
lru_reload( & atlas.region_c.state, allocator)
|
||||
lru_reload( & atlas.region_d.state, allocator)
|
||||
|
||||
LRU_reload( & shape_cache.state, allocator )
|
||||
lru_reload( & shape_cache.state, allocator )
|
||||
for idx : i32 = 0; idx < i32(len(shape_cache.storage)); idx += 1 {
|
||||
stroage_entry := & shape_cache.storage[idx]
|
||||
using stroage_entry
|
||||
@ -308,7 +308,7 @@ hot_reload :: proc( ctx : ^Context, allocator : Allocator )
|
||||
reload_array( & glyph_buffer.clear_draw_list.vertices, allocator )
|
||||
|
||||
reload_array( & shape_cache.storage, allocator )
|
||||
LRU_reload( & shape_cache.state, allocator )
|
||||
lru_reload( & shape_cache.state, allocator )
|
||||
}
|
||||
|
||||
// ve_foncache_shutdown
|
||||
@ -330,10 +330,10 @@ shutdown :: proc( ctx : ^Context )
|
||||
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 )
|
||||
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]
|
||||
@ -342,7 +342,7 @@ shutdown :: proc( ctx : ^Context )
|
||||
delete( glyphs )
|
||||
delete( positions )
|
||||
}
|
||||
LRU_free( & shape_cache.state )
|
||||
lru_free( & shape_cache.state )
|
||||
|
||||
delete( glyph_buffer.draw_list.vertices )
|
||||
delete( glyph_buffer.draw_list.indices )
|
||||
@ -357,7 +357,7 @@ shutdown :: proc( ctx : ^Context )
|
||||
}
|
||||
|
||||
// ve_fontcache_load
|
||||
load_font :: proc( ctx : ^Context, label : string, data : []byte, size_px : f32, glyph_curve_quality : u32 = 0 ) -> (font_id : FontID)
|
||||
load_font :: proc( ctx : ^Context, label : string, data : []byte, size_px : f32, glyph_curve_quality : u32 = 0 ) -> (font_id : Font_ID)
|
||||
{
|
||||
assert( ctx != nil )
|
||||
assert( len(data) > 0 )
|
||||
@ -386,9 +386,9 @@ load_font :: proc( ctx : ^Context, label : string, data : []byte, size_px : f32,
|
||||
shaper_info = shaper_load_font( & shaper_ctx, label, data, transmute(rawptr) id )
|
||||
|
||||
size = size_px
|
||||
size_scale = size_px < 0.0 ? \
|
||||
size_scale = size_px < 0.0 ? \
|
||||
parser_scale_for_pixel_height( & parser_info, -size_px ) \
|
||||
: parser_scale_for_mapping_em_to_pixels( & parser_info, size_px )
|
||||
: parser_scale_for_mapping_em_to_pixels( & parser_info, size_px )
|
||||
|
||||
if glyph_curve_quality == 0 {
|
||||
curve_quality = f32(ctx.default_curve_quality)
|
||||
@ -397,15 +397,15 @@ load_font :: proc( ctx : ^Context, label : string, data : []byte, size_px : f32,
|
||||
curve_quality = f32(glyph_curve_quality)
|
||||
}
|
||||
}
|
||||
entry.id = FontID(id)
|
||||
ctx.entries[ id ].id = FontID(id)
|
||||
entry.id = Font_ID(id)
|
||||
ctx.entries[ id ].id = Font_ID(id)
|
||||
|
||||
font_id = FontID(id)
|
||||
font_id = Font_ID(id)
|
||||
return
|
||||
}
|
||||
|
||||
// ve_fontcache_unload
|
||||
unload_font :: proc( ctx : ^Context, font : FontID )
|
||||
unload_font :: proc( ctx : ^Context, font : Font_ID )
|
||||
{
|
||||
assert( ctx != nil )
|
||||
assert( font >= 0 && int(font) < len(ctx.entries) )
|
||||
@ -430,12 +430,12 @@ configure_snap :: #force_inline proc( ctx : ^Context, snap_width, snap_height :
|
||||
ctx.snap_height = f32(snap_height)
|
||||
}
|
||||
|
||||
get_cursor_pos :: #force_inline proc "contextless" ( ctx : ^Context ) -> Vec2 { return ctx.cursor_pos }
|
||||
set_colour :: #force_inline proc "contextless" ( ctx : ^Context, colour : Colour ) { ctx.colour = colour }
|
||||
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 }
|
||||
|
||||
draw_text :: proc( ctx : ^Context, font : FontID, text_utf8 : string, position, scale : Vec2 ) -> b32
|
||||
draw_text :: proc( ctx : ^Context, font : Font_ID, text_utf8 : string, position, scale : Vec2 ) -> b32
|
||||
{
|
||||
profile(#procedure)
|
||||
// profile(#procedure)
|
||||
assert( ctx != nil )
|
||||
assert( font >= 0 && int(font) < len(ctx.entries) )
|
||||
|
||||
@ -463,14 +463,14 @@ draw_text :: proc( ctx : ^Context, font : FontID, text_utf8 : string, position,
|
||||
return true
|
||||
}
|
||||
|
||||
// ve_fontcache_drawlist
|
||||
get_draw_list :: proc( ctx : ^Context, optimize_before_returning := true ) -> ^DrawList {
|
||||
// ve_fontcache_Draw_List
|
||||
get_draw_list :: proc( ctx : ^Context, optimize_before_returning := true ) -> ^Draw_List {
|
||||
assert( ctx != nil )
|
||||
if optimize_before_returning do optimize_draw_list( & ctx.draw_list, 0 )
|
||||
return & ctx.draw_list
|
||||
}
|
||||
|
||||
get_draw_list_layer :: proc( ctx : ^Context, optimize_before_returning := true ) -> (vertices : []Vertex, indices : []u32, calls : []DrawCall) {
|
||||
get_draw_list_layer :: proc( ctx : ^Context, optimize_before_returning := true ) -> (vertices : []Vertex, indices : []u32, calls : []Draw_Call) {
|
||||
assert( ctx != nil )
|
||||
if optimize_before_returning do optimize_draw_list( & ctx.draw_list, ctx.draw_layer.calls_offset )
|
||||
vertices = ctx.draw_list.vertices[ ctx.draw_layer.vertices_offset : ]
|
||||
@ -479,7 +479,7 @@ get_draw_list_layer :: proc( ctx : ^Context, optimize_before_returning := true )
|
||||
return
|
||||
}
|
||||
|
||||
// ve_fontcache_flush_drawlist
|
||||
// ve_fontcache_flush_Draw_List
|
||||
flush_draw_list :: proc( ctx : ^Context ) {
|
||||
assert( ctx != nil )
|
||||
using ctx
|
||||
@ -501,7 +501,7 @@ flush_draw_list_layer :: proc( ctx : ^Context ) {
|
||||
|
||||
//#region("metrics")
|
||||
|
||||
measure_text_size :: proc( ctx : ^Context, font : FontID, text_utf8 : string ) -> (measured : Vec2)
|
||||
measure_text_size :: proc( ctx : ^Context, font : Font_ID, text_utf8 : string ) -> (measured : Vec2)
|
||||
{
|
||||
// profile(#procedure)
|
||||
assert( ctx != nil )
|
||||
@ -512,7 +512,7 @@ measure_text_size :: proc( ctx : ^Context, font : FontID, text_utf8 : string ) -
|
||||
return shaped.size
|
||||
}
|
||||
|
||||
get_font_vertical_metrics :: #force_inline proc ( ctx : ^Context, font : FontID ) -> ( ascent, descent, line_gap : f32 )
|
||||
get_font_vertical_metrics :: #force_inline proc ( ctx : ^Context, font : Font_ID ) -> ( ascent, descent, line_gap : f32 )
|
||||
{
|
||||
assert( ctx != nil )
|
||||
assert( font >= 0 && int(font) < len(ctx.entries) )
|
||||
|
Loading…
Reference in New Issue
Block a user