mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-15 00:06:09 +03:00
rearrange box rendering to prepare for more sprite drawing
This commit is contained in:
@ -290,7 +290,7 @@ pub fn create(alloc: Allocator, app: *App, config: *const Config) !*Window {
|
|||||||
const cell_size = try renderer.CellSize.init(alloc, font_group);
|
const cell_size = try renderer.CellSize.init(alloc, font_group);
|
||||||
|
|
||||||
// Setup our box font
|
// Setup our box font
|
||||||
font_group.group.box_font = font.BoxFont{
|
font_group.group.sprite = font.sprite.Face{
|
||||||
.width = @floatToInt(u32, cell_size.width),
|
.width = @floatToInt(u32, cell_size.width),
|
||||||
.height = @floatToInt(u32, cell_size.height),
|
.height = @floatToInt(u32, cell_size.height),
|
||||||
.thickness = 2,
|
.thickness = 2,
|
||||||
|
@ -50,10 +50,11 @@ faces: StyleArray,
|
|||||||
/// the codepoint. This can be set after initialization.
|
/// the codepoint. This can be set after initialization.
|
||||||
discover: ?font.Discover = null,
|
discover: ?font.Discover = null,
|
||||||
|
|
||||||
/// Set this to a non-null value to enable box font glyph drawing. If this
|
/// Set this to a non-null value to enable sprite glyph drawing. If this
|
||||||
/// isn't enabled we'll just fall through to trying to use regular fonts
|
/// isn't enabled we'll just fall through to trying to use regular fonts
|
||||||
/// to render box glyphs.
|
/// to render sprite glyphs. But more than likely, if this isn't set then
|
||||||
box_font: ?font.BoxFont = null,
|
/// terminal rendering will look wrong.
|
||||||
|
sprite: ?font.sprite.Face = null,
|
||||||
|
|
||||||
pub fn init(
|
pub fn init(
|
||||||
alloc: Allocator,
|
alloc: Allocator,
|
||||||
@ -129,8 +130,8 @@ pub const FontIndex = packed struct {
|
|||||||
// We start all special fonts at this index so they can be detected.
|
// We start all special fonts at this index so they can be detected.
|
||||||
pub const start = std.math.maxInt(IndexInt);
|
pub const start = std.math.maxInt(IndexInt);
|
||||||
|
|
||||||
/// Box drawing, this is rendered JIT using 2D graphics APIs.
|
/// Sprite drawing, this is rendered JIT using 2D graphics APIs.
|
||||||
box = start,
|
sprite = start,
|
||||||
};
|
};
|
||||||
|
|
||||||
style: Style = .regular,
|
style: Style = .regular,
|
||||||
@ -178,50 +179,11 @@ pub fn indexForCodepoint(
|
|||||||
style: Style,
|
style: Style,
|
||||||
p: ?Presentation,
|
p: ?Presentation,
|
||||||
) ?FontIndex {
|
) ?FontIndex {
|
||||||
// If this is a box drawing glyph, we use the special font index. This
|
// If we have sprite drawing enabled, check if our sprite face can
|
||||||
// will force special logic where we'll render this ourselves. If we don't
|
// handle this.
|
||||||
// have a box font set, then we just try to use regular fonts.
|
if (self.sprite) |sprite| {
|
||||||
if (self.box_font != null) {
|
if (sprite.hasCodepoint(cp, p)) {
|
||||||
if (switch (cp) {
|
return FontIndex.initSpecial(.sprite);
|
||||||
// "Box Drawing" block
|
|
||||||
0x2500...0x257F => true,
|
|
||||||
|
|
||||||
// "Block Elements" block
|
|
||||||
0x2580...0x259f => true,
|
|
||||||
|
|
||||||
// "Braille" block
|
|
||||||
0x2800...0x28FF => true,
|
|
||||||
|
|
||||||
// "Symbols for Legacy Computing" block
|
|
||||||
0x1FB00...0x1FB3B => true,
|
|
||||||
|
|
||||||
0x1FB3C...0x1FB40,
|
|
||||||
0x1FB47...0x1FB4B,
|
|
||||||
0x1FB57...0x1FB5B,
|
|
||||||
0x1FB62...0x1FB66,
|
|
||||||
0x1FB6C...0x1FB6F,
|
|
||||||
=> true,
|
|
||||||
|
|
||||||
0x1FB41...0x1FB45,
|
|
||||||
0x1FB4C...0x1FB50,
|
|
||||||
0x1FB52...0x1FB56,
|
|
||||||
0x1FB5D...0x1FB61,
|
|
||||||
0x1FB68...0x1FB6B,
|
|
||||||
=> true,
|
|
||||||
|
|
||||||
0x1FB46,
|
|
||||||
0x1FB51,
|
|
||||||
0x1FB5C,
|
|
||||||
0x1FB67,
|
|
||||||
0x1FB9A,
|
|
||||||
0x1FB9B,
|
|
||||||
=> true,
|
|
||||||
|
|
||||||
0x1FB70...0x1FB8B => true,
|
|
||||||
|
|
||||||
else => false,
|
|
||||||
}) {
|
|
||||||
return FontIndex.initSpecial(.box);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -275,7 +237,7 @@ fn indexForCodepointExact(self: Group, cp: u32, style: Style, p: ?Presentation)
|
|||||||
/// 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) {
|
||||||
.box => return .text,
|
.sprite => return .text,
|
||||||
};
|
};
|
||||||
|
|
||||||
const face = try self.faceFromIndex(index);
|
const face = try self.faceFromIndex(index);
|
||||||
@ -312,7 +274,7 @@ pub fn renderGlyph(
|
|||||||
) !Glyph {
|
) !Glyph {
|
||||||
// Special-case fonts are rendered directly.
|
// Special-case fonts are rendered directly.
|
||||||
if (index.special()) |sp| switch (sp) {
|
if (index.special()) |sp| switch (sp) {
|
||||||
.box => return try self.box_font.?.renderGlyph(
|
.sprite => return try self.sprite.?.renderGlyph(
|
||||||
alloc,
|
alloc,
|
||||||
atlas,
|
atlas,
|
||||||
glyph_index,
|
glyph_index,
|
||||||
@ -402,12 +364,12 @@ test "box glyph" {
|
|||||||
defer group.deinit();
|
defer group.deinit();
|
||||||
|
|
||||||
// Set box font
|
// Set box font
|
||||||
group.box_font = font.BoxFont{ .width = 18, .height = 36, .thickness = 2 };
|
group.sprite = font.sprite.Face{ .width = 18, .height = 36, .thickness = 2 };
|
||||||
|
|
||||||
// Should find a box glyph
|
// Should find a box glyph
|
||||||
const idx = group.indexForCodepoint(0x2500, .regular, null).?;
|
const idx = group.indexForCodepoint(0x2500, .regular, null).?;
|
||||||
try testing.expectEqual(Style.regular, idx.style);
|
try testing.expectEqual(Style.regular, idx.style);
|
||||||
try testing.expectEqual(@enumToInt(FontIndex.Special.box), idx.idx);
|
try testing.expectEqual(@enumToInt(FontIndex.Special.sprite), idx.idx);
|
||||||
|
|
||||||
// Should render it
|
// Should render it
|
||||||
const glyph = try group.renderGlyph(
|
const glyph = try group.renderGlyph(
|
||||||
|
@ -582,7 +582,7 @@ test "shape box glyphs" {
|
|||||||
defer testdata.deinit();
|
defer testdata.deinit();
|
||||||
|
|
||||||
// Setup the box font
|
// Setup the box font
|
||||||
testdata.cache.group.box_font = font.BoxFont{
|
testdata.cache.group.sprite = font.sprite.Face{
|
||||||
.width = 18,
|
.width = 18,
|
||||||
.height = 36,
|
.height = 36,
|
||||||
.thickness = 2,
|
.thickness = 2,
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const build_options = @import("build_options");
|
const build_options = @import("build_options");
|
||||||
|
|
||||||
pub const BoxFont = @import("BoxFont.zig");
|
|
||||||
pub const discovery = @import("discovery.zig");
|
pub const discovery = @import("discovery.zig");
|
||||||
pub const face = @import("face.zig");
|
pub const face = @import("face.zig");
|
||||||
pub const DeferredFace = @import("DeferredFace.zig");
|
pub const DeferredFace = @import("DeferredFace.zig");
|
||||||
@ -11,6 +10,7 @@ pub const GroupCache = @import("GroupCache.zig");
|
|||||||
pub const Glyph = @import("Glyph.zig");
|
pub const Glyph = @import("Glyph.zig");
|
||||||
pub const Library = @import("Library.zig");
|
pub const Library = @import("Library.zig");
|
||||||
pub const Shaper = @import("Shaper.zig");
|
pub const Shaper = @import("Shaper.zig");
|
||||||
|
pub const sprite = @import("sprite.zig");
|
||||||
pub const Descriptor = discovery.Descriptor;
|
pub const Descriptor = discovery.Descriptor;
|
||||||
pub const Discover = discovery.Discover;
|
pub const Discover = discovery.Discover;
|
||||||
|
|
||||||
|
27
src/font/sprite.zig
Normal file
27
src/font/sprite.zig
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
pub const Face = @import("sprite/Face.zig");
|
||||||
|
|
||||||
|
/// Sprites are represented as special codepoints outside of the Unicode
|
||||||
|
/// codepoint range. Unicode maxes out at U+10FFFF (21 bits), and we use the
|
||||||
|
/// high 11 bits to hide our special characters.
|
||||||
|
///
|
||||||
|
/// These characters are ONLY used for rendering and NEVER used written to
|
||||||
|
/// text files or any other exported format, so we don't use the Private Use
|
||||||
|
/// Area of Unicode.
|
||||||
|
pub const Sprite = enum(u32) {
|
||||||
|
// Start 1 above the maximum Unicode codepoint.
|
||||||
|
const start: u32 = std.math.maxInt(u21) + 1;
|
||||||
|
const end: u32 = std.math.maxInt(u32);
|
||||||
|
|
||||||
|
underline = start,
|
||||||
|
|
||||||
|
// Note: we don't currently put the box drawing glyphs in here because
|
||||||
|
// there are a LOT and I'm lazy. What I want to do is spend more time
|
||||||
|
// studying the patterns to see if we can programmatically build our
|
||||||
|
// enum perhaps and comptime generate the drawing code at the same time.
|
||||||
|
// I'm not sure if that's advisable yet though.
|
||||||
|
};
|
||||||
|
|
||||||
|
test {
|
||||||
|
@import("std").testing.refAllDecls(@This());
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
96
src/font/sprite/Face.zig
Normal file
96
src/font/sprite/Face.zig
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
//! This implements the built-in "sprite face". This font renders
|
||||||
|
//! the built-in glyphs for the terminal, such as box drawing fonts, as well
|
||||||
|
//! as specific sprites that are part of our rendering model such as
|
||||||
|
//! text decorations (underlines).
|
||||||
|
//!
|
||||||
|
//! This isn't really a "font face" so much as it is quacks like a font
|
||||||
|
//! face with regards to how it works with font.Group. We don't use any
|
||||||
|
//! dynamic dispatch so it isn't truly an interface but the functions
|
||||||
|
//! and behaviors are close enough to a system face that it makes it easy
|
||||||
|
//! to integrate with font.Group. This is desirable so that higher level
|
||||||
|
//! processes such as GroupCache, Shaper, etc. don't need to be aware of
|
||||||
|
//! special sprite handling and just treat it like a normal font face.
|
||||||
|
const Face = @This();
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
const builtin = @import("builtin");
|
||||||
|
const assert = std.debug.assert;
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
const font = @import("../main.zig");
|
||||||
|
const Atlas = @import("../../Atlas.zig");
|
||||||
|
const Box = @import("Box.zig");
|
||||||
|
|
||||||
|
/// The cell width and height.
|
||||||
|
width: u32,
|
||||||
|
height: u32,
|
||||||
|
|
||||||
|
/// Base thickness value for lines of sprites. This is in pixels. If you
|
||||||
|
/// want to do any DPI scaling, it is expected to be done earlier.
|
||||||
|
thickness: u32,
|
||||||
|
|
||||||
|
/// Returns true if the codepoint exists in our sprite font.
|
||||||
|
pub fn hasCodepoint(self: Face, cp: u32, p: ?font.Presentation) bool {
|
||||||
|
// We ignore presentation. No matter what presentation is requested
|
||||||
|
// we always provide glyphs for our codepoints.
|
||||||
|
_ = p;
|
||||||
|
_ = self;
|
||||||
|
return Kind.init(cp) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Render the glyph.
|
||||||
|
pub fn renderGlyph(
|
||||||
|
self: Face,
|
||||||
|
alloc: Allocator,
|
||||||
|
atlas: *Atlas,
|
||||||
|
cp: u32,
|
||||||
|
) !font.Glyph {
|
||||||
|
if (std.debug.runtime_safety) assert(self.hasCodepoint(cp, null));
|
||||||
|
|
||||||
|
// Safe to ".?" because of the above assertion.
|
||||||
|
switch (Kind.init(cp).?) {
|
||||||
|
.box => {
|
||||||
|
const f: Box = .{
|
||||||
|
.width = self.width,
|
||||||
|
.height = self.height,
|
||||||
|
.thickness = self.thickness,
|
||||||
|
};
|
||||||
|
|
||||||
|
return try f.renderGlyph(alloc, atlas, cp);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Kind of sprites we have. Drawing is implemented separately for each kind.
|
||||||
|
const Kind = enum {
|
||||||
|
box,
|
||||||
|
|
||||||
|
pub fn init(cp: u32) ?Kind {
|
||||||
|
return switch (cp) {
|
||||||
|
// Box fonts
|
||||||
|
0x2500...0x257F, // "Box Drawing" block
|
||||||
|
0x2580...0x259F, // "Block Elements" block
|
||||||
|
0x2800...0x28FF, // "Braille" block
|
||||||
|
0x1FB00...0x1FB3B, // "Symbols for Legacy Computing" block
|
||||||
|
0x1FB3C...0x1FB40,
|
||||||
|
0x1FB47...0x1FB4B,
|
||||||
|
0x1FB57...0x1FB5B,
|
||||||
|
0x1FB62...0x1FB66,
|
||||||
|
0x1FB6C...0x1FB6F,
|
||||||
|
0x1FB41...0x1FB45,
|
||||||
|
0x1FB4C...0x1FB50,
|
||||||
|
0x1FB52...0x1FB56,
|
||||||
|
0x1FB5D...0x1FB61,
|
||||||
|
0x1FB68...0x1FB6B,
|
||||||
|
0x1FB70...0x1FB8B,
|
||||||
|
0x1FB46,
|
||||||
|
0x1FB51,
|
||||||
|
0x1FB5C,
|
||||||
|
0x1FB67,
|
||||||
|
0x1FB9A,
|
||||||
|
0x1FB9B,
|
||||||
|
=> .box,
|
||||||
|
|
||||||
|
else => null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
@ -409,9 +409,9 @@ pub fn setFontSize(self: *Metal, size: font.face.DesiredSize) !void {
|
|||||||
self.cell_size = new_cell_size;
|
self.cell_size = new_cell_size;
|
||||||
|
|
||||||
// Set the cell size of the box font
|
// Set the cell size of the box font
|
||||||
if (self.font_group.group.box_font) |*box| {
|
if (self.font_group.group.sprite) |*sprite| {
|
||||||
box.width = @floatToInt(u32, self.cell_size.width);
|
sprite.width = @floatToInt(u32, self.cell_size.width);
|
||||||
box.height = @floatToInt(u32, self.cell_size.height);
|
sprite.height = @floatToInt(u32, self.cell_size.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notify the window that the cell size changed.
|
// Notify the window that the cell size changed.
|
||||||
|
@ -502,9 +502,9 @@ pub fn setFontSize(self: *OpenGL, size: font.face.DesiredSize) !void {
|
|||||||
self.cell_size = new_cell_size;
|
self.cell_size = new_cell_size;
|
||||||
|
|
||||||
// Set the cell size of the box font
|
// Set the cell size of the box font
|
||||||
if (self.font_group.group.box_font) |*box| {
|
if (self.font_group.group.sprite) |*sprite| {
|
||||||
box.width = @floatToInt(u32, self.cell_size.width);
|
sprite.width = @floatToInt(u32, self.cell_size.width);
|
||||||
box.height = @floatToInt(u32, self.cell_size.height);
|
sprite.height = @floatToInt(u32, self.cell_size.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notify the window that the cell size changed.
|
// Notify the window that the cell size changed.
|
||||||
|
Reference in New Issue
Block a user