initial underline support, can be improved

This commit is contained in:
Mitchell Hashimoto
2022-06-28 14:21:57 -07:00
parent 427fcc94e2
commit f53efa633a
6 changed files with 93 additions and 41 deletions

View File

@ -1,5 +1,7 @@
Bugs:
* Underline should use freetype underline thickness hint
Performance:
* libuv allocates on every read, we should use a read buffer pool
@ -19,8 +21,6 @@ Major Features:
* Line wrap
* Selection, highlighting
* Copy (paste is done)
* Bold
* Underline
* Strikethrough
* Emoji
* Ligatures

View File

@ -28,6 +28,7 @@ const uint MODE_FG = 2u;
const uint MODE_CURSOR_RECT = 3u;
const uint MODE_CURSOR_RECT_HOLLOW = 4u;
const uint MODE_CURSOR_BAR = 5u;
const uint MODE_UNDERLINE = 6u;
void main() {
switch (mode) {
@ -83,5 +84,9 @@ void main() {
case MODE_CURSOR_BAR:
out_FragColor = color;
break;
case MODE_UNDERLINE:
out_FragColor = color;
break;
}
}

View File

@ -9,6 +9,7 @@ const uint MODE_FG = 2u;
const uint MODE_CURSOR_RECT = 3u;
const uint MODE_CURSOR_RECT_HOLLOW = 4u;
const uint MODE_CURSOR_BAR = 5u;
const uint MODE_UNDERLINE = 6u;
// The grid coordinates (x, y) where x < columns and y < rows
layout (location = 0) in vec2 grid_coord;
@ -158,5 +159,23 @@ void main() {
gl_Position = projection * vec4(cell_pos, 0.0, 1.0);
color = bg_color_in / 255.0;
break;
case MODE_UNDERLINE:
// Make the underline a smaller version of our cell
// TODO: use real font underline thickness
vec2 underline_size = vec2(cell_size.x, cell_size.y*0.05);
// Position our underline so that it is midway between the glyph
// baseline and the bottom of the cell.
vec2 underline_offset = vec2(cell_size.x, cell_size.y - (glyph_baseline / 2));
// Go to the bottom of the cell, take away the size of the
// underline, and that is our position. We also float it slightly
// above the bottom.
cell_pos = cell_pos + underline_offset - underline_size * position;
gl_Position = projection * vec4(cell_pos, 0.0, 1.0);
color = fg_color_in / 255.0;
break;
}
}

View File

@ -271,9 +271,9 @@ pub fn updateCells(self: *Grid, term: Terminal) !void {
try self.cells.ensureTotalCapacity(
self.alloc,
// * 2 for background modes and cursor
// * 3 for background modes and cursor and underlines
// + 1 for cursor
(term.screen.rows * term.screen.cols * 2) + 1,
(term.screen.rows * term.screen.cols * 3) + 1,
);
// Build each cell
@ -308,44 +308,67 @@ pub fn updateCells(self: *Grid, term: Terminal) !void {
});
}
// If the cell is empty then we draw nothing in the box.
if (cell.empty()) continue;
// Determine our glyph styling
const style: font.Style = if (cell.attrs.bold == 1)
.bold
else
.regular;
// Get our glyph
// TODO: if we add a glyph, I think we need to rerender the texture.
const glyph = if (self.font_atlas.getGlyph(cell.char, style)) |glyph|
glyph
else glyph: {
self.atlas_dirty = true;
break :glyph try self.font_atlas.addGlyph(self.alloc, cell.char, style);
};
const fg = cell.fg orelse self.foreground;
self.cells.appendAssumeCapacity(.{
.mode = 2,
.grid_col = @intCast(u16, x),
.grid_row = @intCast(u16, y),
.glyph_x = glyph.atlas_x,
.glyph_y = glyph.atlas_y,
.glyph_width = glyph.width,
.glyph_height = glyph.height,
.glyph_offset_x = glyph.offset_x,
.glyph_offset_y = glyph.offset_y,
.fg_r = fg.r,
.fg_g = fg.g,
.fg_b = fg.b,
.fg_a = 255,
.bg_r = 0,
.bg_g = 0,
.bg_b = 0,
.bg_a = 0,
});
// If the cell is empty then we draw nothing in the box.
if (!cell.empty()) {
// Determine our glyph styling
const style: font.Style = if (cell.attrs.bold == 1)
.bold
else
.regular;
// Get our glyph
// TODO: if we add a glyph, I think we need to rerender the texture.
const glyph = if (self.font_atlas.getGlyph(cell.char, style)) |glyph|
glyph
else glyph: {
self.atlas_dirty = true;
break :glyph try self.font_atlas.addGlyph(self.alloc, cell.char, style);
};
self.cells.appendAssumeCapacity(.{
.mode = 2,
.grid_col = @intCast(u16, x),
.grid_row = @intCast(u16, y),
.glyph_x = glyph.atlas_x,
.glyph_y = glyph.atlas_y,
.glyph_width = glyph.width,
.glyph_height = glyph.height,
.glyph_offset_x = glyph.offset_x,
.glyph_offset_y = glyph.offset_y,
.fg_r = fg.r,
.fg_g = fg.g,
.fg_b = fg.b,
.fg_a = 255,
.bg_r = 0,
.bg_g = 0,
.bg_b = 0,
.bg_a = 0,
});
}
if (cell.attrs.underline == 1) {
self.cells.appendAssumeCapacity(.{
.mode = 6, // underline
.grid_col = @intCast(u16, x),
.grid_row = @intCast(u16, y),
.glyph_x = 0,
.glyph_y = 0,
.glyph_width = 0,
.glyph_height = 0,
.glyph_offset_x = 0,
.glyph_offset_y = 0,
.fg_r = fg.r,
.fg_g = fg.g,
.fg_b = fg.b,
.fg_a = 255,
.bg_r = 0,
.bg_g = 0,
.bg_b = 0,
.bg_a = 0,
});
}
}
}

View File

@ -24,6 +24,7 @@ pub const Cell = struct {
/// TODO: pack it
attrs: struct {
bold: u1 = 0,
underline: u1 = 0,
inverse: u1 = 0,
} = .{},

View File

@ -154,6 +154,10 @@ pub fn setAttribute(self: *Terminal, attr: sgr.Attribute) !void {
self.cursor.pen.attrs.bold = 1;
},
.underline => {
self.cursor.pen.attrs.underline = 1;
},
.inverse => {
self.cursor.pen.attrs.inverse = 1;
},