diff --git a/src/font/shaper/harfbuzz.zig b/src/font/shaper/harfbuzz.zig index 9953a12bb..6ec6d1892 100644 --- a/src/font/shaper/harfbuzz.zig +++ b/src/font/shaper/harfbuzz.zig @@ -26,7 +26,10 @@ pub const Shaper = struct { /// The cell_buf argument is the buffer to use for storing shaped results. /// This should be at least the number of columns in the terminal. - pub fn init(cell_buf: []font.shape.Cell) !Shaper { + pub fn init(alloc: Allocator, cell_buf: []font.shape.Cell) !Shaper { + // Allocator is not used because harfbuzz uses libc + _ = alloc; + return Shaper{ .hb_buf = try harfbuzz.Buffer.create(), .cell_buf = cell_buf, @@ -547,7 +550,7 @@ fn testShaper(alloc: Allocator) !TestShaper { var cell_buf = try alloc.alloc(font.shape.Cell, 80); errdefer alloc.free(cell_buf); - var shaper = try Shaper.init(cell_buf); + var shaper = try Shaper.init(alloc, cell_buf); errdefer shaper.deinit(); return TestShaper{ diff --git a/src/font/shaper/web_canvas.zig b/src/font/shaper/web_canvas.zig index 252ae79d5..ab6af2da7 100644 --- a/src/font/shaper/web_canvas.zig +++ b/src/font/shaper/web_canvas.zig @@ -2,24 +2,62 @@ const std = @import("std"); const assert = std.debug.assert; const Allocator = std.mem.Allocator; const font = @import("../main.zig"); +const terminal = @import("../../terminal/main.zig"); const log = std.log.scoped(.font_shaper); pub const Shaper = struct { + const RunBuf = std.ArrayList(u32); + /// The shared memory used for shaping results. cell_buf: []font.shape.Cell, + /// The shared memory used for storing information about a run. + run_buf: RunBuf, + /// The cell_buf argument is the buffer to use for storing shaped results. /// This should be at least the number of columns in the terminal. - pub fn init(cell_buf: []font.shape.Cell) !Shaper { + pub fn init(alloc: Allocator, cell_buf: []font.shape.Cell) !Shaper { return Shaper{ .cell_buf = cell_buf, + .run_buf = try RunBuf.initCapacity(alloc, cell_buf.len), }; } pub fn deinit(self: *Shaper) void { - _ = self; + self.run_buf.deinit(); + self.* = undefined; } + + /// Returns an iterator that returns one text run at a time for the + /// given terminal row. Note that text runs are are only valid one at a time + /// for a Shaper struct since they share state. + pub fn runIterator( + self: *Shaper, + group: *font.GroupCache, + row: terminal.Screen.Row, + ) font.shape.RunIterator { + return .{ .hooks = .{ .shaper = self }, .group = group, .row = row }; + } + + /// The hooks for RunIterator. + pub const RunIteratorHook = struct { + shaper: *Shaper, + + pub fn prepare(self: RunIteratorHook) !void { + // Reset the buffer for our current run + self.shaper.run_buf.clearRetainingCapacity(); + } + + pub fn addCodepoint(self: RunIteratorHook, cp: u32, cluster: u32) !void { + _ = cluster; + try self.shaper.append(cp); + } + + pub fn finalize(self: RunIteratorHook) !void { + _ = self; + } + }; }; /// The wasm-compatible API. diff --git a/src/renderer/Metal.zig b/src/renderer/Metal.zig index 6c5bb601f..6fe468a84 100644 --- a/src/renderer/Metal.zig +++ b/src/renderer/Metal.zig @@ -180,7 +180,7 @@ pub fn init(alloc: Allocator, options: renderer.Options) !Metal { // avoid allocations later. var shape_buf = try alloc.alloc(font.shape.Cell, 160); errdefer alloc.free(shape_buf); - var font_shaper = try font.Shaper.init(shape_buf); + var font_shaper = try font.Shaper.init(alloc, shape_buf); errdefer font_shaper.deinit(); // Initialize our Metal buffers diff --git a/src/renderer/OpenGL.zig b/src/renderer/OpenGL.zig index 84ac20dd5..a179d2c5b 100644 --- a/src/renderer/OpenGL.zig +++ b/src/renderer/OpenGL.zig @@ -162,7 +162,7 @@ pub fn init(alloc: Allocator, options: renderer.Options) !OpenGL { // Create the initial font shaper var shape_buf = try alloc.alloc(font.shape.Cell, 1); errdefer alloc.free(shape_buf); - var shaper = try font.Shaper.init(shape_buf); + var shaper = try font.Shaper.init(alloc, shape_buf); errdefer shaper.deinit(); // Create our shader