mirror of
https://github.com/Ed94/VEFontCache-Odin.git
synced 2025-08-06 06:52:44 -07:00
wip freetype glyph caching suffering
This commit is contained in:
@@ -1,5 +1,8 @@
|
|||||||
package vefontcache
|
package vefontcache
|
||||||
|
|
||||||
|
import "thirdparty:freetype"
|
||||||
|
import "core:slice"
|
||||||
|
|
||||||
Vertex :: struct {
|
Vertex :: struct {
|
||||||
pos : Vec2,
|
pos : Vec2,
|
||||||
u, v : f32,
|
u, v : f32,
|
||||||
@@ -85,6 +88,114 @@ blit_quad :: proc( draw_list : ^DrawList, p0 : Vec2 = {0, 0}, p1 : Vec2 = {1, 1}
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cache_glyph_freetype :: proc(ctx: ^Context, font: FontID, glyph_index: Glyph, entry: ^Entry, bounds_0, bounds_1: Vec2, scale, translate: Vec2) -> b32
|
||||||
|
{
|
||||||
|
if glyph_index == Glyph(0) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
face := entry.parser_info.freetype_info
|
||||||
|
error := freetype.load_glyph(face, u32(glyph_index), {.No_Bitmap, .No_Hinting, .No_Scale})
|
||||||
|
if error != .Ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
glyph := face.glyph
|
||||||
|
if glyph.format != .Outline {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
outline := &glyph.outline
|
||||||
|
if outline.n_points == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
outside := Vec2{bounds_0.x - 21, bounds_0.y - 33}
|
||||||
|
|
||||||
|
draw := DrawCall_Default
|
||||||
|
draw.pass = FrameBufferPass.Glyph
|
||||||
|
draw.start_index = u32(len(ctx.draw_list.indices))
|
||||||
|
|
||||||
|
path := &ctx.temp_path
|
||||||
|
clear(path)
|
||||||
|
|
||||||
|
points := slice.from_ptr(cast([^]freetype.Vector)outline.points, int(outline.n_points))
|
||||||
|
tags := slice.from_ptr(cast([^]u8)outline.tags, int(outline.n_points))
|
||||||
|
contours := slice.from_ptr(cast([^]i16)outline.contours, int(outline.n_contours))
|
||||||
|
|
||||||
|
curve_quality := max(entry.curve_quality, 12) // Increase minimum curve quality
|
||||||
|
|
||||||
|
start := 0
|
||||||
|
for contour_index in 0..<int(outline.n_contours)
|
||||||
|
{
|
||||||
|
end := int(contours[contour_index]) + 1
|
||||||
|
first_point := Vec2{f32(points[start].x), f32(points[start].y)}
|
||||||
|
|
||||||
|
if len(path) > 0 {
|
||||||
|
draw_filled_path(&ctx.draw_list, outside, path[:], scale, translate, ctx.debug_print_verbose)
|
||||||
|
clear(path)
|
||||||
|
}
|
||||||
|
append(path, Vertex{pos = first_point})
|
||||||
|
|
||||||
|
for point_index := start + 1; point_index <= end; point_index += 1
|
||||||
|
{
|
||||||
|
curr := points[point_index % int(outline.n_points)]
|
||||||
|
curr_tag := tags[point_index % int(outline.n_points)]
|
||||||
|
curr_pos := Vec2 { f32(curr.x), f32(curr.y) }
|
||||||
|
|
||||||
|
if curr_tag & 1 != 0
|
||||||
|
{
|
||||||
|
// On-curve point
|
||||||
|
append(path, Vertex{pos = curr_pos})
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
// Off-curve point
|
||||||
|
prev := path[len(path)-1].pos
|
||||||
|
next: Vec2
|
||||||
|
if point_index == end
|
||||||
|
{
|
||||||
|
next = first_point
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
next_point := points[(point_index + 1) % int(outline.n_points)]
|
||||||
|
next = Vec2{f32(next_point.x), f32(next_point.y)}
|
||||||
|
if tags[(point_index + 1) % int(outline.n_points)] & 1 == 0
|
||||||
|
{
|
||||||
|
// Next point is also off-curve, insert virtual on-curve point
|
||||||
|
next = {(curr_pos.x + next.x) * 0.5, (curr_pos.y + next.y) * 0.5}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i: f32 = 1; i <= curve_quality; i += 1
|
||||||
|
{
|
||||||
|
t := i / curve_quality
|
||||||
|
q := eval_point_on_bezier3(prev, curr_pos, next, t)
|
||||||
|
append(path, Vertex{pos = q})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Explicitly close the contour
|
||||||
|
if path[0].pos != path[len(path)-1].pos {
|
||||||
|
append(path, Vertex{pos = first_point})
|
||||||
|
}
|
||||||
|
|
||||||
|
start = end
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(path) > 0 {
|
||||||
|
draw_filled_path(&ctx.draw_list, outside, path[:], scale, translate, ctx.debug_print_verbose)
|
||||||
|
}
|
||||||
|
|
||||||
|
draw.end_index = u32(len(ctx.draw_list.indices))
|
||||||
|
if draw.end_index > draw.start_index {
|
||||||
|
append(&ctx.draw_list.calls, draw)
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
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 : FontID, glyph_index : Glyph, entry : ^Entry, bounds_0, bounds_1 : Vec2, scale, translate : Vec2) -> b32
|
||||||
{
|
{
|
||||||
// profile(#procedure)
|
// profile(#procedure)
|
||||||
@@ -92,6 +203,11 @@ cache_glyph :: proc(ctx : ^Context, font : FontID, glyph_index : Glyph, entry :
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if entry.parser_info.kind == .Freetype {
|
||||||
|
result := cache_glyph_freetype( ctx, font, glyph_index, entry, bounds_0, bounds_1, scale, translate )
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
shape, error := parser_get_glyph_shape(&entry.parser_info, glyph_index)
|
shape, error := parser_get_glyph_shape(&entry.parser_info, glyph_index)
|
||||||
assert(error == .None)
|
assert(error == .None)
|
||||||
if len(shape) == 0 {
|
if len(shape) == 0 {
|
||||||
@@ -151,7 +267,7 @@ cache_glyph :: proc(ctx : ^Context, font : FontID, glyph_index : Glyph, entry :
|
|||||||
|
|
||||||
draw.end_index = u32(len(ctx.draw_list.indices))
|
draw.end_index = u32(len(ctx.draw_list.indices))
|
||||||
if draw.end_index > draw.start_index {
|
if draw.end_index > draw.start_index {
|
||||||
append(&ctx.draw_list.calls, draw)
|
append( & ctx.draw_list.calls, draw)
|
||||||
}
|
}
|
||||||
|
|
||||||
parser_free_shape(&entry.parser_info, shape)
|
parser_free_shape(&entry.parser_info, shape)
|
||||||
|
@@ -230,92 +230,22 @@ parser_get_glyph_box :: #force_inline proc ( font : ^ParserFontInfo, glyph_index
|
|||||||
|
|
||||||
parser_get_glyph_shape :: proc( font : ^ParserFontInfo, glyph_index : Glyph ) -> (shape : ParserGlyphShape, error : AllocatorError)
|
parser_get_glyph_shape :: proc( font : ^ParserFontInfo, glyph_index : Glyph ) -> (shape : ParserGlyphShape, error : AllocatorError)
|
||||||
{
|
{
|
||||||
|
quad_to_cubic :: proc(p0, p1, p2: freetype.Vector) -> (c1, c2: freetype.Vector) {
|
||||||
|
c1 = freetype.Vector{
|
||||||
|
x = p0.x + ((p1.x - p0.x) * 2 + 1) / 3,
|
||||||
|
y = p0.y + ((p1.y - p0.y) * 2 + 1) / 3,
|
||||||
|
}
|
||||||
|
c2 = freetype.Vector{
|
||||||
|
x = p2.x + ((p1.x - p2.x) * 2 + 1) / 3,
|
||||||
|
y = p2.y + ((p1.y - p2.y) * 2 + 1) / 3,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
switch font.kind
|
switch font.kind
|
||||||
{
|
{
|
||||||
case .Freetype:
|
case .Freetype:
|
||||||
error := freetype.load_glyph(font.freetype_info, cast(u32)glyph_index, {.No_Bitmap, .No_Hinting, .No_Scale})
|
// TODO(Ed): Don't do this we're going a completely different route for handling shapes
|
||||||
if error != .Ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
glyph := font.freetype_info.glyph
|
|
||||||
if glyph.format != .Outline {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
outline := &glyph.outline
|
|
||||||
n_points := int(outline.n_points)
|
|
||||||
n_contours := int(outline.n_contours)
|
|
||||||
|
|
||||||
vertices, alloc_error := make([dynamic]ParserGlyphVertex, 0, n_points + n_contours)
|
|
||||||
if alloc_error != .None {
|
|
||||||
// Handle allocation error
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
points := slice.from_ptr(cast([^]freetype.Vector) outline.points, n_points)
|
|
||||||
tags := slice.from_ptr(cast([^]u8) outline.tags, n_points)
|
|
||||||
contours := slice.from_ptr(cast([^]i16) outline.contours, n_contours)
|
|
||||||
|
|
||||||
start := 0
|
|
||||||
for contour_index in 0 ..< n_contours
|
|
||||||
{
|
|
||||||
end := int(contours[contour_index]) + 1
|
|
||||||
|
|
||||||
first_point := points[start]
|
|
||||||
append( & vertices, ParserGlyphVertex { type = .Move, x = i16(first_point.x), y = i16(first_point.y) })
|
|
||||||
|
|
||||||
for point_index := start; point_index < end; point_index += 1
|
|
||||||
{
|
|
||||||
tag := tags[point_index]
|
|
||||||
point := points[point_index]
|
|
||||||
next_point := points[(point_index + 1) % end]
|
|
||||||
|
|
||||||
if tag & 1 == 0
|
|
||||||
{
|
|
||||||
// Off-curve point
|
|
||||||
if tags[(point_index + 1) % end] & 1 == 0
|
|
||||||
{
|
|
||||||
// Next is also off-curve
|
|
||||||
mid_point := Vec2{
|
|
||||||
(f32(point.x) + f32(next_point.x)) / 2,
|
|
||||||
(f32(point.y) + f32(next_point.y)) / 2,
|
|
||||||
}
|
|
||||||
append(&vertices, ParserGlyphVertex {
|
|
||||||
type = .Curve,
|
|
||||||
x = i16(mid_point.x),
|
|
||||||
y = i16(mid_point.y),
|
|
||||||
contour_x0 = i16(point.x),
|
|
||||||
contour_y0 = i16(point.y),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Next is on-curve
|
|
||||||
append(&vertices, ParserGlyphVertex{
|
|
||||||
type = .Curve,
|
|
||||||
x = i16(next_point.x),
|
|
||||||
y = i16(next_point.y),
|
|
||||||
contour_x0 = i16(point.x),
|
|
||||||
contour_y0 = i16(point.y),
|
|
||||||
})
|
|
||||||
point_index += 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// On-curve point
|
|
||||||
append(&vertices, ParserGlyphVertex{
|
|
||||||
type = .Line,
|
|
||||||
x = i16(point.x), y = i16(point.y),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
start = end
|
|
||||||
}
|
|
||||||
|
|
||||||
shape = vertices
|
|
||||||
|
|
||||||
case .STB_TrueType:
|
case .STB_TrueType:
|
||||||
stb_shape : [^]stbtt.vertex
|
stb_shape : [^]stbtt.vertex
|
||||||
@@ -407,48 +337,3 @@ parser_scale_for_mapping_em_to_pixels :: #force_inline proc "contextless" ( font
|
|||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
when false {
|
|
||||||
parser_convert_conic_to_cubic_freetype :: proc( vertices : Array(ParserGlyphVertex), p0, p1, p2 : freetype.Vector, tolerance : f32 )
|
|
||||||
{
|
|
||||||
scratch : [Kilobyte * 4]u8
|
|
||||||
scratch_arena : Arena; arena_init(& scratch_arena, scratch[:])
|
|
||||||
|
|
||||||
points, error := make( Array(freetype.Vector), 256, allocator = arena_allocator( &scratch_arena) )
|
|
||||||
assert(error == .None)
|
|
||||||
|
|
||||||
append( & points, p0)
|
|
||||||
append( & points, p1)
|
|
||||||
append( & points, p2)
|
|
||||||
|
|
||||||
to_float : f32 = 1.0 / 64.0
|
|
||||||
control_conv :: f32(2.0 / 3.0) // Conic to cubic control point distance
|
|
||||||
|
|
||||||
for ; points.num > 1; {
|
|
||||||
p0 := points.data[0]
|
|
||||||
p1 := points.data[1]
|
|
||||||
p2 := points.data[2]
|
|
||||||
|
|
||||||
fp0 := Vec2{ f32(p0.x), f32(p0.y) } * to_float
|
|
||||||
fp1 := Vec2{ f32(p1.x), f32(p1.y) } * to_float
|
|
||||||
fp2 := Vec2{ f32(p2.x), f32(p2.y) } * to_float
|
|
||||||
|
|
||||||
delta_x := fp0.x - 2 * fp1.x + fp2.x;
|
|
||||||
delta_y := fp0.y - 2 * fp1.y + fp2.y;
|
|
||||||
distance := math.sqrt(delta_x * delta_x + delta_y * delta_y);
|
|
||||||
|
|
||||||
if distance <= tolerance
|
|
||||||
{
|
|
||||||
control1 := {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
control2 := {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Reference in New Issue
Block a user