From 38ebed887442a596e63feb4fa51fa88184266909 Mon Sep 17 00:00:00 2001 From: Ed_ Date: Mon, 3 Jun 2024 21:08:12 -0400 Subject: [PATCH] VEFC: Added bad edge case to handle parsing conic curves, progess on cache_glyph --- code/font/VEFontCache/VEFontCache.odin | 25 ++++++- code/font/VEFontCache/draw.odin | 7 +- code/font/VEFontCache/mappings.odin | 5 ++ code/font/VEFontCache/parser.odin | 95 +++++++++++++++++++++++--- 4 files changed, 118 insertions(+), 14 deletions(-) diff --git a/code/font/VEFontCache/VEFontCache.odin b/code/font/VEFontCache/VEFontCache.odin index 9d83364..7c2f8c6 100644 --- a/code/font/VEFontCache/VEFontCache.odin +++ b/code/font/VEFontCache/VEFontCache.odin @@ -530,7 +530,8 @@ cache_glyph :: proc( ctx : ^Context, font : FontID, glyph_index : Glyph, scale, if ctx.debug_print_verbose { - + log( "shape: \n") + // for } /* @@ -538,8 +539,26 @@ cache_glyph :: proc( ctx : ^Context, font : FontID, glyph_index : Glyph, scale, We need a random point that is outside our shape. We simply pick something diagonally across from top-left bound corner. Note that this outside point is scaled alongside the glyph in ve_fontcache_draw_filled_path, so we don't need to handle that here. */ - bounds_0, bounds_1 : Vec2i - // success := parser_get_glyph_box() + bounds_0, bounds_1 := parser_get_glyph_box( entry.parser_info, glyph_index ) + + outside := Vec2 { + f32(bounds_0.x - 21), + f32(bounds_0.y - 33), + } + + // Note(Original Author): Figure out scaling so it fits within our box. + draw : DrawCall + draw.pass = FrameBufferPass.Glyph + draw.start_index = u32(ctx.draw_list.indices.num) + + // Note(Original Author); + // Draw the path using simplified version of https://medium.com/@evanwallace/easy-scalable-text-rendering-on-the-gpu-c3f4d782c5ac. + // Instead of involving fragment shader code we simply make use of modern GPU ability to crunch triangles and brute force curve definitions. + path := ctx.temp_path + clear(path) + for vertex in shape { + + } return false } diff --git a/code/font/VEFontCache/draw.odin b/code/font/VEFontCache/draw.odin index d03f6a7..8b4fe62 100644 --- a/code/font/VEFontCache/draw.odin +++ b/code/font/VEFontCache/draw.odin @@ -1,6 +1,7 @@ package VEFontCache -FrameBufferPass :: enum { +FrameBufferPass :: enum u32 { + None = 0, Glyph = 1, Atlas = 2, Target = 3, @@ -8,7 +9,7 @@ FrameBufferPass :: enum { } DrawCall :: struct { - pass : u32, + pass : FrameBufferPass, start_index : u32, end_index : u32, clear_before_draw : b32, @@ -17,7 +18,7 @@ DrawCall :: struct { } DrawCall_Default :: DrawCall { - pass = 0, + pass = .None, start_index = 0, end_index = 0, clear_before_draw = false, diff --git a/code/font/VEFontCache/mappings.odin b/code/font/VEFontCache/mappings.odin index 33df591..9d08ca8 100644 --- a/code/font/VEFontCache/mappings.odin +++ b/code/font/VEFontCache/mappings.odin @@ -7,6 +7,11 @@ import "core:mem" Kilobyte :: mem.Kilobyte +Arena :: mem.Arena + +arena_allocator :: mem.arena_allocator +arena_init :: mem.arena_init + Allocator :: mem.Allocator AllocatorError :: mem.Allocator_Error diff --git a/code/font/VEFontCache/parser.odin b/code/font/VEFontCache/parser.odin index 1f51a12..1661988 100644 --- a/code/font/VEFontCache/parser.odin +++ b/code/font/VEFontCache/parser.odin @@ -8,6 +8,7 @@ That interface is not exposed from this parser but could be added to parser_init */ import "core:c" +import "core:math" import stbtt "vendor:stb/truetype" import freetype "thirdparty:freetype" @@ -169,11 +170,50 @@ parser_is_glyph_empty :: proc( font : ^ParserFontInfo, glyph_index : Glyph ) -> return false } -// TODO(Ed): This makes freetype second class I guess but VEFontCache doesn't have native support for freetype originally so.... -// parser_convert_freetype_outline_to_stb_truetype_shape :: proc( outline : freetype.Outline ) -> (shape : ParserGlyphShape, error : AllocatorError) -// { +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 := { + + } + } + } +} +} parser_get_glyph_shape :: proc( font : ^ParserFontInfo, glyph_index : Glyph ) -> (shape : ParserGlyphShape, error : AllocatorError) { @@ -261,6 +301,34 @@ parser_get_glyph_shape :: proc( font : ^ParserFontInfo, glyph_index : Glyph ) -> }) index += 2 } + else if (tag & FT_CURVE_TAG_CONIC) != 0 + { + // TODO(Ed): This is using a very dead simple algo to convert the conic to a cubic curve + // not sure if we need something more sophisticaated + point1 := points[ index + 1 ] + + control_conv :: f32(0.5) // Conic to cubic control point distance + to_float := f32(1.0 / 64.0) + + fp := Vec2 { f32(point.x), f32(point.y) } * to_float + fp1 := Vec2 { f32(point1.x), f32(point1.y) } * to_float + + control1 := freetype.Vector { + point.x + freetype.Pos( (fp1.x - fp.x) * control_conv * 64.0 ), + point.y + freetype.Pos( (fp1.y - fp.y) * control_conv * 64.0 ), + } + control2 := freetype.Vector { + point1.x + freetype.Pos( (fp.x - fp1.x) * control_conv * 64.0 ), + point1.y + freetype.Pos( (fp.y - fp1.y) * control_conv * 64.0 ), + } + append(& vertices, ParserGlyphVertex { type = .Cubic, + x = u16(point1.x), y = u16(point1.y), + contour_x0 = u16(control1.x), contour_y0 = u16(control1.y), + contour_x1 = u16(control2.x), contour_y1 = u16(control2.y), + padding = 0, + }) + index += 1 + } else { append(& vertices, ParserGlyphVertex { type = .Line, @@ -280,6 +348,8 @@ parser_get_glyph_shape :: proc( font : ^ParserFontInfo, glyph_index : Glyph ) -> padding = 0, }) } + + shape = array_to_slice(vertices) } case .STB_TrueType: @@ -298,8 +368,17 @@ parser_get_glyph_shape :: proc( font : ^ParserFontInfo, glyph_index : Glyph ) -> parser_free_shape :: proc( font : ^ParserFontInfo, shape : ParserGlyphShape ) { - // switch font.kind - // { - // case .Freetype - // } + switch font.kind + { + case .Freetype: + delete( array_underlying_slice(shape) ) + + case .STB_TrueType: + stbtt.FreeShape( & font.stbtt_info, transmute( [^]stbtt.vertex) raw_data(shape) ) + } } + +parser_get_glyph_box :: proc( font : ^ParserFontInfo, glyph_index : Glyph ) -> (bounds_0, bounds_1 : Vec2i) +{ + return +} \ No newline at end of file