font/harfbuzz: work with new font structures

This commit is contained in:
Mitchell Hashimoto
2024-04-05 21:48:53 -07:00
parent 6aa659c4b5
commit b77513de1a
2 changed files with 60 additions and 60 deletions

View File

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

View File

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