diff options
author | Kirill Chibisov <contact@kchibisov.com> | 2020-11-17 17:49:05 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-11-17 17:49:05 +0300 |
commit | 9724418d350df881afe8453bde6eb88643e7211e (patch) | |
tree | e3f2cac36404430df1f5093226dcc9f944f66cc0 | |
parent | 8b10e5e77840c0477a4b1575f91a4549d010af04 (diff) | |
download | alacritty-9724418d350df881afe8453bde6eb88643e7211e.tar.gz alacritty-9724418d350df881afe8453bde6eb88643e7211e.zip |
Fix wide characters being cut off
Fixes #791.
-rw-r--r-- | CHANGELOG.md | 4 | ||||
-rw-r--r-- | Cargo.lock | 1 | ||||
-rw-r--r-- | alacritty/Cargo.toml | 1 | ||||
-rw-r--r-- | alacritty/res/text.f.glsl | 38 | ||||
-rw-r--r-- | alacritty/res/text.v.glsl | 31 | ||||
-rw-r--r-- | alacritty/src/renderer/mod.rs | 37 | ||||
-rw-r--r-- | alacritty/src/renderer/rects.rs | 10 | ||||
-rw-r--r-- | alacritty/src/url.rs | 31 | ||||
-rw-r--r-- | alacritty_terminal/src/term/mod.rs | 16 |
9 files changed, 112 insertions, 57 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 77c7dcbb..526f611b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## 0.7.0-dev +### Fixed + +- Wide characters sometimes being cut off + ## 0.6.0 ### Packaging @@ -26,6 +26,7 @@ name = "alacritty" version = "0.7.0-dev" dependencies = [ "alacritty_terminal", + "bitflags", "clap", "copypasta", "crossfont", diff --git a/alacritty/Cargo.toml b/alacritty/Cargo.toml index f8049fed..081ea4dc 100644 --- a/alacritty/Cargo.toml +++ b/alacritty/Cargo.toml @@ -29,6 +29,7 @@ urlocator = "0.1.3" copypasta = { version = "0.7.0", default-features = false } libc = "0.2" unicode-width = "0.1" +bitflags = "1" [build-dependencies] gl_generator = "0.14.0" diff --git a/alacritty/res/text.f.glsl b/alacritty/res/text.f.glsl index c0c1bd1b..d5e26881 100644 --- a/alacritty/res/text.f.glsl +++ b/alacritty/res/text.f.glsl @@ -9,31 +9,31 @@ layout(location = 0, index = 1) out vec4 alphaMask; uniform sampler2D mask; -void main() -{ +#define COLORED 2 + +void main() { if (backgroundPass != 0) { - if (bg.a == 0.0) + if (bg.a == 0.0) { discard; + } alphaMask = vec4(1.0); color = vec4(bg.rgb, 1.0); - } else { - if (fg.a != 0.0) { - // Color glyphs, like emojis. - vec4 glyphColor = texture(mask, TexCoords); - alphaMask = vec4(glyphColor.a); + } else if ((int(fg.a) & COLORED) != 0) { + // Color glyphs, like emojis. + vec4 glyphColor = texture(mask, TexCoords); + alphaMask = vec4(glyphColor.a); - // Revert alpha premultiplication. - if (glyphColor.a != 0) { - glyphColor.rgb = vec3(glyphColor.rgb / glyphColor.a); - } - - color = vec4(glyphColor.rgb, 1.0); - } else { - // Regular text glyphs. - vec3 textColor = texture(mask, TexCoords).rgb; - alphaMask = vec4(textColor, textColor.r); - color = vec4(fg.rgb, 1.0); + // Revert alpha premultiplication. + if (glyphColor.a != 0) { + glyphColor.rgb = vec3(glyphColor.rgb / glyphColor.a); } + + color = vec4(glyphColor.rgb, 1.0); + } else { + // Regular text glyphs. + vec3 textColor = texture(mask, TexCoords).rgb; + alphaMask = vec4(textColor, textColor.r); + color = vec4(fg.rgb, 1.0); } } diff --git a/alacritty/res/text.v.glsl b/alacritty/res/text.v.glsl index 0f5fd3f9..31e6f934 100644 --- a/alacritty/res/text.v.glsl +++ b/alacritty/res/text.v.glsl @@ -1,18 +1,20 @@ #version 330 core // Cell properties. -layout (location = 0) in vec2 gridCoords; +layout(location = 0) in vec2 gridCoords; // Glyph properties. -layout (location = 1) in vec4 glyph; +layout(location = 1) in vec4 glyph; // uv mapping. -layout (location = 2) in vec4 uv; +layout(location = 2) in vec4 uv; -// Text foreground rgb packed together with multicolor flag. -layout (location = 3) in vec4 textColor; +// Text foreground rgb packed together with cell flags. textColor.a +// are the bitflags; consult RenderingGlyphFlags in renderer/mod.rs +// for the possible values. +layout(location = 3) in vec4 textColor; // Background color. -layout (location = 4) in vec4 backgroundColor; +layout(location = 4) in vec4 backgroundColor; out vec2 TexCoords; flat out vec4 fg; @@ -24,9 +26,9 @@ uniform vec4 projection; uniform int backgroundPass; +#define WIDE_CHAR 1 -void main() -{ +void main() { vec2 projectionOffset = projection.xy; vec2 projectionScale = projection.zw; @@ -39,8 +41,14 @@ void main() vec2 cellPosition = cellDim * gridCoords; if (backgroundPass != 0) { - vec2 finalPosition = cellPosition + cellDim * position; - gl_Position = vec4(projectionOffset + projectionScale * finalPosition, 0.0, 1.0); + vec2 backgroundDim = cellDim; + if ((int(textColor.a) & WIDE_CHAR) != 0) { + // Update wide char x dimension so it'll cover the following spacer. + backgroundDim.x *= 2; + } + vec2 finalPosition = cellPosition + backgroundDim * position; + gl_Position = + vec4(projectionOffset + projectionScale * finalPosition, 0.0, 1.0); TexCoords = vec2(0, 0); } else { @@ -49,7 +57,8 @@ void main() glyphOffset.y = cellDim.y - glyphOffset.y; vec2 finalPosition = cellPosition + glyphSize * position + glyphOffset; - gl_Position = vec4(projectionOffset + projectionScale * finalPosition, 0.0, 1.0); + gl_Position = + vec4(projectionOffset + projectionScale * finalPosition, 0.0, 1.0); vec2 uvOffset = uv.xy; vec2 uvSize = uv.zw; diff --git a/alacritty/src/renderer/mod.rs b/alacritty/src/renderer/mod.rs index b347556d..f63f92fd 100644 --- a/alacritty/src/renderer/mod.rs +++ b/alacritty/src/renderer/mod.rs @@ -9,6 +9,7 @@ use std::ptr; use std::sync::mpsc; use std::time::Duration; +use bitflags::bitflags; use crossfont::{ BitmapBuffer, FontDesc, FontKey, GlyphKey, Rasterize, RasterizedGlyph, Rasterizer, Size, Slant, Style, Weight, @@ -123,7 +124,7 @@ pub struct RectShaderProgram { #[derive(Copy, Debug, Clone)] pub struct Glyph { tex_id: GLuint, - multicolor: u8, + multicolor: bool, top: i16, left: i16, width: i16, @@ -359,30 +360,46 @@ impl GlyphCache { } } +// NOTE: These flags must be in sync with their usage in the text.*.glsl shaders. +bitflags! { + #[repr(C)] + struct RenderingGlyphFlags: u8 { + const WIDE_CHAR = 0b0000_0001; + const COLORED = 0b0000_0010; + } +} + #[derive(Debug)] #[repr(C)] struct InstanceData { // Coords. col: u16, row: u16, + // Glyph offset. left: i16, top: i16, + // Glyph size. width: i16, height: i16, + // UV offset. uv_left: f32, uv_bot: f32, + // uv scale. uv_width: f32, uv_height: f32, + // Color. r: u8, g: u8, b: u8, - // Flag indicating that a glyph uses multiple colors; like an Emoji. - multicolor: u8, + + // Cell flags like multicolor or fullwidth character. + cell_flags: RenderingGlyphFlags, + // Background color. bg_r: u8, bg_g: u8, @@ -441,6 +458,10 @@ impl Batch { self.tex = glyph.tex_id; } + let mut cell_flags = RenderingGlyphFlags::empty(); + cell_flags.set(RenderingGlyphFlags::COLORED, glyph.multicolor); + cell_flags.set(RenderingGlyphFlags::WIDE_CHAR, cell.flags.contains(Flags::WIDE_CHAR)); + self.instances.push(InstanceData { col: cell.column.0 as u16, row: cell.line.0 as u16, @@ -458,12 +479,12 @@ impl Batch { r: cell.fg.r, g: cell.fg.g, b: cell.fg.b, + cell_flags, bg_r: cell.bg.r, bg_g: cell.bg.g, bg_b: cell.bg.b, bg_a: (cell.bg_alpha * 255.0) as u8, - multicolor: glyph.multicolor, }); } @@ -586,10 +607,10 @@ impl QuadRenderer { // UV offset. add_attr!(4, gl::FLOAT, f32); - // Color and multicolor flag. + // Color and cell flags. // // These are packed together because of an OpenGL driver issue on macOS, which caused a - // `vec3(u8)` text color and a `u8` multicolor flag to increase the rendering time by a + // `vec3(u8)` text color and a `u8` cell flags to increase the rendering time by a // huge margin. add_attr!(4, gl::UNSIGNED_BYTE, u8); @@ -1067,7 +1088,7 @@ fn load_glyph( }, Err(AtlasInsertError::GlyphTooLarge) => Glyph { tex_id: atlas[*current_atlas].id, - multicolor: 0, + multicolor: false, top: 0, left: 0, width: 0, @@ -1581,7 +1602,7 @@ impl Atlas { Glyph { tex_id: self.id, - multicolor: multicolor as u8, + multicolor, top: glyph.top as i16, left: glyph.left as i16, width: width as i16, diff --git a/alacritty/src/renderer/rects.rs b/alacritty/src/renderer/rects.rs index fcd8c82e..2a727b4c 100644 --- a/alacritty/src/renderer/rects.rs +++ b/alacritty/src/renderer/rects.rs @@ -162,6 +162,12 @@ impl RenderLines { return; } + // Include wide char spacer if the current cell is a wide char. + let mut end: Point = cell.into(); + if cell.flags.contains(Flags::WIDE_CHAR) { + end.col += 1; + } + // Check if there's an active line. if let Some(line) = self.inner.get_mut(&flag).and_then(|lines| lines.last_mut()) { if cell.fg == line.color @@ -169,13 +175,13 @@ impl RenderLines { && cell.line == line.end.line { // Update the length of the line. - line.end = cell.into(); + line.end = end; return; } } // Start new line if there currently is none. - let line = RenderLine { start: cell.into(), end: cell.into(), color: cell.fg }; + let line = RenderLine { start: cell.into(), end, color: cell.fg }; match self.inner.get_mut(&flag) { Some(lines) => lines.push(line), None => { diff --git a/alacritty/src/url.rs b/alacritty/src/url.rs index 5d35667d..b33b532e 100644 --- a/alacritty/src/url.rs +++ b/alacritty/src/url.rs @@ -79,7 +79,12 @@ impl Urls { }; let point: Point = cell.into(); - let end = point; + let mut end = point; + + // Include the following wide char spacer. + if cell.flags.contains(Flags::WIDE_CHAR) { + end.col += 1; + } // Reset URL when empty cells have been skipped. if point != Point::default() && Some(point.sub(num_cols, 1)) != self.last_point { @@ -88,8 +93,8 @@ impl Urls { self.last_point = Some(end); - // Extend current state if a wide char spacer is encountered. - if cell.flags.intersects(Flags::WIDE_CHAR_SPACER | Flags::LEADING_WIDE_CHAR_SPACER) { + // Extend current state if a leading wide char spacer is encountered. + if cell.flags.intersects(Flags::LEADING_WIDE_CHAR_SPACER) { if let UrlLocation::Url(_, mut end_offset) = self.state { if end_offset != 0 { end_offset += 1; @@ -252,4 +257,24 @@ mod tests { assert_eq!(urls.urls[2].start().col, Column(17)); assert_eq!(urls.urls[2].end().col, Column(21)); } + + #[test] + fn wide_urls() { + let input = text_to_cells("test https://こんにちは (http:여보세요) ing"); + let num_cols = input.len() + 9; + + let mut urls = Urls::new(); + + for cell in input { + urls.update(Column(num_cols), &cell); + } + + assert_eq!(urls.urls.len(), 2); + + assert_eq!(urls.urls[0].start().col, Column(5)); + assert_eq!(urls.urls[0].end().col, Column(17)); + + assert_eq!(urls.urls[1].start().col, Column(20)); + assert_eq!(urls.urls[1].end().col, Column(28)); + } } diff --git a/alacritty_terminal/src/term/mod.rs b/alacritty_terminal/src/term/mod.rs index cdcfad9d..fa2c5513 100644 --- a/alacritty_terminal/src/term/mod.rs +++ b/alacritty_terminal/src/term/mod.rs @@ -165,8 +165,8 @@ impl<'a, C> Iterator for RenderableCellsIter<'a, C> { let cell = self.inner.next()?; let cell = RenderableCell::new(self, cell); - // Skip empty cells. - if !cell.is_empty() { + // Skip empty cells and wide char spacers. + if !cell.is_empty() && !cell.flags.contains(Flags::WIDE_CHAR_SPACER) { return Some(cell); } } @@ -282,18 +282,6 @@ impl<'a, C> RenderableCellsIter<'a, C> { // Check line-wrapping, leading spacer. || (self.grid[buffer_prev].flags.contains(Flags::LEADING_WIDE_CHAR_SPACER) && selection.contains(prev.col, prev.line)) - } else if cell.flags.contains(Flags::WIDE_CHAR_SPACER) { - // Check if spacer's wide char is selected. - let prev = point.sub(num_cols, 1); - let buffer_prev = self.grid.visible_to_buffer(prev); - - if self.grid[buffer_prev].flags.contains(Flags::WIDE_CHAR) { - // Check previous cell for trailing spacer. - self.is_selected(prev) - } else { - // Check next cell for line-wrapping, leading spacer. - self.is_selected(point.add(num_cols, 1)) - } } else { false } |