diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/ansi.rs | 4 | ||||
-rw-r--r-- | src/display.rs | 16 | ||||
-rw-r--r-- | src/renderer/lines.rs | 143 | ||||
-rw-r--r-- | src/renderer/mod.rs | 124 | ||||
-rw-r--r-- | src/term/cell.rs | 23 | ||||
-rw-r--r-- | src/term/mod.rs | 4 |
6 files changed, 229 insertions, 85 deletions
diff --git a/src/ansi.rs b/src/ansi.rs index db8022e2..29b1a459 100644 --- a/src/ansi.rs +++ b/src/ansi.rs @@ -642,7 +642,7 @@ pub enum Attr { Reverse, /// Do not display characters Hidden, - /// Strikethrough text + /// Strikeout text Strike, /// Cancel bold CancelBold, @@ -658,7 +658,7 @@ pub enum Attr { CancelReverse, /// Cancel text hiding CancelHidden, - /// Cancel strike through + /// Cancel strikeout CancelStrike, /// Set indexed foreground color Foreground(Color), diff --git a/src/display.rs b/src/display.rs index 120cd1dc..b1575c58 100644 --- a/src/display.rs +++ b/src/display.rs @@ -25,6 +25,7 @@ use crate::config::Config; use font::{self, Rasterize}; use crate::meter::Meter; use crate::renderer::{self, GlyphCache, QuadRenderer}; +use crate::renderer::lines::Lines; use crate::term::{Term, SizeInfo, RenderableCell}; use crate::sync::FairMutex; use crate::window::{self, Window}; @@ -410,19 +411,27 @@ impl Display { { let glyph_cache = &mut self.glyph_cache; + let metrics = glyph_cache.font_metrics(); + let mut cell_line_rects = Lines::new(&metrics, &size_info); // Draw grid { let _sampler = self.meter.sampler(); self.renderer.with_api(config, &size_info, |mut api| { - // Draw the grid - api.render_cells(grid_cells.iter(), glyph_cache); + // Iterate over all non-empty cells in the grid + for cell in grid_cells { + // Update underline/strikeout + cell_line_rects.update_lines(&cell); + + // Draw the cell + api.render_cell(cell, glyph_cache); + } }); } // Draw rectangles - self.renderer.draw_rects(config, &size_info, visual_bell_intensity); + self.renderer.draw_rects(config, &size_info, visual_bell_intensity, cell_line_rects); // Draw render timer if self.render_timer { @@ -490,4 +499,3 @@ impl Display { self.window().set_ime_spot(LogicalPosition::from((nspot_x, nspot_y))); } } - diff --git a/src/renderer/lines.rs b/src/renderer/lines.rs new file mode 100644 index 00000000..556dcb0d --- /dev/null +++ b/src/renderer/lines.rs @@ -0,0 +1,143 @@ +// Copyright 2016 Joe Wilm, The Alacritty Project Contributors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +use std::collections::HashMap; + +use crate::term::{ SizeInfo, RenderableCell}; +use crate::term::cell::Flags; +use crate::renderer::Rect; +use font::Metrics; +use crate::Rgb; + +/// Lines for underline and strikeout. +pub struct Lines<'a> { + inner: Vec<(Rect<f32>, Rgb)>, + last_starts: HashMap<Flags, Option<RenderableCell>>, + last_cell: Option<RenderableCell>, + metrics: &'a Metrics, + size: &'a SizeInfo, +} + +impl<'a> Lines<'a> { + pub fn new(metrics: &'a Metrics, size: &'a SizeInfo) -> Self { + let mut last_starts = HashMap::new(); + last_starts.insert(Flags::UNDERLINE, None); + last_starts.insert(Flags::STRIKEOUT, None); + + Self { + inner: Vec::new(), + last_cell: None, + last_starts, + metrics, + size, + } + } + + /// Convert the stored lines to rectangles for the renderer. + pub fn rects(mut self) -> Vec<(Rect<f32>, Rgb)> { + // If there's still a line pending, draw it until the last cell + for (flag, start_cell) in self.last_starts.iter_mut() { + if let Some(start) = start_cell { + self.inner.push( + create_rect( + &start, + &self.last_cell.unwrap(), + *flag, + &self.metrics, + &self.size, + ) + ); + } + } + + self.inner + } + + /// Update the stored lines with the next cell info. + pub fn update_lines(&mut self, cell: &RenderableCell) { + for (flag, start_cell) in self.last_starts.iter_mut() { + let flag = *flag; + *start_cell = match *start_cell { + // Check for end if line is present + Some(ref mut start) => { + // No change in line + if cell.line == start.line && cell.flags.contains(flag) && cell.fg == start.fg { + continue; + } + + self.inner.push( + create_rect( + &start, + &self.last_cell.unwrap(), + flag, + &self.metrics, + &self.size, + ) + ); + + // Start a new line if the flag is present + if cell.flags.contains(flag) { + Some(*cell) + } else { + None + } + } + // Check for new start of line + None => if cell.flags.contains(flag) { + Some(*cell) + } else { + None + }, + }; + } + + self.last_cell = Some(*cell); + } +} + +/// Create a rectangle between two cells. +fn create_rect( + start: &RenderableCell, + end: &RenderableCell, + flag: Flags, + metrics: &Metrics, + size: &SizeInfo, +) -> (Rect<f32>, Rgb) { + let start_x = start.column.0 as f32 * size.cell_width; + let end_x = (end.column.0 + 1) as f32 * size.cell_width; + let width = end_x - start_x; + + let (position, height) = match flag { + Flags::UNDERLINE => (metrics.underline_position, metrics.underline_thickness), + Flags::STRIKEOUT => (metrics.strikeout_position, metrics.strikeout_thickness), + _ => unimplemented!("Invalid flag for cell line drawing specified"), + }; + + let cell_bottom = (start.line.0 as f32 + 1.) * size.cell_height; + let baseline = cell_bottom + metrics.descent; + + let mut y = baseline - position - height / 2.; + let max_y = cell_bottom - height; + if y > max_y { + y = max_y; + } + + let rect = Rect::new( + start_x + size.padding_x, + y + size.padding_y, + width, + height, + ); + + (rect, start.fg) +} diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs index c05301cc..42a17486 100644 --- a/src/renderer/mod.rs +++ b/src/renderer/mod.rs @@ -23,16 +23,19 @@ use std::time::Duration; use cgmath; use fnv::FnvHasher; +use glutin::dpi::PhysicalSize; use font::{self, FontDesc, FontKey, GlyphKey, Rasterize, RasterizedGlyph, Rasterizer}; +use notify::{watcher, DebouncedEvent, RecursiveMode, Watcher}; + use crate::gl::types::*; use crate::gl; use crate::index::{Column, Line, RangeInclusive}; -use notify::{watcher, DebouncedEvent, RecursiveMode, Watcher}; use crate::Rgb; - use crate::config::{self, Config, Delta}; use crate::term::{self, cell, RenderableCell}; -use glutin::dpi::PhysicalSize; +use crate::renderer::lines::Lines; + +pub mod lines; // Shader paths for live reload static TEXT_SHADER_F_PATH: &'static str = concat!(env!("CARGO_MANIFEST_DIR"), "/res/text.f.glsl"); @@ -699,6 +702,7 @@ impl QuadRenderer { config: &Config, props: &term::SizeInfo, visual_bell_intensity: f64, + cell_line_rects: Lines, ) { // Swap to rectangle rendering program unsafe { @@ -725,6 +729,11 @@ impl QuadRenderer { let rect = Rect::new(0., 0., props.width, props.height); self.render_rect(&rect, color, visual_bell_intensity as f32, props); + // Draw underlines and strikeouts + for cell_line_rect in cell_line_rects.rects() { + self.render_rect(&cell_line_rect.0, cell_line_rect.1, 255., props); + } + // Deactivate rectangle program again unsafe { // Reset blending strategy @@ -902,7 +911,8 @@ impl QuadRenderer { } } -struct Rect<T> { +#[derive(Debug, Copy, Clone)] +pub struct Rect<T> { x: T, y: T, width: T, @@ -910,7 +920,7 @@ struct Rect<T> { } impl<T> Rect<T> { - fn new(x: T, y: T, width: T, height: T) -> Self { + pub fn new(x: T, y: T, width: T, height: T) -> Self { Rect { x, y, width, height } } } @@ -998,7 +1008,9 @@ impl<'a> RenderApi<'a> { }) .collect::<Vec<_>>(); - self.render_cells(cells.iter(), glyph_cache); + for cell in cells { + self.render_cell(cell, glyph_cache); + } } #[inline] @@ -1016,74 +1028,52 @@ impl<'a> RenderApi<'a> { } } - pub fn render_cells<'b, I>( - &mut self, - cells: I, - glyph_cache: &mut GlyphCache - ) - where I: Iterator<Item=&'b RenderableCell> - { - for cell in cells { - // Get font key for cell - // FIXME this is super inefficient. - let font_key = if cell.flags.contains(cell::Flags::BOLD) { - glyph_cache.bold_key - } else if cell.flags.contains(cell::Flags::ITALIC) { - glyph_cache.italic_key - } else { - glyph_cache.font_key - }; - - // Don't render text of HIDDEN cells - let mut chars = if cell.flags.contains(cell::Flags::HIDDEN) { - [' '; cell::MAX_ZEROWIDTH_CHARS + 1] - } else { - cell.chars - }; + pub fn render_cell(&mut self, cell: RenderableCell, glyph_cache: &mut GlyphCache) { + // Get font key for cell + // FIXME this is super inefficient. + let font_key = if cell.flags.contains(cell::Flags::BOLD) { + glyph_cache.bold_key + } else if cell.flags.contains(cell::Flags::ITALIC) { + glyph_cache.italic_key + } else { + glyph_cache.font_key + }; - // Render tabs as spaces in case the font doesn't support it - if chars[0] == '\t' { - chars[0] = ' '; - } + // Don't render text of HIDDEN cells + let mut chars = if cell.flags.contains(cell::Flags::HIDDEN) { + [' '; cell::MAX_ZEROWIDTH_CHARS + 1] + } else { + cell.chars + }; - let mut glyph_key = GlyphKey { - font_key, - size: glyph_cache.font_size, - c: chars[0], - }; + // Render tabs as spaces in case the font doesn't support it + if chars[0] == '\t' { + chars[0] = ' '; + } - // Add cell to batch - let glyph = glyph_cache.get(glyph_key, self); - self.add_render_item(&cell, glyph); + let mut glyph_key = GlyphKey { + font_key, + size: glyph_cache.font_size, + c: chars[0], + }; - // Render zero-width characters - for c in (&chars[1..]).iter().filter(|c| **c != ' ') { - glyph_key.c = *c; - let mut glyph = *glyph_cache.get(glyph_key, self); + // Add cell to batch + let glyph = glyph_cache.get(glyph_key, self); + self.add_render_item(&cell, glyph); - // The metrics of zero-width characters are based on rendering - // the character after the current cell, with the anchor at the - // right side of the preceding character. Since we render the - // zero-width characters inside the preceding character, the - // anchor has been moved to the right by one cell. - glyph.left += glyph_cache.metrics.average_advance as f32; + // Render zero-width characters + for c in (&chars[1..]).iter().filter(|c| **c != ' ') { + glyph_key.c = *c; + let mut glyph = *glyph_cache.get(glyph_key, self); - self.add_render_item(&cell, &glyph); - } + // The metrics of zero-width characters are based on rendering + // the character after the current cell, with the anchor at the + // right side of the preceding character. Since we render the + // zero-width characters inside the preceding character, the + // anchor has been moved to the right by one cell. + glyph.left += glyph_cache.metrics.average_advance as f32; - // FIXME This is a super hacky way to do underlined text. During - // a time crunch to release 0.1, this seemed like a really - // easy, clean hack. - if cell.flags.contains(cell::Flags::UNDERLINE) { - let glyph_key = GlyphKey { - font_key, - size: glyph_cache.font_size, - c: '_', - }; - - let underscore = glyph_cache.get(glyph_key, self); - self.add_render_item(&cell, underscore); - } + self.add_render_item(&cell, &glyph); } } } diff --git a/src/term/cell.rs b/src/term/cell.rs index bd561482..5d3b7036 100644 --- a/src/term/cell.rs +++ b/src/term/cell.rs @@ -23,16 +23,17 @@ pub const MAX_ZEROWIDTH_CHARS: usize = 5; bitflags! { #[derive(Serialize, Deserialize)] pub struct Flags: u16 { - const INVERSE = 0b0_0000_0001; - const BOLD = 0b0_0000_0010; - const ITALIC = 0b0_0000_0100; - const UNDERLINE = 0b0_0000_1000; - const WRAPLINE = 0b0_0001_0000; - const WIDE_CHAR = 0b0_0010_0000; - const WIDE_CHAR_SPACER = 0b0_0100_0000; - const DIM = 0b0_1000_0000; - const DIM_BOLD = 0b0_1000_0010; - const HIDDEN = 0b1_0000_0000; + const INVERSE = 0b00_0000_0001; + const BOLD = 0b00_0000_0010; + const ITALIC = 0b00_0000_0100; + const UNDERLINE = 0b00_0000_1000; + const WRAPLINE = 0b00_0001_0000; + const WIDE_CHAR = 0b00_0010_0000; + const WIDE_CHAR_SPACER = 0b00_0100_0000; + const DIM = 0b00_1000_0000; + const DIM_BOLD = 0b00_1000_0010; + const HIDDEN = 0b01_0000_0000; + const STRIKEOUT = 0b10_0000_0000; } } @@ -117,7 +118,7 @@ impl Cell { (self.c == ' ' || self.c == '\t') && self.extra[0] == ' ' && self.bg == Color::Named(NamedColor::Background) - && !self.flags.intersects(Flags::INVERSE | Flags::UNDERLINE) + && !self.flags.intersects(Flags::INVERSE | Flags::UNDERLINE | Flags::STRIKEOUT) } #[inline] diff --git a/src/term/mod.rs b/src/term/mod.rs index fd2fcf88..bb65fba6 100644 --- a/src/term/mod.rs +++ b/src/term/mod.rs @@ -419,7 +419,7 @@ impl<'a> RenderableCellsIter<'a> { } } -#[derive(Debug)] +#[derive(Copy, Clone, Debug)] pub struct RenderableCell { /// A _Display_ line (not necessarily an _Active_ line) pub line: Line, @@ -1965,6 +1965,8 @@ impl ansi::Handler for Term { Attr::CancelUnderline => self.cursor.template.flags.remove(cell::Flags::UNDERLINE), Attr::Hidden => self.cursor.template.flags.insert(cell::Flags::HIDDEN), Attr::CancelHidden => self.cursor.template.flags.remove(cell::Flags::HIDDEN), + Attr::Strike => self.cursor.template.flags.insert(cell::Flags::STRIKEOUT), + Attr::CancelStrike => self.cursor.template.flags.remove(cell::Flags::STRIKEOUT), _ => { debug!("Term got unhandled attr: {:?}", attr); } |