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_render_glyph,
|
||||
face_debug_canvas,
|
||||
deferred_face_new,
|
||||
deferred_face_free,
|
||||
deferred_face_load,
|
||||
deferred_face_face,
|
||||
atlas_new,
|
||||
atlas_free,
|
||||
atlas_debug_canvas,
|
||||
@ -47,8 +51,13 @@ fetch(url.href).then(response =>
|
||||
const font_ptr = malloc(font.byteLength);
|
||||
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
|
||||
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);
|
||||
|
||||
// Render a glyph
|
||||
|
@ -8,6 +8,7 @@ const DeferredFace = @This();
|
||||
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const fontconfig = @import("fontconfig");
|
||||
const macos = @import("macos");
|
||||
const font = @import("main.zig");
|
||||
@ -16,6 +17,8 @@ const Library = @import("main.zig").Library;
|
||||
const Face = @import("main.zig").Face;
|
||||
const Presentation = @import("main.zig").Presentation;
|
||||
|
||||
const log = std.log.scoped(.deferred_face);
|
||||
|
||||
/// The loaded face (once loaded).
|
||||
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 =
|
||||
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.
|
||||
pub const Fontconfig = struct {
|
||||
/// 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
|
||||
/// takes ownership over the loaded face, deinit will deinit the loaded face.
|
||||
pub fn initLoaded(face: Face) DeferredFace {
|
||||
@ -68,8 +89,7 @@ pub fn deinit(self: *DeferredFace) void {
|
||||
.fontconfig_freetype => if (self.fc) |*fc| fc.deinit(),
|
||||
.coretext, .coretext_freetype => if (self.ct) |*ct| ct.deinit(),
|
||||
.freetype => {},
|
||||
// TODO
|
||||
.web_canvas => unreachable,
|
||||
.web_canvas => if (self.wc) |*wc| wc.deinit(),
|
||||
}
|
||||
self.* = undefined;
|
||||
}
|
||||
@ -83,6 +103,8 @@ pub inline fn loaded(self: DeferredFace) bool {
|
||||
/// face so it doesn't have to be freed.
|
||||
pub fn name(self: DeferredFace) ![:0]const u8 {
|
||||
switch (options.backend) {
|
||||
.freetype => {},
|
||||
|
||||
.fontconfig_freetype => if (self.fc) |fc|
|
||||
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>";
|
||||
},
|
||||
|
||||
.freetype => {},
|
||||
|
||||
// TODO
|
||||
.web_canvas => unreachable,
|
||||
.web_canvas => if (self.wc) |wc| return wc.font_str,
|
||||
}
|
||||
|
||||
return "TODO: built-in font names";
|
||||
@ -125,8 +144,10 @@ pub fn load(
|
||||
return;
|
||||
},
|
||||
|
||||
// TODO
|
||||
.web_canvas => unreachable,
|
||||
.web_canvas => {
|
||||
try self.loadWebCanvas(size);
|
||||
return;
|
||||
},
|
||||
|
||||
// Unreachable because we must be already loaded or have the
|
||||
// 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);
|
||||
}
|
||||
|
||||
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
|
||||
/// presentation. If presentation is null, then it just checks if the
|
||||
/// codepoint is present at all.
|
||||
@ -251,8 +281,9 @@ pub fn hasCodepoint(self: DeferredFace, cp: u32, p: ?Presentation) bool {
|
||||
}
|
||||
},
|
||||
|
||||
// TODO
|
||||
.web_canvas => unreachable,
|
||||
// Canvas always has the codepoint because we have no way of
|
||||
// really checking and we let the browser handle it.
|
||||
.web_canvas => return true,
|
||||
|
||||
.freetype => {},
|
||||
}
|
||||
@ -262,6 +293,57 @@ pub fn hasCodepoint(self: DeferredFace, cp: u32, p: ?Presentation) bool {
|
||||
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" {
|
||||
const testing = std.testing;
|
||||
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.
|
||||
pub usingnamespace if (builtin.target.isWasm()) struct {
|
||||
pub usingnamespace Atlas.Wasm;
|
||||
pub usingnamespace DeferredFace.Wasm;
|
||||
pub usingnamespace Group.Wasm;
|
||||
pub usingnamespace face.web_canvas.Wasm;
|
||||
} else struct {};
|
||||
|
Reference in New Issue
Block a user