Ed_
e282397bf0
This hashtable is not tolerant over going 'over capacity'. I have to eventually swap to another solution similar to Ryans linear probing for string caching. For now I'll just abuse ram with an oversized table.
193 lines
5.8 KiB
Odin
193 lines
5.8 KiB
Odin
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
|
|
|
|
// Font_Default :: ""
|
|
Font_Default :: FontID { 0, "" }
|
|
Font_Default_Point_Size :: 18.0
|
|
|
|
Font_TTF_Default_Chars_Padding :: 4
|
|
|
|
Font_Load_Use_Default_Size :: -1
|
|
Font_Load_Gen_ID :: ""
|
|
|
|
Font_Atlas_Packing_Method :: enum u32 {
|
|
Raylib_Basic = 0, // Basic packing algo
|
|
Skyeline_Rect = 1, // stb_pack_rect
|
|
}
|
|
|
|
FontID :: struct {
|
|
key : u64,
|
|
label : string,
|
|
}
|
|
FontTag :: struct {
|
|
key : FontID,
|
|
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,
|
|
}
|
|
|
|
FontProviderData :: struct {
|
|
font_cache : HMapZPL(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 = zpl_hmap_init_reserve( FontDef, persistent_slab_allocator(), 2 )
|
|
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 id in 0 ..< font_cache.entries.num
|
|
{
|
|
def := & font_cache.entries.data[id].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,
|
|
default_size : f32 = Font_Load_Use_Default_Size,
|
|
desired_id : string = Font_Load_Gen_ID
|
|
) -> FontID
|
|
{
|
|
profile(#procedure)
|
|
log( str_fmt_tmp("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, context.temp_allocator )
|
|
verify( b32(read_succeded), str_fmt_tmp("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 := zpl_hmap_set( & font_cache, key,FontDef {} )
|
|
verify( set_error == AllocatorError.None, "Failed to add new font entry to cache" )
|
|
|
|
def.path_file = path_file
|
|
// def.data = font_data
|
|
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_tmp("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 )
|
|
}
|
|
|
|
free_all( context.temp_allocator )
|
|
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 := zpl_hmap_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
|
|
}
|