diff --git a/src/font/shape.zig b/src/font/shape.zig index 2aa3bf442..df54998a1 100644 --- a/src/font/shape.zig +++ b/src/font/shape.zig @@ -1,6 +1,7 @@ const builtin = @import("builtin"); const options = @import("main.zig").options; const harfbuzz = @import("shaper/harfbuzz.zig"); +const coretext = @import("shaper/coretext.zig"); pub const web_canvas = @import("shaper/web_canvas.zig"); pub usingnamespace @import("shaper/run.zig"); @@ -12,6 +13,8 @@ pub const Shaper = switch (options.backend) { .coretext, => harfbuzz.Shaper, + //.coretext => coretext.Shaper, + .web_canvas => web_canvas.Shaper, }; @@ -24,6 +27,10 @@ pub const Cell = struct { /// caller has access to the original screen cell. 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 /// 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 diff --git a/src/font/shaper/coretext.zig b/src/font/shaper/coretext.zig index 20b9c7539..eab684f33 100644 --- a/src/font/shaper/coretext.zig +++ b/src/font/shaper/coretext.zig @@ -24,6 +24,10 @@ pub const Shaper = struct { /// The string used for shaping the current run. 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. cell_buf: CellBuf, @@ -34,6 +38,29 @@ pub const Shaper = struct { 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 // can turn them off by setting the features to "-liga" for example. 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. /// This should be at least the number of columns in the terminal. pub fn init(alloc: Allocator, opts: font.shape.Options) !Shaper { - // TODO: features - _ = opts; + var feats: FeatureList = .{}; + 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{ .alloc = alloc, .cell_buf = .{}, + .features = feats, }; } pub fn deinit(self: *Shaper) void { self.cell_buf.deinit(self.alloc); self.codepoints.deinit(self.alloc); + for (self.features.items) |feature| feature.deinit(); + self.features.deinit(self.alloc); } pub fn runIterator( @@ -147,23 +192,43 @@ pub const Shaper = struct { assert(glyphs.len == advances.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(); try self.cell_buf.ensureTotalCapacity(self.alloc, glyphs.len); 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; + if (cell_offset.cluster != cluster) cell_offset = .{ + .cluster = cluster, + }; + self.cell_buf.appendAssumeCapacity(.{ .x = @intCast(cluster), + .x_offset = @intFromFloat(@round(cell_offset.x)), + .y_offset = @intFromFloat(@round(cell_offset.y)), .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; - _ = advance; // const i = self.cell_buf.items.len - 1; // log.warn( - // "i={} glyph={} pos={} advance={} index={} cluster={}", - // .{ i, glyph, pos, advance, index, cluster }, + // "i={} codepoint={} glyph={} pos={} advance={} index={} cluster={}", + // .{ i, self.codepoints.items[index].codepoint, glyph, pos, advance, index, cluster }, // ); } + //log.warn("-------------------------------", .{}); return self.cell_buf.items; }