mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-15 00:06:09 +03:00
font Group has Lib, loads deferred faces when needed
This commit is contained in:
@ -160,7 +160,7 @@ pub fn init(
|
|||||||
var font_lib = try font.Library.init();
|
var font_lib = try font.Library.init();
|
||||||
errdefer font_lib.deinit();
|
errdefer font_lib.deinit();
|
||||||
var font_group = try font.GroupCache.init(alloc, group: {
|
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);
|
errdefer group.deinit(alloc);
|
||||||
|
|
||||||
// Our regular font
|
// Our regular font
|
||||||
@ -610,7 +610,7 @@ pub fn updateCell(
|
|||||||
// If the cell has a character, draw it
|
// If the cell has a character, draw it
|
||||||
if (cell.char > 0) {
|
if (cell.char > 0) {
|
||||||
// Render
|
// 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(
|
const glyph = try self.font_group.renderGlyph(
|
||||||
self.alloc,
|
self.alloc,
|
||||||
shaper_run.font_index,
|
shaper_run.font_index,
|
||||||
|
@ -17,6 +17,7 @@ const Library = @import("main.zig").Library;
|
|||||||
const Glyph = @import("main.zig").Glyph;
|
const Glyph = @import("main.zig").Glyph;
|
||||||
const Style = @import("main.zig").Style;
|
const Style = @import("main.zig").Style;
|
||||||
const Presentation = @import("main.zig").Presentation;
|
const Presentation = @import("main.zig").Presentation;
|
||||||
|
const options = @import("main.zig").options;
|
||||||
|
|
||||||
const log = std.log.scoped(.font_group);
|
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.
|
// to the user so we can change this later.
|
||||||
const StyleArray = std.EnumArray(Style, std.ArrayListUnmanaged(DeferredFace));
|
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.
|
/// The available faces we have. This shouldn't be modified manually.
|
||||||
/// Instead, use the functions available on Group.
|
/// Instead, use the functions available on Group.
|
||||||
faces: StyleArray,
|
faces: StyleArray,
|
||||||
|
|
||||||
pub fn init(alloc: Allocator) !Group {
|
pub fn init(alloc: Allocator, lib: Library) !Group {
|
||||||
var result = Group{ .faces = undefined };
|
var result = Group{ .lib = lib, .faces = undefined };
|
||||||
|
|
||||||
// Initialize all our styles to initially sized lists.
|
// Initialize all our styles to initially sized lists.
|
||||||
var i: usize = 0;
|
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.
|
/// Return the Face represented by a given FontIndex.
|
||||||
pub fn faceFromIndex(self: Group, index: FontIndex) Face {
|
pub fn faceFromIndex(self: Group, index: FontIndex) !Face {
|
||||||
const deferred = self.faces.get(index.style).items[@intCast(usize, index.idx)];
|
const deferred = &self.faces.get(index.style).items[@intCast(usize, index.idx)];
|
||||||
|
try deferred.load(self.lib);
|
||||||
return deferred.face.?;
|
return deferred.face.?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -148,8 +153,8 @@ pub fn renderGlyph(
|
|||||||
index: FontIndex,
|
index: FontIndex,
|
||||||
glyph_index: u32,
|
glyph_index: u32,
|
||||||
) !Glyph {
|
) !Glyph {
|
||||||
const face = self.faces.get(index.style).items[@intCast(usize, index.idx)];
|
const face = &self.faces.get(index.style).items[@intCast(usize, index.idx)];
|
||||||
assert(face.loaded());
|
try face.load(self.lib);
|
||||||
return try face.face.?.renderGlyph(alloc, atlas, glyph_index);
|
return try face.face.?.renderGlyph(alloc, atlas, glyph_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,7 +171,7 @@ test {
|
|||||||
var lib = try Library.init();
|
var lib = try Library.init();
|
||||||
defer lib.deinit();
|
defer lib.deinit();
|
||||||
|
|
||||||
var group = try init(alloc);
|
var group = try init(alloc, lib);
|
||||||
defer group.deinit(alloc);
|
defer group.deinit(alloc);
|
||||||
|
|
||||||
try group.addFace(alloc, .regular, DeferredFace.initLoaded(try Face.init(lib, testFont, .{ .points = 12 })));
|
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);
|
try testing.expectEqual(@as(FontIndex.IndexInt, 0), idx.idx);
|
||||||
|
|
||||||
// Render it
|
// Render it
|
||||||
const face = group.faceFromIndex(idx);
|
const face = try group.faceFromIndex(idx);
|
||||||
const glyph_index = face.glyphIndex(i).?;
|
const glyph_index = face.glyphIndex(i).?;
|
||||||
_ = try group.renderGlyph(
|
_ = try group.renderGlyph(
|
||||||
alloc,
|
alloc,
|
||||||
@ -210,3 +215,43 @@ test {
|
|||||||
try testing.expectEqual(@as(FontIndex.IndexInt, 1), idx.idx);
|
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,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -94,7 +94,7 @@ pub fn metrics(self: *GroupCache, alloc: Allocator) !Metrics {
|
|||||||
var i: u32 = 32;
|
var i: u32 = 32;
|
||||||
while (i <= 126) : (i += 1) {
|
while (i <= 126) : (i += 1) {
|
||||||
const index = (try self.indexForCodepoint(alloc, i, .regular, .text)).?;
|
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_index = face.glyphIndex(i).?;
|
||||||
const glyph = try self.renderGlyph(alloc, index, glyph_index);
|
const glyph = try self.renderGlyph(alloc, index, glyph_index);
|
||||||
if (glyph.advance_x > cell_width) {
|
if (glyph.advance_x > cell_width) {
|
||||||
@ -110,7 +110,7 @@ pub fn metrics(self: *GroupCache, alloc: Allocator) !Metrics {
|
|||||||
const cell_height: f32 = cell_height: {
|
const cell_height: f32 = cell_height: {
|
||||||
// Get the '_' char for height
|
// Get the '_' char for height
|
||||||
const index = (try self.indexForCodepoint(alloc, '_', .regular, .text)).?;
|
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_index = face.glyphIndex('_').?;
|
||||||
const glyph = try self.renderGlyph(alloc, index, glyph_index);
|
const glyph = try self.renderGlyph(alloc, index, glyph_index);
|
||||||
|
|
||||||
@ -179,7 +179,7 @@ pub fn renderGlyph(
|
|||||||
if (gop.found_existing) return gop.value_ptr.*;
|
if (gop.found_existing) return gop.value_ptr.*;
|
||||||
|
|
||||||
// Uncached, render it
|
// 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 atlas: *Atlas = if (face.hasColor()) &self.atlas_color else &self.atlas_greyscale;
|
||||||
const glyph = self.group.renderGlyph(
|
const glyph = self.group.renderGlyph(
|
||||||
alloc,
|
alloc,
|
||||||
@ -218,7 +218,7 @@ test {
|
|||||||
var lib = try Library.init();
|
var lib = try Library.init();
|
||||||
defer lib.deinit();
|
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);
|
defer cache.deinit(alloc);
|
||||||
|
|
||||||
// Setup group
|
// Setup group
|
||||||
@ -237,7 +237,7 @@ test {
|
|||||||
try testing.expectEqual(@as(Group.FontIndex.IndexInt, 0), idx.idx);
|
try testing.expectEqual(@as(Group.FontIndex.IndexInt, 0), idx.idx);
|
||||||
|
|
||||||
// Render
|
// Render
|
||||||
const face = cache.group.faceFromIndex(idx);
|
const face = try cache.group.faceFromIndex(idx);
|
||||||
const glyph_index = face.glyphIndex(i).?;
|
const glyph_index = face.glyphIndex(i).?;
|
||||||
_ = try cache.renderGlyph(
|
_ = try cache.renderGlyph(
|
||||||
alloc,
|
alloc,
|
||||||
@ -258,7 +258,7 @@ test {
|
|||||||
try testing.expectEqual(@as(Group.FontIndex.IndexInt, 0), idx.idx);
|
try testing.expectEqual(@as(Group.FontIndex.IndexInt, 0), idx.idx);
|
||||||
|
|
||||||
// Render
|
// Render
|
||||||
const face = group.faceFromIndex(idx);
|
const face = try group.faceFromIndex(idx);
|
||||||
const glyph_index = face.glyphIndex(i).?;
|
const glyph_index = face.glyphIndex(i).?;
|
||||||
_ = try cache.renderGlyph(
|
_ = try cache.renderGlyph(
|
||||||
alloc,
|
alloc,
|
||||||
|
@ -62,7 +62,7 @@ pub fn shape(self: *Shaper, run: TextRun) ![]Cell {
|
|||||||
harfbuzz.Feature.fromString("liga").?,
|
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);
|
harfbuzz.shape(face.hb_font, self.hb_buf, hb_feats);
|
||||||
|
|
||||||
// If our buffer is empty, we short-circuit the rest of the work
|
// 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);
|
var cache_ptr = try alloc.create(GroupCache);
|
||||||
errdefer alloc.destroy(cache_ptr);
|
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);
|
errdefer cache_ptr.*.deinit(alloc);
|
||||||
|
|
||||||
// Setup group
|
// Setup group
|
||||||
|
Reference in New Issue
Block a user