package vefontcache import "base:runtime" import "core:simd" import "core:math" import core_log "core:log" Colour :: [4]f32 Vec2 :: [2]f32 Vec2i :: [2]i32 Vec2_64 :: [2]f64 vec2_from_scalar :: #force_inline proc "contextless" ( scalar : f32 ) -> Vec2 { return { scalar, scalar }} vec2_64_from_vec2 :: #force_inline proc "contextless" ( v2 : Vec2 ) -> Vec2_64 { return { f64(v2.x), f64(v2.y) }} vec2_from_vec2i :: #force_inline proc "contextless" ( v2i : Vec2i ) -> Vec2 { return { f32(v2i.x), f32(v2i.y) }} vec2i_from_vec2 :: #force_inline proc "contextless" ( v2 : Vec2 ) -> Vec2i { return { i32(v2.x), i32(v2.y) }} @(require_results) ceil_vec2 :: proc "contextless" ( v : Vec2 ) -> Vec2 { return { ceil_f32(v.x), ceil_f32(v.y) } } @(require_results) floor_vec2 :: proc "contextless" ( v : Vec2 ) -> Vec2 { return { floor_f32(v.x), floor_f32(v.y) } } // This buffer is used below excluisvely to prevent any allocator recusion when verbose logging from allocators. // This means a single line is limited to 4k buffer // Logger_Allocator_Buffer : [4 * Kilobyte]u8 log :: proc( msg : string, level := core_log.Level.Info, loc := #caller_location ) { // temp_arena : Arena; arena_init(& temp_arena, Logger_Allocator_Buffer[:]) // context.allocator = arena_allocator(& temp_arena) // context.temp_allocator = arena_allocator(& temp_arena) core_log.log( level, msg, location = loc ) } logf :: proc( fmt : string, args : ..any, level := core_log.Level.Info, loc := #caller_location ) { // temp_arena : Arena; arena_init(& temp_arena, Logger_Allocator_Buffer[:]) // context.allocator = arena_allocator(& temp_arena) // context.temp_allocator = arena_allocator(& temp_arena) core_log.logf( level, fmt, ..args, location = loc ) } reload_array :: proc( self : ^[dynamic]$Type, allocator : Allocator ) { raw := transmute( ^runtime.Raw_Dynamic_Array) self raw.allocator = allocator } reload_map :: proc( self : ^map [$KeyType] $EntryType, allocator : Allocator ) { raw := transmute( ^runtime.Raw_Map) self raw.allocator = allocator } font_glyph_lru_code :: #force_inline proc "contextless" ( font : Font_ID, glyph_index : Glyph ) -> (lru_code : u64) { lru_code = u64(glyph_index) + ( ( 0x100000000 * u64(font) ) & 0xFFFFFFFF00000000 ) return } is_empty :: #force_inline proc ( ctx : ^Context, entry : ^Entry, glyph_index : Glyph ) -> b32 { if glyph_index == 0 do return true if parser_is_glyph_empty( & entry.parser_info, glyph_index ) do return true return false } mark_batch_codepoint_seen :: #force_inline proc ( ctx : ^Context, lru_code : u64 ) { ctx.temp_codepoint_seen[lru_code] = true ctx.temp_codepoint_seen_num += 1 } reset_batch_codepoint_state :: #force_inline proc( ctx : ^Context ) { clear_map( & ctx.temp_codepoint_seen ) ctx.temp_codepoint_seen_num = 0 } USE_F64_PRECISION_ON_X_FORM_OPS :: false screenspace_x_form :: #force_inline proc "contextless" ( position, scale : ^Vec2, size : Vec2 ) { when USE_F64_PRECISION_ON_X_FORM_OPS { pos_64 := vec2_64_from_vec2(position^) scale_64 := vec2_64_from_vec2(scale^) quotient : Vec2_64 = 1.0 / vec2_64(size) pos_64 = pos_64 * quotient * 2.0 - 1.0 scale_64 = scale_64 * quotient * 2.0 (position^) = { f32(pos_64.x), f32(pos_64.y) } (scale^) = { f32(scale_64.x), f32(scale_64.y) } } else { pos := position^ scale_32 := scale^ quotient : Vec2 = 1.0 / size pos = pos * quotient * 2.0 - 1.0 scale_32 = scale_32 * quotient * 2.0 (position^) = pos (scale^) = scale_32 } } textspace_x_form :: #force_inline proc "contextless" ( position, scale : ^Vec2, size : Vec2 ) { when USE_F64_PRECISION_ON_X_FORM_OPS { pos_64 := vec2_64_from_vec2(position^) scale_64 := vec2_64_from_vec2(scale^) quotient : Vec2_64 = 1.0 / vec2_64(size) pos_64 *= quotient scale_64 *= quotient (position^) = { f32(pos_64.x), f32(pos_64.y) } (scale^) = { f32(scale_64.x), f32(scale_64.y) } } else { quotient : Vec2 = 1.0 / size (position^) *= quotient (scale^) *= quotient } } USE_MANUAL_SIMD_FOR_BEZIER_OPS :: false when ! USE_MANUAL_SIMD_FOR_BEZIER_OPS { // For a provided alpha value, // allows the function to calculate the position of a point along the curve at any given fraction of its total length // ve_fontcache_eval_bezier (quadratic) eval_point_on_bezier3 :: #force_inline proc "contextless" ( p0, p1, p2 : Vec2, alpha : f32 ) -> Vec2 { // p0 := vec2_64(p0) // p1 := vec2_64(p1) // p2 := vec2_64(p2) // alpha := f64(alpha) weight_start := (1 - alpha) * (1 - alpha) weight_control := 2.0 * (1 - alpha) * alpha weight_end := alpha * alpha starting_point := p0 * weight_start control_point := p1 * weight_control end_point := p2 * weight_end point := starting_point + control_point + end_point return { f32(point.x), f32(point.y) } } // For a provided alpha value, // allows the function to calculate the position of a point along the curve at any given fraction of its total length // ve_fontcache_eval_bezier (cubic) eval_point_on_bezier4 :: #force_inline proc "contextless" ( p0, p1, p2, p3 : Vec2, alpha : f32 ) -> Vec2 { // p0 := vec2_64(p0) // p1 := vec2_64(p1) // p2 := vec2_64(p2) // p3 := vec2_64(p3) // alpha := f64(alpha) weight_start := (1 - alpha) * (1 - alpha) * (1 - alpha) weight_c_a := 3 * (1 - alpha) * (1 - alpha) * alpha weight_c_b := 3 * (1 - alpha) * alpha * alpha weight_end := alpha * alpha * alpha start_point := p0 * weight_start control_a := p1 * weight_c_a control_b := p2 * weight_c_b end_point := p3 * weight_end point := start_point + control_a + control_b + end_point return { f32(point.x), f32(point.y) } } } else { Vec2_SIMD :: simd.f32x4 vec2_to_simd :: #force_inline proc "contextless" (v: Vec2) -> Vec2_SIMD { return Vec2_SIMD{v.x, v.y, 0, 0} } simd_to_vec2 :: #force_inline proc "contextless" (v: Vec2_SIMD) -> Vec2 { return Vec2{ simd.extract(v, 0), simd.extract(v, 1) } } eval_point_on_bezier3 :: #force_inline proc "contextless" (p0, p1, p2: Vec2, alpha: f32) -> Vec2 { simd_p0 := vec2_to_simd(p0) simd_p1 := vec2_to_simd(p1) simd_p2 := vec2_to_simd(p2) one_minus_alpha := 1.0 - alpha weight_start := one_minus_alpha * one_minus_alpha weight_control := 2.0 * one_minus_alpha * alpha weight_end := alpha * alpha simd_weights := Vec2_SIMD{weight_start, weight_control, weight_end, 0} result := simd.add( simd.add( simd.mul( simd_p0, simd.swizzle( simd_weights, 0, 0, 0, 0) ), simd.mul( simd_p1, simd.swizzle( simd_weights, 1, 1, 1, 1) ) ), simd.mul( simd_p2, simd.swizzle(simd_weights, 2, 2, 2, 2) ) ) return simd_to_vec2(result) } eval_point_on_bezier4 :: #force_inline proc "contextless" (p0, p1, p2, p3: Vec2, alpha: f32) -> Vec2 { simd_p0 := vec2_to_simd(p0) simd_p1 := vec2_to_simd(p1) simd_p2 := vec2_to_simd(p2) simd_p3 := vec2_to_simd(p3) one_minus_alpha := 1.0 - alpha weight_start := one_minus_alpha * one_minus_alpha * one_minus_alpha weight_c_a := 3 * one_minus_alpha * one_minus_alpha * alpha weight_c_b := 3 * one_minus_alpha * alpha * alpha weight_end := alpha * alpha * alpha simd_weights := Vec2_SIMD { weight_start, weight_c_a, weight_c_b, weight_end } result := simd.add( simd.add( simd.mul( simd_p0, simd.swizzle(simd_weights, 0, 0, 0, 0) ), simd.mul( simd_p1, simd.swizzle(simd_weights, 1, 1, 1, 1) ) ), simd.add( simd.mul( simd_p2, simd.swizzle(simd_weights, 2, 2, 2, 2) ), simd.mul( simd_p3, simd.swizzle(simd_weights, 3, 3, 3, 3) ) ) ) return simd_to_vec2(result) } }