mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-17 01:06:08 +03:00
font/coretext: shaper applies x/y offsets
This commit is contained in:
@ -1,6 +1,7 @@
|
|||||||
const builtin = @import("builtin");
|
const builtin = @import("builtin");
|
||||||
const options = @import("main.zig").options;
|
const options = @import("main.zig").options;
|
||||||
const harfbuzz = @import("shaper/harfbuzz.zig");
|
const harfbuzz = @import("shaper/harfbuzz.zig");
|
||||||
|
const coretext = @import("shaper/coretext.zig");
|
||||||
pub const web_canvas = @import("shaper/web_canvas.zig");
|
pub const web_canvas = @import("shaper/web_canvas.zig");
|
||||||
pub usingnamespace @import("shaper/run.zig");
|
pub usingnamespace @import("shaper/run.zig");
|
||||||
|
|
||||||
@ -12,6 +13,8 @@ pub const Shaper = switch (options.backend) {
|
|||||||
.coretext,
|
.coretext,
|
||||||
=> harfbuzz.Shaper,
|
=> harfbuzz.Shaper,
|
||||||
|
|
||||||
|
//.coretext => coretext.Shaper,
|
||||||
|
|
||||||
.web_canvas => web_canvas.Shaper,
|
.web_canvas => web_canvas.Shaper,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -24,6 +27,10 @@ pub const Cell = struct {
|
|||||||
/// caller has access to the original screen cell.
|
/// caller has access to the original screen cell.
|
||||||
x: u16,
|
x: u16,
|
||||||
|
|
||||||
|
/// An additional offset to apply to the rendering.
|
||||||
|
x_offset: i16 = 0,
|
||||||
|
y_offset: i16 = 0,
|
||||||
|
|
||||||
/// The glyph index for this cell. The font index to use alongside
|
/// The glyph index for this cell. The font index to use alongside
|
||||||
/// this cell is available in the text run. This glyph index is only
|
/// this cell is available in the text run. This glyph index is only
|
||||||
/// valid for a given GroupCache and FontIndex that was used to create
|
/// valid for a given GroupCache and FontIndex that was used to create
|
||||||
|
@ -24,6 +24,10 @@ pub const Shaper = struct {
|
|||||||
/// The string used for shaping the current run.
|
/// The string used for shaping the current run.
|
||||||
codepoints: CodepointList = .{},
|
codepoints: CodepointList = .{},
|
||||||
|
|
||||||
|
/// The font features we want to use. The hardcoded features are always
|
||||||
|
/// set first.
|
||||||
|
features: FeatureList = .{},
|
||||||
|
|
||||||
/// The shared memory used for shaping results.
|
/// The shared memory used for shaping results.
|
||||||
cell_buf: CellBuf,
|
cell_buf: CellBuf,
|
||||||
|
|
||||||
@ -34,6 +38,29 @@ pub const Shaper = struct {
|
|||||||
cluster: u32,
|
cluster: u32,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const FeatureList = std.ArrayListUnmanaged(Feature);
|
||||||
|
const Feature = struct {
|
||||||
|
key: *macos.foundation.String,
|
||||||
|
value: *macos.foundation.Number,
|
||||||
|
|
||||||
|
pub fn init(name_raw: []const u8) !Feature {
|
||||||
|
const name = if (name_raw[0] == '-') name_raw[1..] else name_raw;
|
||||||
|
const value_num: c_int = if (name_raw[0] == '-') 0 else 1;
|
||||||
|
|
||||||
|
var key = try macos.foundation.String.createWithBytes(name, .utf8, false);
|
||||||
|
errdefer key.release();
|
||||||
|
var value = try macos.foundation.Number.create(.int, &value_num);
|
||||||
|
defer value.release();
|
||||||
|
|
||||||
|
return .{ .key = key, .value = value };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: Feature) void {
|
||||||
|
self.key.release();
|
||||||
|
self.value.release();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// 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.
|
||||||
const hardcoded_features = [_][]const u8{ "dlig", "liga" };
|
const hardcoded_features = [_][]const u8{ "dlig", "liga" };
|
||||||
@ -41,18 +68,36 @@ pub const Shaper = struct {
|
|||||||
/// The cell_buf argument is the buffer to use for storing shaped results.
|
/// 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.
|
/// This should be at least the number of columns in the terminal.
|
||||||
pub fn init(alloc: Allocator, opts: font.shape.Options) !Shaper {
|
pub fn init(alloc: Allocator, opts: font.shape.Options) !Shaper {
|
||||||
// TODO: features
|
var feats: FeatureList = .{};
|
||||||
_ = opts;
|
errdefer {
|
||||||
|
for (feats.items) |feature| feature.deinit();
|
||||||
|
feats.deinit(alloc);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (hardcoded_features) |name| {
|
||||||
|
const feat = try Feature.init(name);
|
||||||
|
errdefer feat.deinit();
|
||||||
|
try feats.append(alloc, feat);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (opts.features) |name| {
|
||||||
|
const feat = try Feature.init(name);
|
||||||
|
errdefer feat.deinit();
|
||||||
|
try feats.append(alloc, feat);
|
||||||
|
}
|
||||||
|
|
||||||
return Shaper{
|
return Shaper{
|
||||||
.alloc = alloc,
|
.alloc = alloc,
|
||||||
.cell_buf = .{},
|
.cell_buf = .{},
|
||||||
|
.features = feats,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
||||||
|
for (self.features.items) |feature| feature.deinit();
|
||||||
|
self.features.deinit(self.alloc);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn runIterator(
|
pub fn runIterator(
|
||||||
@ -147,23 +192,43 @@ pub const Shaper = struct {
|
|||||||
assert(glyphs.len == advances.len);
|
assert(glyphs.len == advances.len);
|
||||||
assert(glyphs.len == indices.len);
|
assert(glyphs.len == indices.len);
|
||||||
|
|
||||||
|
// This keeps track of the current offsets within a single cell.
|
||||||
|
var cell_offset: struct {
|
||||||
|
cluster: u32 = 0,
|
||||||
|
x: f64 = 0,
|
||||||
|
y: f64 = 0,
|
||||||
|
} = .{};
|
||||||
|
|
||||||
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| {
|
||||||
|
// Our cluster is also our cell X position. If the cluster changes
|
||||||
|
// then we need to reset our current cell offsets.
|
||||||
const cluster = self.codepoints.items[index].cluster;
|
const cluster = self.codepoints.items[index].cluster;
|
||||||
|
if (cell_offset.cluster != cluster) cell_offset = .{
|
||||||
|
.cluster = cluster,
|
||||||
|
};
|
||||||
|
|
||||||
self.cell_buf.appendAssumeCapacity(.{
|
self.cell_buf.appendAssumeCapacity(.{
|
||||||
.x = @intCast(cluster),
|
.x = @intCast(cluster),
|
||||||
|
.x_offset = @intFromFloat(@round(cell_offset.x)),
|
||||||
|
.y_offset = @intFromFloat(@round(cell_offset.y)),
|
||||||
.glyph_index = glyph,
|
.glyph_index = glyph,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Add our advances to keep track of our current cell offsets.
|
||||||
|
// Advances apply to the NEXT cell.
|
||||||
|
cell_offset.x += advance.width;
|
||||||
|
cell_offset.y += advance.height;
|
||||||
|
|
||||||
_ = pos;
|
_ = pos;
|
||||||
_ = advance;
|
|
||||||
// const i = self.cell_buf.items.len - 1;
|
// const i = self.cell_buf.items.len - 1;
|
||||||
// log.warn(
|
// log.warn(
|
||||||
// "i={} glyph={} pos={} advance={} index={} cluster={}",
|
// "i={} codepoint={} glyph={} pos={} advance={} index={} cluster={}",
|
||||||
// .{ i, glyph, pos, advance, index, cluster },
|
// .{ i, self.codepoints.items[index].codepoint, glyph, pos, advance, index, cluster },
|
||||||
// );
|
// );
|
||||||
}
|
}
|
||||||
|
//log.warn("-------------------------------", .{});
|
||||||
|
|
||||||
return self.cell_buf.items;
|
return self.cell_buf.items;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user