parent
8b10e5e778
commit
9724418d35
|
@ -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",
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 => {
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue