mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-08-02 14:57:31 +03:00
font: DeferredFace is wasm-compatible
This commit is contained in:
@ -28,6 +28,10 @@ fetch(url.href).then(response =>
|
|||||||
face_free,
|
face_free,
|
||||||
face_render_glyph,
|
face_render_glyph,
|
||||||
face_debug_canvas,
|
face_debug_canvas,
|
||||||
|
deferred_face_new,
|
||||||
|
deferred_face_free,
|
||||||
|
deferred_face_load,
|
||||||
|
deferred_face_face,
|
||||||
atlas_new,
|
atlas_new,
|
||||||
atlas_free,
|
atlas_free,
|
||||||
atlas_debug_canvas,
|
atlas_debug_canvas,
|
||||||
@ -47,8 +51,13 @@ fetch(url.href).then(response =>
|
|||||||
const font_ptr = malloc(font.byteLength);
|
const font_ptr = malloc(font.byteLength);
|
||||||
new Uint8Array(memory.buffer, font_ptr).set(font);
|
new Uint8Array(memory.buffer, font_ptr).set(font);
|
||||||
|
|
||||||
|
// Initialize our deferred face
|
||||||
|
const df = deferred_face_new(font_ptr, font.byteLength);
|
||||||
|
deferred_face_load(df, 72 /* size */);
|
||||||
|
const face = deferred_face_face(df);
|
||||||
|
|
||||||
// Initialize our font face
|
// Initialize our font face
|
||||||
const face = face_new(font_ptr, font.byteLength, 72 /* size in px */);
|
//const face = face_new(font_ptr, font.byteLength, 72 /* size in px */);
|
||||||
free(font_ptr);
|
free(font_ptr);
|
||||||
|
|
||||||
// Render a glyph
|
// Render a glyph
|
||||||
|
@ -8,6 +8,7 @@ const DeferredFace = @This();
|
|||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const assert = std.debug.assert;
|
const assert = std.debug.assert;
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
const fontconfig = @import("fontconfig");
|
const fontconfig = @import("fontconfig");
|
||||||
const macos = @import("macos");
|
const macos = @import("macos");
|
||||||
const font = @import("main.zig");
|
const font = @import("main.zig");
|
||||||
@ -16,6 +17,8 @@ const Library = @import("main.zig").Library;
|
|||||||
const Face = @import("main.zig").Face;
|
const Face = @import("main.zig").Face;
|
||||||
const Presentation = @import("main.zig").Presentation;
|
const Presentation = @import("main.zig").Presentation;
|
||||||
|
|
||||||
|
const log = std.log.scoped(.deferred_face);
|
||||||
|
|
||||||
/// The loaded face (once loaded).
|
/// The loaded face (once loaded).
|
||||||
face: ?Face = null,
|
face: ?Face = null,
|
||||||
|
|
||||||
@ -27,6 +30,10 @@ fc: if (options.backend == .fontconfig_freetype) ?Fontconfig else void =
|
|||||||
ct: if (font.Discover == font.discovery.CoreText) ?CoreText else void =
|
ct: if (font.Discover == font.discovery.CoreText) ?CoreText else void =
|
||||||
if (font.Discover == font.discovery.CoreText) null else {},
|
if (font.Discover == font.discovery.CoreText) null else {},
|
||||||
|
|
||||||
|
/// Canvas
|
||||||
|
wc: if (options.backend == .web_canvas) ?WebCanvas else void =
|
||||||
|
if (options.backend == .web_canvas) null else {},
|
||||||
|
|
||||||
/// Fontconfig specific data. This is only present if building with fontconfig.
|
/// Fontconfig specific data. This is only present if building with fontconfig.
|
||||||
pub const Fontconfig = struct {
|
pub const Fontconfig = struct {
|
||||||
/// The pattern for this font. This must be the "render prepared" pattern.
|
/// The pattern for this font. This must be the "render prepared" pattern.
|
||||||
@ -56,6 +63,20 @@ pub const CoreText = struct {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// WebCanvas specific data. This is only present when building with canvas.
|
||||||
|
pub const WebCanvas = struct {
|
||||||
|
/// The allocator to use for fonts
|
||||||
|
alloc: Allocator,
|
||||||
|
|
||||||
|
/// The string to use for the "font" attribute for the canvas
|
||||||
|
font_str: [:0]const u8,
|
||||||
|
|
||||||
|
pub fn deinit(self: *WebCanvas) void {
|
||||||
|
self.alloc.free(self.font_str);
|
||||||
|
self.* = undefined;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/// Initialize a deferred face that is already pre-loaded. The deferred face
|
/// Initialize a deferred face that is already pre-loaded. The deferred face
|
||||||
/// takes ownership over the loaded face, deinit will deinit the loaded face.
|
/// takes ownership over the loaded face, deinit will deinit the loaded face.
|
||||||
pub fn initLoaded(face: Face) DeferredFace {
|
pub fn initLoaded(face: Face) DeferredFace {
|
||||||
@ -68,8 +89,7 @@ pub fn deinit(self: *DeferredFace) void {
|
|||||||
.fontconfig_freetype => if (self.fc) |*fc| fc.deinit(),
|
.fontconfig_freetype => if (self.fc) |*fc| fc.deinit(),
|
||||||
.coretext, .coretext_freetype => if (self.ct) |*ct| ct.deinit(),
|
.coretext, .coretext_freetype => if (self.ct) |*ct| ct.deinit(),
|
||||||
.freetype => {},
|
.freetype => {},
|
||||||
// TODO
|
.web_canvas => if (self.wc) |*wc| wc.deinit(),
|
||||||
.web_canvas => unreachable,
|
|
||||||
}
|
}
|
||||||
self.* = undefined;
|
self.* = undefined;
|
||||||
}
|
}
|
||||||
@ -83,6 +103,8 @@ pub inline fn loaded(self: DeferredFace) bool {
|
|||||||
/// face so it doesn't have to be freed.
|
/// face so it doesn't have to be freed.
|
||||||
pub fn name(self: DeferredFace) ![:0]const u8 {
|
pub fn name(self: DeferredFace) ![:0]const u8 {
|
||||||
switch (options.backend) {
|
switch (options.backend) {
|
||||||
|
.freetype => {},
|
||||||
|
|
||||||
.fontconfig_freetype => if (self.fc) |fc|
|
.fontconfig_freetype => if (self.fc) |fc|
|
||||||
return (try fc.pattern.get(.fullname, 0)).string,
|
return (try fc.pattern.get(.fullname, 0)).string,
|
||||||
|
|
||||||
@ -91,10 +113,7 @@ pub fn name(self: DeferredFace) ![:0]const u8 {
|
|||||||
return display_name.cstringPtr(.utf8) orelse "<unsupported internal encoding>";
|
return display_name.cstringPtr(.utf8) orelse "<unsupported internal encoding>";
|
||||||
},
|
},
|
||||||
|
|
||||||
.freetype => {},
|
.web_canvas => if (self.wc) |wc| return wc.font_str,
|
||||||
|
|
||||||
// TODO
|
|
||||||
.web_canvas => unreachable,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return "TODO: built-in font names";
|
return "TODO: built-in font names";
|
||||||
@ -125,8 +144,10 @@ pub fn load(
|
|||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
|
|
||||||
// TODO
|
.web_canvas => {
|
||||||
.web_canvas => unreachable,
|
try self.loadWebCanvas(size);
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
|
||||||
// Unreachable because we must be already loaded or have the
|
// Unreachable because we must be already loaded or have the
|
||||||
// proper configuration for one of the other deferred mechanisms.
|
// proper configuration for one of the other deferred mechanisms.
|
||||||
@ -200,6 +221,15 @@ fn loadCoreTextFreetype(
|
|||||||
self.face = try Face.initFile(lib, buf[0..path_slice.len :0], 0, size);
|
self.face = try Face.initFile(lib, buf[0..path_slice.len :0], 0, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn loadWebCanvas(
|
||||||
|
self: *DeferredFace,
|
||||||
|
size: font.face.DesiredSize,
|
||||||
|
) !void {
|
||||||
|
assert(self.face == null);
|
||||||
|
const wc = self.wc.?;
|
||||||
|
self.face = try Face.initNamed(wc.alloc, wc.font_str, size);
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns true if this face can satisfy the given codepoint and
|
/// Returns true if this face can satisfy the given codepoint and
|
||||||
/// presentation. If presentation is null, then it just checks if the
|
/// presentation. If presentation is null, then it just checks if the
|
||||||
/// codepoint is present at all.
|
/// codepoint is present at all.
|
||||||
@ -251,8 +281,9 @@ pub fn hasCodepoint(self: DeferredFace, cp: u32, p: ?Presentation) bool {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// TODO
|
// Canvas always has the codepoint because we have no way of
|
||||||
.web_canvas => unreachable,
|
// really checking and we let the browser handle it.
|
||||||
|
.web_canvas => return true,
|
||||||
|
|
||||||
.freetype => {},
|
.freetype => {},
|
||||||
}
|
}
|
||||||
@ -262,6 +293,57 @@ pub fn hasCodepoint(self: DeferredFace, cp: u32, p: ?Presentation) bool {
|
|||||||
unreachable;
|
unreachable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The wasm-compatible API.
|
||||||
|
pub const Wasm = struct {
|
||||||
|
const wasm = @import("../os/wasm.zig");
|
||||||
|
const alloc = wasm.alloc;
|
||||||
|
|
||||||
|
export fn deferred_face_new(ptr: [*]const u8, len: usize) ?*DeferredFace {
|
||||||
|
return deferred_face_new_(ptr, len) catch |err| {
|
||||||
|
log.warn("error creating deferred face err={}", .{err});
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deferred_face_new_(ptr: [*]const u8, len: usize) !*DeferredFace {
|
||||||
|
var font_str = try alloc.dupeZ(u8, ptr[0..len]);
|
||||||
|
errdefer alloc.free(font_str);
|
||||||
|
|
||||||
|
var face: DeferredFace = .{
|
||||||
|
.wc = .{
|
||||||
|
.alloc = alloc,
|
||||||
|
.font_str = font_str,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
errdefer face.deinit();
|
||||||
|
|
||||||
|
var result = try alloc.create(DeferredFace);
|
||||||
|
errdefer alloc.destroy(result);
|
||||||
|
result.* = face;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
export fn deferred_face_free(ptr: ?*DeferredFace) void {
|
||||||
|
if (ptr) |v| {
|
||||||
|
v.deinit();
|
||||||
|
alloc.destroy(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export fn deferred_face_load(self: *DeferredFace, pts: u16) void {
|
||||||
|
self.load(.{}, .{ .points = pts }) catch |err| {
|
||||||
|
log.warn("error loading deferred face err={}", .{err});
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Caller should not free this, the face is owned by the deferred face.
|
||||||
|
export fn deferred_face_face(self: *DeferredFace) ?*Face {
|
||||||
|
assert(self.loaded());
|
||||||
|
return &self.face.?;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
test "preloaded" {
|
test "preloaded" {
|
||||||
const testing = std.testing;
|
const testing = std.testing;
|
||||||
const testFont = @import("test.zig").fontRegular;
|
const testFont = @import("test.zig").fontRegular;
|
||||||
|
@ -20,6 +20,7 @@ pub usingnamespace @import("library.zig");
|
|||||||
/// If we're targeting wasm then we export some wasm APIs.
|
/// If we're targeting wasm then we export some wasm APIs.
|
||||||
pub usingnamespace if (builtin.target.isWasm()) struct {
|
pub usingnamespace if (builtin.target.isWasm()) struct {
|
||||||
pub usingnamespace Atlas.Wasm;
|
pub usingnamespace Atlas.Wasm;
|
||||||
|
pub usingnamespace DeferredFace.Wasm;
|
||||||
pub usingnamespace Group.Wasm;
|
pub usingnamespace Group.Wasm;
|
||||||
pub usingnamespace face.web_canvas.Wasm;
|
pub usingnamespace face.web_canvas.Wasm;
|
||||||
} else struct {};
|
} else struct {};
|
||||||
|
Reference in New Issue
Block a user