move cell metrics calculation out into src/font

This commit is contained in:
Mitchell Hashimoto
2022-08-29 11:44:05 -07:00
parent bd9c048c02
commit 2cab94e64d
3 changed files with 76 additions and 60 deletions

View File

@ -166,56 +166,12 @@ pub fn init(
break :group group;
});
errdefer font_group.deinit(alloc);
// Load all visible ASCII characters and build our cell width based on
// the widest character that we see.
const cell_width: f32 = cell_width: {
var cell_width: f32 = 0;
var i: u32 = 32;
while (i <= 126) : (i += 1) {
const index = (try font_group.indexForCodepoint(alloc, .regular, i)).?;
const face = font_group.group.faceFromIndex(index);
const glyph_index = face.glyphIndex(i).?;
const glyph = try font_group.renderGlyph(alloc, index, glyph_index);
if (glyph.advance_x > cell_width) {
cell_width = @ceil(glyph.advance_x);
}
}
break :cell_width cell_width;
};
// The cell height is the vertical height required to render underscore
// '_' which should live at the bottom of a cell.
const cell_height: f32 = cell_height: {
// Get the '_' char for height
const index = (try font_group.indexForCodepoint(alloc, .regular, '_')).?;
const face = font_group.group.faceFromIndex(index);
const glyph_index = face.glyphIndex('_').?;
const glyph = try font_group.renderGlyph(alloc, index, glyph_index);
// This is the height reported by the font face
const face_height: i32 = face.unitsToPxY(face.face.handle.*.height);
// Determine the height of the underscore char
var res: i32 = face.unitsToPxY(face.face.handle.*.ascender);
res -= glyph.offset_y;
res += @intCast(i32, glyph.height);
// We take whatever is larger to account for some fonts that
// put the underscore outside f the rectangle.
if (res < face_height) res = face_height;
break :cell_height @intToFloat(f32, res);
};
const cell_baseline = cell_baseline: {
const face = font_group.group.faces.get(.regular).items[0];
break :cell_baseline cell_height - @intToFloat(
f32,
face.unitsToPxY(face.face.handle.*.ascender),
);
};
log.debug("cell dimensions w={d} h={d} baseline={d}", .{ cell_width, cell_height, cell_baseline });
const metrics = try font_group.metrics(alloc);
log.debug("cell dimensions={}", .{metrics});
// Create our shader
const program = try gl.Program.createVF(
@ -226,8 +182,8 @@ pub fn init(
// Set our cell dimensions
const pbind = try program.use();
defer pbind.unbind();
try program.setUniform("cell_size", @Vector(2, f32){ cell_width, cell_height });
try program.setUniform("glyph_baseline", cell_baseline);
try program.setUniform("cell_size", @Vector(2, f32){ metrics.cell_width, metrics.cell_height });
try program.setUniform("glyph_baseline", metrics.cell_baseline);
// Set all of our texture indexes
try program.setUniform("text", 0);
@ -328,7 +284,7 @@ pub fn init(
return Grid{
.alloc = alloc,
.cells = .{},
.cell_size = .{ .width = cell_width, .height = cell_height },
.cell_size = .{ .width = metrics.cell_width, .height = metrics.cell_height },
.size = .{ .rows = 0, .columns = 0 },
.program = program,
.vao = vao,

View File

@ -11,6 +11,7 @@ const Library = @import("main.zig").Library;
const Glyph = @import("main.zig").Glyph;
const Style = @import("main.zig").Style;
const Group = @import("main.zig").Group;
const Metrics = @import("main.zig").Metrics;
const log = std.log.scoped(.font_groupcache);
@ -80,6 +81,66 @@ pub fn reset(self: *GroupCache) void {
self.glyphs.clearRetainingCapacity();
}
/// Calculate the metrics for this group. This also warms the cache
/// since this preloads all the ASCII characters.
pub fn metrics(self: *GroupCache, alloc: Allocator) !Metrics {
// Load all visible ASCII characters and build our cell width based on
// the widest character that we see.
const cell_width: f32 = cell_width: {
var cell_width: f32 = 0;
var i: u32 = 32;
while (i <= 126) : (i += 1) {
const index = (try self.indexForCodepoint(alloc, .regular, i)).?;
const face = self.group.faceFromIndex(index);
const glyph_index = face.glyphIndex(i).?;
const glyph = try self.renderGlyph(alloc, index, glyph_index);
if (glyph.advance_x > cell_width) {
cell_width = @ceil(glyph.advance_x);
}
}
break :cell_width cell_width;
};
// The cell height is the vertical height required to render underscore
// '_' which should live at the bottom of a cell.
const cell_height: f32 = cell_height: {
// Get the '_' char for height
const index = (try self.indexForCodepoint(alloc, .regular, '_')).?;
const face = self.group.faceFromIndex(index);
const glyph_index = face.glyphIndex('_').?;
const glyph = try self.renderGlyph(alloc, index, glyph_index);
// This is the height reported by the font face
const face_height: i32 = face.unitsToPxY(face.face.handle.*.height);
// Determine the height of the underscore char
var res: i32 = face.unitsToPxY(face.face.handle.*.ascender);
res -= glyph.offset_y;
res += @intCast(i32, glyph.height);
// We take whatever is larger to account for some fonts that
// put the underscore outside f the rectangle.
if (res < face_height) res = face_height;
break :cell_height @intToFloat(f32, res);
};
const cell_baseline = cell_baseline: {
const face = self.group.faces.get(.regular).items[0];
break :cell_baseline cell_height - @intToFloat(
f32,
face.unitsToPxY(face.face.handle.*.ascender),
);
};
return Metrics{
.cell_width = cell_width,
.cell_height = cell_height,
.cell_baseline = cell_baseline,
};
}
/// Get the font index for a given codepoint. This is cached.
pub fn indexForCodepoint(self: *GroupCache, alloc: Allocator, style: Style, cp: u32) !?Group.FontIndex {
const key: CodepointKey = .{ .style = style, .codepoint = cp };

View File

@ -14,16 +14,15 @@ pub const Style = enum(u2) {
bold_italic = 3,
};
/// Returns the UTF-32 codepoint for the given value.
pub fn codepoint(v: anytype) u32 {
// We need a UTF32 codepoint for freetype
return switch (@TypeOf(v)) {
u32 => v,
comptime_int, u8 => @intCast(u32, v),
[]const u8 => @intCast(u32, try std.unicode.utfDecode(v)),
else => @compileError("invalid codepoint type"),
};
}
/// Font metrics useful for things such as grid calculation.
pub const Metrics = struct {
/// The width and height of a monospace cell.
cell_width: f32,
cell_height: f32,
/// The baseline offset that can be used to place underlines.
cell_baseline: f32,
};
test {
@import("std").testing.refAllDecls(@This());