From f9e2cb6aec854a05f1f5ad3793cd3c800d3bd073 Mon Sep 17 00:00:00 2001 From: Qwerasd Date: Wed, 2 Oct 2024 16:38:31 -0400 Subject: [PATCH] fix(renderer): use 1-wide ul/st chars, ignore null shaper cells This makes sure that underline styles are consistent and not stretched, and avoids rendering overlapping text decorations or extraneous background cells for the right halves of wide chars. --- src/renderer/Metal.zig | 42 ++++++++++++++++++++++++++--- src/renderer/OpenGL.zig | 58 ++++++++++++++++++++++++++++++++++++++--- 2 files changed, 92 insertions(+), 8 deletions(-) diff --git a/src/renderer/Metal.zig b/src/renderer/Metal.zig index 1790711c8..0fda9cbe4 100644 --- a/src/renderer/Metal.zig +++ b/src/renderer/Metal.zig @@ -2274,6 +2274,10 @@ fn rebuildCells( }; for (shaper_cells) |shaper_cell| { + // The shaper can emit null glyphs representing the right half + // of wide characters, we don't need to do anything with them. + if (shaper_cell.glyph_index == null) continue; + const coord: terminal.Coordinate = .{ .x = shaper_cell.x, .y = y, @@ -2541,7 +2545,7 @@ fn updateCell( font.sprite_index, @intFromEnum(sprite), .{ - .cell_width = if (cell.wide == .wide) 2 else 1, + .cell_width = 1, .grid_metrics = self.grid_metrics, }, ); @@ -2551,7 +2555,7 @@ fn updateCell( try self.cells.add(self.alloc, .underline, .{ .mode = .fg, .grid_pos = .{ @intCast(coord.x), @intCast(coord.y) }, - .constraint_width = cell.gridWidth(), + .constraint_width = 1, .color = .{ color.r, color.g, color.b, alpha }, .glyph_pos = .{ render.glyph.atlas_x, render.glyph.atlas_y }, .glyph_size = .{ render.glyph.width, render.glyph.height }, @@ -2560,6 +2564,21 @@ fn updateCell( @intCast(render.glyph.offset_y), }, }); + // If it's a wide cell we need to underline the right half as well. + if (cell.gridWidth() > 1 and coord.x < self.cells.size.columns - 1) { + try self.cells.add(self.alloc, .underline, .{ + .mode = .fg, + .grid_pos = .{ @intCast(coord.x + 1), @intCast(coord.y) }, + .constraint_width = 1, + .color = .{ color.r, color.g, color.b, alpha }, + .glyph_pos = .{ render.glyph.atlas_x, render.glyph.atlas_y }, + .glyph_size = .{ render.glyph.width, render.glyph.height }, + .bearings = .{ + @intCast(render.glyph.offset_x), + @intCast(render.glyph.offset_y), + }, + }); + } } // If the shaper cell has a glyph, draw it. @@ -2611,7 +2630,7 @@ fn updateCell( font.sprite_index, @intFromEnum(font.Sprite.strikethrough), .{ - .cell_width = if (cell.wide == .wide) 2 else 1, + .cell_width = 1, .grid_metrics = self.grid_metrics, }, ); @@ -2619,7 +2638,7 @@ fn updateCell( try self.cells.add(self.alloc, .strikethrough, .{ .mode = .fg, .grid_pos = .{ @intCast(coord.x), @intCast(coord.y) }, - .constraint_width = cell.gridWidth(), + .constraint_width = 1, .color = .{ colors.fg.r, colors.fg.g, colors.fg.b, alpha }, .glyph_pos = .{ render.glyph.atlas_x, render.glyph.atlas_y }, .glyph_size = .{ render.glyph.width, render.glyph.height }, @@ -2628,6 +2647,21 @@ fn updateCell( @intCast(render.glyph.offset_y), }, }); + // If it's a wide cell we need to strike through the right half as well. + if (cell.gridWidth() > 1 and coord.x < self.cells.size.columns - 1) { + try self.cells.add(self.alloc, .strikethrough, .{ + .mode = .fg, + .grid_pos = .{ @intCast(coord.x + 1), @intCast(coord.y) }, + .constraint_width = 1, + .color = .{ colors.fg.r, colors.fg.g, colors.fg.b, alpha }, + .glyph_pos = .{ render.glyph.atlas_x, render.glyph.atlas_y }, + .glyph_size = .{ render.glyph.width, render.glyph.height }, + .bearings = .{ + @intCast(render.glyph.offset_x), + @intCast(render.glyph.offset_y), + }, + }); + } } return true; diff --git a/src/renderer/OpenGL.zig b/src/renderer/OpenGL.zig index 760721af3..7ea3f6ec8 100644 --- a/src/renderer/OpenGL.zig +++ b/src/renderer/OpenGL.zig @@ -1338,6 +1338,10 @@ pub fn rebuildCells( }; for (shaper_cells) |shaper_cell| { + // The shaper can emit null glyphs representing the right half + // of wide characters, we don't need to do anything with them. + if (shaper_cell.glyph_index == null) continue; + // If this cell falls within our preedit range then we skip it. // We do this so we don't have conflicting data on the same // cell. @@ -1779,7 +1783,7 @@ fn updateCell( font.sprite_index, @intFromEnum(sprite), .{ - .cell_width = if (cell.wide == .wide) 2 else 1, + .cell_width = 1, .grid_metrics = self.grid_metrics, }, ); @@ -1790,7 +1794,7 @@ fn updateCell( .mode = .fg, .grid_col = @intCast(x), .grid_row = @intCast(y), - .grid_width = cell.gridWidth(), + .grid_width = 1, .glyph_x = render.glyph.atlas_x, .glyph_y = render.glyph.atlas_y, .glyph_width = render.glyph.width, @@ -1806,6 +1810,29 @@ fn updateCell( .bg_b = bg[2], .bg_a = bg[3], }); + // If it's a wide cell we need to underline the right half as well. + if (cell.gridWidth() > 1 and x < self.grid_size.columns - 1) { + try self.cells.append(self.alloc, .{ + .mode = .fg, + .grid_col = @intCast(x + 1), + .grid_row = @intCast(y), + .grid_width = 1, + .glyph_x = render.glyph.atlas_x, + .glyph_y = render.glyph.atlas_y, + .glyph_width = render.glyph.width, + .glyph_height = render.glyph.height, + .glyph_offset_x = render.glyph.offset_x, + .glyph_offset_y = render.glyph.offset_y, + .r = color.r, + .g = color.g, + .b = color.b, + .a = alpha, + .bg_r = bg[0], + .bg_g = bg[1], + .bg_b = bg[2], + .bg_a = bg[3], + }); + } } // If the shaper cell has a glyph, draw it. @@ -1866,7 +1893,7 @@ fn updateCell( font.sprite_index, @intFromEnum(font.Sprite.strikethrough), .{ - .cell_width = if (cell.wide == .wide) 2 else 1, + .cell_width = 1, .grid_metrics = self.grid_metrics, }, ); @@ -1875,7 +1902,7 @@ fn updateCell( .mode = .fg, .grid_col = @intCast(x), .grid_row = @intCast(y), - .grid_width = cell.gridWidth(), + .grid_width = 1, .glyph_x = render.glyph.atlas_x, .glyph_y = render.glyph.atlas_y, .glyph_width = render.glyph.width, @@ -1891,6 +1918,29 @@ fn updateCell( .bg_b = bg[2], .bg_a = bg[3], }); + // If it's a wide cell we need to strike through the right half as well. + if (cell.gridWidth() > 1 and x < self.grid_size.columns - 1) { + try self.cells.append(self.alloc, .{ + .mode = .fg, + .grid_col = @intCast(x + 1), + .grid_row = @intCast(y), + .grid_width = 1, + .glyph_x = render.glyph.atlas_x, + .glyph_y = render.glyph.atlas_y, + .glyph_width = render.glyph.width, + .glyph_height = render.glyph.height, + .glyph_offset_x = render.glyph.offset_x, + .glyph_offset_y = render.glyph.offset_y, + .r = colors.fg.r, + .g = colors.fg.g, + .b = colors.fg.b, + .a = alpha, + .bg_r = bg[0], + .bg_g = bg[1], + .bg_b = bg[2], + .bg_a = bg[3], + }); + } } return true;