font: shape API, calls shape and outputs some debug

This commit is contained in:
Mitchell Hashimoto
2022-08-29 20:15:29 -07:00
parent 5a232e0c3e
commit 39376feae0
2 changed files with 61 additions and 2 deletions

View File

@ -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];
}

View File

@ -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,