From b77513de1a6bcfd9d23fc35bcfb5cb86e8cabaaf Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 5 Apr 2024 21:48:53 -0700 Subject: [PATCH] font/harfbuzz: work with new font structures --- src/font/Collection.zig | 2 + src/font/shaper/harfbuzz.zig | 118 +++++++++++++++++------------------ 2 files changed, 60 insertions(+), 60 deletions(-) diff --git a/src/font/Collection.zig b/src/font/Collection.zig index 89d4aee4c..38f4b92f8 100644 --- a/src/font/Collection.zig +++ b/src/font/Collection.zig @@ -554,6 +554,8 @@ test getIndex { } test autoItalicize { + if (comptime !@hasDecl(Face, "italicize")) return error.SkipZigTest; + const testing = std.testing; const alloc = testing.allocator; const testFont = @import("test.zig").fontRegular; diff --git a/src/font/shaper/harfbuzz.zig b/src/font/shaper/harfbuzz.zig index 04143c090..13988ecf3 100644 --- a/src/font/shaper/harfbuzz.zig +++ b/src/font/shaper/harfbuzz.zig @@ -4,10 +4,10 @@ const Allocator = std.mem.Allocator; const harfbuzz = @import("harfbuzz"); const font = @import("../main.zig"); const Face = font.Face; +const Collection = font.Collection; const DeferredFace = font.DeferredFace; -const Group = font.Group; -const GroupCache = font.GroupCache; const Library = font.Library; +const SharedGrid = font.SharedGrid; const Style = font.Style; const Presentation = font.Presentation; const terminal = @import("../../terminal/main.zig"); @@ -83,7 +83,7 @@ pub const Shaper = struct { /// and assume the y value matches. pub fn runIterator( self: *Shaper, - group: *GroupCache, + grid: *SharedGrid, screen: *const terminal.Screen, row: terminal.Pin, selection: ?terminal.Selection, @@ -91,7 +91,7 @@ pub const Shaper = struct { ) font.shape.RunIterator { return .{ .hooks = .{ .shaper = self }, - .group = group, + .grid = grid, .screen = screen, .row = row, .selection = selection, @@ -110,7 +110,13 @@ pub const Shaper = struct { // We only do shaping if the font is not a special-case. For special-case // fonts, the codepoint == glyph_index so we don't need to run any shaping. if (run.font_index.special() == null) { - const face = try run.group.group.faceFromIndex(run.font_index); + // We have to lock the grid to get the face and unfortunately + // freetype faces (typically used with harfbuzz) are not thread + // safe so this has to be an exclusive lock. + run.grid.lock.lock(); + defer run.grid.lock.unlock(); + + const face = try run.grid.resolver.collection.getFace(run.font_index); const i = if (!face.quirks_disable_default_font_features) 0 else i: { // If we are disabling default font features we just offset // our features by the hardcoded items because always @@ -251,7 +257,7 @@ test "run iterator" { // Get our run iterator var shaper = &testdata.shaper; var it = shaper.runIterator( - testdata.cache, + testdata.grid, &screen, screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, null, @@ -270,7 +276,7 @@ test "run iterator" { var shaper = &testdata.shaper; var it = shaper.runIterator( - testdata.cache, + testdata.grid, &screen, screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, null, @@ -290,7 +296,7 @@ test "run iterator" { // Get our run iterator var shaper = &testdata.shaper; var it = shaper.runIterator( - testdata.cache, + testdata.grid, &screen, screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, null, @@ -342,7 +348,7 @@ test "run iterator: empty cells with background set" { // Get our run iterator var shaper = &testdata.shaper; var it = shaper.runIterator( - testdata.cache, + testdata.grid, &screen, screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, null, @@ -385,7 +391,7 @@ test "shape" { // Get our run iterator var shaper = &testdata.shaper; var it = shaper.runIterator( - testdata.cache, + testdata.grid, &screen, screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, null, @@ -414,7 +420,7 @@ test "shape inconsolata ligs" { var shaper = &testdata.shaper; var it = shaper.runIterator( - testdata.cache, + testdata.grid, &screen, screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, null, @@ -439,7 +445,7 @@ test "shape inconsolata ligs" { var shaper = &testdata.shaper; var it = shaper.runIterator( - testdata.cache, + testdata.grid, &screen, screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, null, @@ -473,7 +479,7 @@ test "shape monaspace ligs" { var shaper = &testdata.shaper; var it = shaper.runIterator( - testdata.cache, + testdata.grid, &screen, screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, null, @@ -507,7 +513,7 @@ test "shape emoji width" { var shaper = &testdata.shaper; var it = shaper.runIterator( - testdata.cache, + testdata.grid, &screen, screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, null, @@ -547,7 +553,7 @@ test "shape emoji width long" { // Get our run iterator var shaper = &testdata.shaper; var it = shaper.runIterator( - testdata.cache, + testdata.grid, &screen, screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, null, @@ -586,7 +592,7 @@ test "shape variation selector VS15" { // Get our run iterator var shaper = &testdata.shaper; var it = shaper.runIterator( - testdata.cache, + testdata.grid, &screen, screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, null, @@ -623,7 +629,7 @@ test "shape variation selector VS16" { // Get our run iterator var shaper = &testdata.shaper; var it = shaper.runIterator( - testdata.cache, + testdata.grid, &screen, screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, null, @@ -657,7 +663,7 @@ test "shape with empty cells in between" { // Get our run iterator var shaper = &testdata.shaper; var it = shaper.runIterator( - testdata.cache, + testdata.grid, &screen, screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, null, @@ -695,7 +701,7 @@ test "shape Chinese characters" { // Get our run iterator var shaper = &testdata.shaper; var it = shaper.runIterator( - testdata.cache, + testdata.grid, &screen, screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, null, @@ -722,13 +728,6 @@ test "shape box glyphs" { var testdata = try testShaper(alloc); defer testdata.deinit(); - // Setup the box font - testdata.cache.group.sprite = font.sprite.Face{ - .width = 18, - .height = 36, - .thickness = 2, - }; - var buf: [32]u8 = undefined; var buf_idx: usize = 0; buf_idx += try std.unicode.utf8Encode(0x2500, buf[buf_idx..]); // horiz line @@ -742,7 +741,7 @@ test "shape box glyphs" { // Get our run iterator var shaper = &testdata.shaper; var it = shaper.runIterator( - testdata.cache, + testdata.grid, &screen, screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, null, @@ -779,7 +778,7 @@ test "shape selection boundary" { // Get our run iterator var shaper = &testdata.shaper; var it = shaper.runIterator( - testdata.cache, + testdata.grid, &screen, screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, terminal.Selection.init( @@ -802,7 +801,7 @@ test "shape selection boundary" { // Get our run iterator var shaper = &testdata.shaper; var it = shaper.runIterator( - testdata.cache, + testdata.grid, &screen, screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, terminal.Selection.init( @@ -825,7 +824,7 @@ test "shape selection boundary" { // Get our run iterator var shaper = &testdata.shaper; var it = shaper.runIterator( - testdata.cache, + testdata.grid, &screen, screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, terminal.Selection.init( @@ -848,7 +847,7 @@ test "shape selection boundary" { // Get our run iterator var shaper = &testdata.shaper; var it = shaper.runIterator( - testdata.cache, + testdata.grid, &screen, screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, terminal.Selection.init( @@ -871,7 +870,7 @@ test "shape selection boundary" { // Get our run iterator var shaper = &testdata.shaper; var it = shaper.runIterator( - testdata.cache, + testdata.grid, &screen, screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, terminal.Selection.init( @@ -907,7 +906,7 @@ test "shape cursor boundary" { // Get our run iterator var shaper = &testdata.shaper; var it = shaper.runIterator( - testdata.cache, + testdata.grid, &screen, screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, null, @@ -926,7 +925,7 @@ test "shape cursor boundary" { // Get our run iterator var shaper = &testdata.shaper; var it = shaper.runIterator( - testdata.cache, + testdata.grid, &screen, screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, null, @@ -945,7 +944,7 @@ test "shape cursor boundary" { // Get our run iterator var shaper = &testdata.shaper; var it = shaper.runIterator( - testdata.cache, + testdata.grid, &screen, screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, null, @@ -964,7 +963,7 @@ test "shape cursor boundary" { // Get our run iterator var shaper = &testdata.shaper; var it = shaper.runIterator( - testdata.cache, + testdata.grid, &screen, screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, null, @@ -996,7 +995,7 @@ test "shape cursor boundary and colored emoji" { // Get our run iterator var shaper = &testdata.shaper; var it = shaper.runIterator( - testdata.cache, + testdata.grid, &screen, screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, null, @@ -1015,7 +1014,7 @@ test "shape cursor boundary and colored emoji" { // Get our run iterator var shaper = &testdata.shaper; var it = shaper.runIterator( - testdata.cache, + testdata.grid, &screen, screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, null, @@ -1032,7 +1031,7 @@ test "shape cursor boundary and colored emoji" { // Get our run iterator var shaper = &testdata.shaper; var it = shaper.runIterator( - testdata.cache, + testdata.grid, &screen, screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, null, @@ -1062,7 +1061,7 @@ test "shape cell attribute change" { var shaper = &testdata.shaper; var it = shaper.runIterator( - testdata.cache, + testdata.grid, &screen, screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, null, @@ -1086,7 +1085,7 @@ test "shape cell attribute change" { var shaper = &testdata.shaper; var it = shaper.runIterator( - testdata.cache, + testdata.grid, &screen, screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, null, @@ -1111,7 +1110,7 @@ test "shape cell attribute change" { var shaper = &testdata.shaper; var it = shaper.runIterator( - testdata.cache, + testdata.grid, &screen, screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, null, @@ -1136,7 +1135,7 @@ test "shape cell attribute change" { var shaper = &testdata.shaper; var it = shaper.runIterator( - testdata.cache, + testdata.grid, &screen, screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, null, @@ -1160,7 +1159,7 @@ test "shape cell attribute change" { var shaper = &testdata.shaper; var it = shaper.runIterator( - testdata.cache, + testdata.grid, &screen, screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, null, @@ -1178,13 +1177,13 @@ test "shape cell attribute change" { const TestShaper = struct { alloc: Allocator, shaper: Shaper, - cache: *GroupCache, + grid: *SharedGrid, lib: Library, pub fn deinit(self: *TestShaper) void { self.shaper.deinit(); - self.cache.deinit(self.alloc); - self.alloc.destroy(self.cache); + self.grid.deinit(self.alloc); + self.alloc.destroy(self.grid); self.lib.deinit(); } }; @@ -1210,17 +1209,11 @@ fn testShaperWithFont(alloc: Allocator, font_req: TestFont) !TestShaper { var lib = try Library.init(); errdefer lib.deinit(); - var cache_ptr = try alloc.create(GroupCache); - errdefer alloc.destroy(cache_ptr); - cache_ptr.* = try GroupCache.init(alloc, try Group.init( - alloc, - lib, - .{ .points = 12 }, - )); - errdefer cache_ptr.*.deinit(alloc); + var c = try Collection.init(alloc); + c.load_options = .{ .library = lib }; // Setup group - _ = try cache_ptr.group.addFace(.regular, .{ .loaded = try Face.init( + _ = try c.add(alloc, .regular, .{ .loaded = try Face.init( lib, testFont, .{ .size = .{ .points = 12 } }, @@ -1228,7 +1221,7 @@ fn testShaperWithFont(alloc: Allocator, font_req: TestFont) !TestShaper { if (font.options.backend != .coretext) { // Coretext doesn't support Noto's format - _ = try cache_ptr.group.addFace(.regular, .{ .loaded = try Face.init( + _ = try c.add(alloc, .regular, .{ .loaded = try Face.init( lib, testEmoji, .{ .size = .{ .points = 12 } }, @@ -1245,21 +1238,26 @@ fn testShaperWithFont(alloc: Allocator, font_req: TestFont) !TestShaper { defer disco_it.deinit(); var face = (try disco_it.next()).?; errdefer face.deinit(); - _ = try cache_ptr.group.addFace(.regular, .{ .deferred = face }); + _ = try c.add(alloc, .regular, .{ .deferred = face }); } - _ = try cache_ptr.group.addFace(.regular, .{ .loaded = try Face.init( + _ = try c.add(alloc, .regular, .{ .loaded = try Face.init( lib, testEmojiText, .{ .size = .{ .points = 12 } }, ) }); + const grid_ptr = try alloc.create(SharedGrid); + errdefer alloc.destroy(grid_ptr); + grid_ptr.* = try SharedGrid.init(alloc, .{ .collection = c }, false); + errdefer grid_ptr.*.deinit(alloc); + var shaper = try Shaper.init(alloc, .{}); errdefer shaper.deinit(); return TestShaper{ .alloc = alloc, .shaper = shaper, - .cache = cache_ptr, + .grid = grid_ptr, .lib = lib, }; }