mirror of
https://github.com/PixelGuys/Cubyz.git
synced 2025-09-08 11:44:21 -04:00
Ditch mach-freetype: now building freetype and harfbuzz directly.
Having zig bindings was certainly nice to have, but overall the code isn't too different really. One nice advantage of building freetype and harfbuzz directly is that we can get rid of some unnecessary dependencies, like brotli. Also, since that was the only zig dependency outside the standard library, this finally gives me the freedom to update zig whenever I want. This is a necessary step for #117. Resolves #139
This commit is contained in:
parent
2f6d2ada2c
commit
0c2d309f2a
83
build.zig
83
build.zig
@ -6,6 +6,78 @@ fn addPackageCSourceFiles(exe: *std.Build.Step.Compile, dep: *std.Build.Dependen
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const freetypeSources = [_][]const u8{
|
||||||
|
"src/autofit/autofit.c",
|
||||||
|
"src/base/ftbase.c",
|
||||||
|
"src/base/ftsystem.c",
|
||||||
|
"src/base/ftdebug.c",
|
||||||
|
"src/base/ftbbox.c",
|
||||||
|
"src/base/ftbdf.c",
|
||||||
|
"src/base/ftbitmap.c",
|
||||||
|
"src/base/ftcid.c",
|
||||||
|
"src/base/ftfstype.c",
|
||||||
|
"src/base/ftgasp.c",
|
||||||
|
"src/base/ftglyph.c",
|
||||||
|
"src/base/ftgxval.c",
|
||||||
|
"src/base/ftinit.c",
|
||||||
|
"src/base/ftmm.c",
|
||||||
|
"src/base/ftotval.c",
|
||||||
|
"src/base/ftpatent.c",
|
||||||
|
"src/base/ftpfr.c",
|
||||||
|
"src/base/ftstroke.c",
|
||||||
|
"src/base/ftsynth.c",
|
||||||
|
"src/base/fttype1.c",
|
||||||
|
"src/base/ftwinfnt.c",
|
||||||
|
"src/bdf/bdf.c",
|
||||||
|
"src/bzip2/ftbzip2.c",
|
||||||
|
"src/cache/ftcache.c",
|
||||||
|
"src/cff/cff.c",
|
||||||
|
"src/cid/type1cid.c",
|
||||||
|
"src/gzip/ftgzip.c",
|
||||||
|
"src/lzw/ftlzw.c",
|
||||||
|
"src/pcf/pcf.c",
|
||||||
|
"src/pfr/pfr.c",
|
||||||
|
"src/psaux/psaux.c",
|
||||||
|
"src/pshinter/pshinter.c",
|
||||||
|
"src/psnames/psnames.c",
|
||||||
|
"src/raster/raster.c",
|
||||||
|
"src/sdf/sdf.c",
|
||||||
|
"src/sfnt/sfnt.c",
|
||||||
|
"src/smooth/smooth.c",
|
||||||
|
"src/svg/svg.c",
|
||||||
|
"src/truetype/truetype.c",
|
||||||
|
"src/type1/type1.c",
|
||||||
|
"src/type42/type42.c",
|
||||||
|
"src/winfonts/winfnt.c",
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn addFreetypeAndHarfbuzz(b: *std.Build, exe: *std.build.Step.Compile, c_lib: *std.build.Step.Compile, target: anytype, optimize: std.builtin.OptimizeMode, flags: []const []const u8) void {
|
||||||
|
const freetype = b.dependency("freetype", .{
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
});
|
||||||
|
const harfbuzz = b.dependency("harfbuzz", .{
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
});
|
||||||
|
|
||||||
|
c_lib.defineCMacro("FT2_BUILD_LIBRARY", "1");
|
||||||
|
c_lib.defineCMacro("HAVE_UNISTD_H", "1");
|
||||||
|
c_lib.addIncludePath(freetype.path("include"));
|
||||||
|
exe.addIncludePath(freetype.path("include"));
|
||||||
|
addPackageCSourceFiles(c_lib, freetype, &freetypeSources, flags);
|
||||||
|
if (target.toTarget().os.tag == .macos) c_lib.addCSourceFile(.{
|
||||||
|
.file = freetype.path("src/base/ftmac.c"),
|
||||||
|
.flags = &.{},
|
||||||
|
});
|
||||||
|
|
||||||
|
c_lib.addIncludePath(harfbuzz.path("src"));
|
||||||
|
exe.addIncludePath(harfbuzz.path("src"));
|
||||||
|
c_lib.defineCMacro("HAVE_FREETYPE", "1");
|
||||||
|
c_lib.addCSourceFile(.{.file = harfbuzz.path("src/harfbuzz.cc"), .flags = flags});
|
||||||
|
c_lib.linkLibCpp();
|
||||||
|
}
|
||||||
|
|
||||||
pub fn build(b: *std.build.Builder) !void {
|
pub fn build(b: *std.build.Builder) !void {
|
||||||
// Standard target options allows the person running `zig build` to choose
|
// Standard target options allows the person running `zig build` to choose
|
||||||
// what target to build for. Here we do not override the defaults, which
|
// what target to build for. Here we do not override the defaults, which
|
||||||
@ -27,7 +99,7 @@ pub fn build(b: *std.build.Builder) !void {
|
|||||||
.target = target,
|
.target = target,
|
||||||
.optimize = optimize,
|
.optimize = optimize,
|
||||||
});
|
});
|
||||||
const c_flags = &[_][]const u8{"-g", "-O3"};
|
const c_flags = &[_][]const u8{"-g"};
|
||||||
c_lib.addIncludePath(.{.path = "include"});
|
c_lib.addIncludePath(.{.path = "include"});
|
||||||
exe.addIncludePath(.{.path = "include"});
|
exe.addIncludePath(.{.path = "include"});
|
||||||
c_lib.linkLibC();
|
c_lib.linkLibC();
|
||||||
@ -99,14 +171,7 @@ pub fn build(b: *std.build.Builder) !void {
|
|||||||
exe.addAnonymousModule("gui", .{.source_file = .{.path = "src/gui/gui.zig"}});
|
exe.addAnonymousModule("gui", .{.source_file = .{.path = "src/gui/gui.zig"}});
|
||||||
exe.addAnonymousModule("server", .{.source_file = .{.path = "src/server/server.zig"}});
|
exe.addAnonymousModule("server", .{.source_file = .{.path = "src/server/server.zig"}});
|
||||||
|
|
||||||
const mach_freetype_dep = b.dependency("mach_freetype", .{
|
addFreetypeAndHarfbuzz(b, exe, c_lib, target, optimize, c_flags);
|
||||||
.target = target,
|
|
||||||
.optimize = optimize,
|
|
||||||
});
|
|
||||||
exe.addModule("freetype", mach_freetype_dep.module("mach-freetype"));
|
|
||||||
exe.addModule("harfbuzz", mach_freetype_dep.module("mach-harfbuzz"));
|
|
||||||
@import("mach_freetype").linkFreetype(mach_freetype_dep.builder, exe);
|
|
||||||
@import("mach_freetype").linkHarfbuzz(mach_freetype_dep.builder, exe);
|
|
||||||
|
|
||||||
//exe.strip = true; // Improves compile-time
|
//exe.strip = true; // Improves compile-time
|
||||||
//exe.sanitize_thread = true;
|
//exe.sanitize_thread = true;
|
||||||
|
@ -3,9 +3,13 @@
|
|||||||
.version = "0.0.0",
|
.version = "0.0.0",
|
||||||
.paths = .{""},
|
.paths = .{""},
|
||||||
.dependencies = .{
|
.dependencies = .{
|
||||||
.mach_freetype = .{
|
.harfbuzz = .{
|
||||||
.url = "https://pkg.machengine.org/mach-freetype/f152278c6ccc6dcf6dcf4308bbe027a7598ffe63.tar.gz",
|
.url = "https://github.com/harfbuzz/harfbuzz/archive/refs/tags/8.2.2.tar.gz",
|
||||||
.hash = "1220e0de43cacb583b8f9efddcbe359398cfca17a39e265b56c8f2a10314eb8f7a5f"
|
.hash = "1220d27d0e3ddd47705cbe1505076058cb41649336d35ea51369ec8f042c35991e0f",
|
||||||
|
},
|
||||||
|
.freetype = .{
|
||||||
|
.url = "https://github.com/freetype/freetype/archive/refs/tags/VER-2-13-2.tar.gz",
|
||||||
|
.hash = "1220b81f6ecfb3fd222f76cf9106fecfa6554ab07ec7fdc4124b9bb063ae2adf969d",
|
||||||
},
|
},
|
||||||
.portaudio = .{
|
.portaudio = .{
|
||||||
.url = "https://github.com/PortAudio/portaudio/archive/refs/tags/v19.7.0.tar.gz",
|
.url = "https://github.com/PortAudio/portaudio/archive/refs/tags/v19.7.0.tar.gz",
|
||||||
|
@ -3,8 +3,19 @@
|
|||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
const freetype = @import("freetype");
|
pub const hbft = @cImport({
|
||||||
const harfbuzz = @import("harfbuzz");
|
@cInclude("freetype/ftadvanc.h");
|
||||||
|
@cInclude("freetype/ftbbox.h");
|
||||||
|
@cInclude("freetype/ftbitmap.h");
|
||||||
|
@cInclude("freetype/ftcolor.h");
|
||||||
|
@cInclude("freetype/ftlcdfil.h");
|
||||||
|
@cInclude("freetype/ftsizes.h");
|
||||||
|
@cInclude("freetype/ftstroke.h");
|
||||||
|
@cInclude("freetype/fttrigon.h");
|
||||||
|
@cInclude("freetype/ftsynth.h");
|
||||||
|
@cInclude("hb.h");
|
||||||
|
@cInclude("hb-ft.h");
|
||||||
|
});
|
||||||
|
|
||||||
const vec = @import("vec.zig");
|
const vec = @import("vec.zig");
|
||||||
const Mat4f = vec.Mat4f;
|
const Mat4f = vec.Mat4f;
|
||||||
@ -435,7 +446,7 @@ pub const TextBuffer = struct {
|
|||||||
|
|
||||||
alignment: Alignment,
|
alignment: Alignment,
|
||||||
width: f32,
|
width: f32,
|
||||||
buffer: harfbuzz.Buffer,
|
buffer: ?*hbft.hb_buffer_t,
|
||||||
glyphs: []GlyphData,
|
glyphs: []GlyphData,
|
||||||
lines: std.ArrayList(Line),
|
lines: std.ArrayList(Line),
|
||||||
lineBreaks: std.ArrayList(LineBreak),
|
lineBreaks: std.ArrayList(LineBreak),
|
||||||
@ -572,15 +583,22 @@ pub const TextBuffer = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Let harfbuzz do its thing:
|
// Let harfbuzz do its thing:
|
||||||
var buffer = harfbuzz.Buffer.init() orelse return error.OutOfMemory;
|
var buffer = hbft.hb_buffer_create() orelse return error.OutOfMemory;
|
||||||
defer buffer.deinit();
|
defer hbft.hb_buffer_destroy(buffer);
|
||||||
buffer.addUTF32(parser.parsedText.items, 0, null);
|
hbft.hb_buffer_add_utf32(buffer, parser.parsedText.items.ptr, @intCast(parser.parsedText.items.len), 0, @intCast(parser.parsedText.items.len));
|
||||||
buffer.setDirection(.ltr);
|
hbft.hb_buffer_set_direction(buffer, hbft.HB_DIRECTION_LTR);
|
||||||
buffer.setScript(.common);
|
hbft.hb_buffer_set_script(buffer, hbft.HB_SCRIPT_COMMON);
|
||||||
buffer.setLanguage(harfbuzz.Language.getDefault());
|
hbft.hb_buffer_set_language(buffer, hbft.hb_language_get_default());
|
||||||
TextRendering.harfbuzzFont.shape(buffer, null);
|
hbft.hb_shape(TextRendering.harfbuzzFont, buffer, null, 0);
|
||||||
const glyphInfos = buffer.getGlyphInfos();
|
var glyphInfos: []hbft.hb_glyph_info_t = undefined;
|
||||||
const glyphPositions = buffer.getGlyphPositions().?;
|
var glyphPositions: []hbft.hb_glyph_position_t = undefined;
|
||||||
|
{
|
||||||
|
var len: c_uint = 0;
|
||||||
|
glyphInfos.ptr = hbft.hb_buffer_get_glyph_infos(buffer, &len).?;
|
||||||
|
glyphPositions.ptr = hbft.hb_buffer_get_glyph_positions(buffer, &len).?;
|
||||||
|
glyphInfos.len = len;
|
||||||
|
glyphPositions.len = len;
|
||||||
|
}
|
||||||
|
|
||||||
// Guess the text index from the given cluster indices. Only works if the number of glyphs and the number of characters in a cluster is the same.
|
// Guess the text index from the given cluster indices. Only works if the number of glyphs and the number of characters in a cluster is the same.
|
||||||
var textIndexGuess = try stackFallbackAllocator.alloc(u32, glyphInfos.len);
|
var textIndexGuess = try stackFallbackAllocator.alloc(u32, glyphInfos.len);
|
||||||
@ -893,27 +911,35 @@ const TextRendering = struct {
|
|||||||
alpha: c_int,
|
alpha: c_int,
|
||||||
} = undefined;
|
} = undefined;
|
||||||
|
|
||||||
var freetypeLib: freetype.Library = undefined;
|
var freetypeLib: hbft.FT_Library = undefined;
|
||||||
var freetypeFace: freetype.Face = undefined;
|
var freetypeFace: hbft.FT_Face = undefined;
|
||||||
var harfbuzzFace: harfbuzz.Face = undefined;
|
var harfbuzzFace: ?*hbft.hb_face_t = undefined;
|
||||||
var harfbuzzFont: harfbuzz.Font = undefined;
|
var harfbuzzFont: ?*hbft.hb_font_t = undefined;
|
||||||
var glyphMapping: std.ArrayList(u31) = undefined;
|
var glyphMapping: std.ArrayList(u31) = undefined;
|
||||||
var glyphData: std.ArrayList(Glyph) = undefined;
|
var glyphData: std.ArrayList(Glyph) = undefined;
|
||||||
var glyphTexture: [2]c_uint = undefined;
|
var glyphTexture: [2]c_uint = undefined;
|
||||||
var textureWidth: i32 = 1024;
|
var textureWidth: i32 = 1024;
|
||||||
const textureHeight: i32 = 16;
|
const textureHeight: i32 = 16;
|
||||||
var textureOffset: i32 = 0;
|
var textureOffset: i32 = 0;
|
||||||
|
|
||||||
|
fn ftError(errorCode: hbft.FT_Error) !void {
|
||||||
|
if(errorCode == 0) return;
|
||||||
|
const errorString = hbft.FT_Error_String(errorCode);
|
||||||
|
std.log.err("Got freetype error {s}", .{errorString});
|
||||||
|
return error.freetype;
|
||||||
|
}
|
||||||
|
|
||||||
fn init() !void {
|
fn init() !void {
|
||||||
shader = try Shader.initAndGetUniforms("assets/cubyz/shaders/graphics/Text.vs", "assets/cubyz/shaders/graphics/Text.fs", &uniforms);
|
shader = try Shader.initAndGetUniforms("assets/cubyz/shaders/graphics/Text.vs", "assets/cubyz/shaders/graphics/Text.fs", &uniforms);
|
||||||
shader.bind();
|
shader.bind();
|
||||||
c.glUniform1i(uniforms.texture_sampler, 0);
|
c.glUniform1i(uniforms.texture_sampler, 0);
|
||||||
c.glUniform1f(uniforms.alpha, 1.0);
|
c.glUniform1f(uniforms.alpha, 1.0);
|
||||||
c.glUniform2f(uniforms.fontSize, @floatFromInt(textureWidth), @floatFromInt(textureHeight));
|
c.glUniform2f(uniforms.fontSize, @floatFromInt(textureWidth), @floatFromInt(textureHeight));
|
||||||
freetypeLib = try freetype.Library.init();
|
try ftError(hbft.FT_Init_FreeType(&freetypeLib));
|
||||||
freetypeFace = try freetypeLib.createFace("assets/cubyz/fonts/unscii-16-full.ttf", 0);
|
try ftError(hbft.FT_New_Face(freetypeLib, "assets/cubyz/fonts/unscii-16-full.ttf", 0, &freetypeFace));
|
||||||
try freetypeFace.setPixelSizes(0, textureHeight);
|
try ftError(hbft.FT_Set_Pixel_Sizes(freetypeFace, 0, textureHeight));
|
||||||
harfbuzzFace = harfbuzz.Face.fromFreetypeFace(freetypeFace);
|
harfbuzzFace = hbft.hb_ft_face_create_referenced(freetypeFace);
|
||||||
harfbuzzFont = harfbuzz.Font.init(harfbuzzFace);
|
harfbuzzFont = hbft.hb_font_create(harfbuzzFace);
|
||||||
|
|
||||||
glyphMapping = std.ArrayList(u31).init(main.globalAllocator);
|
glyphMapping = std.ArrayList(u31).init(main.globalAllocator);
|
||||||
glyphData = std.ArrayList(Glyph).init(main.globalAllocator);
|
glyphData = std.ArrayList(Glyph).init(main.globalAllocator);
|
||||||
@ -934,11 +960,11 @@ const TextRendering = struct {
|
|||||||
|
|
||||||
fn deinit() void {
|
fn deinit() void {
|
||||||
shader.deinit();
|
shader.deinit();
|
||||||
freetypeLib.deinit();
|
ftError(hbft.FT_Done_FreeType(freetypeLib)) catch {};
|
||||||
glyphMapping.deinit();
|
glyphMapping.deinit();
|
||||||
glyphData.deinit();
|
glyphData.deinit();
|
||||||
c.glDeleteTextures(2, &glyphTexture);
|
c.glDeleteTextures(2, &glyphTexture);
|
||||||
harfbuzzFont.deinit();
|
hbft.hb_font_destroy(harfbuzzFont);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resizeTexture(newWidth: i32) !void {
|
fn resizeTexture(newWidth: i32) !void {
|
||||||
@ -958,15 +984,15 @@ const TextRendering = struct {
|
|||||||
c.glUniform2f(uniforms.fontSize, @floatFromInt(textureWidth), @floatFromInt(textureHeight));
|
c.glUniform2f(uniforms.fontSize, @floatFromInt(textureWidth), @floatFromInt(textureHeight));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn uploadData(bitmap: freetype.Bitmap) !void {
|
fn uploadData(bitmap: hbft.FT_Bitmap) !void {
|
||||||
const width: i32 = @bitCast(bitmap.width());
|
const width: i32 = @bitCast(bitmap.width);
|
||||||
const height: i32 = @bitCast(bitmap.rows());
|
const height: i32 = @bitCast(bitmap.rows);
|
||||||
const buffer = bitmap.buffer() orelse return;
|
const buffer = bitmap.buffer orelse return;
|
||||||
if(textureOffset + width > textureWidth) {
|
if(textureOffset + width > textureWidth) {
|
||||||
try resizeTexture(textureWidth*2);
|
try resizeTexture(textureWidth*2);
|
||||||
}
|
}
|
||||||
c.glPixelStorei(c.GL_UNPACK_ALIGNMENT, 1);
|
c.glPixelStorei(c.GL_UNPACK_ALIGNMENT, 1);
|
||||||
c.glTexSubImage2D(c.GL_TEXTURE_2D, 0, textureOffset, 0, width, height, c.GL_RED, c.GL_UNSIGNED_BYTE, buffer.ptr);
|
c.glTexSubImage2D(c.GL_TEXTURE_2D, 0, textureOffset, 0, width, height, c.GL_RED, c.GL_UNSIGNED_BYTE, buffer);
|
||||||
textureOffset += width;
|
textureOffset += width;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -975,17 +1001,17 @@ const TextRendering = struct {
|
|||||||
try glyphMapping.appendNTimes(0, index - glyphMapping.items.len + 1);
|
try glyphMapping.appendNTimes(0, index - glyphMapping.items.len + 1);
|
||||||
}
|
}
|
||||||
if(glyphMapping.items[index] == 0) {// glyph was not initialized yet.
|
if(glyphMapping.items[index] == 0) {// glyph was not initialized yet.
|
||||||
try freetypeFace.loadGlyph(index, freetype.LoadFlags{.render = true});
|
try ftError(hbft.FT_Load_Glyph(freetypeFace, index, hbft.FT_LOAD_RENDER));
|
||||||
const glyph = freetypeFace.glyph();
|
const glyph = freetypeFace.*.glyph;
|
||||||
const bitmap = glyph.bitmap();
|
const bitmap = glyph.*.bitmap;
|
||||||
const width = bitmap.width();
|
const width = bitmap.width;
|
||||||
const height = bitmap.rows();
|
const height = bitmap.rows;
|
||||||
glyphMapping.items[index] = @intCast(glyphData.items.len);
|
glyphMapping.items[index] = @intCast(glyphData.items.len);
|
||||||
(try glyphData.addOne()).* = Glyph {
|
(try glyphData.addOne()).* = Glyph {
|
||||||
.textureX = textureOffset,
|
.textureX = textureOffset,
|
||||||
.size = Vec2i{@intCast(width), @intCast(height)},
|
.size = Vec2i{@intCast(width), @intCast(height)},
|
||||||
.bearing = Vec2i{glyph.bitmapLeft(), 16 - glyph.bitmapTop()},
|
.bearing = Vec2i{glyph.*.bitmap_left, 16 - glyph.*.bitmap_top},
|
||||||
.advance = @as(f32, @floatFromInt(glyph.advance().x))/@as(f32, 1 << 6),
|
.advance = @as(f32, @floatFromInt(glyph.*.advance.x))/@as(f32, 1 << 6),
|
||||||
};
|
};
|
||||||
try uploadData(bitmap);
|
try uploadData(bitmap);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user