Progress on VEFontCache port, working on freetype outline to stbtt shape
This commit is contained in:
parent
26ad2d1e49
commit
1741532d64
@ -5,11 +5,17 @@ Status:
|
||||
This port is heavily tied to the grime package in SectrPrototype.
|
||||
|
||||
TODO(Ed): Make an idiomatic port of this for Odin (or just dupe the data structures...)
|
||||
|
||||
Changes:
|
||||
- Support for freetype(WIP)
|
||||
- Font Parser & Glyph Shaper are abstracted to their own interface
|
||||
- Font Face parser info stored separately from entries
|
||||
- ve_fontcache_loadfile not ported (just use odin's core:os or os2), then call load_font
|
||||
*/
|
||||
package VEFontCache
|
||||
|
||||
Font_ID :: i64
|
||||
Glyph :: i32
|
||||
FontID :: distinct i64
|
||||
Glyph :: distinct i32
|
||||
|
||||
Colour :: [4]f32
|
||||
Vec2 :: [2]f32
|
||||
@ -19,7 +25,8 @@ AtlasRegionKind :: enum {
|
||||
A = 0,
|
||||
B = 1,
|
||||
C = 2,
|
||||
D = 3
|
||||
D = 3,
|
||||
E = 4,
|
||||
}
|
||||
|
||||
Vertex :: struct {
|
||||
@ -49,10 +56,14 @@ ShapedTextCache :: struct {
|
||||
}
|
||||
|
||||
Entry :: struct {
|
||||
parser_info : ParserInfo,
|
||||
shaper_info : ShaperInfo,
|
||||
id : Font_ID,
|
||||
parser_info : ^ParserFontInfo,
|
||||
shaper_info : ^ShaperInfo,
|
||||
id : FontID,
|
||||
used : b32,
|
||||
|
||||
// Note(Ed) : Not sure how I feel about the size specification here
|
||||
// I rather have different size glyphs for a font on demand (necessary for the canvas UI)
|
||||
// Might be mis-understaning how this cache works...
|
||||
size : f32,
|
||||
size_scale : f32,
|
||||
}
|
||||
@ -87,9 +98,17 @@ Context :: struct {
|
||||
shape_cache : ShapedTextCache,
|
||||
|
||||
text_shape_adv : b32,
|
||||
|
||||
debug_print_verbose : b32
|
||||
}
|
||||
|
||||
Module_Ctx :: Context
|
||||
font_key_from_label :: proc( label : string ) -> u64 {
|
||||
hash : u64
|
||||
for str_byte in transmute([]byte) label {
|
||||
hash = ((hash << 5) + hash) + u64(str_byte)
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
InitAtlasRegionParams :: struct {
|
||||
width : u32,
|
||||
@ -152,6 +171,7 @@ InitShapeCacheParams_Default :: InitShapeCacheParams {
|
||||
reserve_length = 64,
|
||||
}
|
||||
|
||||
// ve_fontcache_init
|
||||
init :: proc( ctx : ^Context,
|
||||
allocator := context.allocator,
|
||||
atlas_params := InitAtlasParams_Default,
|
||||
@ -250,5 +270,371 @@ init :: proc( ctx : ^Context,
|
||||
assert( error != .None, "VEFontCache.init : Failed to allocate vertices array for clear_draw_list" )
|
||||
}
|
||||
|
||||
parser_init( & parser_ctx )
|
||||
shaper_init( & shaper_ctx )
|
||||
}
|
||||
|
||||
// ve_foncache_shutdown
|
||||
shutdown :: proc( ctx : ^Context )
|
||||
{
|
||||
assert( ctx != nil )
|
||||
context.allocator = ctx.backing
|
||||
using ctx
|
||||
|
||||
for & entry in array_to_slice(entries) {
|
||||
unload_font( ctx, entry.id )
|
||||
}
|
||||
|
||||
shaper_shutdown( & shaper_ctx )
|
||||
}
|
||||
|
||||
// ve_fontcache_load
|
||||
load_font :: proc( ctx : ^Context, label : string, data : []byte, size_px : f32 ) -> FontID
|
||||
{
|
||||
assert( ctx != nil )
|
||||
assert( len(data) > 0 )
|
||||
using ctx
|
||||
|
||||
id : i32 = -1
|
||||
for index : i32 = 0; index < i32(entries.num); index += 1 {
|
||||
if entries.data[index].used do continue
|
||||
id = index
|
||||
break
|
||||
}
|
||||
if id == -1 {
|
||||
append( & entries, Entry {})
|
||||
id = cast(i32) entries.num - 1
|
||||
}
|
||||
assert( id >= 0 && id < i32(entries.num) )
|
||||
|
||||
entry := & entries.data[ id ]
|
||||
{
|
||||
using entry
|
||||
parser_info = parser_load_font( parser_ctx, label, data )
|
||||
assert( parser_info != nil, "VEFontCache.load_font: Failed to load font info from parser" )
|
||||
|
||||
size = size_px
|
||||
size_scale = size_px < 0.0 ? \
|
||||
parser_scale_for_pixel_height( parser_info, -size_px ) \
|
||||
: parser_scale_for_mapping_em_to_pixels( parser_info, size_px )
|
||||
|
||||
used = true
|
||||
|
||||
shaper_info = shaper_load_font( & shaper_ctx, label, data, transmute(rawptr) id )
|
||||
assert( shaper_info != nil, "VEFontCache.load_font: Failed to load font from shaper")
|
||||
|
||||
return id
|
||||
}
|
||||
}
|
||||
|
||||
// ve_fontcache_unload
|
||||
unload_font :: proc( ctx : ^Context, font : FontID )
|
||||
{
|
||||
assert( ctx != nil )
|
||||
assert( font >= 0 && u64(font) < ctx.entries.num )
|
||||
|
||||
using ctx
|
||||
entry := & entries.data[ font ]
|
||||
entry.used = false
|
||||
|
||||
parser_unload_font( entry.parser_info )
|
||||
shaper_unload_font( entry.shaper_info )
|
||||
}
|
||||
|
||||
// ve_fontcache_configure_snap
|
||||
configure_snap :: proc( ctx : ^Context, snap_width, snap_height : u32 ) {
|
||||
assert( ctx != nil )
|
||||
ctx.snap_width = snap_width
|
||||
ctx.snap_height = snap_height
|
||||
}
|
||||
|
||||
// ve_fontcache_drawlist
|
||||
get_draw_list :: proc( ctx : ^Context ) -> ^DrawList {
|
||||
assert( ctx != nil )
|
||||
return & ctx.draw_list
|
||||
}
|
||||
|
||||
// ve_fontcache_clear_drawlist
|
||||
clear_draw_list :: proc( draw_list : ^DrawList ) {
|
||||
clear( draw_list.calls )
|
||||
clear( draw_list.indices )
|
||||
clear( draw_list.vertices )
|
||||
}
|
||||
|
||||
// ve_fontcache_merge_drawlist
|
||||
merge_draw_list :: proc( dst, src : ^DrawList )
|
||||
{
|
||||
error : AllocatorError
|
||||
|
||||
v_offset := cast(u32) dst.vertices.num
|
||||
// for index : u32 = 0; index < cast(u32) src.vertices.num; index += 1 {
|
||||
// error = append( & dst.vertices, src.vertices.data[index] )
|
||||
// assert( error == .None )
|
||||
// }
|
||||
error = append( & dst.vertices, src.vertices )
|
||||
assert( error == .None )
|
||||
|
||||
i_offset := cast(u32) dst.indices.num
|
||||
for index : u32 = 0; index < cast(u32) src.indices.num; index += 1 {
|
||||
error = append( & dst.indices, src.indices.data[index] + v_offset )
|
||||
assert( error == .None )
|
||||
}
|
||||
|
||||
for index : u32 = 0; index < cast(u32) src.calls.num; index += 1 {
|
||||
src_call := src.calls.data[ index ]
|
||||
src_call.start_index += i_offset
|
||||
src_call.end_index += i_offset
|
||||
append( & dst.calls, src_call )
|
||||
assert( error == .None )
|
||||
}
|
||||
}
|
||||
|
||||
// ve_fontcache_flush_drawlist
|
||||
flush_draw_list :: proc( ctx : ^Context ) {
|
||||
assert( ctx != nil )
|
||||
clear_draw_list( & ctx.draw_list )
|
||||
}
|
||||
|
||||
// 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 :: proc( p0, p1, p2 : Vec2, alpha : f32 ) -> Vec2
|
||||
{
|
||||
starting_point := p0 * (1 - alpha) * (1 - alpha)
|
||||
control_point := p1 * 2.0 * (1 - alpha)
|
||||
end_point := p2 * alpha * alpha
|
||||
|
||||
point := starting_point + control_point + end_point
|
||||
return point
|
||||
}
|
||||
|
||||
// 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 :: proc( p0, p1, p2, p3 : Vec2, alpha : f32 ) -> Vec2
|
||||
{
|
||||
start_point := p0 * (1 - alpha) * (1 - alpha) * (1 - alpha)
|
||||
control_a := p1 * 3 * (1 - alpha) * (1 - alpha) * alpha
|
||||
control_b := p2 * 3 * (1 - alpha) * alpha * alpha
|
||||
end_point := p3 * alpha * alpha * alpha
|
||||
|
||||
point := start_point + control_a + control_b + end_point
|
||||
return point
|
||||
}
|
||||
|
||||
// Constructs a triangle fan to fill a shape using the provided path
|
||||
// outside_point represents the center point of the fan.
|
||||
//
|
||||
// Note(Original Author):
|
||||
// WARNING: doesn't actually append drawcall; caller is responsible for actually appending the drawcall.
|
||||
// ve_fontcache_draw_filled_path
|
||||
draw_filled_path :: proc( draw_list : ^DrawList, outside_point : Vec2, path : []Vec2,
|
||||
scale := Vec2 { 1, 1 },
|
||||
translate := Vec2 { 0, 0 },
|
||||
debug_print_verbose : b32 = false
|
||||
)
|
||||
{
|
||||
if debug_print_verbose
|
||||
{
|
||||
log("outline_path: \n")
|
||||
for point in path {
|
||||
logf(" %.2f %.2f\n", point.x * scale )
|
||||
}
|
||||
}
|
||||
|
||||
v_offset := cast(u32) draw_list.vertices.num
|
||||
for point in path {
|
||||
vertex := Vertex {
|
||||
pos = point * scale + translate,
|
||||
u = 0,
|
||||
v = 0,
|
||||
}
|
||||
append( & draw_list.vertices, vertex )
|
||||
}
|
||||
|
||||
outside_vertex := cast(u32) draw_list.vertices.num
|
||||
{
|
||||
vertex := Vertex {
|
||||
pos = outside_point * scale + translate,
|
||||
u = 0,
|
||||
v = 0,
|
||||
}
|
||||
append( & draw_list.vertices, vertex )
|
||||
}
|
||||
|
||||
for index : u32 = 1; index < u32(len(path)); index += 1 {
|
||||
indices := & draw_list.indices
|
||||
append( indices, outside_vertex )
|
||||
append( indices, v_offset + index - 1 )
|
||||
append( indices, v_offset + index )
|
||||
}
|
||||
}
|
||||
|
||||
blit_quad :: proc( draw_list : ^DrawList, p0, p1 : Vec2, uv0, uv1 : Vec2 )
|
||||
{
|
||||
v_offset := cast(u32) draw_list.vertices.num
|
||||
|
||||
vertex := Vertex {
|
||||
{p0.x, p0.y},
|
||||
uv0.x,
|
||||
uv0.y
|
||||
}
|
||||
append( & draw_list.vertices, vertex )
|
||||
vertex = Vertex {
|
||||
{p0.x, p1.y},
|
||||
uv0.x,
|
||||
uv1.y
|
||||
}
|
||||
append( & draw_list.vertices, vertex )
|
||||
vertex = Vertex {
|
||||
{p1.x, p0.y},
|
||||
uv1.x,
|
||||
uv0.y
|
||||
}
|
||||
append( & draw_list.vertices, vertex )
|
||||
vertex = Vertex {
|
||||
{p1.x, p1.y},
|
||||
uv1.x,
|
||||
uv1.y
|
||||
}
|
||||
append( & draw_list.vertices, vertex )
|
||||
|
||||
quad_indices : []u32 = {
|
||||
0, 1, 2,
|
||||
2, 1, 3
|
||||
}
|
||||
for index : i32 = 0; index < 6; index += 1 {
|
||||
append( & draw_list.indices, v_offset + quad_indices[ index ] )
|
||||
}
|
||||
}
|
||||
|
||||
cache_glyph :: proc( ctx : ^Context, font : FontID, glyph_index : Glyph, scale, translate : Vec2 ) -> b32
|
||||
{
|
||||
assert( ctx != nil )
|
||||
assert( font >= 0 && u64(font) < ctx.entries.num )
|
||||
entry := & ctx.entries.data[ font ]
|
||||
if glyph_index == Glyph(0) {
|
||||
// Note(Original Author): Glyph not in current hb_font
|
||||
return false
|
||||
}
|
||||
|
||||
// No shpae to retrieve
|
||||
if parser_is_glyph_empty( entry.parser_info, glyph_index ) do return true
|
||||
|
||||
// Retrieve the shape definition from the parser.
|
||||
shape, error := parser_get_glyph_shape( entry.parser_info, glyph_index )
|
||||
assert( error == .None )
|
||||
if len(shape) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
if ctx.debug_print_verbose
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
Note(Original Author):
|
||||
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()
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
decide_codepoint_region :: proc() -> AtlasRegionKind
|
||||
{
|
||||
return {}
|
||||
}
|
||||
|
||||
flush_glyph_buffer_to_atlas :: proc()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
screenspace_x_form :: proc()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
textspace_x_form :: proc()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
atlas_bbox :: proc()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
cache_glyph_to_atlas :: proc()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
shape_text_uncached :: proc()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
ELFhash64 :: proc()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
shape_text_cached :: proc()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
directly_draw_massive_glyph :: proc()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
empty :: proc()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
draw_cached_glyph :: proc()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
reset_batch_codepoint_state :: proc()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
can_batch_glyph :: proc()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
draw_text_batch :: proc()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
draw_text :: proc()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
get_cursor_pos :: proc()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
optimize_draw_list :: proc()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
set_colour :: proc()
|
||||
{
|
||||
|
||||
}
|
||||
|
@ -38,6 +38,7 @@ HMapChained :: grime.HMapChained
|
||||
hmap_chained_init :: grime.hmap_chained_init
|
||||
hmap_chained_get :: grime.hmap_chained_get
|
||||
hmap_chained_remove :: grime.hmap_chained_remove
|
||||
hmap_chained_set :: grime.hmap_chained_set
|
||||
hmap_closest_prime :: grime.hmap_closest_prime
|
||||
|
||||
// Pool :: grime.Pool
|
||||
@ -51,6 +52,10 @@ stack_peek_ref :: grime.stack_peek_ref
|
||||
stack_peek :: grime.stack_peek
|
||||
stack_push_contextless :: grime.stack_push_contextless
|
||||
|
||||
// logging
|
||||
log :: grime.log
|
||||
logf :: grime.logf
|
||||
|
||||
//#region("Proc overload mappings")
|
||||
|
||||
append :: proc {
|
||||
@ -85,6 +90,10 @@ remove_at :: proc {
|
||||
array_remove_at,
|
||||
}
|
||||
|
||||
set :: proc {
|
||||
hmap_chained_set,
|
||||
}
|
||||
|
||||
to_slice :: proc {
|
||||
array_to_slice,
|
||||
}
|
||||
|
@ -1,19 +1,250 @@
|
||||
package VEFontCache
|
||||
|
||||
/*
|
||||
Notes:
|
||||
|
||||
Freetype will do memory allocations and has an interface the user can implement.
|
||||
That interface is not exposed from this parser but could be added to parser_init.
|
||||
*/
|
||||
|
||||
import "core:c"
|
||||
import stbtt "vendor:stb/truetype"
|
||||
import freetype "thirdparty:freetype"
|
||||
|
||||
ParserKind :: enum u32 {
|
||||
stb_true_type,
|
||||
freetype,
|
||||
STB_TrueType,
|
||||
Freetype,
|
||||
}
|
||||
|
||||
ParserInfo :: struct #raw_union {
|
||||
ParserFontInfo :: struct {
|
||||
label : string,
|
||||
kind : ParserKind,
|
||||
using _ : struct #raw_union {
|
||||
stbtt_info : stbtt.fontinfo,
|
||||
freetype_info : freetype.Face
|
||||
}
|
||||
}
|
||||
|
||||
// Based directly off of stb_truetype's vertex
|
||||
ParserGlyphVertex :: struct {
|
||||
x, y : u16,
|
||||
contour_x0, contour_y0 : u16,
|
||||
contour_x1, contour_y1 : u16,
|
||||
type, padding : u8,
|
||||
}
|
||||
ParserGlyphShape :: []ParserGlyphVertex
|
||||
|
||||
ParserContext :: struct {
|
||||
ft_library : freetype.Library
|
||||
kind : ParserKind,
|
||||
ft_library : freetype.Library,
|
||||
|
||||
fonts : HMapChained(ParserFontInfo),
|
||||
}
|
||||
|
||||
parser_init :: proc( ctx : ^ParserContext )
|
||||
{
|
||||
switch ctx.kind
|
||||
{
|
||||
case .Freetype:
|
||||
result := freetype.init_free_type( & ctx.ft_library )
|
||||
assert( result == freetype.Error.Ok, "VEFontCache.parser_init: Failed to initialize freetype" )
|
||||
|
||||
case .STB_TrueType:
|
||||
// Do nothing intentional
|
||||
}
|
||||
|
||||
error : AllocatorError
|
||||
ctx.fonts, error = make( HMapChained(ParserFontInfo), 256 )
|
||||
assert( error == .None, "VEFontCache.parser_init: Failed to allocate fonts array" )
|
||||
}
|
||||
|
||||
parser_load_font :: proc( ctx : ParserContext, label : string, data : []byte ) -> (font : ^ParserFontInfo)
|
||||
{
|
||||
key := font_key_from_label(label)
|
||||
font = get( ctx.fonts, key )
|
||||
if font != nil do return
|
||||
|
||||
error : AllocatorError
|
||||
font, error = set( ctx.fonts, key, ParserFontInfo {} )
|
||||
assert( error != .None, "VEFontCache.parser_load_font: Failed to set a new parser font info" )
|
||||
switch ctx.kind
|
||||
{
|
||||
case .Freetype:
|
||||
error := freetype.new_memory_face( ctx.ft_library, raw_data(data), cast(i32) len(data), 0, & font.freetype_info )
|
||||
if error != .Ok do return
|
||||
|
||||
case .STB_TrueType:
|
||||
success := stbtt.InitFont( & font.stbtt_info, raw_data(data), 0 )
|
||||
if ! success do return
|
||||
}
|
||||
|
||||
font.label = label
|
||||
return
|
||||
}
|
||||
|
||||
parser_unload_font :: proc( font : ^ParserFontInfo )
|
||||
{
|
||||
switch font.kind {
|
||||
case .Freetype:
|
||||
error := freetype.done_face( font.freetype_info )
|
||||
assert( error == .Ok, "VEFontCache.parser_unload_font: Failed to unload freetype face" )
|
||||
|
||||
case .STB_TrueType:
|
||||
// Do Nothing
|
||||
}
|
||||
}
|
||||
|
||||
parser_scale_for_pixel_height :: #force_inline proc( font : ^ParserFontInfo, size : f32 ) -> f32
|
||||
{
|
||||
switch font.kind {
|
||||
case .Freetype:
|
||||
freetype.set_pixel_sizes( font.freetype_info, 0, cast(u32) size )
|
||||
size_scale := size / cast(f32)font.freetype_info.units_per_em
|
||||
return size_scale
|
||||
|
||||
case.STB_TrueType:
|
||||
return stbtt.ScaleForPixelHeight( & font.stbtt_info, size )
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
parser_scale_for_mapping_em_to_pixels :: proc( font : ^ParserFontInfo, size : f32 ) -> f32
|
||||
{
|
||||
switch font.kind {
|
||||
case .Freetype:
|
||||
Inches_To_CM :: cast(f32) 2.54
|
||||
Points_Per_CM :: cast(f32) 28.3465
|
||||
CM_Per_Point :: cast(f32) 1.0 / DPT_DPCM
|
||||
CM_Per_Pixel :: cast(f32) 1.0 / DPT_PPCM
|
||||
DPT_DPCM :: cast(f32) 72.0 * Inches_To_CM // 182.88 points/dots per cm
|
||||
DPT_PPCM :: cast(f32) 96.0 * Inches_To_CM // 243.84 pixels per cm
|
||||
DPT_DPI :: cast(f32) 72.0
|
||||
|
||||
// TODO(Ed): Don't assume the dots or pixels per inch.
|
||||
system_dpi :: DPT_DPI
|
||||
|
||||
FT_Font_Size_Point_Unit :: 1.0 / 64.0
|
||||
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;
|
||||
return size_scale
|
||||
|
||||
case .STB_TrueType:
|
||||
return stbtt.ScaleForMappingEmToPixels( & font.stbtt_info, size )
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
parser_is_glyph_empty :: proc( font : ^ParserFontInfo, glyph_index : Glyph ) -> b32
|
||||
{
|
||||
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
|
||||
{
|
||||
if font.freetype_info.glyph.format == .Outline {
|
||||
return font.freetype_info.glyph.outline.n_points == 0
|
||||
}
|
||||
else if font.freetype_info.glyph.format == .Bitmap {
|
||||
return font.freetype_info.glyph.bitmap.width == 0 && font.freetype_info.glyph.bitmap.rows == 0;
|
||||
}
|
||||
}
|
||||
return false
|
||||
|
||||
case .STB_TrueType:
|
||||
return stbtt.IsGlyphEmpty( & font.stbtt_info, cast(c.int) glyph_index )
|
||||
}
|
||||
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)
|
||||
// {
|
||||
|
||||
// }
|
||||
|
||||
parser_get_glyph_shape :: proc( font : ^ParserFontInfo, glyph_index : Glyph ) -> (shape : ParserGlyphShape, error : AllocatorError)
|
||||
{
|
||||
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
|
||||
}
|
||||
|
||||
/*
|
||||
convert freetype outline to stb_truetype shape
|
||||
|
||||
freetype docs: https://freetype.org/freetype2/docs/glyphs/glyphs-6.html
|
||||
|
||||
stb_truetype shape info:
|
||||
The shape is a series of contours. Each one starts with
|
||||
a STBTT_moveto, then consists of a series of mixed
|
||||
STBTT_lineto and STBTT_curveto segments. A lineto
|
||||
draws a line from previous endpoint to its x,y; a curveto
|
||||
draws a quadratic bezier from previous endpoint to
|
||||
its x,y, using cx,cy as the bezier control point.
|
||||
*/
|
||||
{
|
||||
FT_CURVE_TAG_CONIC :: 0x00
|
||||
FT_CURVE_TAG_ON :: 0x01
|
||||
FT_CURVE_TAG_CUBIC :: 0x02
|
||||
|
||||
// TODO(Ed): This makes freetype second class I guess but VEFontCache doesn't have native support for freetype originally so....
|
||||
outline := & glyph.outline
|
||||
|
||||
contours := transmute([^]i16) outline.contours
|
||||
for contour : i32 = 0; contour < i32(outline.n_contours); contour += 1
|
||||
{
|
||||
start_point := (contour == 0) ? 0 : i32( contours[contour - 1] + 1)
|
||||
end_point := i32(contours[contour])
|
||||
|
||||
for index := start_point; index < end_point; index += 1
|
||||
{
|
||||
points := transmute( [^]freetype.Vector) outline.points
|
||||
tags := transmute( [^]u8) outline.tags
|
||||
|
||||
point := points[index]
|
||||
tag := tags[index]
|
||||
|
||||
next_index := (index == end_point) ? start_point : index + 1
|
||||
next_point := points[next_index]
|
||||
next_tag := tags[index]
|
||||
|
||||
if (tag & FT_CURVE_TAG_CONIC) > 0 {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case .STB_TrueType:
|
||||
stb_shape : [^]stbtt.vertex
|
||||
nverts := stbtt.GetGlyphShape( & font.stbtt_info, cast(i32) glyph_index, & stb_shape )
|
||||
if nverts == 0 || shape == nil {
|
||||
shape = transmute(ParserGlyphShape) stb_shape[0:0]
|
||||
}
|
||||
shape = transmute(ParserGlyphShape) stb_shape[:nverts]
|
||||
error = AllocatorError.None
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
parser_free_shape :: proc( font : ^ParserFontInfo, shape : ParserGlyphShape )
|
||||
{
|
||||
// switch font.kind
|
||||
// {
|
||||
// case .Freetype
|
||||
// }
|
||||
}
|
||||
|
@ -1,9 +1,17 @@
|
||||
package VEFontCache
|
||||
|
||||
import "core:c"
|
||||
import "thirdparty:harfbuzz"
|
||||
|
||||
ShaperKind :: enum {
|
||||
Naive = 0,
|
||||
Harfbuzz = 1,
|
||||
}
|
||||
|
||||
ShaperContext :: struct {
|
||||
hb_buffer : harfbuzz.Buffer,
|
||||
|
||||
infos : HMapChained(ShaperInfo),
|
||||
}
|
||||
|
||||
ShaperInfo :: struct {
|
||||
@ -16,3 +24,35 @@ shaper_init :: proc( ctx : ^ShaperContext )
|
||||
{
|
||||
ctx.hb_buffer = harfbuzz.buffer_create()
|
||||
}
|
||||
|
||||
shaper_shutdown :: proc( ctx : ^ShaperContext )
|
||||
{
|
||||
if ctx.hb_buffer != nil {
|
||||
harfbuzz.buffer_destory( ctx.hb_buffer )
|
||||
}
|
||||
}
|
||||
|
||||
shaper_load_font :: proc( ctx : ^ShaperContext, label : string, data : []byte, user_data : rawptr ) -> (info : ^ShaperInfo)
|
||||
{
|
||||
key := font_key_from_label( label )
|
||||
info = get( ctx.infos, key )
|
||||
if info != nil do return
|
||||
|
||||
error : AllocatorError
|
||||
info, error = set( ctx.infos, key, ShaperInfo {} )
|
||||
assert( error != .None, "VEFontCache.parser_load_font: Failed to set a new shaper info" )
|
||||
|
||||
using info
|
||||
blob = harfbuzz.blob_create( raw_data(data), cast(c.uint) len(data), harfbuzz.Memory_Mode.READONLY, user_data, nil )
|
||||
face = harfbuzz.face_create( blob, 0 )
|
||||
font = harfbuzz.font_create( face )
|
||||
return
|
||||
}
|
||||
|
||||
shaper_unload_font :: proc( ctx : ^ShaperInfo )
|
||||
{
|
||||
using ctx
|
||||
if blob != nil do harfbuzz.font_destroy( font )
|
||||
if face != nil do harfbuzz.face_destroy( face )
|
||||
if blob != nil do harfbuzz.blob_destroy( blob )
|
||||
}
|
||||
|
@ -1,2 +1 @@
|
||||
|
||||
package sectr
|
||||
|
@ -1,3 +1,3 @@
|
||||
package sectr
|
||||
|
||||
import "codebase:font/VEFontCache"
|
||||
import "codebase:font/VEFontCache"
|
||||
|
@ -359,7 +359,7 @@ ui_resizable_handles :: proc( parent : ^UI_Widget, pos : ^Vec2, size : ^Vec2,
|
||||
}
|
||||
}
|
||||
|
||||
process_handle_drag :: #force_inline proc ( handle : ^UI_Widget,
|
||||
process_handle_drag :: proc ( handle : ^UI_Widget,
|
||||
direction : Vec2,
|
||||
target_alignment : Vec2,
|
||||
target_center_aligned : Vec2,
|
||||
|
Loading…
Reference in New Issue
Block a user