font: strikethrough uses sprite rendering

This commit is contained in:
Mitchell Hashimoto
2024-04-22 10:25:33 -07:00
parent 4f8aa0e34e
commit 0f348e809e
5 changed files with 75 additions and 3 deletions

View File

@ -129,6 +129,7 @@ fn reloadMetrics(self: *SharedGrid, thicken: bool) !void {
.thickness = self.metrics.underline_thickness * .thickness = self.metrics.underline_thickness *
@as(u32, if (thicken) 2 else 1), @as(u32, if (thicken) 2 else 1),
.underline_position = self.metrics.underline_position, .underline_position = self.metrics.underline_position,
.strikethrough_position = self.metrics.strikethrough_position,
}; };
} }

View File

@ -20,6 +20,8 @@ pub const Sprite = enum(u32) {
underline_dashed, underline_dashed,
underline_curly, underline_curly,
strikethrough,
cursor_rect, cursor_rect,
cursor_hollow_rect, cursor_hollow_rect,
cursor_bar, cursor_bar,

View File

@ -30,11 +30,17 @@ height: u32,
/// Base thickness value for lines of sprites. This is in pixels. If you /// 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. /// want to do any DPI scaling, it is expected to be done earlier.
thickness: u32, thickness: u32 = 1,
/// The position of the underline. /// The position of the underline.
underline_position: u32 = 0, underline_position: u32 = 0,
/// The position of the strikethrough.
// NOTE(mitchellh): We don't use a dedicated strikethrough thickness
// setting yet but fonts can in theory set this. If this becomes an
// issue in practice we can add it here.
strikethrough_position: u32 = 0,
/// Returns true if the codepoint exists in our sprite font. /// Returns true if the codepoint exists in our sprite font.
pub fn hasCodepoint(self: Face, cp: u32, p: ?font.Presentation) bool { pub fn hasCodepoint(self: Face, cp: u32, p: ?font.Presentation) bool {
// We ignore presentation. No matter what presentation is requested // We ignore presentation. No matter what presentation is requested
@ -113,6 +119,16 @@ pub fn renderGlyph(
self.thickness, self.thickness,
), ),
.strikethrough => try underline.renderGlyph(
alloc,
atlas,
@enumFromInt(cp),
width,
self.height,
self.strikethrough_position,
self.thickness,
),
.powerline => powerline: { .powerline => powerline: {
const f: Powerline = .{ const f: Powerline = .{
.width = width, .width = width,
@ -129,6 +145,7 @@ pub fn renderGlyph(
const Kind = enum { const Kind = enum {
box, box,
underline, underline,
strikethrough,
powerline, powerline,
pub fn init(cp: u32) ?Kind { pub fn init(cp: u32) ?Kind {
@ -141,6 +158,9 @@ const Kind = enum {
.underline_curly, .underline_curly,
=> .underline, => .underline,
.strikethrough,
=> .strikethrough,
.cursor_rect, .cursor_rect,
.cursor_hollow_rect, .cursor_hollow_rect,
.cursor_bar, .cursor_bar,
@ -189,3 +209,7 @@ const Kind = enum {
}; };
} }
}; };
test {
@import("std").testing.refAllDecls(@This());
}

View File

@ -7,6 +7,9 @@
//! to maintain and debug another set of shaders for each renderer instead of //! to maintain and debug another set of shaders for each renderer instead of
//! just relying on the glyph system we already need to support for text //! just relying on the glyph system we already need to support for text
//! anyways. //! anyways.
//!
//! This also renders strikethrough, so its really more generally a
//! "horizontal line" renderer.
const std = @import("std"); const std = @import("std");
const builtin = @import("builtin"); const builtin = @import("builtin");
const assert = std.debug.assert; const assert = std.debug.assert;
@ -71,6 +74,7 @@ const Draw = struct {
.underline_dotted => self.drawDotted(canvas), .underline_dotted => self.drawDotted(canvas),
.underline_dashed => self.drawDashed(canvas), .underline_dashed => self.drawDashed(canvas),
.underline_curly => self.drawCurly(canvas), .underline_curly => self.drawCurly(canvas),
.strikethrough => self.drawSingle(canvas),
else => unreachable, else => unreachable,
} }
} }
@ -225,6 +229,24 @@ test "single" {
); );
} }
test "strikethrough" {
const testing = std.testing;
const alloc = testing.allocator;
var atlas_greyscale = try font.Atlas.init(alloc, 512, .greyscale);
defer atlas_greyscale.deinit(alloc);
_ = try renderGlyph(
alloc,
&atlas_greyscale,
.strikethrough,
36,
18,
9,
2,
);
}
test "single large thickness" { test "single large thickness" {
const testing = std.testing; const testing = std.testing;
const alloc = testing.allocator; const alloc = testing.allocator;

View File

@ -1914,13 +1914,36 @@ fn updateCell(
} }
if (style.flags.strikethrough) { if (style.flags.strikethrough) {
const render = try self.font_grid.renderGlyph(
self.alloc,
font.sprite_index,
@intFromEnum(font.Sprite.strikethrough),
.{
.cell_width = if (cell.wide == .wide) 2 else 1,
.grid_metrics = self.grid_metrics,
},
);
const color = style.underlineColor(palette) orelse colors.fg;
self.cells.appendAssumeCapacity(.{ self.cells.appendAssumeCapacity(.{
.mode = .strikethrough, .mode = .fg,
.grid_pos = .{ @as(f32, @floatFromInt(x)), @as(f32, @floatFromInt(y)) }, .grid_pos = .{ @as(f32, @floatFromInt(x)), @as(f32, @floatFromInt(y)) },
.cell_width = cell.gridWidth(), .cell_width = cell.gridWidth(),
.color = .{ colors.fg.r, colors.fg.g, colors.fg.b, alpha }, .color = .{ color.r, color.g, color.b, alpha },
.bg_color = bg, .bg_color = bg,
.glyph_pos = .{ render.glyph.atlas_x, render.glyph.atlas_y },
.glyph_size = .{ render.glyph.width, render.glyph.height },
.glyph_offset = .{ render.glyph.offset_x, render.glyph.offset_y },
}); });
// self.cells.appendAssumeCapacity(.{
// .mode = .strikethrough,
// .grid_pos = .{ @as(f32, @floatFromInt(x)), @as(f32, @floatFromInt(y)) },
// .cell_width = cell.gridWidth(),
// .color = .{ colors.fg.r, colors.fg.g, colors.fg.b, alpha },
// .bg_color = bg,
// });
} }
return true; return true;