1
1
mirror of https://github.com/wez/wezterm.git synced 2024-11-28 01:06:37 +03:00

Add third pass draw for underline/strikethrough rendering

This commit is contained in:
Wez Furlong 2018-02-18 17:12:53 -08:00
parent f51b99cb73
commit 3ccb0c24ef

View File

@ -3,6 +3,7 @@ use euclid;
use failure::{self, Error}; use failure::{self, Error};
use font::{FontConfiguration, GlyphInfo, ftwrap}; use font::{FontConfiguration, GlyphInfo, ftwrap};
use glium::{self, IndexBuffer, Surface, VertexBuffer}; use glium::{self, IndexBuffer, Surface, VertexBuffer};
use glium::texture::SrgbTexture2d;
use pty::MasterPty; use pty::MasterPty;
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::HashMap; use std::collections::HashMap;
@ -78,6 +79,7 @@ struct Vertex {
/// Count of how many underlines there are /// Count of how many underlines there are
underline: f32, underline: f32,
strikethrough: f32, strikethrough: f32,
v_idx: f32,
} }
implement_vertex!( implement_vertex!(
@ -89,7 +91,8 @@ implement_vertex!(
bg_color, bg_color,
has_color, has_color,
underline, underline,
strikethrough strikethrough,
v_idx,
); );
const VERTEX_SHADER: &str = r#" const VERTEX_SHADER: &str = r#"
@ -101,39 +104,74 @@ in vec4 fg_color;
in vec4 bg_color; in vec4 bg_color;
in float has_color; in float has_color;
in float underline; in float underline;
in float strikethrough; in float v_idx;
uniform mat4 projection; uniform mat4 projection;
uniform mat4 translation; uniform mat4 translation;
uniform bool bg_fill; uniform bool bg_fill;
uniform bool underlining;
out vec2 tex_coords; out vec2 tex_coords;
out vec4 o_fg_color; out vec4 o_fg_color;
out vec4 o_bg_color; out vec4 o_bg_color;
out float o_has_color; out float o_has_color;
out float o_underline; out float o_underline;
out float o_strikethrough;
vec4 cell_pos() { // Offset from the RHS texture coordinate to the LHS.
if (bg_fill) { // This is an underestimation to avoid the shader interpolating
return vec4(position, 0.0, 1.0); // the underline gylph into its neighbor.
} else { const float underline_offset = (1.0 / 5.0);
return vec4(position + adjust, 0.0, 1.0);
}
}
void main() { void main() {
tex_coords = tex;
o_fg_color = fg_color; o_fg_color = fg_color;
o_bg_color = bg_color; o_bg_color = bg_color;
o_has_color = has_color; o_has_color = has_color;
o_underline = underline; o_underline = underline;
o_strikethrough = strikethrough;
gl_Position = projection * cell_pos(); if (bg_fill || underlining) {
gl_Position = projection * vec4(position, 0.0, 1.0);
if (underlining) {
// Populate the underline texture coordinates based on the
// v_idx (which tells us which corner of the cell we're
// looking at) and o_underline which corresponds to one
// of the U_XXX constants defined in the rust code below
// and which holds the RHS position in the texture coordinate
// space for the underline texture layer.
if (v_idx == 0.0) { // top left
tex_coords = vec2(o_underline - underline_offset, -1.0);
} else if (v_idx == 1.0) { // top right
tex_coords = vec2(o_underline, -1.0);
} else if (v_idx == 2.0) { // bot left
tex_coords = vec2(o_underline- underline_offset, 0.0);
} else { // bot right
tex_coords = vec2(o_underline, 0.0);
}
}
} else {
gl_Position = projection * vec4(position + adjust, 0.0, 1.0);
tex_coords = tex;
}
} }
"#; "#;
/// How many columns the underline texture has
const U_COLS: f32 = 5.0;
/// The glyph has no underline or strikethrough
const U_NONE: f32 = 0.0;
/// The glyph has a single underline. This value is actually the texture
/// coordinate for the right hand side of the underline.
const U_ONE: f32 = 1.0 / U_COLS;
/// Texture coord for the RHS of the double underline glyph
const U_TWO: f32 = 2.0 / U_COLS;
/// Texture coord for the RHS of the strikethrough glyph
const U_STRIKE: f32 = 3.0 / U_COLS;
/// Texture coord for the RHS of the strikethrough + single underline glyph
const U_STRIKE_ONE: f32 = 4.0 / U_COLS;
/// Texture coord for the RHS of the strikethrough + double underline glyph
const U_STRIKE_TWO: f32 = 5.0 / U_COLS;
const FRAGMENT_SHADER: &str = r#" const FRAGMENT_SHADER: &str = r#"
#version 300 es #version 300 es
precision mediump float; precision mediump float;
@ -142,16 +180,22 @@ in vec4 o_fg_color;
in vec4 o_bg_color; in vec4 o_bg_color;
in float o_has_color; in float o_has_color;
in float o_underline; in float o_underline;
in float o_strikethrough;
out vec4 color; out vec4 color;
uniform sampler2D glyph_tex; uniform sampler2D glyph_tex;
uniform bool has_color; uniform sampler2D underline_tex;
uniform bool bg_fill; uniform bool bg_fill;
uniform bool underlining;
void main() { void main() {
if (bg_fill) { if (bg_fill) {
color = o_bg_color; color = o_bg_color;
} else if (underlining) {
if (o_underline != 0.0) {
color = texture2D(underline_tex, tex_coords) * o_fg_color;
} else {
discard;
}
} else { } else {
color = texture2D(glyph_tex, tex_coords); color = texture2D(glyph_tex, tex_coords);
if (o_has_color == 0.0) { if (o_has_color == 0.0) {
@ -188,6 +232,7 @@ pub struct TerminalWindow<'a> {
glyph_index_buffer: IndexBuffer<u32>, glyph_index_buffer: IndexBuffer<u32>,
projection: Transform3D, projection: Transform3D,
atlas: RefCell<Atlas>, atlas: RefCell<Atlas>,
underline_tex: SrgbTexture2d,
} }
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@ -341,6 +386,77 @@ impl<'a> TerminalWindow<'a> {
let cell_height = cell_height.ceil() as usize; let cell_height = cell_height.ceil() as usize;
let cell_width = cell_width.ceil() as usize; let cell_width = cell_width.ceil() as usize;
// Create the texture atlas for the line decoration layer.
// This is a bitmap with columns to accomodate the U_XXX
// constants defined above.
let underline_tex = {
let width = 5 * cell_width;
let mut underline_data = Vec::with_capacity(width * cell_height * 4);
underline_data.resize(width * cell_height * 4, 0u8);
let descender_row = (cell_height as isize + descender) as usize;
let strike_row = descender_row / 2;
// First, the single underline.
// We place this as the descender position.
{
let col = 0;
let offset = ((width * 4) * descender_row) + (col * 4 * cell_width);
for i in 0..4 * cell_width {
underline_data[offset + i] = 0xff;
}
}
// Double underline,
// We place this just above and below the descender
{
let col = 1;
let offset_one = ((width * 4) * (descender_row - 1)) + (col * 4 * cell_width);
let offset_two = ((width * 4) * (descender_row + 1)) + (col * 4 * cell_width);
for i in 0..4 * cell_width {
underline_data[offset_one + i] = 0xff;
underline_data[offset_two + i] = 0xff;
}
}
// Strikethrough
{
let col = 2;
let offset = (width * 4) * strike_row + (col * 4 * cell_width);
for i in 0..4 * cell_width {
underline_data[offset + i] = 0xff;
}
}
// Strikethrough and single underline
{
let col = 3;
let offset_one = ((width * 4) * descender_row) + (col * 4 * cell_width);
let offset_two = ((width * 4) * strike_row) + (col * 4 * cell_width);
for i in 0..4 * cell_width {
underline_data[offset_one + i] = 0xff;
underline_data[offset_two + i] = 0xff;
}
}
// Strikethrough and double underline
{
let col = 4;
let offset_one = ((width * 4) * (descender_row - 1)) + (col * 4 * cell_width);
let offset_two = ((width * 4) * strike_row) + (col * 4 * cell_width);
let offset_three = ((width * 4) * (descender_row + 1)) + (col * 4 * cell_width);
for i in 0..4 * cell_width {
underline_data[offset_one + i] = 0xff;
underline_data[offset_two + i] = 0xff;
underline_data[offset_three + i] = 0xff;
}
}
glium::texture::SrgbTexture2d::new(
&host.window,
glium::texture::RawImage2d::from_raw_rgba(
underline_data,
(width as u32, cell_height as u32),
),
)?
};
let ch = cell_height as f32; let ch = cell_height as f32;
let cw = cell_width as f32; let cw = cell_width as f32;
@ -370,6 +486,7 @@ impl<'a> TerminalWindow<'a> {
glyph_cache: RefCell::new(HashMap::new()), glyph_cache: RefCell::new(HashMap::new()),
palette, palette,
projection: Self::compute_projection(width as f32, height as f32), projection: Self::compute_projection(width as f32, height as f32),
underline_tex,
}) })
} }
@ -399,21 +516,25 @@ impl<'a> TerminalWindow<'a> {
verts.push(Vertex { verts.push(Vertex {
// Top left // Top left
position: Point::new(x_pos, y_pos), position: Point::new(x_pos, y_pos),
v_idx: V_TOP_LEFT as f32,
..Default::default() ..Default::default()
}); });
verts.push(Vertex { verts.push(Vertex {
// Top Right // Top Right
position: Point::new(x_pos + cell_width, y_pos), position: Point::new(x_pos + cell_width, y_pos),
v_idx: V_TOP_RIGHT as f32,
..Default::default() ..Default::default()
}); });
verts.push(Vertex { verts.push(Vertex {
// Bottom Left // Bottom Left
position: Point::new(x_pos, y_pos + cell_height), position: Point::new(x_pos, y_pos + cell_height),
v_idx: V_BOT_LEFT as f32,
..Default::default() ..Default::default()
}); });
verts.push(Vertex { verts.push(Vertex {
// Bottom Right // Bottom Right
position: Point::new(x_pos + cell_width, y_pos + cell_height), position: Point::new(x_pos + cell_width, y_pos + cell_height),
v_idx: V_BOT_RIGHT as f32,
..Default::default() ..Default::default()
}); });
@ -664,68 +785,6 @@ impl<'a> TerminalWindow<'a> {
let mut font = font.borrow_mut(); let mut font = font.borrow_mut();
font.shape(0, s) font.shape(0, s)
} }
/*
/// Render a line strike through the glyph at the given coords.
fn render_strikethrough(
&self,
target: &mut glium::Frame,
x: isize,
cell_top: isize,
baseline: isize,
num_cells_wide: u8,
glyph_color: RgbColor,
) -> Result<(), Error> {
self.draw_line(
target,
x,
cell_top + (baseline - cell_top) / 2,
num_cells_wide,
glyph_color,
)?;
Ok(())
}
/// Render a specific style of underline at the given coords.
fn render_underline(
&self,
target: &mut glium::Frame,
x: isize,
baseline: isize,
num_cells_wide: u8,
style: Underline,
glyph_color: RgbColor,
) -> Result<(), Error> {
match style {
Underline::None => {}
Underline::Single => {
self.draw_line(
target,
x,
baseline + 2,
num_cells_wide,
glyph_color,
)?;
}
Underline::Double => {
self.draw_line(
target,
x,
baseline + 1,
num_cells_wide,
glyph_color,
)?;
self.draw_line(
target,
x,
baseline + 3,
num_cells_wide,
glyph_color,
)?;
}
}
Ok(())
}
*/
fn render_screen_line( fn render_screen_line(
&self, &self,
@ -811,21 +870,29 @@ impl<'a> TerminalWindow<'a> {
let top = (self.cell_height as f32 + self.descender as f32) - let top = (self.cell_height as f32 + self.descender as f32) -
(glyph.y_offset as f32 + glyph.bearing_y as f32); (glyph.y_offset as f32 + glyph.bearing_y as f32);
// TODO: underline and strikethrough // underline and strikethrough
// Figure out what we're going to draw for the underline. // Figure out what we're going to draw for the underline.
// If the current cell is part of the current URL highlight // If the current cell is part of the current URL highlight
// then we want to show the underline. // then we want to show the underline.
let underline: f32 = match (is_highlited_hyperlink, attrs.underline()) { let underline: f32 = match (
(true, Underline::None) => 1.0, is_highlited_hyperlink,
(true, Underline::Single) => 2.0, attrs.strikethrough(),
(true, Underline::Double) => 1.0, attrs.underline(),
(false, Underline::None) => 0.0, ) {
(false, Underline::Single) => 1.0, (true, false, Underline::None) => U_ONE,
(false, Underline::Double) => 2.0, (true, false, Underline::Single) => U_TWO,
(true, false, Underline::Double) => U_ONE,
(true, true, Underline::None) => U_STRIKE_ONE,
(true, true, Underline::Single) => U_STRIKE_TWO,
(true, true, Underline::Double) => U_STRIKE_ONE,
(false, false, Underline::None) => U_NONE,
(false, false, Underline::Single) => U_ONE,
(false, false, Underline::Double) => U_TWO,
(false, true, Underline::None) => U_STRIKE,
(false, true, Underline::Single) => U_STRIKE_ONE,
(false, true, Underline::Double) => U_STRIKE_TWO,
}; };
let strikethrough: f32 = if attrs.strikethrough() { 1.0 } else { 0.0 };
// Iterate each cell that comprises this glyph. There is usually // Iterate each cell that comprises this glyph. There is usually
// a single cell per glyph but combining characters, ligatures // a single cell per glyph but combining characters, ligatures
// and emoji can be 2 or more cells wide. // and emoji can be 2 or more cells wide.
@ -876,11 +943,6 @@ impl<'a> TerminalWindow<'a> {
vert[V_BOT_LEFT].underline = underline; vert[V_BOT_LEFT].underline = underline;
vert[V_BOT_RIGHT].underline = underline; vert[V_BOT_RIGHT].underline = underline;
vert[V_TOP_LEFT].strikethrough = strikethrough;
vert[V_TOP_RIGHT].strikethrough = strikethrough;
vert[V_BOT_LEFT].strikethrough = strikethrough;
vert[V_BOT_RIGHT].strikethrough = strikethrough;
match &glyph.texture { match &glyph.texture {
&Some(ref texture) => { &Some(ref texture) => {
let slice = SpriteSlice { let slice = SpriteSlice {
@ -961,6 +1023,7 @@ impl<'a> TerminalWindow<'a> {
let tex = self.atlas.borrow().texture(); let tex = self.atlas.borrow().texture();
// Pass 1: Draw backgrounds
target.draw( target.draw(
&*self.glyph_vertex_buffer.borrow(), &*self.glyph_vertex_buffer.borrow(),
&self.glyph_index_buffer, &self.glyph_index_buffer,
@ -968,9 +1031,9 @@ impl<'a> TerminalWindow<'a> {
&uniform! { &uniform! {
projection: self.projection.to_column_arrays(), projection: self.projection.to_column_arrays(),
glyph_tex: &*tex, glyph_tex: &*tex,
//has_color: glyph.has_color,
//bg_color: bg_color.to_linear_tuple_rgba(),
bg_fill: true, bg_fill: true,
underlining: false,
underline_tex: &self.underline_tex,
}, },
&glium::DrawParameters { &glium::DrawParameters {
blend: glium::Blend::alpha_blending(), blend: glium::Blend::alpha_blending(),
@ -978,6 +1041,8 @@ impl<'a> TerminalWindow<'a> {
..Default::default() ..Default::default()
}, },
)?; )?;
// Pass 2: Draw glyphs
target.draw( target.draw(
&*self.glyph_vertex_buffer.borrow(), &*self.glyph_vertex_buffer.borrow(),
&self.glyph_index_buffer, &self.glyph_index_buffer,
@ -985,9 +1050,26 @@ impl<'a> TerminalWindow<'a> {
&uniform! { &uniform! {
projection: self.projection.to_column_arrays(), projection: self.projection.to_column_arrays(),
glyph_tex: &*tex, glyph_tex: &*tex,
//has_color: glyph.has_color,
//bg_color: bg_color.to_linear_tuple_rgba(),
bg_fill: false, bg_fill: false,
underlining: false,
},
&glium::DrawParameters {
blend: glium::Blend::alpha_blending(),
dithering: false,
..Default::default()
},
)?;
// Pass 3: Draw underline/strikethrough
target.draw(
&*self.glyph_vertex_buffer.borrow(),
&self.glyph_index_buffer,
&self.program,
&uniform! {
projection: self.projection.to_column_arrays(),
glyph_tex: &*tex,
bg_fill: false,
underlining: true,
}, },
&glium::DrawParameters { &glium::DrawParameters {
blend: glium::Blend::alpha_blending(), blend: glium::Blend::alpha_blending(),