font: start SharedGrid

This commit is contained in:
Mitchell Hashimoto
2024-04-02 21:20:58 -07:00
parent 88db80b7b0
commit 9fb883666a
2 changed files with 121 additions and 0 deletions

120
src/font/SharedGrid.zig Normal file
View File

@ -0,0 +1,120 @@
//! This structure represents the state required to render a terminal
//! grid using the font subsystem. It is "shared" because it is able to
//! be shared across multiple surfaces.
//!
//! It is desirable for the grid state to be shared because the font
//! configuration for a set of surfaces is almost always the same and
//! font data is relatively memory intensive. Further, the font subsystem
//! should be read-heavy compared to write-heavy, so it handles concurrent
//! reads well. Going even further, the font subsystem should be very rarely
//! read at all since it should only be necessary when the grid actively
//! changes.
const SharedGrid = @This();
// TODO(fontmem):
// - consider config changes and how they affect the shared grid.
const std = @import("std");
const assert = std.debug.assert;
const Allocator = std.mem.Allocator;
const font = @import("main.zig");
const Atlas = font.Atlas;
const CodepointResolver = font.CodepointResolver;
const Collection = font.Collection;
const Glyph = font.Glyph;
const Metrics = font.face.Metrics;
const Presentation = font.Presentation;
const Style = font.Style;
const RenderOptions = font.face.RenderOptions;
const log = std.log.scoped(.font_shared_grid);
/// Cache for codepoints to font indexes in a group.
codepoints: std.AutoHashMapUnmanaged(CodepointKey, ?Collection.Index) = .{},
/// Cache for glyph renders into the atlas.
glyphs: std.AutoHashMapUnmanaged(GlyphKey, Glyph) = .{},
/// The texture atlas to store renders in. The Glyph data in the glyphs
/// cache is dependent on the atlas matching.
atlas_greyscale: Atlas,
atlas_color: Atlas,
/// The underlying resolver for font data, fallbacks, etc. The shared
/// grid takes ownership of the resolver and will free it.
resolver: CodepointResolver,
/// The currently active grid metrics dictating the layout of the grid.
/// This is calculated based on the resolver and current fonts.
metrics: Metrics,
pub fn init(
alloc: Allocator,
resolver: CodepointResolver,
thicken: bool,
) !SharedGrid {
// We need to support loading options since we use the size data
assert(resolver.collection.load_options != null);
var atlas_greyscale = try Atlas.init(alloc, 512, .greyscale);
errdefer atlas_greyscale.deinit(alloc);
var atlas_color = try Atlas.init(alloc, 512, .rgba);
errdefer atlas_color.deinit(alloc);
var result: SharedGrid = .{
.resolver = resolver,
.atlas_greyscale = atlas_greyscale,
.atlas_color = atlas_color,
};
// We set an initial capacity that can fit a good number of characters.
// This number was picked empirically based on my own terminal usage.
try result.codepoints.ensureTotalCapacity(alloc, 128);
try result.glyphs.ensureTotalCapacity(alloc, 128);
// Initialize our metrics.
try result.reloadMetrics(thicken);
return result;
}
pub fn deinit(self: *SharedGrid, alloc: Allocator) void {
self.codepoints.deinit(alloc);
self.glyphs.deinit(alloc);
self.atlas_greyscale.deinit(alloc);
self.atlas_color.deinit(alloc);
self.resolver.deinit(alloc);
}
fn reloadMetrics(self: *SharedGrid, thicken: bool) !void {
// Get our cell metrics based on a regular font ascii 'M'. Why 'M'?
// Doesn't matter, any normal ASCII will do we're just trying to make
// sure we use the regular font.
// We don't go through our caching layer because we want to minimize
// possible failures.
const collection = &self.resolver.collection;
const index = collection.getIndex('M', .regular, .{ .any = {} }).?;
const face = try collection.getFace(index);
self.metrics = face.metrics;
// Setup our sprite font.
self.resolver.sprite = .{
.width = self.metrics.cell_width,
.height = self.metrics.cell_height,
.thickness = self.metrics.underline_thickness *
@as(u32, if (thicken) 2 else 1),
.underline_position = self.metrics.underline_position,
};
}
const CodepointKey = struct {
style: Style,
codepoint: u32,
presentation: ?Presentation,
};
const GlyphKey = struct {
index: Collection.Index,
glyph: u32,
opts: RenderOptions,
};

View File

@ -16,6 +16,7 @@ pub const GroupCacheSet = @import("GroupCacheSet.zig");
pub const Glyph = @import("Glyph.zig");
pub const shape = @import("shape.zig");
pub const Shaper = shape.Shaper;
pub const SharedGrid = @import("SharedGrid.zig");
pub const sprite = @import("sprite.zig");
pub const Sprite = sprite.Sprite;
pub const SpriteFace = sprite.Face;