11const std = @import ("std" );
2- const ft = @import ("mach-freetype" );
3- const harfbuzz = @import ("mach-harfbuzz" );
2+ const c = @cImport ({
3+ @cInclude ("ft2build.h" );
4+ @cInclude ("freetype/freetype.h" );
5+ @cInclude ("harfbuzz/hb.h" );
6+ @cInclude ("harfbuzz/hb-ft.h" );
7+ });
48const TextRun = @import ("TextRun.zig" );
59const px_per_pt = @import ("../main.zig" ).px_per_pt ;
610const RenderedGlyph = @import ("../main.zig" ).RenderedGlyph ;
@@ -11,58 +15,66 @@ const Font = @This();
1115
1216var freetype_ready_mu : std.Thread.Mutex = .{};
1317var freetype_ready : bool = false ;
14- var freetype : ft.Library = undefined ;
18+ var ft_library : c.FT_Library = null ;
1519
16- face : ft.Face ,
20+ face : c.FT_Face ,
1721bitmap : std .ArrayListUnmanaged (RGBA32 ) = .{},
1822
1923pub fn initFreetype () ! void {
2024 freetype_ready_mu .lock ();
2125 defer freetype_ready_mu .unlock ();
2226 if (! freetype_ready ) {
23- freetype = try ft . Library . init () ;
27+ if ( c . FT_Init_FreeType ( & ft_library ) != 0 ) return error . FreetypeInitFailed ;
2428 freetype_ready = true ;
2529 }
2630}
2731
2832pub fn initBytes (font_bytes : []const u8 ) anyerror ! Font {
2933 try initFreetype ();
30- return .{
31- .face = try freetype .createFaceMemory (font_bytes , 0 ),
32- };
34+ var face : c.FT_Face = null ;
35+ if (c .FT_New_Memory_Face (ft_library , font_bytes .ptr , @intCast (font_bytes .len ), 0 , & face ) != 0 )
36+ return error .FreetypeError ;
37+ return .{ .face = face };
3338}
3439
3540pub fn shape (f : * const Font , r : * TextRun ) anyerror ! void {
3641 // Guess text segment properties.
37- r .buffer . guessSegmentProps ( );
42+ c . hb_buffer_guess_segment_properties ( r .buffer );
3843 // TODO: Optionally override specific text segment properties?
39- // r.buffer.setDirection(.ltr );
40- // r.buffer.setScript(.latin );
41- // r.buffer.setLanguage(harfbuzz.Language.fromString( "en"));
44+ // hb_buffer_set_direction( r.buffer, ... );
45+ // hb_buffer_set_script( r.buffer, ... );
46+ // hb_buffer_set_language( r.buffer, hb_language_from_string( "en", -1 ));
4247
4348 const font_size_pt = r .font_size_px / px_per_pt ;
4449 const font_size_pt_frac : i32 = @intFromFloat (font_size_pt * 64.0 );
45- f .face .setCharSize (font_size_pt_frac , font_size_pt_frac , 0 , 0 ) catch return error .RenderError ;
50+ if (c .FT_Set_Char_Size (f .face , font_size_pt_frac , font_size_pt_frac , 0 , 0 ) != 0 )
51+ return error .RenderError ;
4652
47- const hb_face = harfbuzz . Face . fromFreetypeFace (f .face );
48- const hb_font = harfbuzz . Font . init (hb_face );
49- defer hb_font . deinit ( );
53+ const hb_face = c . hb_ft_face_create_referenced (f .face ) orelse return error . RenderError ;
54+ const hb_font = c . hb_font_create (hb_face ) orelse return error . RenderError ;
55+ defer c . hb_font_destroy ( hb_font );
5056
51- hb_font . setScale ( font_size_pt_frac , font_size_pt_frac );
52- hb_font . setPTEM ( font_size_pt );
57+ c . hb_font_set_scale ( hb_font , font_size_pt_frac , font_size_pt_frac );
58+ c . hb_font_set_ptem ( hb_font , font_size_pt );
5359
5460 // TODO: optionally pass shaping features?
55- hb_font . shape ( r .buffer , null );
61+ c . hb_shape ( hb_font , r .buffer , null , 0 );
5662
5763 r .index = 0 ;
58- r .infos = r .buffer .getGlyphInfos ();
59- r .positions = r .buffer .getGlyphPositions () orelse return error .OutOfMemory ;
64+ var info_count : u32 = 0 ;
65+ const infos_ptr = c .hb_buffer_get_glyph_infos (r .buffer , & info_count );
66+ r .infos = if (infos_ptr ) | p | p [0.. info_count ] else return error .OutOfMemory ;
67+
68+ var pos_count : u32 = 0 ;
69+ const pos_ptr = c .hb_buffer_get_glyph_positions (r .buffer , & pos_count );
70+ r .positions = if (pos_ptr ) | p | p [0.. pos_count ] else return error .OutOfMemory ;
6071
6172 for (r .positions , r .infos ) | * pos , info | {
6273 const glyph_index = info .codepoint ;
63- f .face .loadGlyph (glyph_index , .{ .render = false }) catch return error .RenderError ;
64- const glyph = f .face .glyph ();
65- const metrics = glyph .metrics ();
74+ if (c .FT_Load_Glyph (f .face , glyph_index , c .FT_LOAD_DEFAULT ) != 0 )
75+ return error .RenderError ;
76+ const glyph = f .face .* .glyph ;
77+ const metrics = glyph .* .metrics ;
6678 pos .* .x_offset += @intCast (metrics .horiBearingX );
6779 pos .* .y_offset += @intCast (metrics .horiBearingY );
6880 // TODO: use vertBearingX / vertBearingY for vertical layouts
@@ -71,13 +83,14 @@ pub fn shape(f: *const Font, r: *TextRun) anyerror!void {
7183
7284pub fn render (f : * Font , allocator : std.mem.Allocator , glyph_index : u32 , opt : RenderOptions ) anyerror ! RenderedGlyph {
7385 _ = opt ;
74- f .face .loadGlyph (glyph_index , .{ .render = true }) catch return error .RenderError ;
75-
76- const glyph = f .face .glyph ();
77- const glyph_bitmap = glyph .bitmap ();
78- const buffer = glyph_bitmap .buffer ();
79- const width = glyph_bitmap .width ();
80- const height = glyph_bitmap .rows ();
86+ if (c .FT_Load_Glyph (f .face , glyph_index , c .FT_LOAD_RENDER ) != 0 )
87+ return error .RenderError ;
88+
89+ const glyph = f .face .* .glyph ;
90+ const glyph_bitmap = glyph .* .bitmap ;
91+ const buffer : ? [* ]const u8 = glyph_bitmap .buffer ;
92+ const width = glyph_bitmap .width ;
93+ const height = glyph_bitmap .rows ;
8194 const margin = 1 ;
8295
8396 if (buffer == null ) return RenderedGlyph {
@@ -99,7 +112,7 @@ pub fn render(f: *Font, allocator: std.mem.Allocator, glyph_index: u32, opt: Ren
99112 if (x < margin or x > (width + margin ) or y < margin or y > (height + margin )) {
100113 data .* = RGBA32 { .r = 0 , .g = 0 , .b = 0 , .a = 0 };
101114 } else {
102- const alpha = buffer .? [((y - margin ) * width + (x - margin )) % buffer .? . len ];
115+ const alpha = buffer .? [((y - margin ) * width + (x - margin )) % ( glyph_bitmap . pitch * height ) ];
103116 data .* = RGBA32 { .r = 0 , .g = 0 , .b = 0 , .a = alpha };
104117 }
105118 }
@@ -112,6 +125,6 @@ pub fn render(f: *Font, allocator: std.mem.Allocator, glyph_index: u32, opt: Ren
112125}
113126
114127pub fn deinit (f : * Font , allocator : std.mem.Allocator ) void {
115- f .face . deinit ( );
128+ _ = c . FT_Done_Face ( f .face );
116129 f .bitmap .deinit (allocator );
117130}
0 commit comments