font/shaper: new API

This commit is contained in:
Mitchell Hashimoto
2024-03-08 09:58:19 -08:00
parent efe037bb9f
commit 05470bb36a
3 changed files with 149 additions and 108 deletions

View File

@ -1047,103 +1047,133 @@ test "shape cursor boundary and colored emoji" {
} }
} }
// test "shape cell attribute change" { test "shape cell attribute change" {
// const testing = std.testing; const testing = std.testing;
// const alloc = testing.allocator; const alloc = testing.allocator;
//
// var testdata = try testShaper(alloc); var testdata = try testShaper(alloc);
// defer testdata.deinit(); defer testdata.deinit();
//
// // Plain >= should shape into 1 run // Plain >= should shape into 1 run
// { {
// var screen = try terminal.Screen.init(alloc, 3, 10, 0); var screen = try terminal.Screen.init(alloc, 10, 3, 0);
// defer screen.deinit(); defer screen.deinit();
// try screen.testWriteString(">="); try screen.testWriteString(">=");
//
// var shaper = &testdata.shaper; var shaper = &testdata.shaper;
// var it = shaper.runIterator(testdata.cache, screen.getRow(.{ .screen = 0 }), null, null); var it = shaper.runIterator(
// var count: usize = 0; testdata.cache,
// while (try it.next(alloc)) |run| { &screen,
// count += 1; screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
// _ = try shaper.shape(run); null,
// } null,
// try testing.expectEqual(@as(usize, 1), count); );
// } var count: usize = 0;
// while (try it.next(alloc)) |run| {
// // Bold vs regular should split count += 1;
// { _ = try shaper.shape(run);
// var screen = try terminal.Screen.init(alloc, 3, 10, 0); }
// defer screen.deinit(); try testing.expectEqual(@as(usize, 1), count);
// try screen.testWriteString(">"); }
// screen.cursor.pen.attrs.bold = true;
// try screen.testWriteString("="); // Bold vs regular should split
// {
// var shaper = &testdata.shaper; var screen = try terminal.Screen.init(alloc, 3, 10, 0);
// var it = shaper.runIterator(testdata.cache, screen.getRow(.{ .screen = 0 }), null, null); defer screen.deinit();
// var count: usize = 0; try screen.testWriteString(">");
// while (try it.next(alloc)) |run| { try screen.setAttribute(.{ .bold = {} });
// count += 1; try screen.testWriteString("=");
// _ = try shaper.shape(run);
// } var shaper = &testdata.shaper;
// try testing.expectEqual(@as(usize, 2), count); var it = shaper.runIterator(
// } testdata.cache,
// &screen,
// // Changing fg color should split screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
// { null,
// var screen = try terminal.Screen.init(alloc, 3, 10, 0); null,
// defer screen.deinit(); );
// screen.cursor.pen.fg = .{ .rgb = .{ .r = 1, .g = 2, .b = 3 } }; var count: usize = 0;
// try screen.testWriteString(">"); while (try it.next(alloc)) |run| {
// screen.cursor.pen.fg = .{ .rgb = .{ .r = 3, .g = 2, .b = 1 } }; count += 1;
// try screen.testWriteString("="); _ = try shaper.shape(run);
// }
// var shaper = &testdata.shaper; try testing.expectEqual(@as(usize, 2), count);
// var it = shaper.runIterator(testdata.cache, screen.getRow(.{ .screen = 0 }), null, null); }
// var count: usize = 0;
// while (try it.next(alloc)) |run| { // Changing fg color should split
// count += 1; {
// _ = try shaper.shape(run); var screen = try terminal.Screen.init(alloc, 3, 10, 0);
// } defer screen.deinit();
// try testing.expectEqual(@as(usize, 2), count); try screen.setAttribute(.{ .direct_color_fg = .{ .r = 1, .g = 2, .b = 3 } });
// } try screen.testWriteString(">");
// try screen.setAttribute(.{ .direct_color_fg = .{ .r = 3, .g = 2, .b = 1 } });
// // Changing bg color should split try screen.testWriteString("=");
// {
// var screen = try terminal.Screen.init(alloc, 3, 10, 0); var shaper = &testdata.shaper;
// defer screen.deinit(); var it = shaper.runIterator(
// screen.cursor.pen.bg = .{ .rgb = .{ .r = 1, .g = 2, .b = 3 } }; testdata.cache,
// try screen.testWriteString(">"); &screen,
// screen.cursor.pen.bg = .{ .rgb = .{ .r = 3, .g = 2, .b = 1 } }; screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
// try screen.testWriteString("="); null,
// null,
// var shaper = &testdata.shaper; );
// var it = shaper.runIterator(testdata.cache, screen.getRow(.{ .screen = 0 }), null, null); var count: usize = 0;
// var count: usize = 0; while (try it.next(alloc)) |run| {
// while (try it.next(alloc)) |run| { count += 1;
// count += 1; _ = try shaper.shape(run);
// _ = try shaper.shape(run); }
// } try testing.expectEqual(@as(usize, 2), count);
// try testing.expectEqual(@as(usize, 2), count); }
// }
// // Changing bg color should split
// // Same bg color should not split {
// { var screen = try terminal.Screen.init(alloc, 3, 10, 0);
// var screen = try terminal.Screen.init(alloc, 3, 10, 0); defer screen.deinit();
// defer screen.deinit(); try screen.setAttribute(.{ .direct_color_bg = .{ .r = 1, .g = 2, .b = 3 } });
// screen.cursor.pen.bg = .{ .rgb = .{ .r = 1, .g = 2, .b = 3 } }; try screen.testWriteString(">");
// try screen.testWriteString(">"); try screen.setAttribute(.{ .direct_color_bg = .{ .r = 3, .g = 2, .b = 1 } });
// try screen.testWriteString("="); try screen.testWriteString("=");
//
// var shaper = &testdata.shaper; var shaper = &testdata.shaper;
// var it = shaper.runIterator(testdata.cache, screen.getRow(.{ .screen = 0 }), null, null); var it = shaper.runIterator(
// var count: usize = 0; testdata.cache,
// while (try it.next(alloc)) |run| { &screen,
// count += 1; screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
// _ = try shaper.shape(run); null,
// } null,
// try testing.expectEqual(@as(usize, 1), count); );
// } var count: usize = 0;
// } while (try it.next(alloc)) |run| {
count += 1;
_ = try shaper.shape(run);
}
try testing.expectEqual(@as(usize, 2), count);
}
// Same bg color should not split
{
var screen = try terminal.Screen.init(alloc, 3, 10, 0);
defer screen.deinit();
try screen.setAttribute(.{ .direct_color_bg = .{ .r = 1, .g = 2, .b = 3 } });
try screen.testWriteString(">");
try screen.testWriteString("=");
var shaper = &testdata.shaper;
var it = shaper.runIterator(
testdata.cache,
&screen,
screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
null,
null,
);
var count: usize = 0;
while (try it.next(alloc)) |run| {
count += 1;
_ = try shaper.shape(run);
}
try testing.expectEqual(@as(usize, 1), count);
}
}
const TestShaper = struct { const TestShaper = struct {
alloc: Allocator, alloc: Allocator,

View File

@ -54,6 +54,9 @@ pub const RunIterator = struct {
// Allow the hook to prepare // Allow the hook to prepare
try self.hooks.prepare(); try self.hooks.prepare();
// Let's get our style that we'll expect for the run.
const style = self.row.style(&cells[0]);
// Go through cell by cell and accumulate while we build our run. // Go through cell by cell and accumulate while we build our run.
var j: usize = self.i; var j: usize = self.i;
while (j < max) : (j += 1) { while (j < max) : (j += 1) {
@ -92,14 +95,13 @@ pub const RunIterator = struct {
// Text runs break when font styles change so we need to get // Text runs break when font styles change so we need to get
// the proper style. // the proper style.
const style: font.Style = style: { const font_style: font.Style = style: {
// TODO(paged-terminal) if (style.flags.bold) {
// if (cell.attrs.bold) { if (style.flags.italic) break :style .bold_italic;
// if (cell.attrs.italic) break :style .bold_italic; break :style .bold;
// break :style .bold; }
// }
// if (style.flags.italic) break :style .italic;
// if (cell.attrs.italic) break :style .italic;
break :style .regular; break :style .regular;
}; };
@ -166,7 +168,7 @@ pub const RunIterator = struct {
if (try self.indexForCell( if (try self.indexForCell(
alloc, alloc,
cell, cell,
style, font_style,
presentation, presentation,
)) |idx| break :font_info .{ .idx = idx }; )) |idx| break :font_info .{ .idx = idx };
@ -175,7 +177,7 @@ pub const RunIterator = struct {
if (try self.group.indexForCodepoint( if (try self.group.indexForCodepoint(
alloc, alloc,
0xFFFD, // replacement char 0xFFFD, // replacement char
style, font_style,
presentation, presentation,
)) |idx| break :font_info .{ .idx = idx, .fallback = 0xFFFD }; )) |idx| break :font_info .{ .idx = idx, .fallback = 0xFFFD };
@ -183,7 +185,7 @@ pub const RunIterator = struct {
if (try self.group.indexForCodepoint( if (try self.group.indexForCodepoint(
alloc, alloc,
' ', ' ',
style, font_style,
presentation, presentation,
)) |idx| break :font_info .{ .idx = idx, .fallback = ' ' }; )) |idx| break :font_info .{ .idx = idx, .fallback = ' ' };

View File

@ -2059,6 +2059,15 @@ pub const Pin = struct {
return self.page.data.lookupGrapheme(cell); return self.page.data.lookupGrapheme(cell);
} }
/// Returns the style for the given cell in this pin.
pub fn style(self: Pin, cell: *pagepkg.Cell) stylepkg.Style {
if (cell.style_id == stylepkg.default_id) return .{};
return self.page.data.styles.lookupId(
self.page.data.memory,
cell.style_id,
).?.*;
}
/// Iterators. These are the same as PageList iterator funcs but operate /// Iterators. These are the same as PageList iterator funcs but operate
/// on pins rather than points. This is MUCH more efficient than calling /// on pins rather than points. This is MUCH more efficient than calling
/// pointFromPin and building up the iterator from points. /// pointFromPin and building up the iterator from points.