diff --git a/src/Grid.zig b/src/Grid.zig index 9630cfc6e..6ff0eaa08 100644 --- a/src/Grid.zig +++ b/src/Grid.zig @@ -160,7 +160,7 @@ pub fn init( var font_lib = try font.Library.init(); errdefer font_lib.deinit(); var font_group = try font.GroupCache.init(alloc, group: { - var group = try font.Group.init(alloc); + var group = try font.Group.init(alloc, font_lib); errdefer group.deinit(alloc); // Our regular font @@ -610,7 +610,7 @@ pub fn updateCell( // If the cell has a character, draw it if (cell.char > 0) { // Render - const face = self.font_group.group.faceFromIndex(shaper_run.font_index); + const face = try self.font_group.group.faceFromIndex(shaper_run.font_index); const glyph = try self.font_group.renderGlyph( self.alloc, shaper_run.font_index, diff --git a/src/font/Group.zig b/src/font/Group.zig index b49ef0c58..3c9454e28 100644 --- a/src/font/Group.zig +++ b/src/font/Group.zig @@ -17,6 +17,7 @@ const Library = @import("main.zig").Library; const Glyph = @import("main.zig").Glyph; const Style = @import("main.zig").Style; const Presentation = @import("main.zig").Presentation; +const options = @import("main.zig").options; const log = std.log.scoped(.font_group); @@ -27,12 +28,15 @@ const log = std.log.scoped(.font_group); // to the user so we can change this later. const StyleArray = std.EnumArray(Style, std.ArrayListUnmanaged(DeferredFace)); +/// The library being used for all the faces. +lib: Library, + /// The available faces we have. This shouldn't be modified manually. /// Instead, use the functions available on Group. faces: StyleArray, -pub fn init(alloc: Allocator) !Group { - var result = Group{ .faces = undefined }; +pub fn init(alloc: Allocator, lib: Library) !Group { + var result = Group{ .lib = lib, .faces = undefined }; // Initialize all our styles to initially sized lists. var i: usize = 0; @@ -125,8 +129,9 @@ fn indexForCodepointExact(self: Group, cp: u32, style: Style, p: ?Presentation) } /// Return the Face represented by a given FontIndex. -pub fn faceFromIndex(self: Group, index: FontIndex) Face { - const deferred = self.faces.get(index.style).items[@intCast(usize, index.idx)]; +pub fn faceFromIndex(self: Group, index: FontIndex) !Face { + const deferred = &self.faces.get(index.style).items[@intCast(usize, index.idx)]; + try deferred.load(self.lib); return deferred.face.?; } @@ -148,8 +153,8 @@ pub fn renderGlyph( index: FontIndex, glyph_index: u32, ) !Glyph { - const face = self.faces.get(index.style).items[@intCast(usize, index.idx)]; - assert(face.loaded()); + const face = &self.faces.get(index.style).items[@intCast(usize, index.idx)]; + try face.load(self.lib); return try face.face.?.renderGlyph(alloc, atlas, glyph_index); } @@ -166,7 +171,7 @@ test { var lib = try Library.init(); defer lib.deinit(); - var group = try init(alloc); + var group = try init(alloc, lib); defer group.deinit(alloc); try group.addFace(alloc, .regular, DeferredFace.initLoaded(try Face.init(lib, testFont, .{ .points = 12 }))); @@ -181,7 +186,7 @@ test { try testing.expectEqual(@as(FontIndex.IndexInt, 0), idx.idx); // Render it - const face = group.faceFromIndex(idx); + const face = try group.faceFromIndex(idx); const glyph_index = face.glyphIndex(i).?; _ = try group.renderGlyph( alloc, @@ -210,3 +215,43 @@ test { try testing.expectEqual(@as(FontIndex.IndexInt, 1), idx.idx); } } + +test { + if (!options.fontconfig) return error.SkipZigTest; + + const testing = std.testing; + const alloc = testing.allocator; + const Discover = @import("main.zig").Discover; + + // Search for fonts + var fc = Discover.init(); + var it = try fc.discover(.{ .family = "monospace", .size = 12 }); + defer it.deinit(); + + // Initialize the group with the deferred face + var lib = try Library.init(); + defer lib.deinit(); + var group = try init(alloc, lib); + defer group.deinit(alloc); + try group.addFace(alloc, .regular, (try it.next()).?); + + // Should find all visible ASCII + var atlas_greyscale = try Atlas.init(alloc, 512, .greyscale); + defer atlas_greyscale.deinit(alloc); + var i: u32 = 32; + while (i < 127) : (i += 1) { + const idx = group.indexForCodepoint(i, .regular, null).?; + try testing.expectEqual(Style.regular, idx.style); + try testing.expectEqual(@as(FontIndex.IndexInt, 0), idx.idx); + + // Render it + const face = try group.faceFromIndex(idx); + const glyph_index = face.glyphIndex(i).?; + _ = try group.renderGlyph( + alloc, + &atlas_greyscale, + idx, + glyph_index, + ); + } +} diff --git a/src/font/GroupCache.zig b/src/font/GroupCache.zig index c81ea42e8..479d11b4b 100644 --- a/src/font/GroupCache.zig +++ b/src/font/GroupCache.zig @@ -94,7 +94,7 @@ pub fn metrics(self: *GroupCache, alloc: Allocator) !Metrics { var i: u32 = 32; while (i <= 126) : (i += 1) { const index = (try self.indexForCodepoint(alloc, i, .regular, .text)).?; - const face = self.group.faceFromIndex(index); + const face = try 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) { @@ -110,7 +110,7 @@ pub fn metrics(self: *GroupCache, alloc: Allocator) !Metrics { const cell_height: f32 = cell_height: { // Get the '_' char for height const index = (try self.indexForCodepoint(alloc, '_', .regular, .text)).?; - const face = self.group.faceFromIndex(index); + const face = try self.group.faceFromIndex(index); const glyph_index = face.glyphIndex('_').?; const glyph = try self.renderGlyph(alloc, index, glyph_index); @@ -179,7 +179,7 @@ pub fn renderGlyph( if (gop.found_existing) return gop.value_ptr.*; // Uncached, render it - const face = self.group.faceFromIndex(index); + const face = try self.group.faceFromIndex(index); const atlas: *Atlas = if (face.hasColor()) &self.atlas_color else &self.atlas_greyscale; const glyph = self.group.renderGlyph( alloc, @@ -218,7 +218,7 @@ test { var lib = try Library.init(); defer lib.deinit(); - var cache = try init(alloc, try Group.init(alloc)); + var cache = try init(alloc, try Group.init(alloc, lib)); defer cache.deinit(alloc); // Setup group @@ -237,7 +237,7 @@ test { try testing.expectEqual(@as(Group.FontIndex.IndexInt, 0), idx.idx); // Render - const face = cache.group.faceFromIndex(idx); + const face = try cache.group.faceFromIndex(idx); const glyph_index = face.glyphIndex(i).?; _ = try cache.renderGlyph( alloc, @@ -258,7 +258,7 @@ test { try testing.expectEqual(@as(Group.FontIndex.IndexInt, 0), idx.idx); // Render - const face = group.faceFromIndex(idx); + const face = try group.faceFromIndex(idx); const glyph_index = face.glyphIndex(i).?; _ = try cache.renderGlyph( alloc, diff --git a/src/font/Shaper.zig b/src/font/Shaper.zig index 4c901bd06..e1968e92a 100644 --- a/src/font/Shaper.zig +++ b/src/font/Shaper.zig @@ -62,7 +62,7 @@ pub fn shape(self: *Shaper, run: TextRun) ![]Cell { harfbuzz.Feature.fromString("liga").?, }; - const face = run.group.group.faceFromIndex(run.font_index); + const face = try run.group.group.faceFromIndex(run.font_index); harfbuzz.shape(face.hb_font, self.hb_buf, hb_feats); // If our buffer is empty, we short-circuit the rest of the work @@ -596,7 +596,7 @@ fn testShaper(alloc: Allocator) !TestShaper { var cache_ptr = try alloc.create(GroupCache); errdefer alloc.destroy(cache_ptr); - cache_ptr.* = try GroupCache.init(alloc, try Group.init(alloc)); + cache_ptr.* = try GroupCache.init(alloc, try Group.init(alloc, lib)); errdefer cache_ptr.*.deinit(alloc); // Setup group