From 26e2aa96aaa212628c826fe6cd73d83742a7186d Mon Sep 17 00:00:00 2001 From: Ed_ Date: Tue, 2 Jul 2024 00:16:08 -0400 Subject: [PATCH] wip freetype glyph caching suffering --- vefontcache/draw.odin | 118 ++++++++++++++++++++++++++++++++- vefontcache/parser.odin | 141 ++++------------------------------------ 2 files changed, 130 insertions(+), 129 deletions(-) diff --git a/vefontcache/draw.odin b/vefontcache/draw.odin index 776fda5..650e0aa 100644 --- a/vefontcache/draw.odin +++ b/vefontcache/draw.odin @@ -1,5 +1,8 @@ package vefontcache +import "thirdparty:freetype" +import "core:slice" + Vertex :: struct { pos : Vec2, u, v : f32, @@ -85,6 +88,114 @@ blit_quad :: proc( draw_list : ^DrawList, p0 : Vec2 = {0, 0}, p1 : Vec2 = {1, 1} 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.. 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 { // profile(#procedure) @@ -92,6 +203,11 @@ cache_glyph :: proc(ctx : ^Context, font : FontID, glyph_index : Glyph, entry : 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) assert(error == .None) 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)) 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) diff --git a/vefontcache/parser.odin b/vefontcache/parser.odin index 3b33127..26e5966 100644 --- a/vefontcache/parser.odin +++ b/vefontcache/parser.odin @@ -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) { + 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 { case .Freetype: - error := freetype.load_glyph(font.freetype_info, cast(u32)glyph_index, {.No_Bitmap, .No_Hinting, .No_Scale}) - 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 + // TODO(Ed): Don't do this we're going a completely different route for handling shapes case .STB_TrueType: stb_shape : [^]stbtt.vertex @@ -407,48 +337,3 @@ parser_scale_for_mapping_em_to_pixels :: #force_inline proc "contextless" ( font } 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 := { - - } - } - } -} -}