1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
|
const std = @import("std");
const assert = std.debug.assert;
const Allocator = std.mem.Allocator;
const trace = @import("tracy").trace;
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");
const log = std.log.scoped(.font_shaper);
/// Shaper that doesn't do any shaping. Each individual codepoint is mapped
/// directly to the detected text run font's glyph index.
pub const Shaper = struct {
/// The allocated used for the feature list and cell buf.
alloc: Allocator,
/// The string used for shaping the current run.
run_state: RunState,
/// The shared memory used for shaping results.
cell_buf: CellBuf,
const CellBuf = std.ArrayListUnmanaged(font.shape.Cell);
const CodepointList = std.ArrayListUnmanaged(Codepoint);
const Codepoint = struct {
codepoint: u32,
cluster: u32,
};
const RunState = struct {
codepoints: CodepointList = .{},
fn deinit(self: *RunState, alloc: Allocator) void {
self.codepoints.deinit(alloc);
}
fn reset(self: *RunState) !void {
self.codepoints.clearRetainingCapacity();
}
};
/// 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.
pub fn init(alloc: Allocator, opts: font.shape.Options) !Shaper {
_ = opts;
return Shaper{
.alloc = alloc,
.cell_buf = .{},
.run_state = .{},
};
}
pub fn deinit(self: *Shaper) void {
self.cell_buf.deinit(self.alloc);
self.run_state.deinit(self.alloc);
}
pub fn endFrame(self: *const Shaper) void {
_ = self;
}
pub fn runIterator(
self: *Shaper,
opts: font.shape.RunOptions,
) font.shape.RunIterator {
return .{
.hooks = .{ .shaper = self },
.opts = opts,
};
}
pub fn shape(self: *Shaper, run: font.shape.TextRun) ![]const font.shape.Cell {
const state = &self.run_state;
// Special fonts aren't shaped and their codepoint == glyph so we
// can just return the codepoints as-is.
if (run.font_index.special() != null) {
self.cell_buf.clearRetainingCapacity();
try self.cell_buf.ensureTotalCapacity(self.alloc, state.codepoints.items.len);
for (state.codepoints.items) |entry| {
self.cell_buf.appendAssumeCapacity(.{
.x = @intCast(entry.cluster),
.glyph_index = @intCast(entry.codepoint),
});
}
return self.cell_buf.items;
}
// Go through the run and map each codepoint to a glyph index.
self.cell_buf.clearRetainingCapacity();
// Note: this is digging into some internal details, we should maybe
// expose a public API for this.
const face = try run.grid.resolver.collection.getFace(run.font_index);
for (state.codepoints.items) |entry| {
const glyph_index = face.glyphIndex(entry.codepoint) orelse {
// The run iterator shared logic should guarantee that
// there is a glyph index for all codepoints in the run.
// This is not well tested because we don't use the noop
// shaper in any release builds.
unreachable;
};
try self.cell_buf.append(self.alloc, .{
.x = @intCast(entry.cluster),
.glyph_index = glyph_index,
});
}
return self.cell_buf.items;
}
/// The hooks for RunIterator.
pub const RunIteratorHook = struct {
shaper: *Shaper,
pub fn prepare(self: *RunIteratorHook) !void {
try self.shaper.run_state.reset();
}
pub fn addCodepoint(self: RunIteratorHook, cp: u32, cluster: u32) !void {
try self.shaper.run_state.codepoints.append(self.shaper.alloc, .{
.codepoint = cp,
.cluster = cluster,
});
}
pub fn finalize(self: RunIteratorHook) !void {
_ = self;
}
};
};
test {
@import("std").testing.refAllDecls(@This());
}
|