From 2cab94e64daddf71697720c61192e73b7fa274aa Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Mon, 29 Aug 2022 11:44:05 -0700 Subject: [PATCH] move cell metrics calculation out into src/font --- src/Grid.zig | 56 ++++--------------------------------- src/font/GroupCache.zig | 61 +++++++++++++++++++++++++++++++++++++++++ src/font/main.zig | 19 ++++++------- 3 files changed, 76 insertions(+), 60 deletions(-) diff --git a/src/Grid.zig b/src/Grid.zig index cc5d83dd1..4dc8b1007 100644 --- a/src/Grid.zig +++ b/src/Grid.zig @@ -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, diff --git a/src/font/GroupCache.zig b/src/font/GroupCache.zig index 2b740cfda..d319a3ce2 100644 --- a/src/font/GroupCache.zig +++ b/src/font/GroupCache.zig @@ -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 }; diff --git a/src/font/main.zig b/src/font/main.zig index 85b61b92a..ec3e8b73a 100644 --- a/src/font/main.zig +++ b/src/font/main.zig @@ -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());