Gut raylib usage from the codebase.

Going to either fully commit to sokol or if it fails, rolling the platform layer myself.
This commit is contained in:
2024-05-25 11:52:23 -04:00
parent 13c3032dba
commit 797ab227e9
25 changed files with 571 additions and 1368 deletions

View File

@ -1,2 +1,2 @@
package sectr
package sectr

View File

@ -0,0 +1,353 @@
/*
Yet another port of fontstash.
I decided t use this instead of the odin port as it already deviated from the original impl.
So The code was small enough that I mine as well learn it by porting for my use case.
Original copyright for fonstash.h:
------------------------------------------------------------------------------
Copyright (c) 2009-2013 Mikko Mononen memon@inside.org
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
------------------------------------------------------------------------------
*/
package sectr
import stbtt "vendor:stb/truetype"
Range2_i16 :: struct #raw_union {
using pts : Vec2_i16,
using xy : struct {
x0, y0, x1, y1 : i16,
}
}
Vec2_i16 :: [2]i16
FSTASH_Invalid :: -1
FSTASH_Hash_Lut_Size :: 256
FSTASH_Max_Fallbacks :: 20
FSTASH_Max_States :: 20
FSTASH_Vertex_Count :: 1024
FSTASH_Init_Atlas_Nodes :: 256
FSTASH_FontLuts :: [FSTASH_Hash_Lut_Size]i32
FSTASH_FontFallbacks :: [FSTASH_Max_Fallbacks]i32
FSTASH_HandleErrorProc :: #type proc( uptr : rawptr, error, val : i32 )
FSTASH_RenderCreateProc :: #type proc( uptr : rawptr, width, height : i32 )
FSTASH_RenderResizeProc :: #type proc( uptr : rawptr, width, height : i32 )
FSTASH_RenderUpdateProc :: #type proc( uptr : rawptr, rect : ^i32, data : ^u8 )
FSTASH_RenderDrawProc :: #type proc( uptr : rawptr, verts : ^f32, tcoords : ^f32, colors : ^i32, num_verts : i32 )
FSTASH_RenderDelete :: #type proc( uptr : rawptr )
FSTASH_AlignFlag :: enum u32 {
Left,
Center,
Right,
Top,
Middle,
Bottom,
Baseline,
}
FSTASH_AlignFlags :: bit_set[ FSTASH_AlignFlag; u32 ]
// FONSflags
FSTASH_QuadLocation :: enum u32 {
Top_Left = 1,
Bottom_Left = 2,
}
FSTASH_Atlas :: struct {
dud : i32,
}
FSTASH_AtlasNode :: struct {
x, y, width : i16,
}
FSTASH_ErrorCode :: enum u32 {
Atlas_Full,
Scratch_Full,
States_Overflow,
States_Underflow,
}
FSTASH_Quad :: struct {
x0, y0, s0, t0 : f32,
x1, y1, s1, t1 : f32,
}
FSTASH_Font :: struct {
info : stbtt.fontinfo,
name : string,
data : []byte,
free_data : bool,
ascender : f32,
descender : f32,
line_height : f32,
glyphs : Array(FSTASH_Glyph),
lut : FSTASH_FontLuts,
fallbacks : FSTASH_FontFallbacks,
num_fallbacks : i32,
}
FSTASH_Glyph :: struct {
codepoint : rune,
index, next : i32,
size, blur : i16,
x_advance : i16,
box : Range2_i16,
offset : Vec2_i16,
}
FSTASH_Params :: struct {
width, height : i32,
quad_location : FSTASH_QuadLocation, // (flags)
render_create : FSTASH_RenderCreateProc,
render_resize : FSTASH_RenderResizeProc,
render_update : FSTASH_RenderUpdateProc,
render_draw : FSTASH_RenderDrawProc,
render_delete : FSTASH_RenderDelete,
}
FSTASH_State :: struct {
font : i32,
alignment : i32,
size : f32,
color : [4]u8,
blur : f32,
spacing : f32,
}
FSTASH_TextIter :: struct {
x, y : f32,
next_x, next_y : f32,
scale, spacing : f32,
isize, iblur : i16,
font : ^FSTASH_Font,
prev_glyph_id : i32,
codepoint : rune,
utf8_state : rune,
str : string,
next : string,
end : string,
}
FSTASH_Context :: struct {
params : FSTASH_Params,
// Atlas
atlas : Array(FSTASH_AtlasNode),
texture_data : []byte,
width, height : i32,
// ----
normalized_size : Vec2,
verts : [FSTASH_Vertex_Count * 2]f32,
tcoords : [FSTASH_Vertex_Count * 2]f32,
colors : [FSTASH_Vertex_Count ]f32,
states : [FSTASH_Max_States]FSTASH_State,
num_states : i32,
handle_error : FSTASH_HandleErrorProc,
error_uptr : rawptr,
}
fstash_decode_utf8 :: proc( state : ^rune, codepoint : ^rune, to_decode : byte ) -> bool
{
UTF8_Accept :: 0
UTF8_Reject :: 1
@static UTF8_Decode_Table := [?]u8 {
// The first part of the table maps bytes to character classes that
// to reduce the size of the transition table and create bitmasks.
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 00..1F
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 20..3F
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 40..5F
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 60..7F
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, // 80..9F
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, // A0..BF
8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // C0..DF
10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8, // E0..FF
// The second part is a transition table that maps a combination
// of a state of the automaton and a character class to a state.
0,12,24,36,60,96,84,12,12,12,48,72, 12,12,12,12,12,12,12,12,12,12,12,12,
12, 0,12,12,12,12,12, 0,12, 0,12,12, 12,24,12,12,12,12,12,24,12,24,12,12,
12,12,12,12,12,12,12,24,12,12,12,12, 12,24,12,12,12,12,12,12,12,24,12,12,
12,12,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,36,12,36,12,12,
12,36,12,12,12,12,12,12,12,12,12,12,
}
to_decode_rune := rune(to_decode)
type := UTF8_Decode_Table[to_decode_rune]
// Update codepoint otherwise initialize it.
(codepoint^) = ((state^) != UTF8_Accept) ? \
((to_decode_rune & 0x3F) | ((codepoint^) << 6)) \
: ((0xFF >> type) & (to_decode_rune))
(state^) = cast(rune)(UTF8_Decode_Table[256 + (state^) * 16 + rune(type)])
return (state^) == UTF8_Accept
}
fstash_atlas_delete :: proc ( ctx : ^FSTASH_Context ) {
using ctx
array_free( ctx.atlas )
}
fstash_atlas_expand :: proc( ctx : ^FSTASH_Context, width, height : i32 )
{
if width > ctx.width {
fstash_atlas_insert( ctx, ctx.atlas.num, ctx.width, 0, width - ctx.width )
}
ctx.width = width
ctx.height = height
}
fstash_atlas_init :: proc( ctx : ^FSTASH_Context, width, height : i32, num_nodes : u32 = FSTASH_Init_Atlas_Nodes )
{
error : AllocatorError
ctx.atlas, error = array_init_reserve( FSTASH_AtlasNode, context.allocator, u64(num_nodes), dbg_name = "font atlas" )
ensure(error != AllocatorError.None, "Failed to allocate font atlas")
ctx.width = width
ctx.height = height
array_append( & ctx.atlas, FSTASH_AtlasNode{ width = i16(width)} )
}
fstash_atlas_insert :: proc( ctx : ^FSTASH_Context, id : u64, x, y, width : i32 ) -> (error : AllocatorError)
{
error = array_append_at( & ctx.atlas, FSTASH_AtlasNode{ i16(x), i16(y), i16(width) }, id )
return
}
fstash_atlas_remove :: proc( ctx : ^FSTASH_Context, id : u64 )
{
array_remove_at( ctx.atlas, id )
}
fstash_atlas_reset :: proc( ctx : ^FSTASH_Context, width, height : i32 )
{
ctx.width = width
ctx.height = height
array_clear( ctx.atlas )
array_append( & ctx.atlas, FSTASH_AtlasNode{ width = i16(width)} )
}
fstash_atlas_add_skyline_level :: proc (ctx : ^FSTASH_Context, id : u64, x, y, width, height : i32 ) -> (error : AllocatorError)
{
insert :: fstash_atlas_insert
remove :: fstash_atlas_remove
error = insert( ctx, id, x, y + height, width)
if error != AllocatorError.None {
ensure( false, "Failed to insert into atlas")
return
}
// Delete skyline segments that fall under the shadow of the new segment.
for sky_id := id; sky_id < ctx.atlas.num; sky_id += 1
{
curr := & ctx.atlas.data[sky_id ]
next := & ctx.atlas.data[sky_id + 1]
if curr.x >= next.x + next.width do break
shrink := i16(next.x + next.width - curr.x)
curr.x += shrink
curr.width -= shrink
if curr.width > 0 do break
remove(ctx, sky_id)
sky_id -= 1
}
// Merge same height skyline segments that are next to each other.
for sky_id := id; sky_id < ctx.atlas.num - 1;
{
curr := & ctx.atlas.data[sky_id ]
next := & ctx.atlas.data[sky_id + 1]
if curr.y == next.y {
curr.width += next.width
remove(ctx, sky_id + 1)
}
else {
sky_id += 1
}
}
return
}
fstash_atlas_rect_fits :: proc( ctx : ^FSTASH_Context, location, width, height : i32 ) -> (max_height : i32)
{
// Checks if there is enough space at the location of skyline span 'i',
// and return the max height of all skyline spans under that at that location,
// (think tetris block being dropped at that position). Or -1 if no space found.
atlas := array_to_slice(ctx.atlas)
node := atlas[location]
space_left : i32
if i32(node.x) + width > ctx.width {
max_height = -1
return
}
space_left = width;
y := i32(node.y)
location := location
for ; space_left > 0;
{
if u64(location) == ctx.atlas.num {
max_height = -1
return
}
node := atlas[location]
y := max(y, i32(node.y))
if y + height > ctx.height {
max_height = -1
return
}
space_left -= i32(node.width)
location += 1
}
max_height = y
return
}
fstash_atlas_add_rect :: proc( ctx : ^FSTASH_Context, )
{
}

View File

@ -1,13 +1,5 @@
package sectr
import "core:fmt"
import "core:math"
import "core:mem"
import "core:path/filepath"
import "core:os"
import rl "vendor:raylib"
Font_Largest_Px_Size :: 32
Font_Size_Interval :: 2
@ -35,158 +27,30 @@ FontTag :: struct {
point_size : f32
}
FontGlyphsRender :: struct {
size : i32,
count : i32,
padding : i32,
texture : rl.Texture2D,
recs : [^]rl.Rectangle, // Characters rectangles in texture
glyphs : [^]rl.GlyphInfo, // Characters info data
}
FontDef :: struct {
path_file : string,
// TODO(Ed) : you may have to store font data in the future if we render on demand
// data : []u8,
default_size : i32,
size_table : [Font_Largest_Px_Size / Font_Size_Interval] FontGlyphsRender,
placeholder : int,
}
FontProviderData :: struct {
// font_cache : HMapZPL(FontDef),
font_cache : HMapChainedPtr(FontDef),
}
font_provider_startup :: proc()
{
profile(#procedure)
state := get_state()
font_provider_data := & get_state().font_provider_data; using font_provider_data
font_cache_alloc_error : AllocatorError
font_cache, font_cache_alloc_error = hmap_chained_init(FontDef, hmap_closest_prime(1 * Kilo), persistent_allocator(), dbg_name = "font_cache" )
verify( font_cache_alloc_error == AllocatorError.None, "Failed to allocate font_cache" )
log("font_cache created")
log("font_provider initialized")
}
font_provider_shutdown :: proc()
{
font_provider_data := & get_state().font_provider_data; using font_provider_data
for & entry in font_cache.lookup
{
if entry == nil do continue
def := entry.value
for & px_render in def.size_table {
using px_render
rl.UnloadFontData( glyphs, count )
rl.UnloadTexture ( texture )
rl.MemFree( recs )
}
}
}
font_load :: proc( path_file : string,
font_load :: proc(ath_file : string,
default_size : f32 = Font_Load_Use_Default_Size,
desired_id : string = Font_Load_Gen_ID
) -> FontID
{
profile(#procedure)
log( str_fmt("Loading font: %v", path_file))
font_provider_data := & get_state().font_provider_data; using font_provider_data
font_data, read_succeded : = os.read_entire_file( path_file )
verify( b32(read_succeded), str_fmt("Failed to read font file for: %v", path_file) )
font_data_size := cast(i32) len(font_data)
desired_id := desired_id
// Use file name as key
if len(desired_id) == 0 {
// NOTE(Ed): This should never be used except for laziness so I'll be throwing a warning everytime.
log("desired_key not provided, using file name. Give it a proper name!", LogLevel.Warning)
// desired_id = cast(FontID) file_name_from_path(path_file)
desired_id = file_name_from_path(path_file)
}
default_size := default_size
if default_size == Font_Load_Use_Default_Size {
default_size = Font_Default_Point_Size
}
key := cast(u64) crc32( transmute([]byte) desired_id )
def, set_error := hmap_chained_set(font_cache, key, FontDef{})
verify( set_error == AllocatorError.None, "Failed to add new font entry to cache" )
def.path_file = path_file
def.default_size = i32(points_to_pixels(default_size))
// TODO(Ed): this is slow & eats quite a bit of memory early on. Setup a more on demand load for a specific size.
// Also, we need to eventually switch to a SDF shader for rendering
// Render all sizes at once
// Note(Ed) : We only generate textures for even multiples of the font.
for font_size : i32 = Font_Size_Interval; font_size <= Font_Largest_Px_Size; font_size += Font_Size_Interval
{
profile("font size render")
id := (font_size / Font_Size_Interval) + (font_size % Font_Size_Interval)
px_render := & def.size_table[id - 1]
using px_render
size = font_size
count = 95 // This is the default codepoint count from raylib when loading a font.
padding = Font_TTF_Default_Chars_Padding
glyphs = rl.LoadFontData( raw_data(font_data), font_data_size,
fontSize = size,
codepoints = nil,
codepointCount = count,
type = rl.FontType.DEFAULT )
verify( glyphs != nil, str_fmt("Failed to load glyphs for font: %v at desired size: %v", desired_id, size ) )
atlas := rl.GenImageFontAtlas( glyphs, & recs, count, size, padding, i32(Font_Atlas_Packing_Method.Raylib_Basic) )
texture = rl.LoadTextureFromImage( atlas )
// glyphs_slice := slice_ptr( glyphs, count )
// for glyph in glyphs_slice {
// TODO(Ed) : See if above can properly reference
// NOTE(raylib): Update glyphs[i].image to use alpha, required to be used on image_draw_text()
for glyph_id : i32 = 0; glyph_id < count; glyph_id += 1 {
glyph := & glyphs[glyph_id]
rl.UnloadImage( glyph.image )
glyph.image = rl.ImageFromImage( atlas, recs[glyph_id] )
}
rl.UnloadImage( atlas )
}
return { key, desired_id }
}
Font_Use_Default_Size :: f32(0.0)
to_rl_Font :: proc( id : FontID, size := Font_Use_Default_Size ) -> rl.Font
{
font_provider_data := & get_state().font_provider_data; using font_provider_data
even_size := math.round(size * (1.0/f32(Font_Size_Interval))) * f32(Font_Size_Interval)
size := clamp( i32( even_size), 4, Font_Largest_Px_Size )
def := hmap_chained_get( font_cache, id.key )
size = size if size != i32(Font_Use_Default_Size) else def.default_size
id := (size / Font_Size_Interval) + (size % Font_Size_Interval)
px_render := & def.size_table[ id - 1 ]
rl_font : rl.Font
rl_font.baseSize = px_render.size
rl_font.glyphCount = px_render.count
rl_font.glyphPadding = px_render.padding
rl_font.glyphs = px_render.glyphs
rl_font.recs = px_render.recs
rl_font.texture = px_render.texture
return rl_font
return {}
}

View File