From f447e6f9df87fd7942630261151935b8dedcad65 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sat, 6 Jan 2024 18:57:42 -0800 Subject: [PATCH] font: insert blank cells for multi-cell ligatures for styling Up to this point, every font I've experienced with ligatures has replaced the codepoints that were replaced for combining with a space. For example, if a font has a ligature for "!=" to turn it into a glyph, it'd shape to `[not equal glyph, space]`, so it'd still take up two cells, allowing us to style both. Monaspace, however, does not do this. It turns "!=" into `[not equal glyph]` so styles like backgrounds, underlines, etc. were not extending. This commit detects multi-cell glyphs and inserts synthetic blank cells so that styling returns. I decided to do this via synthetic blank cells instead of introducing a `cell_width` to the shaper result because this simplifies the renderers to assume each shaper cell is one cell. We can change this later if we need to. Annoyingly, this does make the shaper slightly slower for EVERYONE to accomodate one known font that behaves this way. I haven't benchmarked it but my belief is that the performance impact will be negligible because to figure out cell width we're only accessing subsequent cells so they're likely to be in the CPU cache and also 99% of cells are going to be width 1. --- src/font/res/MonaspaceNeon-Regular.otf | Bin 0 -> 73232 bytes src/font/shape.zig | 5 +- src/font/shaper/coretext.zig | 4 + src/font/shaper/harfbuzz.zig | 104 ++++++++++++++++++++++--- src/font/test.zig | 4 + src/renderer/Metal.zig | 4 +- src/renderer/OpenGL.zig | 4 +- 7 files changed, 110 insertions(+), 15 deletions(-) create mode 100644 src/font/res/MonaspaceNeon-Regular.otf diff --git a/src/font/res/MonaspaceNeon-Regular.otf b/src/font/res/MonaspaceNeon-Regular.otf new file mode 100644 index 0000000000000000000000000000000000000000..75aeb627f06eb3359fd646e972a9578b295d6e24 GIT binary patch literal 73232 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 1) { + // To make the renderer implementations simpler, we convert + // the extra spaces for width to blank cells. + for (1..cell_width) |j| { + try self.cell_buf.append(self.alloc, .{ + .x = @intCast(info_v.cluster + j), + .glyph_index = null, + }); + } + } + // const i = self.cell_buf.items.len - 1; // log.warn("i={} info={} pos={} cell={}", .{ i, info_v, pos_v, self.cell_buf.items[i] }); } @@ -334,7 +371,9 @@ test "shape inconsolata ligs" { count += 1; const cells = try shaper.shape(run); - try testing.expectEqual(@as(usize, 1), cells.len); + try testing.expectEqual(@as(usize, 2), cells.len); + try testing.expect(cells[0].glyph_index != null); + try testing.expect(cells[1].glyph_index == null); } try testing.expectEqual(@as(usize, 1), count); } @@ -351,7 +390,38 @@ test "shape inconsolata ligs" { count += 1; const cells = try shaper.shape(run); - try testing.expectEqual(@as(usize, 1), cells.len); + try testing.expectEqual(@as(usize, 3), cells.len); + try testing.expect(cells[0].glyph_index != null); + try testing.expect(cells[1].glyph_index == null); + try testing.expect(cells[2].glyph_index == null); + } + try testing.expectEqual(@as(usize, 1), count); + } +} + +test "shape monaspace ligs" { + const testing = std.testing; + const alloc = testing.allocator; + + var testdata = try testShaperWithFont(alloc, .monaspace_neon); + defer testdata.deinit(); + + { + var screen = try terminal.Screen.init(alloc, 3, 5, 0); + defer screen.deinit(); + try screen.testWriteString("==="); + + var shaper = &testdata.shaper; + var it = shaper.runIterator(testdata.cache, screen.getRow(.{ .screen = 0 }), null, null); + var count: usize = 0; + while (try it.next(alloc)) |run| { + count += 1; + + const cells = try shaper.shape(run); + try testing.expectEqual(@as(usize, 3), cells.len); + try testing.expect(cells[0].glyph_index != null); + try testing.expect(cells[1].glyph_index == null); + try testing.expect(cells[2].glyph_index == null); } try testing.expectEqual(@as(usize, 1), count); } @@ -376,7 +446,7 @@ test "shape emoji width" { count += 1; const cells = try shaper.shape(run); - try testing.expectEqual(@as(usize, 1), cells.len); + try testing.expectEqual(@as(usize, 2), cells.len); } try testing.expectEqual(@as(usize, 1), count); } @@ -411,7 +481,9 @@ test "shape emoji width long" { try testing.expectEqual(@as(u32, 4), shaper.hb_buf.getLength()); const cells = try shaper.shape(run); - try testing.expectEqual(@as(usize, 1), cells.len); + + // screen.testWriteString isn't grapheme aware, otherwise this is two + try testing.expectEqual(@as(usize, 5), cells.len); } try testing.expectEqual(@as(usize, 1), count); } @@ -574,9 +646,9 @@ test "shape box glyphs" { try testing.expectEqual(@as(u32, 2), shaper.hb_buf.getLength()); const cells = try shaper.shape(run); try testing.expectEqual(@as(usize, 2), cells.len); - try testing.expectEqual(@as(u32, 0x2500), cells[0].glyph_index); + try testing.expectEqual(@as(u32, 0x2500), cells[0].glyph_index.?); try testing.expectEqual(@as(u16, 0), cells[0].x); - try testing.expectEqual(@as(u32, 0x2501), cells[1].glyph_index); + try testing.expectEqual(@as(u32, 0x2501), cells[1].glyph_index.?); try testing.expectEqual(@as(u16, 1), cells[1].x); } try testing.expectEqual(@as(usize, 1), count); @@ -902,11 +974,23 @@ const TestShaper = struct { } }; +const TestFont = enum { + inconsolata, + monaspace_neon, +}; + /// Helper to return a fully initialized shaper. fn testShaper(alloc: Allocator) !TestShaper { - const testFont = @import("../test.zig").fontRegular; + return try testShaperWithFont(alloc, .inconsolata); +} + +fn testShaperWithFont(alloc: Allocator, font_req: TestFont) !TestShaper { const testEmoji = @import("../test.zig").fontEmoji; const testEmojiText = @import("../test.zig").fontEmojiText; + const testFont = switch (font_req) { + .inconsolata => @import("../test.zig").fontRegular, + .monaspace_neon => @import("../test.zig").fontMonaspaceNeon, + }; var lib = try Library.init(); errdefer lib.deinit(); diff --git a/src/font/test.zig b/src/font/test.zig index 06bdc40d2..09909691e 100644 --- a/src/font/test.zig +++ b/src/font/test.zig @@ -15,3 +15,7 @@ pub const fontVariable = @embedFile("res/Lilex-VF.ttf"); /// Cozette is a unique font because it embeds some emoji characters /// but has a text presentation. pub const fontCozette = @embedFile("res/CozetteVector.ttf"); + +/// Monaspace has weird ligature behaviors we want to test in our shapers +/// so we embed it here. +pub const fontMonaspaceNeon = @embedFile("res/MonaspaceNeon-Regular.otf"); diff --git a/src/renderer/Metal.zig b/src/renderer/Metal.zig index 81518c6e6..9c17702e0 100644 --- a/src/renderer/Metal.zig +++ b/src/renderer/Metal.zig @@ -1796,12 +1796,12 @@ fn updateCell( }; // If the cell has a character, draw it - if (cell.char > 0) { + if (cell.char > 0) fg: { // Render const glyph = try self.font_group.renderGlyph( self.alloc, shaper_run.font_index, - shaper_cell.glyph_index, + shaper_cell.glyph_index orelse break :fg, .{ .grid_metrics = self.grid_metrics, .thicken = self.config.font_thicken, diff --git a/src/renderer/OpenGL.zig b/src/renderer/OpenGL.zig index aea6991df..66fd966ee 100644 --- a/src/renderer/OpenGL.zig +++ b/src/renderer/OpenGL.zig @@ -1504,12 +1504,12 @@ fn updateCell( }; // If the cell has a character, draw it - if (cell.char > 0) { + if (cell.char > 0) fg: { // Render const glyph = try self.font_group.renderGlyph( self.alloc, shaper_run.font_index, - shaper_cell.glyph_index, + shaper_cell.glyph_index orelse break :fg, .{ .grid_metrics = self.grid_metrics, .thicken = self.config.font_thicken,