From 44c97eec7127a50688bf6d2fcecc05d824e2cbd4 Mon Sep 17 00:00:00 2001 From: Ed_ Date: Thu, 2 Jan 2025 18:16:46 -0500 Subject: [PATCH] move freetype function to its own file to declutter draw.odin --- code/font/vefontcache/draw.odin | 196 ++++-------------------- code/font/vefontcache/freetype_wip.odin | 157 +++++++++++++++++++ 2 files changed, 183 insertions(+), 170 deletions(-) create mode 100644 code/font/vefontcache/freetype_wip.odin diff --git a/code/font/vefontcache/draw.odin b/code/font/vefontcache/draw.odin index d54dfae..0b30808 100644 --- a/code/font/vefontcache/draw.odin +++ b/code/font/vefontcache/draw.odin @@ -122,164 +122,6 @@ blit_quad :: #force_inline proc ( draw_list : ^Draw_List, p0 : Vec2 = {0, 0}, p1 return } -// TODO(Ed): glyph caching cannot be handled in a 'font parser' abstraction. Just going to have explicit procedures to grab info neatly... -// cache_glyph_freetype :: proc(ctx: ^Context, font: Font_ID, glyph_index: Glyph, entry: ^Entry, bounds_0, bounds_1: Vec2, scale, translate: Vec2) -> b32 -// { -// draw_filled_path_freetype :: proc( draw_list : ^Draw_List, outside_point : Vec2, path : []Vertex, -// scale := Vec2 { 1, 1 }, -// translate := Vec2 { 0, 0 }, -// debug_print_verbose : b32 = false -// ) -// { -// if debug_print_verbose { -// log("outline_path:") -// for point in path { -// vec := point.pos * scale + translate -// logf(" %0.2f %0.2f", vec.x, vec.y ) -// } -// } - -// v_offset := cast(u32) len(draw_list.vertices) -// for point in path -// { -// transformed_point := Vertex { -// pos = point.pos * scale + translate, -// u = 0, -// v = 0 -// } -// append( & draw_list.vertices, transformed_point ) -// } - -// if len(path) > 2 -// { -// indices := & draw_list.indices -// for index : u32 = 1; index < cast(u32) len(path) - 1; index += 1 { -// to_add := [3]u32 { -// v_offset, -// v_offset + index, -// v_offset + index + 1 -// } -// append( indices, ..to_add[:] ) -// } - -// // Close the path by connecting the last vertex to the first two -// to_add := [3]u32 { -// v_offset, -// v_offset + cast(u32)(len(path) - 1), -// v_offset + 1 -// } -// append( indices, ..to_add[:] ) -// } -// } - -// if glyph_index == Glyph(0) { -// return false -// } - -// face := entry.parser_info.freetype_info -// error := freetype.load_glyph(face, u32(glyph_index), {.No_Bitmap, .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 -// } - -// draw := Draw_Call_Default -// draw.pass = Frame_Buffer_Pass.Glyph -// draw.start_index = cast(u32) len(ctx.draw_list.indices) - -// contours := slice.from_ptr(cast( [^]i16) outline.contours, int(outline.n_contours)) -// 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)) - -// path := &ctx.temp_path -// clear(path) - -// outside := Vec2{ bounds_0.x - 21, bounds_0.y - 33 } - -// start_index: int = 0 -// for contour_index in 0 ..< int(outline.n_contours) -// { -// end_index := int(contours[contour_index]) + 1 -// prev_point : Vec2 -// first_point : Vec2 - -// for idx := start_index; idx < end_index; idx += 1 -// { -// current_pos := Vec2 { f32( points[idx].x ), f32( points[idx].y ) } -// if ( tags[idx] & 1 ) == 0 -// { -// // If current point is off-curve -// if (idx == start_index || (tags[ idx - 1 ] & 1) != 0) -// { -// // current is the first or following an on-curve point -// prev_point = current_pos -// } -// else -// { -// // current and previous are off-curve, calculate midpoint -// midpoint := (prev_point + current_pos) * 0.5 -// append( path, Vertex { pos = midpoint } ) // Add midpoint as on-curve point -// if idx < end_index - 1 -// { -// // perform interp from prev_point to current_pos via midpoint -// step := 1.0 / entry.curve_quality -// for alpha : f32 = 0.0; alpha <= 1.0; alpha += step -// { -// bezier_point := eval_point_on_bezier3( prev_point, midpoint, current_pos, alpha ) -// append( path, Vertex{ pos = bezier_point } ) -// } -// } - -// prev_point = current_pos -// } -// } -// else -// { -// if idx == start_index { -// first_point = current_pos -// } -// if prev_point != (Vec2{}) { -// // there was an off-curve point before this -// append(path, Vertex{ pos = prev_point}) // Ensure previous off-curve is handled -// } -// append(path, Vertex{ pos = current_pos}) -// prev_point = {} -// } -// } - -// // ensure the contour is closed -// if path[0].pos != path[ len(path) - 1 ].pos { -// append(path, Vertex{pos = path[0].pos}) -// } -// draw_filled_path(&ctx.draw_list, bounds_0, path[:], scale, translate) -// // draw_filled_path(&ctx.draw_list, bounds_0, path[:], scale, translate, ctx.debug_print_verbose) -// clear(path) -// start_index = end_index -// } - -// if len(path) > 0 { -// // draw_filled_path(&ctx.draw_list, outside, path[:], scale, translate, ctx.debug_print_verbose) -// draw_filled_path(&ctx.draw_list, outside, path[:], scale, translate) -// } - -// draw.end_index = cast(u32) len(ctx.draw_list.indices) -// if draw.end_index > draw.start_index { -// append( & ctx.draw_list.calls, draw) -// } - -// return true -// } - -// TODO(Ed): Is it better to cache the glyph vertices for when it must be re-drawn (directly or two atlas)? generate_glyph_pass_draw_list :: #force_inline proc(ctx : ^Context, glyph_index : Glyph, glyph_shape : Parser_Glyph_Shape, @@ -674,20 +516,34 @@ generate_shape_draw_list :: #force_no_inline proc( ctx : ^Context, for & glyph, index in glyph_pack { region := atlas.regions[glyph.region_kind] - glyph.atlas_index = lru_get( & region.state, glyph.lru_code ) - glyph.in_atlas, glyph.should_cache = check_and_reserve_slot_in_atlas( ctx ^, glyph.index, glyph.lru_code, & glyph.atlas_index, region ) + + if ctx.temp_codepoint_seen_num <= i32(cap(ctx.temp_codepoint_seen)) + { + if glyph.atlas_index == - 1 + { + // Check to see if we reached capacity for the atlas + if region.next_idx > region.state.capacity + { + // We will evict LRU. We must predict which LRU will get evicted, and if it's something we've seen then we need to take slowpath and flush batch. + next_evict_codepoint := lru_get_next_evicted( region.state ) + found, success := ctx.temp_codepoint_seen[next_evict_codepoint] + assert(success != false) + if (found) { + continue + } + } + + profile("append to_cache") + glyph.atlas_index = atlas_reserve_slot(region, glyph.lru_code) + glyph.region_pos, glyph.region_size = atlas_region_bbox(region ^, glyph.atlas_index) + append_sub_pack(to_cache, glyph) + continue + } + } + glyph.region_pos, glyph.region_size = atlas_region_bbox(region ^, glyph.atlas_index) - - if glyph.should_cache { - profile("append to_cache") - append_sub_pack(to_cache, glyph) - } - else { - profile("append cached") - append_sub_pack(cached, glyph) - } - + append_sub_pack(cached, glyph) mark_batch_codepoint_seen(ctx, glyph.lru_code) } profile_end() diff --git a/code/font/vefontcache/freetype_wip.odin b/code/font/vefontcache/freetype_wip.odin new file mode 100644 index 0000000..f962225 --- /dev/null +++ b/code/font/vefontcache/freetype_wip.odin @@ -0,0 +1,157 @@ + +// TODO(Ed): glyph caching cannot be handled in a 'font parser' abstraction. Just going to have explicit procedures to grab info neatly... +// cache_glyph_freetype :: proc(ctx: ^Context, font: Font_ID, glyph_index: Glyph, entry: ^Entry, bounds_0, bounds_1: Vec2, scale, translate: Vec2) -> b32 +// { +// draw_filled_path_freetype :: proc( draw_list : ^Draw_List, outside_point : Vec2, path : []Vertex, +// scale := Vec2 { 1, 1 }, +// translate := Vec2 { 0, 0 }, +// debug_print_verbose : b32 = false +// ) +// { +// if debug_print_verbose { +// log("outline_path:") +// for point in path { +// vec := point.pos * scale + translate +// logf(" %0.2f %0.2f", vec.x, vec.y ) +// } +// } + +// v_offset := cast(u32) len(draw_list.vertices) +// for point in path +// { +// transformed_point := Vertex { +// pos = point.pos * scale + translate, +// u = 0, +// v = 0 +// } +// append( & draw_list.vertices, transformed_point ) +// } + +// if len(path) > 2 +// { +// indices := & draw_list.indices +// for index : u32 = 1; index < cast(u32) len(path) - 1; index += 1 { +// to_add := [3]u32 { +// v_offset, +// v_offset + index, +// v_offset + index + 1 +// } +// append( indices, ..to_add[:] ) +// } + +// // Close the path by connecting the last vertex to the first two +// to_add := [3]u32 { +// v_offset, +// v_offset + cast(u32)(len(path) - 1), +// v_offset + 1 +// } +// append( indices, ..to_add[:] ) +// } +// } + +// if glyph_index == Glyph(0) { +// return false +// } + +// face := entry.parser_info.freetype_info +// error := freetype.load_glyph(face, u32(glyph_index), {.No_Bitmap, .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 +// } + +// draw := Draw_Call_Default +// draw.pass = Frame_Buffer_Pass.Glyph +// draw.start_index = cast(u32) len(ctx.draw_list.indices) + +// contours := slice.from_ptr(cast( [^]i16) outline.contours, int(outline.n_contours)) +// 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)) + +// path := &ctx.temp_path +// clear(path) + +// outside := Vec2{ bounds_0.x - 21, bounds_0.y - 33 } + +// start_index: int = 0 +// for contour_index in 0 ..< int(outline.n_contours) +// { +// end_index := int(contours[contour_index]) + 1 +// prev_point : Vec2 +// first_point : Vec2 + +// for idx := start_index; idx < end_index; idx += 1 +// { +// current_pos := Vec2 { f32( points[idx].x ), f32( points[idx].y ) } +// if ( tags[idx] & 1 ) == 0 +// { +// // If current point is off-curve +// if (idx == start_index || (tags[ idx - 1 ] & 1) != 0) +// { +// // current is the first or following an on-curve point +// prev_point = current_pos +// } +// else +// { +// // current and previous are off-curve, calculate midpoint +// midpoint := (prev_point + current_pos) * 0.5 +// append( path, Vertex { pos = midpoint } ) // Add midpoint as on-curve point +// if idx < end_index - 1 +// { +// // perform interp from prev_point to current_pos via midpoint +// step := 1.0 / entry.curve_quality +// for alpha : f32 = 0.0; alpha <= 1.0; alpha += step +// { +// bezier_point := eval_point_on_bezier3( prev_point, midpoint, current_pos, alpha ) +// append( path, Vertex{ pos = bezier_point } ) +// } +// } + +// prev_point = current_pos +// } +// } +// else +// { +// if idx == start_index { +// first_point = current_pos +// } +// if prev_point != (Vec2{}) { +// // there was an off-curve point before this +// append(path, Vertex{ pos = prev_point}) // Ensure previous off-curve is handled +// } +// append(path, Vertex{ pos = current_pos}) +// prev_point = {} +// } +// } + +// // ensure the contour is closed +// if path[0].pos != path[ len(path) - 1 ].pos { +// append(path, Vertex{pos = path[0].pos}) +// } +// draw_filled_path(&ctx.draw_list, bounds_0, path[:], scale, translate) +// // draw_filled_path(&ctx.draw_list, bounds_0, path[:], scale, translate, ctx.debug_print_verbose) +// clear(path) +// start_index = end_index +// } + +// if len(path) > 0 { +// // draw_filled_path(&ctx.draw_list, outside, path[:], scale, translate, ctx.debug_print_verbose) +// draw_filled_path(&ctx.draw_list, outside, path[:], scale, translate) +// } + +// draw.end_index = cast(u32) len(ctx.draw_list.indices) +// if draw.end_index > draw.start_index { +// append( & ctx.draw_list.calls, draw) +// } + +// return true +// }