mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-08-02 14:57:31 +03:00
font: web canvas shaper yields runs
This commit is contained in:
@ -46,6 +46,9 @@ fetch(url.href).then(response =>
|
|||||||
atlas_new,
|
atlas_new,
|
||||||
atlas_free,
|
atlas_free,
|
||||||
atlas_debug_canvas,
|
atlas_debug_canvas,
|
||||||
|
shaper_new,
|
||||||
|
shaper_free,
|
||||||
|
shaper_test,
|
||||||
} = results.instance.exports;
|
} = results.instance.exports;
|
||||||
// Give us access to the zjs value for debugging.
|
// Give us access to the zjs value for debugging.
|
||||||
globalThis.zjs = zjs;
|
globalThis.zjs = zjs;
|
||||||
@ -54,13 +57,19 @@ fetch(url.href).then(response =>
|
|||||||
// Initialize our zig-js memory
|
// Initialize our zig-js memory
|
||||||
zjs.memory = memory;
|
zjs.memory = memory;
|
||||||
|
|
||||||
|
// Helpers
|
||||||
|
const makeStr = (str) => {
|
||||||
|
const utf8 = new TextEncoder().encode(str);
|
||||||
|
const ptr = malloc(utf8.byteLength);
|
||||||
|
new Uint8Array(memory.buffer, ptr).set(utf8);
|
||||||
|
return { ptr: ptr, len: utf8.byteLength };
|
||||||
|
};
|
||||||
|
|
||||||
// Create our atlas
|
// Create our atlas
|
||||||
// const atlas = atlas_new(512, 0 /* greyscale */);
|
// const atlas = atlas_new(512, 0 /* greyscale */);
|
||||||
|
|
||||||
// Create some memory for our string
|
// Create some memory for our string
|
||||||
const font = new TextEncoder().encode("monospace");
|
const font_name = makeStr("monospace");
|
||||||
const font_ptr = malloc(font.byteLength);
|
|
||||||
new Uint8Array(memory.buffer, font_ptr).set(font);
|
|
||||||
|
|
||||||
// Initialize our deferred face
|
// Initialize our deferred face
|
||||||
// const df = deferred_face_new(font_ptr, font.byteLength, 0 /* text */);
|
// const df = deferred_face_new(font_ptr, font.byteLength, 0 /* text */);
|
||||||
@ -73,8 +82,8 @@ fetch(url.href).then(response =>
|
|||||||
|
|
||||||
// Create our group
|
// Create our group
|
||||||
const group = group_new(72 /* size */);
|
const group = group_new(72 /* size */);
|
||||||
group_add_face(group, 0 /* regular */, deferred_face_new(font_ptr, font.byteLength, 0 /* text */));
|
group_add_face(group, 0 /* regular */, deferred_face_new(font_name.ptr, font_name.len, 0 /* text */));
|
||||||
group_add_face(group, 0 /* regular */, deferred_face_new(font_ptr, font.byteLength, 1 /* emoji */));
|
group_add_face(group, 0 /* regular */, deferred_face_new(font_name.ptr, font_name.len, 1 /* emoji */));
|
||||||
|
|
||||||
// Create our group cache
|
// Create our group cache
|
||||||
const group_cache = group_cache_new(group);
|
const group_cache = group_cache_new(group);
|
||||||
@ -112,5 +121,10 @@ fetch(url.href).then(response =>
|
|||||||
document.getElementById("atlas-color-canvas").append(zjs.deleteValue(id));
|
document.getElementById("atlas-color-canvas").append(zjs.deleteValue(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Let's try shaping
|
||||||
|
const shaper = shaper_new(120);
|
||||||
|
const input = makeStr("hello");
|
||||||
|
shaper_test(shaper, group_cache, input.ptr, input.len);
|
||||||
|
|
||||||
//face_free(face);
|
//face_free(face);
|
||||||
});
|
});
|
||||||
|
@ -40,6 +40,19 @@ pub const Shaper = struct {
|
|||||||
return .{ .hooks = .{ .shaper = self }, .group = group, .row = row };
|
return .{ .hooks = .{ .shaper = self }, .group = group, .row = row };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Shape the given text run. The text run must be the immediately previous
|
||||||
|
/// text run that was iterated since the text run does share state with the
|
||||||
|
/// Shaper struct.
|
||||||
|
///
|
||||||
|
/// The return value is only valid until the next shape call is called.
|
||||||
|
///
|
||||||
|
/// If there is not enough space in the cell buffer, an error is returned.
|
||||||
|
pub fn shape(self: *Shaper, run: font.shape.TextRun) ![]font.shape.Cell {
|
||||||
|
_ = self;
|
||||||
|
_ = run;
|
||||||
|
return error.Unimplemented;
|
||||||
|
}
|
||||||
|
|
||||||
/// The hooks for RunIterator.
|
/// The hooks for RunIterator.
|
||||||
pub const RunIteratorHook = struct {
|
pub const RunIteratorHook = struct {
|
||||||
shaper: *Shaper,
|
shaper: *Shaper,
|
||||||
@ -51,7 +64,7 @@ pub const Shaper = struct {
|
|||||||
|
|
||||||
pub fn addCodepoint(self: RunIteratorHook, cp: u32, cluster: u32) !void {
|
pub fn addCodepoint(self: RunIteratorHook, cp: u32, cluster: u32) !void {
|
||||||
_ = cluster;
|
_ = cluster;
|
||||||
try self.shaper.append(cp);
|
try self.shaper.run_buf.append(cp);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn finalize(self: RunIteratorHook) !void {
|
pub fn finalize(self: RunIteratorHook) !void {
|
||||||
@ -64,4 +77,60 @@ pub const Shaper = struct {
|
|||||||
pub const Wasm = struct {
|
pub const Wasm = struct {
|
||||||
const wasm = @import("../../os/wasm.zig");
|
const wasm = @import("../../os/wasm.zig");
|
||||||
const alloc = wasm.alloc;
|
const alloc = wasm.alloc;
|
||||||
|
|
||||||
|
export fn shaper_new(cap: usize) ?*Shaper {
|
||||||
|
return shaper_new_(cap) catch null;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn shaper_new_(cap: usize) !*Shaper {
|
||||||
|
var cell_buf = try alloc.alloc(font.shape.Cell, cap);
|
||||||
|
errdefer alloc.free(cell_buf);
|
||||||
|
|
||||||
|
var shaper = try Shaper.init(alloc, cell_buf);
|
||||||
|
errdefer shaper.deinit();
|
||||||
|
|
||||||
|
var result = try alloc.create(Shaper);
|
||||||
|
errdefer alloc.destroy(result);
|
||||||
|
result.* = shaper;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
export fn shaper_free(ptr: ?*Shaper) void {
|
||||||
|
if (ptr) |v| {
|
||||||
|
alloc.free(v.cell_buf);
|
||||||
|
v.deinit();
|
||||||
|
alloc.destroy(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Runs a test to verify shaping works properly.
|
||||||
|
export fn shaper_test(
|
||||||
|
self: *Shaper,
|
||||||
|
group: *font.GroupCache,
|
||||||
|
str: [*]const u8,
|
||||||
|
len: usize,
|
||||||
|
) void {
|
||||||
|
shaper_test_(self, group, str[0..len]) catch |err| {
|
||||||
|
log.warn("error during shaper test err={}", .{err});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn shaper_test_(self: *Shaper, group: *font.GroupCache, str: []const u8) !void {
|
||||||
|
// Create a terminal and print all our characters into it.
|
||||||
|
var term = try terminal.Terminal.init(alloc, self.cell_buf.len, 80);
|
||||||
|
defer term.deinit(alloc);
|
||||||
|
for (str) |c| try term.print(c);
|
||||||
|
|
||||||
|
// Iterate over the rows and print out all the runs we get.
|
||||||
|
var rowIter = term.screen.rowIterator(.viewport);
|
||||||
|
var y: usize = 0;
|
||||||
|
while (rowIter.next()) |row| {
|
||||||
|
defer y += 1;
|
||||||
|
|
||||||
|
var iter = self.runIterator(group, row);
|
||||||
|
while (try iter.next(alloc)) |run| {
|
||||||
|
log.info("y={} run={}", .{ y, run });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user