Skip to content

Commit

Permalink
compute most line-glyphs on-the-fly
Browse files Browse the repository at this point in the history
We now have too many permutations to pre-render in the initial
texture size, so do this on the fly instead.

refs: #415
  • Loading branch information
wez committed Jan 5, 2021
1 parent 7c8f2b7 commit 9357669
Show file tree
Hide file tree
Showing 5 changed files with 161 additions and 209 deletions.
2 changes: 1 addition & 1 deletion termwiz/src/cell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ impl Default for Intensity {

/// Specify just how underlined you want your `Cell` to be
#[cfg_attr(feature = "use_serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u16)]
pub enum Underline {
/// The cell is not underlined
Expand Down
153 changes: 153 additions & 0 deletions wezterm-gui/src/gui/glyphcache.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use super::utilsprites::RenderMetrics;
use ::window::bitmaps::atlas::{Atlas, Sprite};
use ::window::bitmaps::{Image, Texture2d};
use ::window::glium::backend::Context as GliumContext;
Expand All @@ -12,6 +13,7 @@ use std::sync::Arc;
use termwiz::image::ImageData;
use wezterm_font::units::*;
use wezterm_font::{FontConfiguration, GlyphInfo};
use wezterm_term::Underline;

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct GlyphKey {
Expand Down Expand Up @@ -112,18 +114,28 @@ impl<T: Texture2d> std::fmt::Debug for CachedGlyph<T> {
}
}

#[derive(Copy, Clone, PartialEq, Eq, Hash)]
struct LineKey {
strike_through: bool,
underline: Underline,
overline: bool,
}

pub struct GlyphCache<T: Texture2d> {
glyph_cache: HashMap<GlyphKey, Rc<CachedGlyph<T>>>,
pub atlas: Atlas<T>,
fonts: Rc<FontConfiguration>,
image_cache: HashMap<usize, Sprite<T>>,
line_glyphs: HashMap<LineKey, Sprite<T>>,
metrics: RenderMetrics,
}

impl GlyphCache<SrgbTexture2d> {
pub fn new_gl(
backend: &Rc<GliumContext>,
fonts: &Rc<FontConfiguration>,
size: usize,
metrics: &RenderMetrics,
) -> anyhow::Result<Self> {
let surface = Rc::new(SrgbTexture2d::empty_with_format(
backend,
Expand All @@ -139,13 +151,16 @@ impl GlyphCache<SrgbTexture2d> {
glyph_cache: HashMap::new(),
image_cache: HashMap::new(),
atlas,
metrics: metrics.clone(),
line_glyphs: HashMap::new(),
})
}

pub fn clear(&mut self) {
self.atlas.clear();
self.image_cache.clear();
self.glyph_cache.clear();
self.line_glyphs.clear();
}
}

Expand Down Expand Up @@ -310,4 +325,142 @@ impl<T: Texture2d> GlyphCache<T> {

Ok(sprite)
}

fn line_sprite(&mut self, key: LineKey) -> anyhow::Result<Sprite<T>> {
let mut buffer = Image::new(
self.metrics.cell_size.width as usize,
self.metrics.cell_size.height as usize,
);
let black = ::window::color::Color::rgba(0, 0, 0, 0);
let white = ::window::color::Color::rgb(0xff, 0xff, 0xff);

let cell_rect = Rect::new(Point::new(0, 0), self.metrics.cell_size);

let draw_single = |buffer: &mut Image| {
for row in 0..self.metrics.underline_height {
buffer.draw_line(
Point::new(
cell_rect.origin.x,
cell_rect.origin.y + self.metrics.descender_row + row,
),
Point::new(
cell_rect.origin.x + self.metrics.cell_size.width,
cell_rect.origin.y + self.metrics.descender_row + row,
),
white,
Operator::Source,
);
}
};

let draw_double = |buffer: &mut Image| {
for row in 0..self.metrics.underline_height {
buffer.draw_line(
Point::new(
cell_rect.origin.x,
cell_rect.origin.y + self.metrics.descender_row + row,
),
Point::new(
cell_rect.origin.x + self.metrics.cell_size.width,
cell_rect.origin.y + self.metrics.descender_row + row,
),
white,
Operator::Source,
);
buffer.draw_line(
Point::new(
cell_rect.origin.x,
cell_rect.origin.y + self.metrics.descender_plus_two + row,
),
Point::new(
cell_rect.origin.x + self.metrics.cell_size.width,
cell_rect.origin.y + self.metrics.descender_plus_two + row,
),
white,
Operator::Source,
);
}
};

let draw_strike = |buffer: &mut Image| {
for row in 0..self.metrics.underline_height {
buffer.draw_line(
Point::new(
cell_rect.origin.x,
cell_rect.origin.y + self.metrics.strike_row + row,
),
Point::new(
cell_rect.origin.x + self.metrics.cell_size.width,
cell_rect.origin.y + self.metrics.strike_row + row,
),
white,
Operator::Source,
);
}
};

let draw_overline = |buffer: &mut Image| {
for row in 0..self.metrics.underline_height {
buffer.draw_line(
Point::new(cell_rect.origin.x, cell_rect.origin.y + row),
Point::new(
cell_rect.origin.x + self.metrics.cell_size.width,
cell_rect.origin.y + row,
),
white,
Operator::Source,
);
}
};

buffer.clear_rect(cell_rect, black);
if key.overline {
draw_overline(&mut buffer);
}
match key.underline {
Underline::None => {}
Underline::Single |
// FIXME: these extra styles need to be rendered separately!
Underline::Curly | Underline::Dotted | Underline::Dashed => {
draw_single(&mut buffer)
}
Underline::Double => draw_double(&mut buffer),
}
if key.strike_through {
draw_strike(&mut buffer);
}
let sprite = self.atlas.allocate(&buffer)?;
self.line_glyphs.insert(key, sprite.clone());
Ok(sprite)
}

/// Figure out what we're going to draw for the underline.
/// If the current cell is part of the current URL highlight
/// then we want to show the underline.
pub fn cached_line_sprite(
&mut self,
is_highlited_hyperlink: bool,
is_strike_through: bool,
underline: Underline,
overline: bool,
) -> anyhow::Result<Sprite<T>> {
let effective_underline = match (is_highlited_hyperlink, underline) {
(true, Underline::None) => Underline::Single,
(true, Underline::Single) => Underline::Double,
(true, _) => Underline::Single,
(false, u) => u,
};

let key = LineKey {
strike_through: is_strike_through,
overline,
underline: effective_underline,
};

if let Some(s) = self.line_glyphs.get(&key) {
return Ok(s.clone());
}

self.line_sprite(key)
}
}
5 changes: 3 additions & 2 deletions wezterm-gui/src/gui/renderstate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ impl OpenGLRenderState {
pixel_height: usize,
) -> anyhow::Result<Self> {
loop {
let glyph_cache = RefCell::new(GlyphCache::new_gl(&context, fonts, atlas_size)?);
let glyph_cache =
RefCell::new(GlyphCache::new_gl(&context, fonts, atlas_size, metrics)?);
let result = UtilSprites::new(&mut *glyph_cache.borrow_mut(), metrics);
match result {
Ok(util_sprites) => {
Expand Down Expand Up @@ -267,7 +268,7 @@ impl RenderState {
RenderState::Software(_) => {}
RenderState::GL(gl) => {
let size = size.unwrap_or_else(|| gl.glyph_cache.borrow().atlas.size());
let mut glyph_cache = GlyphCache::new_gl(&gl.context, fonts, size)?;
let mut glyph_cache = GlyphCache::new_gl(&gl.context, fonts, size, metrics)?;
gl.util_sprites = UtilSprites::new(&mut glyph_cache, metrics)?;
*gl.glyph_cache.borrow_mut() = glyph_cache;
}
Expand Down
7 changes: 4 additions & 3 deletions wezterm-gui/src/gui/termwindow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2937,13 +2937,14 @@ impl TermWindow {

// underline and strikethrough
let underline_tex_rect = gl_state
.util_sprites
.select_sprite(
.glyph_cache
.borrow_mut()
.cached_line_sprite(
is_highlited_hyperlink,
attrs.strikethrough(),
attrs.underline(),
attrs.overline(),
)
)?
.texture_coords();

// Iterate each cell that comprises this glyph. There is usually
Expand Down
Loading

0 comments on commit 9357669

Please sign in to comment.