mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-08-02 14:57:31 +03:00
font: shape API, calls shape and outputs some debug
This commit is contained in:
@ -192,7 +192,10 @@ pub const Buffer = struct {
|
||||
/// long as buffer contents are not modified.
|
||||
pub fn getGlyphInfos(self: Buffer) []GlyphInfo {
|
||||
var length: u32 = 0;
|
||||
const ptr = c.hb_buffer_get_glyph_infos(self.handle, &length);
|
||||
const ptr = @ptrCast(
|
||||
[*c]GlyphInfo,
|
||||
c.hb_buffer_get_glyph_infos(self.handle, &length),
|
||||
);
|
||||
return ptr[0..length];
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
const Shaper = @This();
|
||||
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const harfbuzz = @import("harfbuzz");
|
||||
const Atlas = @import("../Atlas.zig");
|
||||
@ -12,6 +13,8 @@ const Library = @import("main.zig").Library;
|
||||
const Style = @import("main.zig").Style;
|
||||
const terminal = @import("../terminal/main.zig");
|
||||
|
||||
const log = std.log.scoped(.font_shaper);
|
||||
|
||||
/// The font group to use under the covers
|
||||
group: *GroupCache,
|
||||
|
||||
@ -37,6 +40,28 @@ pub fn runIterator(self: *Shaper, row: terminal.Screen.Row) RunIterator {
|
||||
return .{ .shaper = self, .row = row };
|
||||
}
|
||||
|
||||
/// Shape the given text run. The text run must be the immediately previous
|
||||
/// text run that was iterated since the text run does share state with the
|
||||
/// Shaper struct.
|
||||
///
|
||||
/// NOTE: there is no return value here yet because its still WIP
|
||||
pub fn shape(self: Shaper, run: TextRun) void {
|
||||
const face = self.group.group.faceFromIndex(run.font_index);
|
||||
harfbuzz.shape(face.hb_font, self.hb_buf, null);
|
||||
|
||||
const info = self.hb_buf.getGlyphInfos();
|
||||
const pos = self.hb_buf.getGlyphPositions() orelse return;
|
||||
|
||||
// This is perhaps not true somewhere, but we currently assume it is true.
|
||||
// If it isn't true, I'd like to catch it and learn more.
|
||||
assert(info.len == pos.len);
|
||||
|
||||
// log.warn("info={} pos={}", .{ info.len, pos.len });
|
||||
// for (info) |v, i| {
|
||||
// log.warn("info {} = {}", .{ i, v });
|
||||
// }
|
||||
}
|
||||
|
||||
/// A single text run. A text run is only valid for one Shaper and
|
||||
/// until the next run is created.
|
||||
pub const TextRun = struct {
|
||||
@ -74,13 +99,14 @@ pub const RunIterator = struct {
|
||||
// Determine the font for this cell
|
||||
const font_idx_opt = try self.shaper.group.indexForCodepoint(alloc, style, cell.char);
|
||||
const font_idx = font_idx_opt.?;
|
||||
//log.warn("char={x} idx={}", .{ cell.char, font_idx });
|
||||
if (j == self.i) current_font = font_idx;
|
||||
|
||||
// If our fonts are not equal, then we're done with our run.
|
||||
if (font_idx.int() != current_font.int()) break;
|
||||
|
||||
// Continue with our run
|
||||
self.shaper.hb_buf.add(cell.char, @intCast(u32, j - self.i));
|
||||
self.shaper.hb_buf.add(cell.char, @intCast(u32, j));
|
||||
}
|
||||
|
||||
// Finalize our buffer
|
||||
@ -134,6 +160,36 @@ test "run iterator" {
|
||||
}
|
||||
}
|
||||
|
||||
test "shape" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
var testdata = try testShaper(alloc);
|
||||
defer testdata.deinit();
|
||||
|
||||
var buf: [32]u8 = undefined;
|
||||
var buf_idx: usize = 0;
|
||||
buf_idx += try std.unicode.utf8Encode(0x1F44D, buf[buf_idx..]); // Thumbs up plain
|
||||
buf_idx += try std.unicode.utf8Encode(0x1F44D, buf[buf_idx..]); // Thumbs up plain
|
||||
buf_idx += try std.unicode.utf8Encode(0x1F3FD, buf[buf_idx..]); // Medium skin tone
|
||||
|
||||
// Make a screen with some data
|
||||
var screen = try terminal.Screen.init(alloc, 3, 10, 0);
|
||||
defer screen.deinit(alloc);
|
||||
screen.testWriteString(buf[0..buf_idx]);
|
||||
|
||||
// Get our run iterator
|
||||
var shaper = testdata.shaper;
|
||||
var it = shaper.runIterator(screen.getRow(.{ .screen = 0 }));
|
||||
var count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
count += 1;
|
||||
try testing.expectEqual(@as(u32, 3), shaper.hb_buf.getLength());
|
||||
shaper.shape(run);
|
||||
}
|
||||
try testing.expectEqual(@as(usize, 1), count);
|
||||
}
|
||||
|
||||
const TestShaper = struct {
|
||||
alloc: Allocator,
|
||||
shaper: Shaper,
|
||||
|
Reference in New Issue
Block a user