Various improvements to VEFontCache's font rendering
This commit is contained in:
		| @@ -17,11 +17,13 @@ import "core:mem" | ||||
|  | ||||
| Advance_Snap_Smallfont_Size :: 12 | ||||
|  | ||||
| Colour :: [4]f32 | ||||
| Vec2   :: [2]f32 | ||||
| Vec2i  :: [2]i32 | ||||
| Colour  :: [4]f32 | ||||
| Vec2    :: [2]f32 | ||||
| Vec2i   :: [2]i32 | ||||
| Vec2_64 :: [2]f64 | ||||
|  | ||||
| vec2_from_scalar :: proc( scalar : f32 ) -> Vec2 { return { scalar, scalar } } | ||||
| vec2_from_scalar  :: #force_inline proc( scalar : f32  ) -> Vec2    { return { scalar, scalar } } | ||||
| vec2_64_from_vec2 :: #force_inline proc( v2     : Vec2 ) -> Vec2_64 { return { f64(v2.x), f64(v2.y) }} | ||||
|  | ||||
| FontID  :: distinct i64 | ||||
| Glyph   :: distinct i32 | ||||
| @@ -98,7 +100,7 @@ InitAtlasParams :: struct { | ||||
| InitAtlasParams_Default :: InitAtlasParams { | ||||
| 	width         = 4096, | ||||
| 	height        = 2048, | ||||
| 	glyph_padding = 1, | ||||
| 	glyph_padding = 2, | ||||
|  | ||||
| 	region_a = { | ||||
| 		width  = 32, | ||||
| @@ -125,9 +127,10 @@ InitGlyphDrawParams :: struct { | ||||
| } | ||||
|  | ||||
| InitGlyphDrawParams_Default :: InitGlyphDrawParams { | ||||
| 	over_sample   = { 8, 8 }, | ||||
| 	over_sample   = { 4, 4 }, | ||||
| 	buffer_batch  = 4, | ||||
| 	draw_padding  = InitAtlasParams_Default.glyph_padding, | ||||
| 	// draw_padding  = InitAtlasParams_Default.glyph_padding, | ||||
| } | ||||
|  | ||||
| InitShapeCacheParams :: struct { | ||||
| @@ -159,7 +162,7 @@ init :: proc( ctx : ^Context, parser_kind : ParserKind, | ||||
| 	context.allocator = ctx.backing | ||||
|  | ||||
| 	if curve_quality == 0 { | ||||
| 		curve_quality = 6 | ||||
| 		curve_quality = 12 | ||||
| 	} | ||||
| 	ctx.curve_quality = curve_quality | ||||
|  | ||||
| @@ -525,8 +528,8 @@ cache_glyph_to_atlas :: proc( ctx : ^Context, font : FontID, glyph_index : Glyph | ||||
|  | ||||
| 	// Get hb_font text metrics. These are unscaled! | ||||
| 	bounds_0, bounds_1 := parser_get_glyph_box( & entry.parser_info, glyph_index ) | ||||
| 	bounds_width  := bounds_1.x - bounds_0.x | ||||
| 	bounds_height := bounds_1.y - bounds_0.y | ||||
| 	bounds_width  := f32(bounds_1.x - bounds_0.x) | ||||
| 	bounds_height := f32(bounds_1.y - bounds_0.y) | ||||
|  | ||||
| 	region_kind, region, over_sample := decide_codepoint_region( ctx, entry, glyph_index ) | ||||
|  | ||||
| @@ -560,8 +563,12 @@ cache_glyph_to_atlas :: proc( ctx : ^Context, font : FontID, glyph_index : Glyph | ||||
| 		assert( LRU_get( & region.state, lru_code ) != - 1 ) | ||||
| 	} | ||||
|  | ||||
| 	atlas         := & ctx.atlas | ||||
| 	glyph_padding := cast(f32) atlas.glyph_padding | ||||
| 	atlas               := & ctx.atlas | ||||
| 	atlas_width         := f32(atlas.width) | ||||
| 	atlas_height        := f32(atlas.height) | ||||
| 	glyph_buffer_width  := f32(atlas.buffer_width) | ||||
| 	glyph_buffer_height := f32(atlas.buffer_height) | ||||
| 	glyph_padding       := cast(f32) atlas.glyph_padding | ||||
|  | ||||
| 	if ctx.debug_print | ||||
| 	{ | ||||
| @@ -572,42 +579,41 @@ cache_glyph_to_atlas :: proc( ctx : ^Context, font : FontID, glyph_index : Glyph | ||||
|  | ||||
| 	// Draw oversized glyph to update FBO | ||||
| 	glyph_draw_scale       := over_sample * entry.size_scale | ||||
| 	glyph_draw_translate   := Vec2 { -f32(bounds_0.x), -f32(bounds_0.y) } * glyph_draw_scale + Vec2{ glyph_padding, glyph_padding } | ||||
| 	glyph_draw_translate   := -1 * Vec2 { f32(bounds_0.x), f32(bounds_0.y) } * glyph_draw_scale + vec2( glyph_padding ) | ||||
| 	glyph_draw_translate.x  = cast(f32) (i32(glyph_draw_translate.x + 0.9999999)) | ||||
| 	glyph_draw_translate.y  = cast(f32) (i32(glyph_draw_translate.y + 0.9999999)) | ||||
|  | ||||
| 	// Allocate a glyph_update_FBO region | ||||
| 	gwidth_scaled_px := i32( f32(bounds_width) * f32(glyph_draw_scale.x) + 1.0 ) + i32(2 * over_sample.x * glyph_padding) | ||||
| 	gwidth_scaled_px := i32( bounds_width * glyph_draw_scale.x + 1.0 ) + i32(over_sample.x * glyph_padding) | ||||
|   if i32(atlas.update_batch_x + gwidth_scaled_px) >= i32(atlas.buffer_width) { | ||||
| 		flush_glyph_buffer_to_atlas( ctx ) | ||||
| 	} | ||||
|  | ||||
| 	// Calculate the src and destination regions | ||||
| 	dst_position, dst_width, dst_height := atlas_bbox( atlas, region_kind, atlas_index ) | ||||
| 	dst_glyph_position := dst_position  + { glyph_padding, glyph_padding } | ||||
| 	dst_glyph_width    := f32(bounds_width)  * entry.size_scale | ||||
| 	dst_glyph_height   := f32(bounds_height) * entry.size_scale | ||||
| 	dst_glyph_position -= { glyph_padding, glyph_padding } | ||||
| 	dst_glyph_width  += 2 * glyph_padding | ||||
| 	dst_glyph_height += 2 * glyph_padding | ||||
| 	dst_glyph_position := dst_position | ||||
| 	dst_glyph_width    := bounds_width  * entry.size_scale | ||||
| 	dst_glyph_height   := bounds_height * entry.size_scale | ||||
| 	dst_glyph_width    += glyph_padding | ||||
| 	dst_glyph_height   += glyph_padding | ||||
|  | ||||
| 	dst_size       := Vec2 { dst_width, dst_height } | ||||
| 	dst_glyph_size := Vec2 { dst_glyph_width, dst_glyph_height } | ||||
| 	screenspace_x_form( & dst_glyph_position, & dst_glyph_size, f32(atlas.width), f32(atlas.height) ) | ||||
| 	screenspace_x_form( & dst_position,       & dst_size,       f32(atlas.width), f32(atlas.height) ) | ||||
| 	screenspace_x_form( & dst_glyph_position, & dst_glyph_size, atlas_width, atlas_height ) | ||||
| 	screenspace_x_form( & dst_position,       & dst_size,       atlas_width, atlas_height ) | ||||
|  | ||||
| 	src_position := Vec2 { f32(atlas.update_batch_x), 0 } | ||||
| 	src_size     := Vec2 { | ||||
| 		f32(bounds_width)  * glyph_draw_scale.x, | ||||
| 		f32(bounds_height) * glyph_draw_scale.y, | ||||
| 		bounds_width  * glyph_draw_scale.x, | ||||
| 		bounds_height * glyph_draw_scale.y, | ||||
| 	} | ||||
| 	src_size += 2 * over_sample * glyph_padding | ||||
| 	textspace_x_form( & src_position, & src_size, f32(atlas.buffer_width), f32(atlas.buffer_height) ) | ||||
| 	src_size += over_sample * glyph_padding | ||||
| 	textspace_x_form( & src_position, & src_size, glyph_buffer_width, glyph_buffer_height ) | ||||
|  | ||||
| 	// Advance glyph_update_batch_x and calculate final glyph drawing transform | ||||
| 	glyph_draw_translate.x += f32(atlas.update_batch_x) | ||||
| 	atlas.update_batch_x   += gwidth_scaled_px | ||||
| 	screenspace_x_form( & glyph_draw_translate, & glyph_draw_scale, f32(atlas.buffer_width), f32(atlas.buffer_height)) | ||||
| 	screenspace_x_form( & glyph_draw_translate, & glyph_draw_scale, glyph_buffer_width, glyph_buffer_height ) | ||||
|  | ||||
| 	call : DrawCall | ||||
| 	{ | ||||
| @@ -652,7 +658,7 @@ measure_text_size :: proc( ctx : ^Context, font : FontID, text_utf8 : string ) - | ||||
| 	atlas   := ctx.atlas | ||||
| 	shaped  := shape_text_cached( ctx, font, text_utf8 ) | ||||
| 	entry   := & ctx.entries.data[ font ] | ||||
| 	padding := 2 * cast(f32) atlas.glyph_padding | ||||
| 	padding := cast(f32) atlas.glyph_padding | ||||
|  | ||||
| 	for index : i32 = 0; index < i32(shaped.glyphs.num); index += 1 | ||||
| 	{ | ||||
| @@ -666,14 +672,14 @@ measure_text_size :: proc( ctx : ^Context, font : FontID, text_utf8 : string ) - | ||||
| 		// region_kind, region, over_sample := decide_codepoint_region( ctx, entry, glyph_index ) | ||||
|  | ||||
| 		glyph_size := Vec2 { | ||||
| 			f32(bounds_width)  * entry.size_scale + padding, | ||||
| 			f32(bounds_height) * entry.size_scale + padding, | ||||
| 			f32(bounds_width)  * entry.size_scale, //+ padding, | ||||
| 			f32(bounds_height) * entry.size_scale, //+ padding, | ||||
| 		} | ||||
|  | ||||
| 		dummy_position : Vec2 | ||||
| 		measured.y = max(measured.y, glyph_size.y) | ||||
| 	} | ||||
| 	measured.x = shaped.end_cursor_pos.x - padding | ||||
| 	measured.x = shaped.end_cursor_pos.x | ||||
|  | ||||
| 	return measured | ||||
| } | ||||
|   | ||||
| @@ -1,5 +1,7 @@ | ||||
| package VEFontCache | ||||
|  | ||||
| import "core:math" | ||||
|  | ||||
| DrawCall :: struct { | ||||
| 	pass              : FrameBufferPass, | ||||
| 	start_index       : u32, | ||||
| @@ -123,8 +125,8 @@ directly_draw_massive_glyph :: proc( ctx : ^Context, entry : ^Entry, glyph : Gly | ||||
| 	dst        := position + scale * bounds_scaled | ||||
| 	dst_width  := scale.x * glyph_dst_width | ||||
| 	dst_height := scale.y * glyph_dst_height | ||||
| 	dst.x      -= scale.x * f32(ctx.atlas.glyph_padding) | ||||
| 	dst.y      -= scale.y * f32(ctx.atlas.glyph_padding) | ||||
| 	dst.x      -= scale.x * f32(ctx.atlas.draw_padding) | ||||
| 	dst.y      -= scale.y * f32(ctx.atlas.draw_padding) | ||||
| 	dst_size   := Vec2{ dst_width, dst_height } | ||||
|  | ||||
| 	glyph_size := Vec2 { glyph_width, glyph_height } | ||||
| @@ -158,8 +160,8 @@ draw_cached_glyph :: proc( ctx : ^Context, entry : ^Entry, glyph_index : Glyph, | ||||
|  | ||||
| 	bounds_0, bounds_1 := parser_get_glyph_box( & entry.parser_info, glyph_index ) | ||||
|  | ||||
| 	bounds_width  := bounds_1.x - bounds_0.x | ||||
| 	bounds_height := bounds_1.y - bounds_0.y | ||||
| 	bounds_width  := f32(bounds_1.x - bounds_0.x) | ||||
| 	bounds_height := f32(bounds_1.y - bounds_0.y) | ||||
|  | ||||
| 	// Decide which atlas to target | ||||
| 	region_kind, region, over_sample := decide_codepoint_region( ctx, entry, glyph_index ) | ||||
| @@ -167,7 +169,7 @@ draw_cached_glyph :: proc( ctx : ^Context, entry : ^Entry, glyph_index : Glyph, | ||||
| 	// E region is special case and not cached to atlas | ||||
| 	if region_kind == .E | ||||
| 	{ | ||||
| 		directly_draw_massive_glyph( ctx, entry, glyph_index, bounds_0, bounds_width, bounds_height, over_sample, position, scale ) | ||||
| 		directly_draw_massive_glyph( ctx, entry, glyph_index, bounds_0, cast(i32) bounds_width, cast(i32) bounds_height, over_sample, position, scale ) | ||||
| 		return true | ||||
| 	} | ||||
|  | ||||
| @@ -180,32 +182,36 @@ draw_cached_glyph :: proc( ctx : ^Context, entry : ^Entry, glyph_index : Glyph, | ||||
| 	} | ||||
|  | ||||
| 	atlas := & ctx.atlas | ||||
| 	atlas_width   := f32(atlas.width) | ||||
| 	atlas_height  := f32(atlas.height) | ||||
| 	glyph_padding := f32(atlas.glyph_padding) | ||||
|  | ||||
| 	// Figure out the source bounding box in the atlas texture | ||||
| 	atlas_position, atlas_width, atlas_height := atlas_bbox( atlas, region_kind, atlas_index ) | ||||
| 	glyph_atlas_position, glyph_atlas_width, glyph_atlas_height := atlas_bbox( atlas, region_kind, atlas_index ) | ||||
|  | ||||
| 	glyph_position := atlas_position //* {1, 2} | ||||
| 	glyph_width    := f32(bounds_width)  * entry.size_scale | ||||
| 	glyph_height   := f32(bounds_height) * entry.size_scale | ||||
| 	glyph_width    := bounds_width  * entry.size_scale | ||||
| 	glyph_height   := bounds_height * entry.size_scale | ||||
|  | ||||
| 	glyph_width  += 2 * f32(atlas.glyph_padding) | ||||
| 	glyph_height += 2 * f32(atlas.glyph_padding) | ||||
| 	glyph_width  += glyph_padding | ||||
| 	glyph_height += glyph_padding | ||||
| 	glyph_scale  := Vec2 { glyph_width, glyph_height } | ||||
|  | ||||
| 	bounds_0_scaled := Vec2{ f32(bounds_0.x), f32(bounds_0.y) } * entry.size_scale - { 0.5, 0.5 } | ||||
| 	bounds_0_scaled := Vec2{ f32(bounds_0.x), f32(bounds_0.y) } * entry.size_scale //- { 0.5, 0.5 } | ||||
| 	bounds_0_scaled  = { | ||||
| 		cast(f32) cast(i32) bounds_0_scaled.x, | ||||
| 		cast(f32) cast(i32) bounds_0_scaled.y, | ||||
| 		math.ceil(bounds_0_scaled.x), | ||||
| 		math.ceil(bounds_0_scaled.y), | ||||
| 	} | ||||
| 	// dst := position * scale * bounds_0_scaled | ||||
| 	dst := Vec2 { | ||||
| 		position.x + scale.x * bounds_0_scaled.x, | ||||
| 		position.y + scale.y * bounds_0_scaled.y, | ||||
| 		position.x + bounds_0_scaled.x * scale.x, | ||||
| 		position.y + bounds_0_scaled.y * scale.y, | ||||
| 	} | ||||
| 	dst_width  := scale.x * glyph_width | ||||
| 	dst_height := scale.y * glyph_height | ||||
| 	dst        -= scale * { f32(atlas.glyph_padding), f32(atlas.glyph_padding) } | ||||
| 	dst        -= scale   * glyph_padding | ||||
| 	dst_scale  := Vec2 { dst_width, dst_height } | ||||
| 	textspace_x_form( & glyph_position, & glyph_scale, f32(atlas.width), f32(atlas.height) ) | ||||
|  | ||||
| 	textspace_x_form( & glyph_atlas_position, & glyph_scale, atlas_width, atlas_height ) | ||||
|  | ||||
| 	// Add the glyph drawcall | ||||
| 	call := DrawCall_Default | ||||
| @@ -215,7 +221,7 @@ draw_cached_glyph :: proc( ctx : ^Context, entry : ^Entry, glyph_index : Glyph, | ||||
| 		colour      = ctx.colour | ||||
| 		start_index = cast(u32) ctx.draw_list.indices.num | ||||
|  | ||||
| 		blit_quad( & ctx.draw_list, dst, dst + dst_scale, glyph_position, glyph_position + glyph_scale ) | ||||
| 		blit_quad( & ctx.draw_list, dst, dst + dst_scale, glyph_atlas_position, glyph_atlas_position + glyph_scale ) | ||||
| 		end_index   = cast(u32) ctx.draw_list.indices.num | ||||
| 	} | ||||
| 	append( & ctx.draw_list.calls, call ) | ||||
|   | ||||
| @@ -31,6 +31,11 @@ shape_lru_hash :: #force_inline proc( label : string ) -> u64 { | ||||
| // ve_fontcache_eval_bezier (quadratic) | ||||
| eval_point_on_bezier3 :: proc( p0, p1, p2 : Vec2, alpha : f32 ) -> Vec2 | ||||
| { | ||||
| 	p0    := vec2_64_from_vec2(p0) | ||||
| 	p1    := vec2_64_from_vec2(p1) | ||||
| 	p2    := vec2_64_from_vec2(p2) | ||||
| 	alpha := f64(alpha) | ||||
|  | ||||
| 	weight_start   := (1 - alpha) * (1 - alpha) | ||||
| 	weight_control := 2.0 * (1 - alpha) * alpha | ||||
| 	weight_end     := alpha * alpha | ||||
| @@ -40,7 +45,7 @@ eval_point_on_bezier3 :: proc( p0, p1, p2 : Vec2, alpha : f32 ) -> Vec2 | ||||
| 	end_point      := p2 * weight_end | ||||
|  | ||||
| 	point := starting_point + control_point + end_point | ||||
| 	return point | ||||
| 	return { f32(point.x), f32(point.y) } | ||||
| } | ||||
|  | ||||
| // For a provided alpha value, | ||||
| @@ -48,6 +53,12 @@ eval_point_on_bezier3 :: proc( p0, p1, p2 : Vec2, alpha : f32 ) -> Vec2 | ||||
| // ve_fontcache_eval_bezier (cubic) | ||||
| eval_point_on_bezier4 :: proc( p0, p1, p2, p3 : Vec2, alpha : f32 ) -> Vec2 | ||||
| { | ||||
| 	p0    := vec2_64_from_vec2(p0) | ||||
| 	p1    := vec2_64_from_vec2(p1) | ||||
| 	p2    := vec2_64_from_vec2(p2) | ||||
| 	p3    := vec2_64_from_vec2(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 | ||||
| @@ -59,7 +70,7 @@ eval_point_on_bezier4 :: proc( p0, p1, p2, p3 : Vec2, alpha : f32 ) -> Vec2 | ||||
| 	end_point   := p3 * weight_end | ||||
|  | ||||
| 	point := start_point + control_a + control_b + end_point | ||||
| 	return point | ||||
| 	return { f32(point.x), f32(point.y) } | ||||
| } | ||||
|  | ||||
| reset_batch_codepoint_state :: proc( ctx : ^Context ) { | ||||
| @@ -68,13 +79,31 @@ reset_batch_codepoint_state :: proc( ctx : ^Context ) { | ||||
| } | ||||
|  | ||||
| screenspace_x_form :: proc( position, scale : ^Vec2, width, height : f32 ) { | ||||
| 	quotient    := 1.0 / Vec2 { width, height } | ||||
| 	(position^) = (position^) * quotient * 2.0 - 1.0 | ||||
| 	(scale^)    = (scale^)    * quotient * 2.0 | ||||
| 	pos_64   := vec2_64_from_vec2(position^) | ||||
| 	scale_64 := vec2_64_from_vec2(scale^) | ||||
| 	width    := f64(width) | ||||
| 	height   := f64(height) | ||||
|  | ||||
| 	quotient : Vec2_64 = 1.0 / { width, height } | ||||
| 	// quotient : Vec2 = 1.0 / { width, height } | ||||
| 	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) } | ||||
| } | ||||
|  | ||||
| textspace_x_form :: proc( position, scale : ^Vec2, width, height : f32 ) { | ||||
| 	quotient := 1.0 / Vec2 { width, height } | ||||
| 	(position^) *= quotient | ||||
| 	(scale^)    *= quotient | ||||
| 	pos_64   := vec2_64_from_vec2(position^) | ||||
| 	scale_64 := vec2_64_from_vec2(scale^) | ||||
| 	width    := f64(width) | ||||
| 	height   := f64(height) | ||||
|  | ||||
| 	quotient : Vec2_64 = 1.0 / { width, height } | ||||
| 	// quotient : Vec2 = 1.0 / { width, height } | ||||
| 	pos_64   *= quotient | ||||
| 	scale_64 *= quotient | ||||
|  | ||||
| 	(position^) = { f32(pos_64.x), f32(pos_64.y) } | ||||
| 	(scale^)    = { f32(scale_64.x), f32(scale_64.y) } | ||||
| } | ||||
|   | ||||
| @@ -438,8 +438,8 @@ parser_scale_for_mapping_em_to_pixels :: proc( font : ^ParserFontInfo, size : f3 | ||||
| 			FT_Point_10             :: 64.0 | ||||
|  | ||||
| 			points_per_em := (size / system_dpi ) * DPT_DPI | ||||
| 			freetype.set_char_size( font.freetype_info, 0, cast(freetype.F26Dot6) (f32(points_per_em) * FT_Point_10), cast(u32) DPT_DPI, cast(u32) DPT_DPI ) | ||||
| 			size_scale := size / cast(f32) font.freetype_info.units_per_em; | ||||
| 			freetype.set_char_size( font.freetype_info, 0, cast(freetype.F26Dot6) f32(points_per_em * FT_Point_10), cast(u32) DPT_DPI, cast(u32) DPT_DPI ) | ||||
| 			size_scale := f32(f64(size) / cast(f64) font.freetype_info.units_per_em) | ||||
| 			return size_scale | ||||
|  | ||||
| 		case .STB_TrueType: | ||||
|   | ||||
		Reference in New Issue
	
	Block a user