Skip to content

Commit 097dbc4

Browse files
committed
remove mach-freetype
Signed-off-by: Emi <emi@hexops.com>
1 parent 7ee7bdc commit 097dbc4

5 files changed

Lines changed: 94 additions & 67 deletions

File tree

build.zig

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -122,13 +122,15 @@ pub fn build(b: *std.Build) !void {
122122
if (target.result.os.tag == .linux) module.link_libc = true;
123123

124124
if (target.result.cpu.arch != .wasm32) {
125-
if (b.lazyDependency("mach_freetype", .{
125+
if (b.lazyDependency("freetype", .{
126126
.target = target,
127127
.optimize = optimize,
128-
})) |dep| {
129-
module.addImport("mach-freetype", dep.module("mach-freetype"));
130-
module.addImport("mach-harfbuzz", dep.module("mach-harfbuzz"));
131-
}
128+
})) |dep| module.linkLibrary(dep.artifact("freetype"));
129+
if (b.lazyDependency("harfbuzz", .{
130+
.target = target,
131+
.optimize = optimize,
132+
.enable_freetype = true,
133+
})) |dep| module.linkLibrary(dep.artifact("harfbuzz"));
132134
if (b.lazyDependency("mach_opus", .{
133135
.target = target,
134136
.optimize = .ReleaseFast,
@@ -444,10 +446,10 @@ fn buildExamples(
444446
})) |dep| app_mod.addImport("assets", dep.module("mach-example-assets"));
445447
},
446448
.freetype => {
447-
if (b.lazyDependency("mach_freetype", .{
449+
if (b.lazyDependency("freetype", .{
448450
.target = target,
449451
.optimize = optimize,
450-
})) |dep| app_mod.addImport("freetype", dep.module("mach-freetype"));
452+
})) |dep| app_mod.linkLibrary(dep.artifact("freetype"));
451453
},
452454
.zigimg => {
453455
if (b.lazyDependency("zigimg", .{

build.zig.zon

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,14 @@
1111
"README.md",
1212
},
1313
.dependencies = .{
14-
.mach_freetype = .{
15-
.url = "https://pkg.machengine.org/mach-freetype/d63efa5534c17f3a12ed3d327e0ad42a64adc20a.tar.gz",
16-
.hash = "1220adfccce3dbc4e4fa8650fdaec110a676f6b8a1462ed6ef422815207f8288e9d2",
14+
.freetype = .{
15+
.url = "https://pkg.machengine.org/freetype/972cd37bccecae2cc9f54cf0b562263a13209d02.tar.gz",
16+
.hash = "12204cba3a237cd2c4ab983f5a28d9b54e7a9912d8c7c6e38e23140b0171d6e1ebf8",
17+
.lazy = true,
18+
},
19+
.harfbuzz = .{
20+
.url = "https://pkg.machengine.org/harfbuzz/c514da98afcf5d9ad6854a7f09192f9ecfaeb061.tar.gz",
21+
.hash = "1220203218ac17e497f3399e08115e73cb9505f1b9f07738eb0c5cc38ca443dec953",
1722
.lazy = true,
1823
},
1924
.mach_objc = .{

examples/glyphs/App.zig

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
const std = @import("std");
22
const mach = @import("mach");
3-
const freetype = @import("freetype");
3+
const c = @cImport({
4+
@cInclude("ft2build.h");
5+
@cInclude("freetype/freetype.h");
6+
});
47
const assets = @import("assets");
58

69
const gpu = mach.gpu;
@@ -45,8 +48,8 @@ player_id: mach.ObjectID = undefined,
4548
pipeline_id: mach.ObjectID = undefined,
4649
texture_atlas: mach.gfx.Atlas = undefined,
4750
texture: *gpu.Texture = undefined,
48-
ft: freetype.Library = undefined,
49-
face: freetype.Face = undefined,
51+
ft: c.FT_Library = null,
52+
face: c.FT_Face = null,
5053
regions: RegionMap = .{},
5154

5255
pub const main = mach.schedule(.{
@@ -83,8 +86,8 @@ pub fn init(
8386
pub fn deinit(app: *App) void {
8487
app.texture_atlas.deinit(app.allocator);
8588
app.texture.release();
86-
app.face.deinit();
87-
app.ft.deinit();
89+
_ = c.FT_Done_Face(app.face);
90+
_ = c.FT_Done_FreeType(app.ft);
8891
app.regions.deinit(app.allocator);
8992
}
9093

@@ -118,8 +121,9 @@ fn setupPipeline(
118121
.rgba,
119122
);
120123

121-
app.ft = try freetype.Library.init();
122-
app.face = try app.ft.createFaceMemory(assets.roboto_medium_ttf, 0);
124+
if (c.FT_Init_FreeType(&app.ft) != 0) return error.FreetypeInitFailed;
125+
if (c.FT_New_Memory_Face(app.ft, assets.roboto_medium_ttf.ptr, @intCast(assets.roboto_medium_ttf.len), 0, &app.face) != 0)
126+
return error.FreetypeError;
123127
try prepareGlyphs(window.queue, app);
124128

125129
// Create a sprite rendering pipeline
@@ -145,27 +149,29 @@ fn prepareGlyphs(queue: *gpu.Queue, app: *App) !void {
145149
const codepoints: []const u21 = &[_]u21{ '?', '!', 'a', 'b', '#', '@', '%', '$', '&', '^', '*', '+', '=', '<', '>', '/', ':', ';', 'Q', '~' };
146150
for (codepoints) |codepoint| {
147151
const font_size = 48 * 1;
148-
try app.face.setCharSize(font_size * 64, 0, 50, 0);
149-
try app.face.loadChar(codepoint, .{ .render = true });
150-
const glyph = app.face.glyph();
151-
const metrics = glyph.metrics();
152+
if (c.FT_Set_Char_Size(app.face, font_size * 64, 0, 50, 0) != 0) return error.FreetypeError;
153+
if (c.FT_Load_Char(app.face, codepoint, c.FT_LOAD_RENDER) != 0) return error.FreetypeError;
154+
const glyph = app.face.*.glyph;
155+
const metrics = glyph.*.metrics;
152156

153-
const glyph_bitmap = glyph.bitmap();
154-
const glyph_width = glyph_bitmap.width();
155-
const glyph_height = glyph_bitmap.rows();
157+
const glyph_bitmap = glyph.*.bitmap;
158+
const glyph_width = glyph_bitmap.width;
159+
const glyph_height = glyph_bitmap.rows;
156160

157161
// Add 1 pixel padding to texture to avoid bleeding over other textures
158162
const margin = 1;
159163
const glyph_data = try app.allocator.alloc([4]u8, (glyph_width + (margin * 2)) * (glyph_height + (margin * 2)));
160164
defer app.allocator.free(glyph_data);
161-
const glyph_buffer = glyph_bitmap.buffer().?;
165+
const glyph_buffer: [*]const u8 = glyph_bitmap.buffer;
166+
const glyph_buffer_len = glyph_bitmap.pitch * @as(c_int, @intCast(glyph_height));
167+
const glyph_buffer_slice = glyph_buffer[0..@intCast(glyph_buffer_len)];
162168
for (glyph_data, 0..) |*data, i| {
163169
const x = i % (glyph_width + (margin * 2));
164170
const y = i / (glyph_width + (margin * 2));
165171
if (x < margin or x > (glyph_width + margin) or y < margin or y > (glyph_height + margin)) {
166172
data.* = [4]u8{ 0, 0, 0, 0 };
167173
} else {
168-
const alpha = glyph_buffer[((y - margin) * glyph_width + (x - margin)) % glyph_buffer.len];
174+
const alpha = glyph_buffer_slice[((y - margin) * glyph_width + (x - margin)) % glyph_buffer_slice.len];
169175
data.* = [4]u8{ 0, 0, 0, alpha };
170176
}
171177
}

src/gfx/font/native/Font.zig

Lines changed: 46 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
const 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+
});
48
const TextRun = @import("TextRun.zig");
59
const px_per_pt = @import("../main.zig").px_per_pt;
610
const RenderedGlyph = @import("../main.zig").RenderedGlyph;
@@ -11,58 +15,66 @@ const Font = @This();
1115

1216
var freetype_ready_mu: std.Thread.Mutex = .{};
1317
var 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,
1721
bitmap: std.ArrayListUnmanaged(RGBA32) = .{},
1822

1923
pub 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

2832
pub 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

3540
pub 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

7284
pub 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

114127
pub 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
}

src/gfx/font/native/TextRun.zig

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
const std = @import("std");
2-
const harfbuzz = @import("mach-harfbuzz");
2+
const c = @cImport({
3+
@cInclude("harfbuzz/hb.h");
4+
});
35
const math = @import("../../../main.zig").math;
46
const vec2 = math.vec2;
57
const Vec2 = math.Vec2;
@@ -11,19 +13,19 @@ font_size_px: f32 = 16.0,
1113
px_density: u8 = 1,
1214

1315
// Internal / private fields.
14-
buffer: harfbuzz.Buffer,
16+
buffer: *c.hb_buffer_t,
1517
index: usize = 0,
16-
infos: []harfbuzz.GlyphInfo = undefined,
17-
positions: []harfbuzz.GlyphPosition = undefined,
18+
infos: []c.hb_glyph_info_t = undefined,
19+
positions: []c.hb_glyph_position_t = undefined,
1820

1921
pub fn init() anyerror!TextRun {
2022
return TextRun{
21-
.buffer = harfbuzz.Buffer.init() orelse return error.OutOfMemory,
23+
.buffer = c.hb_buffer_create() orelse return error.OutOfMemory,
2224
};
2325
}
2426

2527
pub fn addText(s: *const TextRun, utf8_text: []const u8) void {
26-
s.buffer.addUTF8(utf8_text, 0, null);
28+
c.hb_buffer_add_utf8(s.buffer, utf8_text.ptr, @intCast(utf8_text.len), 0, @intCast(utf8_text.len));
2729
}
2830

2931
pub fn next(s: *TextRun) ?Glyph {
@@ -43,6 +45,5 @@ pub fn next(s: *TextRun) ?Glyph {
4345
}
4446

4547
pub fn deinit(s: *const TextRun) void {
46-
s.buffer.deinit();
47-
return;
48+
c.hb_buffer_destroy(s.buffer);
4849
}

0 commit comments

Comments
 (0)