font: Group maintains face ownership

This commit is contained in:
Mitchell Hashimoto
2023-08-25 13:00:04 -07:00
parent ea3b957bc7
commit 1dcf921ed9
2 changed files with 47 additions and 19 deletions

View File

@ -22,6 +22,7 @@ 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 options = @import("main.zig").options;
const quirks = @import("../quirks.zig");
const log = std.log.scoped(.font_group); const log = std.log.scoped(.font_group);
@ -30,7 +31,18 @@ const log = std.log.scoped(.font_group);
// usually only one font group for the entire process so this isn't the // usually only one font group for the entire process so this isn't the
// most important memory efficiency we can look for. This is totally opaque // most important memory efficiency we can look for. This is totally opaque
// 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(StyleElem));
const StyleElem = union(enum) {
deferred: DeferredFace, // Not loaded
loaded: Face, // Loaded
pub fn deinit(self: *StyleElem) void {
switch (self.*) {
.deferred => |*v| v.deinit(),
.loaded => |*v| v.deinit(),
}
}
};
/// The allocator for this group /// The allocator for this group
alloc: Allocator, alloc: Allocator,
@ -92,7 +104,7 @@ pub fn addFace(self: *Group, alloc: Allocator, style: Style, face: DeferredFace)
// We have some special indexes so we must never pass those. // We have some special indexes so we must never pass those.
if (list.items.len >= FontIndex.Special.start - 1) return error.GroupFull; if (list.items.len >= FontIndex.Special.start - 1) return error.GroupFull;
try list.append(alloc, face); try list.append(alloc, .{ .deferred = face });
} }
/// Get the face for the given style. This will always return the first /// Get the face for the given style. This will always return the first
@ -113,10 +125,10 @@ pub fn setSize(self: *Group, size: font.face.DesiredSize) !void {
// Resize all our faces that are loaded // Resize all our faces that are loaded
var it = self.faces.iterator(); var it = self.faces.iterator();
while (it.next()) |entry| { while (it.next()) |entry| {
for (entry.value.items) |*deferred| { for (entry.value.items) |*elem| switch (elem.*) {
if (!deferred.loaded()) continue; .deferred => continue,
try deferred.face.?.setSize(size); .loaded => |*f| try f.setSize(size),
} };
} }
// Set our size for future loads // Set our size for future loads
@ -231,8 +243,16 @@ pub fn indexForCodepoint(
} }
fn indexForCodepointExact(self: Group, cp: u32, style: Style, p: ?Presentation) ?FontIndex { fn indexForCodepointExact(self: Group, cp: u32, style: Style, p: ?Presentation) ?FontIndex {
for (self.faces.get(style).items, 0..) |deferred, i| { for (self.faces.get(style).items, 0..) |elem, i| {
if (deferred.hasCodepoint(cp, p)) { const has_cp = switch (elem) {
.deferred => |v| v.hasCodepoint(cp, p),
.loaded => |face| loaded: {
if (p) |desired| if (face.presentation != desired) break :loaded false;
break :loaded face.glyphIndex(cp) != null;
},
};
if (has_cp) {
return FontIndex{ return FontIndex{
.style = style, .style = style,
.idx = @intCast(i), .idx = @intCast(i),
@ -246,7 +266,7 @@ fn indexForCodepointExact(self: Group, cp: u32, style: Style, p: ?Presentation)
/// Returns the presentation for a specific font index. This is useful for /// Returns the presentation for a specific font index. This is useful for
/// determining what atlas is needed. /// determining what atlas is needed.
pub fn presentationFromIndex(self: Group, index: FontIndex) !font.Presentation { pub fn presentationFromIndex(self: *Group, index: FontIndex) !font.Presentation {
if (index.special()) |sp| switch (sp) { if (index.special()) |sp| switch (sp) {
.sprite => return .text, .sprite => return .text,
}; };
@ -256,12 +276,21 @@ pub fn presentationFromIndex(self: Group, index: FontIndex) !font.Presentation {
} }
/// Return the Face represented by a given FontIndex. Note that special /// Return the Face represented by a given FontIndex. Note that special
/// fonts (i.e. box glyphs) do not have a face. /// fonts (i.e. box glyphs) do not have a face. The returned face pointer is
pub fn faceFromIndex(self: Group, index: FontIndex) !*Face { /// only valid until the set of faces change.
pub fn faceFromIndex(self: *Group, index: FontIndex) !*Face {
if (index.special() != null) return error.SpecialHasNoFace; if (index.special() != null) return error.SpecialHasNoFace;
const deferred = &self.faces.get(index.style).items[@intCast(index.idx)]; const list = self.faces.getPtr(index.style);
try deferred.load(self.lib, self.size); const item = &list.items[@intCast(index.idx)];
return &deferred.face.?; return switch (item.*) {
.deferred => |*d| deferred: {
try d.load(self.lib, self.size);
item.* = .{ .loaded = d.face.? };
break :deferred &item.loaded;
},
.loaded => |*f| f,
};
} }
/// Render a glyph by glyph index into the given font atlas and return /// Render a glyph by glyph index into the given font atlas and return
@ -276,7 +305,7 @@ pub fn faceFromIndex(self: Group, index: FontIndex) !*Face {
/// doing text shaping, the text shaping library (i.e. HarfBuzz) will automatically /// doing text shaping, the text shaping library (i.e. HarfBuzz) will automatically
/// determine glyph indexes for a text run. /// determine glyph indexes for a text run.
pub fn renderGlyph( pub fn renderGlyph(
self: Group, self: *Group,
alloc: Allocator, alloc: Allocator,
atlas: *font.Atlas, atlas: *font.Atlas,
index: FontIndex, index: FontIndex,
@ -292,9 +321,8 @@ pub fn renderGlyph(
), ),
}; };
const face = &self.faces.get(index.style).items[@intCast(index.idx)]; const face = try self.faceFromIndex(index);
try face.load(self.lib, self.size); const glyph = try face.renderGlyph(alloc, atlas, glyph_index, opts);
const glyph = try face.face.?.renderGlyph(alloc, atlas, glyph_index, opts);
// log.warn("GLYPH={}", .{glyph}); // log.warn("GLYPH={}", .{glyph});
return glyph; return glyph;
} }

View File

@ -187,7 +187,7 @@ test {
.regular, .regular,
DeferredFace.initLoaded(try Face.init(lib, testFont, .{ .points = 12 })), DeferredFace.initLoaded(try Face.init(lib, testFont, .{ .points = 12 })),
); );
const group = cache.group; var group = cache.group;
// Visible ASCII. Do it twice to verify cache. // Visible ASCII. Do it twice to verify cache.
var i: u32 = 32; var i: u32 = 32;