font: DeferredFace is wasm-compatible

This commit is contained in:
Mitchell Hashimoto
2022-12-05 20:28:50 -08:00
parent e71dc470fd
commit 5291501251
3 changed files with 103 additions and 11 deletions

View File

@ -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

View File

@ -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;

View File

@ -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 {};