render wide chars

This commit is contained in:
Mitchell Hashimoto
2022-08-20 16:49:55 -07:00
parent da79aae0ab
commit 8a1d7070b3
3 changed files with 73 additions and 18 deletions

View File

@ -31,6 +31,7 @@ const uint MODE_CURSOR_RECT = 3u;
const uint MODE_CURSOR_RECT_HOLLOW = 4u;
const uint MODE_CURSOR_BAR = 5u;
const uint MODE_UNDERLINE = 6u;
const uint MODE_WIDE_MASK = 128u; // 0b1000_0000
void main() {
float a;

View File

@ -11,6 +11,7 @@ const uint MODE_CURSOR_RECT = 3u;
const uint MODE_CURSOR_RECT_HOLLOW = 4u;
const uint MODE_CURSOR_BAR = 5u;
const uint MODE_UNDERLINE = 6u;
const uint MODE_WIDE_MASK = 128u; // 0b1000_0000
// The grid coordinates (x, y) where x < columns and y < rows
layout (location = 0) in vec2 grid_coord;
@ -77,8 +78,12 @@ uniform float glyph_baseline;
*/
void main() {
// We always forward our mode
mode = mode_in;
// Remove any masks from our mode
uint mode_unmasked = mode_in & ~MODE_WIDE_MASK;
// We always forward our mode unmasked because the fragment
// shader doesn't use any of the masks.
mode = mode_unmasked;
// Top-left cell coordinates converted to world space
// Example: (1,0) with a 30 wide cell is converted to (30,0)
@ -103,12 +108,18 @@ void main() {
position.x = (gl_VertexID == 0 || gl_VertexID == 1) ? 1. : 0.;
position.y = (gl_VertexID == 0 || gl_VertexID == 3) ? 0. : 1.;
switch (mode_in) {
// Scaled for wide chars
vec2 cell_size_scaled = cell_size;
if ((mode_in & MODE_WIDE_MASK) == MODE_WIDE_MASK) {
cell_size_scaled.x = cell_size_scaled.x * 2;
}
switch (mode) {
case MODE_BG:
// Calculate the final position of our cell in world space.
// We have to add our cell size since our vertices are offset
// one cell up and to the left. (Do the math to verify yourself)
cell_pos = cell_pos + cell_size * position;
cell_pos = cell_pos + cell_size_scaled * position;
gl_Position = projection * vec4(cell_pos, cell_z, 1.0);
color = bg_color_in / 255.0;
@ -122,8 +133,8 @@ void main() {
// TODO: for now, we assume this means it is a full width character
// TODO: in the future, use unicode libs to verify this.
vec2 glyph_size_downsampled = glyph_size;
if (mode_in == MODE_FG_COLOR && glyph_size.x > cell_size.x) {
glyph_size_downsampled.x = cell_size.x * 2;
if (glyph_size.x > cell_size.x) {
glyph_size_downsampled.x = cell_size_scaled.x;
glyph_size_downsampled.y = glyph_size.y * (glyph_size_downsampled.x / glyph_size.x);
glyph_offset_calc.y = glyph_offset.y * (glyph_size_downsampled.x / glyph_size.x);
}
@ -132,7 +143,7 @@ void main() {
// to the baseline is the offset (+y is up). Our grid goes down.
// So we flip it with `cell_size.y - glyph_offset.y`. The glyph_baseline
// uniform sets our line baseline where characters "sit".
glyph_offset_calc.y = cell_size.y - glyph_offset_calc.y - glyph_baseline;
glyph_offset_calc.y = cell_size_scaled.y - glyph_offset_calc.y - glyph_baseline;
// Calculate the final position of the cell.
cell_pos = cell_pos + glyph_size_downsampled * position + glyph_offset_calc;
@ -141,7 +152,7 @@ void main() {
// We need to convert our texture position and size to normalized
// device coordinates (0 to 1.0) by dividing by the size of the texture.
ivec2 text_size;
switch(mode_in) {
switch(mode) {
case MODE_FG:
text_size = textureSize(text, 0);
break;
@ -160,7 +171,7 @@ void main() {
case MODE_CURSOR_RECT:
// Same as background since we're taking up the whole cell.
cell_pos = cell_pos + cell_size * position;
cell_pos = cell_pos + cell_size_scaled * position;
gl_Position = projection * vec4(cell_pos, cell_z, 1.0);
color = bg_color_in / 255.0;
@ -171,7 +182,7 @@ void main() {
screen_cell_pos = cell_pos;
// Same as background since we're taking up the whole cell.
cell_pos = cell_pos + cell_size * position;
cell_pos = cell_pos + cell_size_scaled * position;
gl_Position = projection * vec4(cell_pos, cell_z, 1.0);
color = bg_color_in / 255.0;
@ -191,11 +202,11 @@ void main() {
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);
vec2 underline_size = vec2(cell_size_scaled.x, cell_size_scaled.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));
vec2 underline_offset = vec2(cell_size_scaled.x, cell_size_scaled.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

View File

@ -109,7 +109,30 @@ const GPUCell = struct {
bg_a: u8,
/// uint mode
mode: u8,
mode: GPUCellMode,
};
const GPUCellMode = enum(u8) {
bg = 1,
fg = 2,
fg_color = 7,
cursor_rect = 3,
cursor_rect_hollow = 4,
cursor_bar = 5,
underline = 6,
wide_mask = 0b1000_0000,
// Non-exhaustive because masks change it
_,
/// Apply a mask to the mode.
pub fn mask(self: GPUCellMode, m: GPUCellMode) GPUCellMode {
return @intToEnum(
GPUCellMode,
@enumToInt(self) | @enumToInt(m),
);
}
};
pub fn init(alloc: Allocator, config: *const Config) !Grid {
@ -399,8 +422,19 @@ pub fn finalizeCells(self: *Grid, term: Terminal) !void {
fn addCursor(self: *Grid, term: Terminal) void {
// Add the cursor
if (self.cursor_visible and term.screen.viewportIsBottom()) {
const cell = term.screen.getCell(
term.screen.cursor.y,
term.screen.cursor.x,
);
var mode: GPUCellMode = @intToEnum(
GPUCellMode,
@enumToInt(self.cursor_style),
);
if (cell.attrs.wide == 1) mode = mode.mask(.wide_mask);
self.cells.appendAssumeCapacity(.{
.mode = @enumToInt(self.cursor_style),
.mode = mode,
.grid_col = @intCast(u16, term.screen.cursor.x),
.grid_row = @intCast(u16, term.screen.cursor.y),
.fg_r = 0,
@ -488,8 +522,11 @@ pub fn updateCell(
// If the cell has a background, we always draw it.
if (colors.bg) |rgb| {
var mode: GPUCellMode = .bg;
if (cell.attrs.wide == 1) mode = mode.mask(.wide_mask);
self.cells.appendAssumeCapacity(.{
.mode = 1,
.mode = mode,
.grid_col = @intCast(u16, x),
.grid_row = @intCast(u16, y),
.glyph_x = 0,
@ -517,14 +554,17 @@ pub fn updateCell(
else
.regular;
var mode: u8 = 2; // MODE_FG
var mode: GPUCellMode = .fg;
// Get our glyph. Try our normal font atlas first.
const goa = try self.font_set.getOrAddGlyph(self.alloc, cell.char, style);
if (!goa.found_existing) self.atlas_dirty = true;
if (goa.family == 1) mode = 7; // MODE_FG_COLOR
if (goa.family == 1) mode = .fg_color;
const glyph = goa.glyph;
// If the cell is wide, we need to note that in the mode
if (cell.attrs.wide == 1) mode = mode.mask(.wide_mask);
self.cells.appendAssumeCapacity(.{
.mode = mode,
.grid_col = @intCast(u16, x),
@ -547,8 +587,11 @@ pub fn updateCell(
}
if (cell.attrs.underline == 1) {
var mode: GPUCellMode = .underline;
if (cell.attrs.wide == 1) mode = mode.mask(.wide_mask);
self.cells.appendAssumeCapacity(.{
.mode = 6, // underline
.mode = mode,
.grid_col = @intCast(u16, x),
.grid_row = @intCast(u16, y),
.glyph_x = 0,