General code style clean up for vendor:fontstash

This commit is contained in:
gingerBill
2023-06-28 11:36:27 +01:00
parent d50a844720
commit 9e9f3c485b
+124 -159
View File
@@ -7,6 +7,7 @@ import "core:os"
import "core:mem" import "core:mem"
import "core:math" import "core:math"
import "core:strings" import "core:strings"
import "core:slice"
import stbtt "vendor:stb/truetype" import stbtt "vendor:stb/truetype"
// This is a port from Fontstash into odin - specialized for nanovg // This is a port from Fontstash into odin - specialized for nanovg
@@ -118,24 +119,24 @@ FontContext :: struct {
callbackUpdate: proc(data: rawptr, dirtyRect: [4]f32, textureData: rawptr), callbackUpdate: proc(data: rawptr, dirtyRect: [4]f32, textureData: rawptr),
} }
Init :: proc(using ctx: ^FontContext, w, h: int, loc: QuadLocation) { Init :: proc(ctx: ^FontContext, w, h: int, loc: QuadLocation) {
userData = ctx ctx.userData = ctx
location = loc ctx.location = loc
fonts = make([dynamic]Font, 0, 8) ctx.fonts = make([dynamic]Font, 0, 8)
itw = f32(1) / f32(w) ctx.itw, ctx.ith = 1.0 / f32(w), 1.0 / f32(h)
ith = f32(1) / f32(h)
textureData = make([]byte, w * h)
width = w ctx.textureData = make([]byte, w * h)
height = h
nodes = make([dynamic]AtlasNode, 0, INIT_ATLAS_NODES) ctx.width = w
ctx.height = h
ctx.nodes = make([dynamic]AtlasNode, 0, INIT_ATLAS_NODES)
__dirtyRectReset(ctx) __dirtyRectReset(ctx)
states = make([]State, MAX_STATES) ctx.states = make([]State, MAX_STATES)
// NOTE NECESSARY // NOTE NECESSARY
append(&nodes, AtlasNode { append(&ctx.nodes, AtlasNode{
width = i16(w), width = i16(w),
}) })
@@ -145,8 +146,8 @@ Init :: proc(using ctx: ^FontContext, w, h: int, loc: QuadLocation) {
ClearState(ctx) ClearState(ctx)
} }
Destroy :: proc(using ctx: ^FontContext) { Destroy :: proc(ctx: ^FontContext) {
for font in fonts { for font in ctx.fonts {
if font.freeLoadedData { if font.freeLoadedData {
delete(font.loadedData) delete(font.loadedData)
} }
@@ -155,18 +156,18 @@ Destroy :: proc(using ctx: ^FontContext) {
delete(font.glyphs) delete(font.glyphs)
} }
delete(states) delete(ctx.states)
delete(textureData) delete(ctx.textureData)
delete(fonts) delete(ctx.fonts)
delete(nodes) delete(ctx.nodes)
} }
Reset :: proc(using ctx: ^FontContext) { Reset :: proc(ctx: ^FontContext) {
__atlasReset(ctx, width, height) __atlasReset(ctx, ctx.width, ctx.height)
__dirtyRectReset(ctx) __dirtyRectReset(ctx)
mem.zero_slice(textureData) slice.zero(ctx.textureData)
for &font in fonts { for &font in ctx.fonts {
__lutReset(&font) __lutReset(&font)
} }
@@ -175,84 +176,67 @@ Reset :: proc(using ctx: ^FontContext) {
ClearState(ctx) ClearState(ctx)
} }
__atlasInsertNode :: proc(using ctx: ^FontContext, idx, x, y, w: int) { __atlasInsertNode :: proc(ctx: ^FontContext, idx: int, x, y, w: int) {
// resize is alright here inject_at(&ctx.nodes, idx, AtlasNode{
resize(&nodes, len(nodes) + 1) x = i16(x),
y = i16(y),
// shift nodes up once to leave space at idx
for i := len(nodes) - 1; i > idx; i -= 1 {
nodes[i] = nodes[i - 1]
}
// set new inserted one to properties
nodes[idx].x = i16(x)
nodes[idx].y = i16(y)
nodes[idx].width = i16(w)
}
__atlasRemoveNode :: proc(using ctx: ^FontContext, idx: int) {
if len(nodes) == 0 {
return
}
// remove node at index, shift elements down
for i in idx..<len(nodes) - 1 {
nodes[i] = nodes[i + 1]
}
// reduce size of array
raw := transmute(^mem.Raw_Dynamic_Array) &nodes
raw.len -= 1
}
__atlasExpand :: proc(using ctx: ^FontContext, w, h: int) {
if w > width {
__atlasInsertNode(ctx, len(nodes), width, 0, w - width)
}
width = w
height = h
}
__atlasReset :: proc(using ctx: ^FontContext, w, h: int) {
width = w
height = h
clear(&nodes)
// init root node
append(&nodes, AtlasNode {
width = i16(w), width = i16(w),
}) })
} }
__AtlasAddSkylineLevel :: proc(using ctx: ^FontContext, idx, x, y, w, h: int) { __atlasRemoveNode :: proc(ctx: ^FontContext, idx: int) {
if len(ctx.nodes) == 0 {
return
}
ordered_remove(&ctx.nodes, idx)
}
__atlasExpand :: proc(ctx: ^FontContext, w, h: int) {
if w > ctx.width {
__atlasInsertNode(ctx, len(ctx.nodes), ctx.width, 0, w - ctx.width)
}
ctx.width, ctx.height = w, h
}
__atlasReset :: proc(ctx: ^FontContext, w, h: int) {
ctx.width, ctx.height = w, h
clear(&ctx.nodes)
// init root node
append(&ctx.nodes, AtlasNode{
width = i16(w),
})
}
__AtlasAddSkylineLevel :: proc(using ctx: ^FontContext, idx: int, x, y, w, h: int) {
// insert new node // insert new node
__atlasInsertNode(ctx, idx, x, y + h, w) __atlasInsertNode(ctx, idx, x, y + h, w)
// Delete skyline segments that fall under the shadow of the new segment. // Delete skyline segments that fall under the shadow of the new segment.
for i := idx + 1; i < len(nodes); i += 1 { for i := idx + 1; i < len(nodes); i += 1 {
if nodes[i].x < nodes[i - 1].x + nodes[i - 1].width { if nodes[i].x >= nodes[i-1].x + nodes[i-1].width {
break
}
shrink := nodes[i-1].x + nodes[i-1].width - nodes[i].x shrink := nodes[i-1].x + nodes[i-1].width - nodes[i].x
nodes[i].x += i16(shrink) nodes[i].x += i16(shrink)
nodes[i].width -= i16(shrink) nodes[i].width -= i16(shrink)
if nodes[i].width <= 0 { if nodes[i].width > 0 {
break
}
__atlasRemoveNode(ctx, i) __atlasRemoveNode(ctx, i)
i -= 1 i -= 1
} else {
break
}
} else {
break
}
} }
// Merge same height skyline segments that are next to each other. // Merge same height skyline segments that are next to each other.
for i := 0; i < len(nodes) - 1; i += 1 { for i := 0; i < len(nodes) - 1; /**/ {
if nodes[i].y == nodes[i + 1].y { if nodes[i].y == nodes[i+1].y {
nodes[i].width += nodes[i + 1].width nodes[i].width += nodes[i+1].width
__atlasRemoveNode(ctx, i + 1) __atlasRemoveNode(ctx, i+1)
i -= 1 } else {
i += 1
} }
} }
} }
@@ -261,14 +245,14 @@ __AtlasRectFits :: proc(using ctx: ^FontContext, i, w, h: int) -> int {
// Checks if there is enough space at the location of skyline span 'i', // 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, // 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. // (think tetris block being dropped at that position). Or -1 if no space found.
x := int(nodes[i].x)
y := int(nodes[i].y) i := i
x, y := int(nodes[i].x), int(nodes[i].y)
if x + w > width { if x + w > width {
return -1 return -1
} }
i := i
space_left := w space_left := w
for space_left > 0 { for space_left > 0 {
if i == len(nodes) { if i == len(nodes) {
@@ -288,8 +272,7 @@ __AtlasRectFits :: proc(using ctx: ^FontContext, i, w, h: int) -> int {
} }
__AtlasAddRect :: proc(using ctx: ^FontContext, rw, rh: int) -> (rx, ry: int, ok: bool) { __AtlasAddRect :: proc(using ctx: ^FontContext, rw, rh: int) -> (rx, ry: int, ok: bool) {
besth := height bestw, besth := width, height
bestw := width
besti, bestx, besty := -1, -1, -1 besti, bestx, besty := -1, -1, -1
// Bottom left fit heuristic. // Bottom left fit heuristic.
@@ -313,18 +296,11 @@ __AtlasAddRect :: proc(using ctx: ^FontContext, rw, rh: int) -> (rx, ry: int, ok
// Perform the actual packing. // Perform the actual packing.
__AtlasAddSkylineLevel(ctx, besti, bestx, besty, rw, rh) __AtlasAddSkylineLevel(ctx, besti, bestx, besty, rw, rh)
ok = true return bestx, besty, true
rx = bestx
ry = besty
return
} }
__AtlasAddWhiteRect :: proc(ctx: ^FontContext, w, h: int) { __AtlasAddWhiteRect :: proc(ctx: ^FontContext, w, h: int) -> bool {
gx, gy, ok := __AtlasAddRect(ctx, w, h) gx, gy := __AtlasAddRect(ctx, w, h) or_return
if !ok {
return
}
// Rasterize // Rasterize
dst := ctx.textureData[gx + gy * ctx.width:] dst := ctx.textureData[gx + gy * ctx.width:]
@@ -336,10 +312,12 @@ __AtlasAddWhiteRect :: proc(ctx: ^FontContext, w, h: int) {
dst = dst[ctx.width:] dst = dst[ctx.width:]
} }
ctx.dirtyRect[0] = cast(f32) min(int(ctx.dirtyRect[0]), gx) ctx.dirtyRect[0] = f32(min(int(ctx.dirtyRect[0]), gx))
ctx.dirtyRect[1] = cast(f32) min(int(ctx.dirtyRect[1]), gy) ctx.dirtyRect[1] = f32(min(int(ctx.dirtyRect[1]), gy))
ctx.dirtyRect[2] = cast(f32) max(int(ctx.dirtyRect[2]), gx + w) ctx.dirtyRect[2] = f32(max(int(ctx.dirtyRect[2]), gx + w))
ctx.dirtyRect[3] = cast(f32) max(int(ctx.dirtyRect[3]), gy + h) ctx.dirtyRect[3] = f32(max(int(ctx.dirtyRect[3]), gy + h))
return true
} }
AddFontPath :: proc( AddFontPath :: proc(
@@ -364,7 +342,7 @@ AddFontMem :: proc(
data: []u8, data: []u8,
freeLoadedData: bool, freeLoadedData: bool,
) -> int { ) -> int {
append(&ctx.fonts, Font {}) append(&ctx.fonts, Font{})
res := &ctx.fonts[len(ctx.fonts) - 1] res := &ctx.fonts[len(ctx.fonts) - 1]
res.loadedData = data res.loadedData = data
res.freeLoadedData = freeLoadedData res.freeLoadedData = freeLoadedData
@@ -372,6 +350,7 @@ AddFontMem :: proc(
stbtt.InitFont(&res.info, &res.loadedData[0], 0) stbtt.InitFont(&res.info, &res.loadedData[0], 0)
ascent, descent, line_gap: i32 ascent, descent, line_gap: i32
stbtt.GetFontVMetrics(&res.info, &ascent, &descent, &line_gap) stbtt.GetFontVMetrics(&res.info, &ascent, &descent, &line_gap)
fh := f32(ascent - descent) fh := f32(ascent - descent)
res.ascender = f32(ascent) / fh res.ascender = f32(ascent) / fh
@@ -417,9 +396,7 @@ GetFontByName :: proc(ctx: ^FontContext, name: string) -> int {
__lutReset :: proc(font: ^Font) { __lutReset :: proc(font: ^Font) {
// set lookup table // set lookup table
for i in 0..<HASH_LUT_SIZE { slice.fill(font.lut[:], -1)
font.lut[i] = -1
}
} }
__hashint :: proc(a: u32) -> u32 { __hashint :: proc(a: u32) -> u32 {
@@ -464,23 +441,21 @@ __getGlyph :: proc(
codepoint: rune, codepoint: rune,
isize: i16, isize: i16,
blur: i16 = 0, blur: i16 = 0,
) -> (res: ^Glyph) #no_bounds_check { ) -> (res: ^Glyph, ok: bool) #no_bounds_check {
if isize < 2 { if isize < 2 {
return return
} }
// find code point and size // find code point and size
h := __hashint(u32(codepoint)) & (HASH_LUT_SIZE - 1) h := __hashint(u32(codepoint)) & (HASH_LUT_SIZE - 1)
i := font.lut[h] for i := font.lut[h]; i != -1; /**/ {
for i != -1 {
glyph := &font.glyphs[i] glyph := &font.glyphs[i]
if if glyph.codepoint == codepoint &&
glyph.codepoint == codepoint &&
glyph.isize == isize && glyph.isize == isize &&
glyph.blurSize == blur glyph.blurSize == blur {
{
res = glyph res = glyph
ok = true
return return
} }
@@ -513,20 +488,15 @@ __getGlyph :: proc(
gh := (y1 - y0) + i32(padding) * 2 gh := (y1 - y0) + i32(padding) * 2
// Find free spot for the rect in the atlas // Find free spot for the rect in the atlas
gx, gy, ok := __AtlasAddRect(ctx, int(gw), int(gh)) gx, gy, rect_ok := __AtlasAddRect(ctx, int(gw), int(gh))
if !ok { if !rect_ok {
// try again with expanded // try again with expanded
ExpandAtlas(ctx, ctx.width * 2, ctx.height * 2) ExpandAtlas(ctx, ctx.width * 2, ctx.height * 2)
gx, gy, ok = __AtlasAddRect(ctx, int(gw), int(gh)) gx, gy = __AtlasAddRect(ctx, int(gw), int(gh)) or_return
}
// still not ok?
if !ok {
return
} }
// Init glyph. // Init glyph.
append(&font.glyphs, Glyph { append(&font.glyphs, Glyph{
codepoint = codepoint, codepoint = codepoint,
isize = isize, isize = isize,
blurSize = blurSize, blurSize = blurSize,
@@ -575,11 +545,12 @@ __getGlyph :: proc(
__blur(dst, int(gw), int(gh), ctx.width, blurSize) __blur(dst, int(gw), int(gh), ctx.width, blurSize)
} }
ctx.dirtyRect[0] = cast(f32) min(int(ctx.dirtyRect[0]), int(res.x0)) ctx.dirtyRect[0] = f32(min(int(ctx.dirtyRect[0]), int(res.x0)))
ctx.dirtyRect[1] = cast(f32) min(int(ctx.dirtyRect[1]), int(res.y0)) ctx.dirtyRect[1] = f32(min(int(ctx.dirtyRect[1]), int(res.y0)))
ctx.dirtyRect[2] = cast(f32) max(int(ctx.dirtyRect[2]), int(res.x1)) ctx.dirtyRect[2] = f32(max(int(ctx.dirtyRect[2]), int(res.x1)))
ctx.dirtyRect[3] = cast(f32) max(int(ctx.dirtyRect[3]), int(res.y1)) ctx.dirtyRect[3] = f32(max(int(ctx.dirtyRect[3]), int(res.y1)))
ok = true
return return
} }
@@ -640,7 +611,7 @@ __blurRows :: proc(dst: []u8, w, h, dstStride, alpha: int) {
} }
__blur :: proc(dst: []u8, w, h, dstStride: int, blurSize: i16) { __blur :: proc(dst: []u8, w, h, dstStride: int, blurSize: i16) {
assert(blurSize != 0) assert(blurSize > 0)
// Calculate the alpha such that 90% of the kernel is within the radius. (Kernel extends to infinity) // Calculate the alpha such that 90% of the kernel is within the radius. (Kernel extends to infinity)
sigma := f32(blurSize) * 0.57735 // 1 / sqrt(3) sigma := f32(blurSize) * 0.57735 // 1 / sqrt(3)
@@ -710,9 +681,10 @@ ExpandAtlas :: proc(ctx: ^FontContext, width, height: int, allocator := context.
ResetAtlas :: proc(ctx: ^FontContext, width, height: int, allocator := context.allocator) -> bool { ResetAtlas :: proc(ctx: ^FontContext, width, height: int, allocator := context.allocator) -> bool {
if width == ctx.width && height == ctx.height { if width == ctx.width && height == ctx.height {
// just clear // just clear
mem.zero_slice(ctx.textureData) slice.zero(ctx.textureData)
} else { } else {
// realloc // realloc
delete(ctx.textureData, allocator)
ctx.textureData = make([]byte, width * height, allocator) ctx.textureData = make([]byte, width * height, allocator)
} }
@@ -732,7 +704,7 @@ ResetAtlas :: proc(ctx: ^FontContext, width, height: int, allocator := context.a
ctx.itw = 1.0 / f32(width) ctx.itw = 1.0 / f32(width)
ctx.ith = 1.0 / f32(height) ctx.ith = 1.0 / f32(height)
__AtlasAddWhiteRect(ctx, 2, 2) _ = __AtlasAddWhiteRect(ctx, 2, 2)
return true return true
} }
@@ -842,7 +814,7 @@ UTF8_ACCEPT :: 0
UTF8_REJECT :: 1 UTF8_REJECT :: 1
@(private) @(private)
utf8d := [400]u8 { utf8d := [400]u8{
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, // 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, // 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, // 40..5f
@@ -1041,6 +1013,9 @@ TextIterInit :: proc(
y: f32, y: f32,
text: string, text: string,
) -> (res: TextIter) { ) -> (res: TextIter) {
x, y := x, y
state := __getState(ctx) state := __getState(ctx)
res.font = __getFont(ctx, state.font) res.font = __getFont(ctx, state.font)
res.isize = i16(f32(state.size) * 10) res.isize = i16(f32(state.size) * 10)
@@ -1048,15 +1023,12 @@ TextIterInit :: proc(
res.scale = __getPixelHeightScale(res.font, f32(res.isize) / 10) res.scale = __getPixelHeightScale(res.font, f32(res.isize) / 10)
// align horizontally // align horizontally
x := x
y := y
switch state.ah { switch state.ah {
case .LEFT: {} case .LEFT:
/**/
case .CENTER: case .CENTER:
width := TextBounds(ctx, text, x, y, nil) width := TextBounds(ctx, text, x, y, nil)
x = math.round(x - width * 0.5) x = math.round(x - width * 0.5)
case .RIGHT: case .RIGHT:
width := TextBounds(ctx, text, x, y, nil) width := TextBounds(ctx, text, x, y, nil)
x -= width x -= width
@@ -1066,10 +1038,8 @@ TextIterInit :: proc(
y = math.round(y + __getVerticalAlign(ctx, res.font, state.av, res.isize)) y = math.round(y + __getVerticalAlign(ctx, res.font, state.av, res.isize))
// set positions // set positions
res.x = x res.x, res.nextx = x, x
res.nextx = x res.y, res.nexty = y, y
res.y = y
res.nexty = y
res.previousGlyphIndex = -1 res.previousGlyphIndex = -1
res.spacing = state.spacing res.spacing = state.spacing
res.text = text res.text = text
@@ -1097,13 +1067,12 @@ TextIterNext :: proc(
iter.x = iter.nextx iter.x = iter.nextx
iter.y = iter.nexty iter.y = iter.nexty
iter.codepointCount += 1 iter.codepointCount += 1
glyph := __getGlyph(ctx, iter.font, iter.codepoint, iter.isize, iter.iblur) if glyph, ok := __getGlyph(ctx, iter.font, iter.codepoint, iter.isize, iter.iblur); ok {
if glyph != nil {
__getQuad(ctx, iter.font, iter.previousGlyphIndex, glyph, iter.scale, iter.spacing, &iter.nextx, &iter.nexty, quad) __getQuad(ctx, iter.font, iter.previousGlyphIndex, glyph, iter.scale, iter.spacing, &iter.nextx, &iter.nexty, quad)
iter.previousGlyphIndex = glyph.index
} else {
iter.previousGlyphIndex = -1
} }
iter.previousGlyphIndex = glyph == nil ? -1 : glyph.index
ok = true ok = true
break break
} }
@@ -1127,12 +1096,9 @@ TextBounds :: proc(
font := __getFont(ctx, state.font) font := __getFont(ctx, state.font)
// bunch of state // bunch of state
x := x x, y := x, y
y := y minx, maxx := x, x
minx := x miny, maxy := y, y
maxx := x
miny := y
maxy := y
start_x := x start_x := x
// iterate // iterate
@@ -1143,9 +1109,7 @@ TextBounds :: proc(
codepoint: rune codepoint: rune
for byte_offset in 0..<len(text) { for byte_offset in 0..<len(text) {
if __decutf8(&utf8state, &codepoint, text[byte_offset]) { if __decutf8(&utf8state, &codepoint, text[byte_offset]) {
glyph := __getGlyph(ctx, font, codepoint, isize, iblur) if glyph, ok := __getGlyph(ctx, font, codepoint, isize, iblur); ok {
if glyph != nil {
__getQuad(ctx, font, previousGlyphIndex, glyph, scale, state.spacing, &x, &y, &quad) __getQuad(ctx, font, previousGlyphIndex, glyph, scale, state.spacing, &x, &y, &quad)
if quad.x0 < minx { if quad.x0 < minx {
@@ -1170,21 +1134,23 @@ TextBounds :: proc(
maxy = quad.y0 maxy = quad.y0
} }
} }
previousGlyphIndex = glyph.index
} else {
previousGlyphIndex = -1
} }
previousGlyphIndex = glyph == nil ? -1 : glyph.index
} }
} }
// horizontal alignment // horizontal alignment
advance := x - start_x advance := x - start_x
switch state.ah { switch state.ah {
case .LEFT: {} case .LEFT:
/**/
case .CENTER: case .CENTER:
minx -= advance * 0.5 minx -= advance * 0.5
maxx -= advance * 0.5 maxx -= advance * 0.5
case .RIGHT: case .RIGHT:
minx -= advance minx -= advance
maxx -= advance maxx -= advance
@@ -1210,8 +1176,8 @@ VerticalMetrics :: proc(
} }
// reset to single state // reset to single state
BeginState :: proc(using ctx: ^FontContext) { BeginState :: proc(ctx: ^FontContext) {
state_count = 0 ctx.state_count = 0
PushState(ctx) PushState(ctx)
ClearState(ctx) ClearState(ctx)
} }
@@ -1223,7 +1189,6 @@ EndState :: proc(using ctx: ^FontContext) {
if callbackUpdate != nil { if callbackUpdate != nil {
callbackUpdate(userData, dirtyRect, raw_data(textureData)) callbackUpdate(userData, dirtyRect, raw_data(textureData))
} }
__dirtyRectReset(ctx) __dirtyRectReset(ctx)
} }
} }