mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-16 16:56:09 +03:00
font/coretext: coretext shaper is cleaner
This commit is contained in:
@ -22,15 +22,17 @@ pub const Shaper = struct {
|
|||||||
alloc: Allocator,
|
alloc: Allocator,
|
||||||
|
|
||||||
/// The string used for shaping the current run.
|
/// The string used for shaping the current run.
|
||||||
str: ?*macos.foundation.MutableString = null,
|
codepoints: CodepointList = .{},
|
||||||
codepoints: ClusterBuf = .{},
|
|
||||||
clusters: ClusterBuf = .{},
|
|
||||||
|
|
||||||
/// The shared memory used for shaping results.
|
/// The shared memory used for shaping results.
|
||||||
cell_buf: CellBuf,
|
cell_buf: CellBuf,
|
||||||
|
|
||||||
const CellBuf = std.ArrayListUnmanaged(font.shape.Cell);
|
const CellBuf = std.ArrayListUnmanaged(font.shape.Cell);
|
||||||
const ClusterBuf = std.ArrayListUnmanaged(u32);
|
const CodepointList = std.ArrayListUnmanaged(Codepoint);
|
||||||
|
const Codepoint = struct {
|
||||||
|
codepoint: u32,
|
||||||
|
cluster: u32,
|
||||||
|
};
|
||||||
|
|
||||||
// These features are hardcoded to always be on by default. Users
|
// These features are hardcoded to always be on by default. Users
|
||||||
// can turn them off by setting the features to "-liga" for example.
|
// can turn them off by setting the features to "-liga" for example.
|
||||||
@ -51,8 +53,6 @@ pub const Shaper = struct {
|
|||||||
pub fn deinit(self: *Shaper) void {
|
pub fn deinit(self: *Shaper) void {
|
||||||
self.cell_buf.deinit(self.alloc);
|
self.cell_buf.deinit(self.alloc);
|
||||||
self.codepoints.deinit(self.alloc);
|
self.codepoints.deinit(self.alloc);
|
||||||
self.clusters.deinit(self.alloc);
|
|
||||||
if (self.str) |str| str.release();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn runIterator(
|
pub fn runIterator(
|
||||||
@ -79,10 +79,10 @@ pub const Shaper = struct {
|
|||||||
if (run.font_index.special() != null) {
|
if (run.font_index.special() != null) {
|
||||||
self.cell_buf.clearRetainingCapacity();
|
self.cell_buf.clearRetainingCapacity();
|
||||||
try self.cell_buf.ensureTotalCapacity(self.alloc, self.codepoints.items.len);
|
try self.cell_buf.ensureTotalCapacity(self.alloc, self.codepoints.items.len);
|
||||||
for (self.codepoints.items, self.clusters.items) |cp, x| {
|
for (self.codepoints.items) |entry| {
|
||||||
self.cell_buf.appendAssumeCapacity(.{
|
self.cell_buf.appendAssumeCapacity(.{
|
||||||
.x = @intCast(x),
|
.x = @intCast(entry.cluster),
|
||||||
.glyph_index = @intCast(cp),
|
.glyph_index = @intCast(entry.codepoint),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,6 +94,26 @@ pub const Shaper = struct {
|
|||||||
defer arena.deinit();
|
defer arena.deinit();
|
||||||
const alloc = arena.allocator();
|
const alloc = arena.allocator();
|
||||||
|
|
||||||
|
// Build up our string contents
|
||||||
|
const str = str: {
|
||||||
|
const str = try macos.foundation.MutableString.create(0);
|
||||||
|
errdefer str.release();
|
||||||
|
|
||||||
|
for (self.codepoints.items) |entry| {
|
||||||
|
var unichars: [2]u16 = undefined;
|
||||||
|
const pair = macos.foundation.stringGetSurrogatePairForLongCharacter(
|
||||||
|
entry.codepoint,
|
||||||
|
&unichars,
|
||||||
|
);
|
||||||
|
const len: usize = if (pair) 2 else 1;
|
||||||
|
str.appendCharacters(unichars[0..len]);
|
||||||
|
// log.warn("append codepoint={} unichar_len={}", .{ cp, len });
|
||||||
|
}
|
||||||
|
|
||||||
|
break :str str;
|
||||||
|
};
|
||||||
|
defer str.release();
|
||||||
|
|
||||||
// Get our font and use that get the attributes to set for the
|
// Get our font and use that get the attributes to set for the
|
||||||
// attributed string so the whole string uses the same font.
|
// attributed string so the whole string uses the same font.
|
||||||
const attr_dict = dict: {
|
const attr_dict = dict: {
|
||||||
@ -106,7 +126,7 @@ pub const Shaper = struct {
|
|||||||
|
|
||||||
// Create an attributed string from our string
|
// Create an attributed string from our string
|
||||||
const attr_str = try macos.foundation.AttributedString.create(
|
const attr_str = try macos.foundation.AttributedString.create(
|
||||||
self.str.?.string(),
|
str.string(),
|
||||||
attr_dict,
|
attr_dict,
|
||||||
);
|
);
|
||||||
defer attr_str.release();
|
defer attr_str.release();
|
||||||
@ -130,7 +150,7 @@ pub const Shaper = struct {
|
|||||||
self.cell_buf.clearRetainingCapacity();
|
self.cell_buf.clearRetainingCapacity();
|
||||||
try self.cell_buf.ensureTotalCapacity(self.alloc, glyphs.len);
|
try self.cell_buf.ensureTotalCapacity(self.alloc, glyphs.len);
|
||||||
for (glyphs, positions, advances, indices) |glyph, pos, advance, index| {
|
for (glyphs, positions, advances, indices) |glyph, pos, advance, index| {
|
||||||
const cluster = self.clusters.items[index];
|
const cluster = self.codepoints.items[index].cluster;
|
||||||
self.cell_buf.appendAssumeCapacity(.{
|
self.cell_buf.appendAssumeCapacity(.{
|
||||||
.x = @intCast(cluster),
|
.x = @intCast(cluster),
|
||||||
.glyph_index = glyph,
|
.glyph_index = glyph,
|
||||||
@ -153,20 +173,14 @@ pub const Shaper = struct {
|
|||||||
shaper: *Shaper,
|
shaper: *Shaper,
|
||||||
|
|
||||||
pub fn prepare(self: *RunIteratorHook) !void {
|
pub fn prepare(self: *RunIteratorHook) !void {
|
||||||
if (self.shaper.str) |str| str.release();
|
|
||||||
self.shaper.str = try macos.foundation.MutableString.create(0);
|
|
||||||
self.shaper.codepoints.clearRetainingCapacity();
|
self.shaper.codepoints.clearRetainingCapacity();
|
||||||
self.shaper.clusters.clearRetainingCapacity();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn addCodepoint(self: RunIteratorHook, cp: u32, cluster: u32) !void {
|
pub fn addCodepoint(self: RunIteratorHook, cp: u32, cluster: u32) !void {
|
||||||
var unichars: [2]u16 = undefined;
|
try self.shaper.codepoints.append(self.shaper.alloc, .{
|
||||||
const pair = macos.foundation.stringGetSurrogatePairForLongCharacter(cp, &unichars);
|
.codepoint = cp,
|
||||||
const len: usize = if (pair) 2 else 1;
|
.cluster = cluster,
|
||||||
// log.warn("append codepoint={} unichar_len={}", .{ cp, len });
|
});
|
||||||
self.shaper.str.?.appendCharacters(unichars[0..len]);
|
|
||||||
try self.shaper.codepoints.append(self.shaper.alloc, cp);
|
|
||||||
try self.shaper.clusters.appendNTimes(self.shaper.alloc, cluster, len);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn finalize(self: RunIteratorHook) !void {
|
pub fn finalize(self: RunIteratorHook) !void {
|
||||||
@ -253,7 +267,7 @@ test "run iterator: empty cells with background set" {
|
|||||||
|
|
||||||
// The run should have length 3 because of the two background
|
// The run should have length 3 because of the two background
|
||||||
// cells.
|
// cells.
|
||||||
try testing.expectEqual(@as(usize, 3), shaper.str.?.string().getLength());
|
try testing.expectEqual(@as(usize, 3), shaper.codepoints.items.len);
|
||||||
const cells = try shaper.shape(run);
|
const cells = try shaper.shape(run);
|
||||||
try testing.expectEqual(@as(usize, 3), cells.len);
|
try testing.expectEqual(@as(usize, 3), cells.len);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user