Lifted fontstash to its own package, made packages for RFont and VEFontCache

Going to do fonstash first then rfont ports.

After will complete the VEFontCache port
This commit is contained in:
Edward R. Gonzalez 2024-05-31 13:04:52 -04:00
parent 300727198b
commit 3db403017e
10 changed files with 489 additions and 409 deletions

View File

@ -0,0 +1,2 @@
package RFont

View File

@ -0,0 +1,3 @@
package VEFontCache

View File

@ -0,0 +1,132 @@
package fontstash
atlas_add_rect :: proc( ctx : ^Context, )
{
}
atlas_add_skyline_level :: proc (ctx : ^Context, id : u64, x, y, width, height : i32 ) -> (error : AllocatorError)
{
error = atlas_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
atlas_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
atlas_remove(ctx, sky_id + 1)
}
else {
sky_id += 1
}
}
return
}
atlas_delete :: proc ( ctx : ^Context ) {
using ctx
array_free( ctx.atlas )
}
atlas_expand :: proc( ctx : ^Context, width, height : i32 )
{
if width > ctx.width {
atlas_insert( ctx, ctx.atlas.num, ctx.width, 0, width - ctx.width )
}
ctx.width = width
ctx.height = height
}
atlas_init :: proc( ctx : ^Context, width, height : i32, num_nodes : u32 = Init_Atlas_Nodes )
{
error : AllocatorError
ctx.atlas, error = init_reserve( 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, AtlasNode{ width = i16(width)} )
}
atlas_insert :: proc( ctx : ^Context, id : u64, x, y, width : i32 ) -> (error : AllocatorError)
{
error = array_append_at( & ctx.atlas, AtlasNode{ i16(x), i16(y), i16(width) }, id )
return
}
atlas_remove :: #force_inline proc( ctx : ^Context, id : u64 ) { remove_at( ctx.atlas, id ) }
atlas_reset :: proc( ctx : ^Context, width, height : i32 )
{
ctx.width = width
ctx.height = height
clear( ctx.atlas )
array_append( & ctx.atlas, AtlasNode{ width = i16(width)} )
}
atlas_rect_fits :: proc( ctx : ^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 := 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
}

View File

@ -0,0 +1,220 @@
/*
Yet another port of fontstash.
I decided to use this instead of the odin port as it deviated from the original, making it difficult to sift through.
So The code was small enough that I mine as well learn it by porting for my use case.
TODO(Ed): Add docs here and throughout
TODO(Ed): This is unfinished...
Original author's 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 fontstash
Range2_i16 :: struct #raw_union {
using pts : Vec2_i16,
using xy : struct {
x0, y0, x1, y1 : i16,
}
}
Vec2 :: [2]f32
Vec2_i16 :: [2]i16
Invalid :: -1
Hash_Lut_Size :: 256
Max_Fallbacks :: 20
Max_States :: 20
Vertex_Count :: 1024
Init_Atlas_Nodes :: 256
FontLuts :: [Hash_Lut_Size]i32
FontFallbacks :: [Max_Fallbacks]i32
HandleErrorProc :: #type proc( uptr : rawptr, error, val : i32 )
RenderCreateProc :: #type proc( uptr : rawptr, width, height : i32 )
RenderResizeProc :: #type proc( uptr : rawptr, width, height : i32 )
RenderUpdateProc :: #type proc( uptr : rawptr, rect : ^i32, data : ^u8 )
RenderDrawProc :: #type proc( uptr : rawptr, verts : ^f32, tcoords : ^f32, colors : ^i32, num_verts : i32 )
RenderDelete :: #type proc( uptr : rawptr )
AlignFlag :: enum u32 {
Left,
Center,
Right,
Top,
Middle,
Bottom,
Baseline,
}
AlignFlags :: bit_set[ AlignFlag; u32 ]
// FONSflags
QuadLocation :: enum u32 {
Top_Left = 1,
Bottom_Left = 2,
}
Atlas :: struct {
dud : i32,
}
AtlasNode :: struct {
x, y, width : i16,
}
ErrorCode :: enum u32 {
Atlas_Full,
Scratch_Full,
States_Overflow,
States_Underflow,
}
Quad :: struct {
x0, y0, s0, t0 : f32,
x1, y1, s1, t1 : f32,
}
Glyph :: struct {
codepoint : rune,
index, next : i32,
size, blur : i16,
x_advance : i16,
box : Range2_i16,
offset : Vec2_i16,
}
Font :: struct {
parser_kind : ParserKind,
parser_data : ParserData,
name : string,
data : []byte,
free_data : bool,
ascender : f32,
descender : f32,
line_height : f32,
glyphs : Array(Glyph),
lut : FontLuts,
fallbacks : FontFallbacks,
num_fallbacks : i32,
}
Params :: struct {
width, height : i32,
quad_location : QuadLocation, // (flags)
render_create : RenderCreateProc,
render_resize : RenderResizeProc,
render_update : RenderUpdateProc,
render_draw : RenderDrawProc,
render_delete : RenderDelete,
}
State :: struct {
font : i32,
alignment : i32,
size : f32,
color : [4]u8,
blur : f32,
spacing : f32,
}
TextIter :: struct {
x, y : f32,
next_x, next_y : f32,
scale, spacing : f32,
isize, iblur : i16,
font : ^Font,
prev_glyph_id : i32,
codepoint : rune,
utf8_state : rune,
str : string,
next : string,
end : string,
}
Context :: struct {
params : Params,
// Atlas
atlas : Array(AtlasNode),
texture_data : []byte,
width, height : i32,
// ----
normalized_size : Vec2,
verts : [Vertex_Count * 2]f32,
tcoords : [Vertex_Count * 2]f32,
colors : [Vertex_Count ]f32,
states : [Max_States]State,
num_states : i32,
handle_error : HandleErrorProc,
error_uptr : rawptr,
}
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
}

View File

@ -0,0 +1,58 @@
package fontstash
import "core:mem"
AllocatorError :: mem.Allocator_Error
import "codebase:grime"
// asserts
ensure :: grime.ensure
// container
Array :: grime.Array
array_init_reserve :: grime.array_init_reserve
array_append :: grime.array_append
array_append_at :: grime.array_append_at
array_clear :: grime.array_clear
array_free :: grime.array_free
array_remove_at :: grime.array_remove_at
array_to_slice :: grime.array_to_slice
//#region("Proc overload mappings")
append :: proc {
grime.array_append_array,
grime.array_append_slice,
grime.array_append_value,
}
append_at :: proc {
array_append_at,
}
clear :: proc {
array_clear,
}
free :: proc {
array_free,
}
init_reserve :: proc {
array_init_reserve,
}
remove_at :: proc {
array_remove_at,
}
to_slice :: proc {
array_to_slice,
}
//#endregion("Proc overload mappings")

View File

@ -0,0 +1,66 @@
package fontstash
import stbtt "vendor:stb/truetype"
import freetype "thirdparty:freetype"
ParserKind :: enum u32 {
stb_true_type,
freetype,
}
ParserData :: struct #raw_union {
stbtt_info : stbtt.fontinfo,
// freetype_info :
}
//#region("freetype")
//#endregion("freetype")
//#region("stb_truetype")
fstash_tt_init :: proc( ctx : ^Context ) -> i32 { return 1 }
fstash_tt_load_font :: proc( ctx : ^Context, parser_data : ^ParserData, data : []byte ) -> b32
{
parser_data.stbtt_info.userdata = ctx
stb_error := stbtt.InitFont( & parser_data.stbtt_info, & data[0], 0 )
return stb_error
}
fstash_tt_get_font_metrics :: proc( parser_data : ^ParserData, ascent, descent, line_gap : ^i32 ) {
stbtt.GetFontVMetrics( & parser_data.stbtt_info, ascent, descent, line_gap )
}
fstash_tt_get_pixel_height_scale :: proc( parser_data : ^ParserData, size : f32 ) -> f32
{
return stbtt.ScaleForPixelHeight( & parser_data.stbtt_info, size )
}
fstash_tt_get_glyph_index :: proc( parser_data : ^ParserData, codepoint : rune ) -> i32
{
return stbtt.FindGlyphIndex( & parser_data.stbtt_info, codepoint )
}
fstash_tt_build_glyph_bitmap :: proc( parser_data : ^ParserData, glyph_index : i32,
size, scale : f32, advance, left_side_bearing, x0, y0, x1, y1 : ^i32 ) -> i32
{
stbtt.GetGlyphHMetrics( & parser_data.stbtt_info, glyph_index, advance, left_side_bearing )
stbtt.GetGlyphBitmapBox( & parser_data.stbtt_info, glyph_index, scale, scale, x0, y0, x1, y1 )
return 1
}
fstash_tt_render_glyph_bitmap :: proc( parser_data : ^ParserData, output : [^]byte,
out_width, out_height, out_stride : i32, scale_x, scale_y : f32, glyph_index : i32 )
{
stbtt.MakeGlyphBitmap( & parser_data.stbtt_info, output, out_width, out_height, out_stride, scale_x, scale_y, glyph_index )
}
fstash_tt_get_glyph_kern_advance :: proc( parser_data : ^ParserData, glyph_1, glyph_2 : i32 ) -> i32
{
return stbtt.GetGlyphKernAdvance( & parser_data.stbtt_info, glyph_1, glyph_2 )
}
//#endregion("stb_truetype")

View File

@ -113,6 +113,7 @@ logger_interface :: proc(
}
// This buffer is used below excluisvely to prevent any allocator recusion when verbose logging from allocators.
// This means a single line is limited to 32k buffer (increase naturally if this SOMEHOW becomes a bottleneck...)
Logger_Allocator_Buffer : [32 * Kilobyte]u8
log :: proc( msg : string, level := LogLevel.Info, loc := #caller_location ) {

View File

@ -1,409 +0,0 @@
/*
Yet another port of fontstash.
I decided to use this instead of the odin port as it deviated from the original, making it difficult to sift through.
So The code was small enough that I mine as well learn it by porting for my use case.
TODO(Ed): Add docs here and throughout
TODO(Ed): This is unfinished...
Original author's 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"
FStash_Use_stb_truetype :: #config(FSTASH_USE_STB_TRUE_TYPE, true)
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,
}
when FStash_Use_stb_truetype
{
FStash_FontParserData :: struct {
stbtt_info : stbtt.fontinfo,
}
}
FStash_Glyph :: struct {
codepoint : rune,
index, next : i32,
size, blur : i16,
x_advance : i16,
box : Range2_i16,
offset : Vec2_i16,
}
FStash_Font :: struct {
parser_data : FStash_FontParserData,
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_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,
}
when FStash_Use_stb_truetype
{
fstash_tt_init :: proc( ctx : ^FStash_Context ) -> i32 { return 1 }
fstash_tt_load_font :: proc( ctx : ^FStash_Context, parser_data : ^FStash_FontParserData, data : []byte ) -> b32
{
parser_data.stbtt_info.userdata = ctx
stb_error := stbtt.InitFont( & parser_data.stbtt_info, & data[0], 0 )
return stb_error
}
fstash_tt_get_font_metrics :: proc( parser_data : ^FStash_FontParserData, ascent, descent, line_gap : ^i32 ) {
stbtt.GetFontVMetrics( & parser_data.stbtt_info, ascent, descent, line_gap )
}
fstash_tt_get_pixel_height_scale :: proc( parser_data : ^FStash_FontParserData, size : f32 ) -> f32
{
return stbtt.ScaleForPixelHeight( & parser_data.stbtt_info, size )
}
fstash_tt_get_glyph_index :: proc( parser_data : ^FStash_FontParserData, codepoint : rune ) -> i32
{
return stbtt.FindGlyphIndex( & parser_data.stbtt_info, codepoint )
}
fstash_tt_build_glyph_bitmap :: proc( parser_data : ^FStash_FontParserData, glyph_index : i32,
size, scale : f32, advance, left_side_bearing, x0, y0, x1, y1 : ^i32 ) -> i32
{
stbtt.GetGlyphHMetrics( & parser_data.stbtt_info, glyph_index, advance, left_side_bearing )
stbtt.GetGlyphBitmapBox( & parser_data.stbtt_info, glyph_index, scale, scale, x0, y0, x1, y1 )
return 1
}
fstash_tt_render_glyph_bitmap :: proc( parser_data : ^FStash_FontParserData, output : [^]byte,
out_width, out_height, out_stride : i32, scale_x, scale_y : f32, glyph_index : i32 )
{
stbtt.MakeGlyphBitmap( & parser_data.stbtt_info, output, out_width, out_height, out_stride, scale_x, scale_y, glyph_index )
}
fstash_tt_get_glyph_kern_advance :: proc( parser_data : ^FStash_FontParserData, glyph_1, glyph_2 : i32 ) -> i32
{
return stbtt.GetGlyphKernAdvance( & parser_data.stbtt_info, glyph_1, glyph_2 )
}
} // when FStash_Use_stb_true-type
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

@ -0,0 +1,3 @@
package sectr
import fstash "codebase:font/fontstash"

View File

@ -127,6 +127,8 @@ import "thirdparty:backtrace"
stacktrace :: backtrace.trace
stacktrace_lines :: backtrace.lines
#region("codebase")
import "codebase:grime"
// asserts
ensure :: grime.ensure
@ -235,6 +237,8 @@ import "codebase:grime"
varena_allocator :: grime.varena_allocator
#endregion("codebase")
#region("Procedure overload mappings")
// This has to be done on a per-module basis.