mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-14 07:46:12 +03:00
font: shaper dynamically allocates cell buffer
Pathlogical grapheme clusters can use a LOT of memory, so we need to be able to grow.
This commit is contained in:
@ -33,10 +33,6 @@ pub const Cell = struct {
|
||||
|
||||
/// Options for shapers.
|
||||
pub const Options = 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.
|
||||
cell_buf: []Cell,
|
||||
|
||||
/// Font features to use when shaping. These can be in the following
|
||||
/// formats: "-feat" "+feat" "feat". A "-"-prefix is used to disable
|
||||
/// a feature and the others are used to enable a feature. If a feature
|
||||
|
@ -17,17 +17,21 @@ const log = std.log.scoped(.font_shaper);
|
||||
|
||||
/// Shaper that uses Harfbuzz.
|
||||
pub const Shaper = struct {
|
||||
/// The allocated used for the feature list and cell buf.
|
||||
alloc: Allocator,
|
||||
|
||||
/// The buffer used for text shaping. We reuse it across multiple shaping
|
||||
/// calls to prevent allocations.
|
||||
hb_buf: harfbuzz.Buffer,
|
||||
|
||||
/// The shared memory used for shaping results.
|
||||
cell_buf: []font.shape.Cell,
|
||||
cell_buf: CellBuf,
|
||||
|
||||
/// The features to use for shaping.
|
||||
hb_feats: FeatureList,
|
||||
|
||||
const FeatureList = std.ArrayList(harfbuzz.Feature);
|
||||
const CellBuf = std.ArrayListUnmanaged(font.shape.Cell);
|
||||
const FeatureList = std.ArrayListUnmanaged(harfbuzz.Feature);
|
||||
|
||||
// These features are hardcoded to always be on by default. Users
|
||||
// can turn them off by setting the features to "-liga" for example.
|
||||
@ -39,34 +43,36 @@ pub const Shaper = struct {
|
||||
// Parse all the features we want to use. We use
|
||||
var hb_feats = hb_feats: {
|
||||
var list = try FeatureList.initCapacity(alloc, opts.features.len + hardcoded_features.len);
|
||||
errdefer list.deinit();
|
||||
errdefer list.deinit(alloc);
|
||||
|
||||
for (hardcoded_features) |name| {
|
||||
if (harfbuzz.Feature.fromString(name)) |feat| {
|
||||
try list.append(feat);
|
||||
try list.append(alloc, feat);
|
||||
} else log.warn("failed to parse font feature: {s}", .{name});
|
||||
}
|
||||
|
||||
for (opts.features) |name| {
|
||||
if (harfbuzz.Feature.fromString(name)) |feat| {
|
||||
try list.append(feat);
|
||||
try list.append(alloc, feat);
|
||||
} else log.warn("failed to parse font feature: {s}", .{name});
|
||||
}
|
||||
|
||||
break :hb_feats list;
|
||||
};
|
||||
errdefer hb_feats.deinit();
|
||||
errdefer hb_feats.deinit(alloc);
|
||||
|
||||
return Shaper{
|
||||
.alloc = alloc,
|
||||
.hb_buf = try harfbuzz.Buffer.create(),
|
||||
.cell_buf = opts.cell_buf,
|
||||
.cell_buf = .{},
|
||||
.hb_feats = hb_feats,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Shaper) void {
|
||||
self.hb_buf.destroy();
|
||||
self.hb_feats.deinit();
|
||||
self.cell_buf.deinit(self.alloc);
|
||||
self.hb_feats.deinit(self.alloc);
|
||||
}
|
||||
|
||||
/// Returns an iterator that returns one text run at a time for the
|
||||
@ -99,7 +105,7 @@ pub const Shaper = struct {
|
||||
/// The return value is only valid until the next shape call is called.
|
||||
///
|
||||
/// If there is not enough space in the cell buffer, an error is returned.
|
||||
pub fn shape(self: *Shaper, run: font.shape.TextRun) ![]font.shape.Cell {
|
||||
pub fn shape(self: *Shaper, run: font.shape.TextRun) ![]const font.shape.Cell {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
@ -119,7 +125,7 @@ pub const Shaper = struct {
|
||||
|
||||
// If our buffer is empty, we short-circuit the rest of the work
|
||||
// return nothing.
|
||||
if (self.hb_buf.getLength() == 0) return self.cell_buf[0..0];
|
||||
if (self.hb_buf.getLength() == 0) return self.cell_buf.items[0..0];
|
||||
const info = self.hb_buf.getGlyphInfos();
|
||||
const pos = self.hb_buf.getGlyphPositions() orelse return error.HarfbuzzFailed;
|
||||
|
||||
@ -128,19 +134,18 @@ pub const Shaper = struct {
|
||||
assert(info.len == pos.len);
|
||||
|
||||
// Convert all our info/pos to cells and set it.
|
||||
if (info.len > self.cell_buf.len) return error.OutOfMemory;
|
||||
//log.warn("info={} pos={} run={}", .{ info.len, pos.len, run });
|
||||
|
||||
for (info, 0..) |v, i| {
|
||||
self.cell_buf[i] = .{
|
||||
self.cell_buf.clearRetainingCapacity();
|
||||
try self.cell_buf.ensureTotalCapacity(self.alloc, info.len);
|
||||
for (info) |v| {
|
||||
self.cell_buf.appendAssumeCapacity(.{
|
||||
.x = @intCast(v.cluster),
|
||||
.glyph_index = v.codepoint,
|
||||
};
|
||||
});
|
||||
|
||||
// log.warn("i={} info={} pos={} cell={}", .{ i, v, pos[i], self.cell_buf[i] });
|
||||
}
|
||||
|
||||
return self.cell_buf[0..info.len];
|
||||
return self.cell_buf.items;
|
||||
}
|
||||
|
||||
/// The hooks for RunIterator.
|
||||
@ -178,7 +183,7 @@ test "run iterator" {
|
||||
try screen.testWriteString("ABCD");
|
||||
|
||||
// Get our run iterator
|
||||
var shaper = testdata.shaper;
|
||||
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)) |_| count += 1;
|
||||
@ -191,7 +196,7 @@ test "run iterator" {
|
||||
defer screen.deinit();
|
||||
try screen.testWriteString("ABCD EFG");
|
||||
|
||||
var shaper = testdata.shaper;
|
||||
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)) |_| count += 1;
|
||||
@ -205,7 +210,7 @@ test "run iterator" {
|
||||
try screen.testWriteString("A😃D");
|
||||
|
||||
// Get our run iterator
|
||||
var shaper = testdata.shaper;
|
||||
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)) |_| {
|
||||
@ -239,7 +244,7 @@ test "run iterator: empty cells with background set" {
|
||||
row.getCellPtr(2).* = screen.cursor.pen;
|
||||
|
||||
// Get our run iterator
|
||||
var shaper = testdata.shaper;
|
||||
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| {
|
||||
@ -274,7 +279,7 @@ test "shape" {
|
||||
try screen.testWriteString(buf[0..buf_idx]);
|
||||
|
||||
// Get our run iterator
|
||||
var shaper = testdata.shaper;
|
||||
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| {
|
||||
@ -297,7 +302,7 @@ test "shape inconsolata ligs" {
|
||||
defer screen.deinit();
|
||||
try screen.testWriteString(">=");
|
||||
|
||||
var shaper = testdata.shaper;
|
||||
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| {
|
||||
@ -314,7 +319,7 @@ test "shape inconsolata ligs" {
|
||||
defer screen.deinit();
|
||||
try screen.testWriteString("===");
|
||||
|
||||
var shaper = testdata.shaper;
|
||||
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| {
|
||||
@ -339,7 +344,7 @@ test "shape emoji width" {
|
||||
defer screen.deinit();
|
||||
try screen.testWriteString("👍");
|
||||
|
||||
var shaper = testdata.shaper;
|
||||
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| {
|
||||
@ -373,7 +378,7 @@ test "shape emoji width long" {
|
||||
try screen.testWriteString(buf[0..buf_idx]);
|
||||
|
||||
// Get our run iterator
|
||||
var shaper = testdata.shaper;
|
||||
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| {
|
||||
@ -404,7 +409,7 @@ test "shape variation selector VS15" {
|
||||
try screen.testWriteString(buf[0..buf_idx]);
|
||||
|
||||
// Get our run iterator
|
||||
var shaper = testdata.shaper;
|
||||
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| {
|
||||
@ -435,7 +440,7 @@ test "shape variation selector VS16" {
|
||||
try screen.testWriteString(buf[0..buf_idx]);
|
||||
|
||||
// Get our run iterator
|
||||
var shaper = testdata.shaper;
|
||||
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| {
|
||||
@ -463,7 +468,7 @@ test "shape with empty cells in between" {
|
||||
try screen.testWriteString("B");
|
||||
|
||||
// Get our run iterator
|
||||
var shaper = testdata.shaper;
|
||||
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| {
|
||||
@ -495,7 +500,7 @@ test "shape Chinese characters" {
|
||||
try screen.testWriteString(buf[0..buf_idx]);
|
||||
|
||||
// Get our run iterator
|
||||
var shaper = testdata.shaper;
|
||||
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| {
|
||||
@ -536,7 +541,7 @@ test "shape box glyphs" {
|
||||
try screen.testWriteString(buf[0..buf_idx]);
|
||||
|
||||
// Get our run iterator
|
||||
var shaper = testdata.shaper;
|
||||
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| {
|
||||
@ -567,7 +572,7 @@ test "shape selection boundary" {
|
||||
// Full line selection
|
||||
{
|
||||
// Get our run iterator
|
||||
var shaper = testdata.shaper;
|
||||
var shaper = &testdata.shaper;
|
||||
var it = shaper.runIterator(testdata.cache, screen.getRow(.{ .screen = 0 }), .{
|
||||
.start = .{ .x = 0, .y = 0 },
|
||||
.end = .{ .x = screen.cols - 1, .y = 0 },
|
||||
@ -583,7 +588,7 @@ test "shape selection boundary" {
|
||||
// Offset x, goes to end of line selection
|
||||
{
|
||||
// Get our run iterator
|
||||
var shaper = testdata.shaper;
|
||||
var shaper = &testdata.shaper;
|
||||
var it = shaper.runIterator(testdata.cache, screen.getRow(.{ .screen = 0 }), .{
|
||||
.start = .{ .x = 2, .y = 0 },
|
||||
.end = .{ .x = screen.cols - 1, .y = 0 },
|
||||
@ -599,7 +604,7 @@ test "shape selection boundary" {
|
||||
// Offset x, starts at beginning of line
|
||||
{
|
||||
// Get our run iterator
|
||||
var shaper = testdata.shaper;
|
||||
var shaper = &testdata.shaper;
|
||||
var it = shaper.runIterator(testdata.cache, screen.getRow(.{ .screen = 0 }), .{
|
||||
.start = .{ .x = 0, .y = 0 },
|
||||
.end = .{ .x = 3, .y = 0 },
|
||||
@ -615,7 +620,7 @@ test "shape selection boundary" {
|
||||
// Selection only subset of line
|
||||
{
|
||||
// Get our run iterator
|
||||
var shaper = testdata.shaper;
|
||||
var shaper = &testdata.shaper;
|
||||
var it = shaper.runIterator(testdata.cache, screen.getRow(.{ .screen = 0 }), .{
|
||||
.start = .{ .x = 1, .y = 0 },
|
||||
.end = .{ .x = 3, .y = 0 },
|
||||
@ -631,7 +636,7 @@ test "shape selection boundary" {
|
||||
// Selection only one character
|
||||
{
|
||||
// Get our run iterator
|
||||
var shaper = testdata.shaper;
|
||||
var shaper = &testdata.shaper;
|
||||
var it = shaper.runIterator(testdata.cache, screen.getRow(.{ .screen = 0 }), .{
|
||||
.start = .{ .x = 1, .y = 0 },
|
||||
.end = .{ .x = 1, .y = 0 },
|
||||
@ -660,7 +665,7 @@ test "shape cursor boundary" {
|
||||
// No cursor is full line
|
||||
{
|
||||
// Get our run iterator
|
||||
var shaper = testdata.shaper;
|
||||
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| {
|
||||
@ -673,7 +678,7 @@ test "shape cursor boundary" {
|
||||
// Cursor at index 0 is two runs
|
||||
{
|
||||
// Get our run iterator
|
||||
var shaper = testdata.shaper;
|
||||
var shaper = &testdata.shaper;
|
||||
var it = shaper.runIterator(testdata.cache, screen.getRow(.{ .screen = 0 }), null, 0);
|
||||
var count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
@ -686,7 +691,7 @@ test "shape cursor boundary" {
|
||||
// Cursor at index 1 is three runs
|
||||
{
|
||||
// Get our run iterator
|
||||
var shaper = testdata.shaper;
|
||||
var shaper = &testdata.shaper;
|
||||
var it = shaper.runIterator(testdata.cache, screen.getRow(.{ .screen = 0 }), null, 1);
|
||||
var count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
@ -699,7 +704,7 @@ test "shape cursor boundary" {
|
||||
// Cursor at last col is two runs
|
||||
{
|
||||
// Get our run iterator
|
||||
var shaper = testdata.shaper;
|
||||
var shaper = &testdata.shaper;
|
||||
var it = shaper.runIterator(testdata.cache, screen.getRow(.{ .screen = 0 }), null, 9);
|
||||
var count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
@ -725,7 +730,7 @@ test "shape cursor boundary and colored emoji" {
|
||||
// No cursor is full line
|
||||
{
|
||||
// Get our run iterator
|
||||
var shaper = testdata.shaper;
|
||||
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| {
|
||||
@ -738,7 +743,7 @@ test "shape cursor boundary and colored emoji" {
|
||||
// Cursor on emoji does not split it
|
||||
{
|
||||
// Get our run iterator
|
||||
var shaper = testdata.shaper;
|
||||
var shaper = &testdata.shaper;
|
||||
var it = shaper.runIterator(testdata.cache, screen.getRow(.{ .screen = 0 }), null, 0);
|
||||
var count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
@ -749,7 +754,7 @@ test "shape cursor boundary and colored emoji" {
|
||||
}
|
||||
{
|
||||
// Get our run iterator
|
||||
var shaper = testdata.shaper;
|
||||
var shaper = &testdata.shaper;
|
||||
var it = shaper.runIterator(testdata.cache, screen.getRow(.{ .screen = 0 }), null, 1);
|
||||
var count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
@ -773,7 +778,7 @@ test "shape cell attribute change" {
|
||||
defer screen.deinit();
|
||||
try screen.testWriteString(">=");
|
||||
|
||||
var shaper = testdata.shaper;
|
||||
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| {
|
||||
@ -791,7 +796,7 @@ test "shape cell attribute change" {
|
||||
screen.cursor.pen.attrs.bold = true;
|
||||
try screen.testWriteString("=");
|
||||
|
||||
var shaper = testdata.shaper;
|
||||
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| {
|
||||
@ -811,7 +816,7 @@ test "shape cell attribute change" {
|
||||
screen.cursor.pen.fg = .{ .r = 3, .g = 2, .b = 1 };
|
||||
try screen.testWriteString("=");
|
||||
|
||||
var shaper = testdata.shaper;
|
||||
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| {
|
||||
@ -831,7 +836,7 @@ test "shape cell attribute change" {
|
||||
screen.cursor.pen.bg = .{ .r = 3, .g = 2, .b = 1 };
|
||||
try screen.testWriteString("=");
|
||||
|
||||
var shaper = testdata.shaper;
|
||||
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| {
|
||||
@ -850,7 +855,7 @@ test "shape cell attribute change" {
|
||||
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 count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
@ -866,13 +871,11 @@ const TestShaper = struct {
|
||||
shaper: Shaper,
|
||||
cache: *GroupCache,
|
||||
lib: Library,
|
||||
cell_buf: []font.shape.Cell,
|
||||
|
||||
pub fn deinit(self: *TestShaper) void {
|
||||
self.shaper.deinit();
|
||||
self.cache.deinit(self.alloc);
|
||||
self.alloc.destroy(self.cache);
|
||||
self.alloc.free(self.cell_buf);
|
||||
self.lib.deinit();
|
||||
}
|
||||
};
|
||||
@ -917,10 +920,7 @@ fn testShaper(alloc: Allocator) !TestShaper {
|
||||
}
|
||||
_ = try cache_ptr.group.addFace(.regular, .{ .loaded = try Face.init(lib, testEmojiText, .{ .points = 12 }) });
|
||||
|
||||
var cell_buf = try alloc.alloc(font.shape.Cell, 80);
|
||||
errdefer alloc.free(cell_buf);
|
||||
|
||||
var shaper = try Shaper.init(alloc, .{ .cell_buf = cell_buf });
|
||||
var shaper = try Shaper.init(alloc, .{});
|
||||
errdefer shaper.deinit();
|
||||
|
||||
return TestShaper{
|
||||
@ -928,6 +928,5 @@ fn testShaper(alloc: Allocator) !TestShaper {
|
||||
.shaper = shaper,
|
||||
.cache = cache_ptr,
|
||||
.lib = lib,
|
||||
.cell_buf = cell_buf,
|
||||
};
|
||||
}
|
||||
|
@ -218,10 +218,7 @@ pub fn init(alloc: Allocator, options: renderer.Options) !Metal {
|
||||
// Create the font shaper. We initially create a shaper that can support
|
||||
// a width of 160 which is a common width for modern screens to help
|
||||
// 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(alloc, .{
|
||||
.cell_buf = shape_buf,
|
||||
.features = options.config.font_features.items,
|
||||
});
|
||||
errdefer font_shaper.deinit();
|
||||
@ -288,7 +285,6 @@ pub fn deinit(self: *Metal) void {
|
||||
self.cells_bg.deinit(self.alloc);
|
||||
|
||||
self.font_shaper.deinit();
|
||||
self.alloc.free(self.font_shaper.cell_buf);
|
||||
|
||||
self.config.deinit();
|
||||
|
||||
@ -417,14 +413,6 @@ pub fn setFontSize(self: *Metal, size: font.face.DesiredSize) !void {
|
||||
if (std.meta.eql(self.cell_size, new_cell_size)) return;
|
||||
self.cell_size = new_cell_size;
|
||||
|
||||
// Resize our font shaping buffer to fit the new width.
|
||||
if (self.gridSize()) |grid_size| {
|
||||
var shape_buf = try self.alloc.alloc(font.shape.Cell, grid_size.columns * 2);
|
||||
errdefer self.alloc.free(shape_buf);
|
||||
self.alloc.free(self.font_shaper.cell_buf);
|
||||
self.font_shaper.cell_buf = shape_buf;
|
||||
}
|
||||
|
||||
// Set the sprite font up
|
||||
self.font_group.group.sprite = font.sprite.Face{
|
||||
.width = self.cell_size.width,
|
||||
@ -998,7 +986,6 @@ pub fn changeConfig(self: *Metal, config: *DerivedConfig) !void {
|
||||
// easier and rare enough to not cause performance issues.
|
||||
{
|
||||
var font_shaper = try font.Shaper.init(self.alloc, .{
|
||||
.cell_buf = self.font_shaper.cell_buf,
|
||||
.features = config.font_features.items,
|
||||
});
|
||||
errdefer font_shaper.deinit();
|
||||
@ -1033,13 +1020,6 @@ pub fn setScreenSize(
|
||||
.{});
|
||||
const padded_dim = dim.subPadding(padding);
|
||||
|
||||
// Update our shaper
|
||||
// TODO: don't reallocate if it is close enough (but bigger)
|
||||
var shape_buf = try self.alloc.alloc(font.shape.Cell, grid_size.columns * 2);
|
||||
errdefer self.alloc.free(shape_buf);
|
||||
self.alloc.free(self.font_shaper.cell_buf);
|
||||
self.font_shaper.cell_buf = shape_buf;
|
||||
|
||||
// Set the size of the drawable surface to the bounds
|
||||
self.swapchain.setProperty("drawableSize", macos.graphics.Size{
|
||||
.width = @floatFromInt(dim.width),
|
||||
|
@ -280,10 +280,7 @@ pub const DerivedConfig = struct {
|
||||
|
||||
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(alloc, .{
|
||||
.cell_buf = shape_buf,
|
||||
.features = options.config.font_features.items,
|
||||
});
|
||||
errdefer shaper.deinit();
|
||||
@ -318,7 +315,6 @@ pub fn init(alloc: Allocator, options: renderer.Options) !OpenGL {
|
||||
|
||||
pub fn deinit(self: *OpenGL) void {
|
||||
self.font_shaper.deinit();
|
||||
self.alloc.free(self.font_shaper.cell_buf);
|
||||
|
||||
if (self.gl_state) |*v| v.deinit();
|
||||
|
||||
@ -525,15 +521,6 @@ pub fn setFontSize(self: *OpenGL, size: font.face.DesiredSize) !void {
|
||||
if (std.meta.eql(self.cell_size, new_cell_size)) return;
|
||||
self.cell_size = new_cell_size;
|
||||
|
||||
// Resize our font shaping buffer to fit the new width.
|
||||
if (self.screen_size) |dim| {
|
||||
const grid_size = self.gridSize(dim);
|
||||
var shape_buf = try self.alloc.alloc(font.shape.Cell, grid_size.columns * 2);
|
||||
errdefer self.alloc.free(shape_buf);
|
||||
self.alloc.free(self.font_shaper.cell_buf);
|
||||
self.font_shaper.cell_buf = shape_buf;
|
||||
}
|
||||
|
||||
// Notify the window that the cell size changed.
|
||||
_ = self.surface_mailbox.push(.{
|
||||
.cell_size = new_cell_size,
|
||||
@ -1228,7 +1215,6 @@ pub fn changeConfig(self: *OpenGL, config: *DerivedConfig) !void {
|
||||
// easier and rare enough to not cause performance issues.
|
||||
{
|
||||
var font_shaper = try font.Shaper.init(self.alloc, .{
|
||||
.cell_buf = self.font_shaper.cell_buf,
|
||||
.features = config.font_features.items,
|
||||
});
|
||||
errdefer font_shaper.deinit();
|
||||
@ -1264,12 +1250,6 @@ pub fn setScreenSize(
|
||||
self.padding.explicit,
|
||||
});
|
||||
|
||||
// Update our shaper
|
||||
var shape_buf = try self.alloc.alloc(font.shape.Cell, grid_size.columns * 2);
|
||||
errdefer self.alloc.free(shape_buf);
|
||||
self.alloc.free(self.font_shaper.cell_buf);
|
||||
self.font_shaper.cell_buf = shape_buf;
|
||||
|
||||
// Defer our OpenGL updates
|
||||
self.deferred_screen_size = .{ .size = dim };
|
||||
}
|
||||
|
Reference in New Issue
Block a user