1
1
mirror of https://github.com/wez/wezterm.git synced 2024-11-23 23:21:08 +03:00

gui: avoid false cache sharing in glyph cache, fix custom block advance

Since we may have two different sizes/namespaces of fonts between
the title font and the main terminal font, we need to be a bit more
careful to pass down distinguishing font information when caching
glyphs.

In addition, I noticed that the advance for custom block glyphs
(eg: powerline!) weren't right in the tab bar.  To resolve this,
when shaping, we skip using the glyph from the font and synthesize
a placeholder with the appropriate advance.
This commit is contained in:
Wez Furlong 2021-10-09 13:34:59 -07:00
parent 5a6317ebe4
commit 64271288ac
6 changed files with 216 additions and 127 deletions

View File

@ -1,4 +1,4 @@
use crate::glyphcache::GlyphCache;
use crate::glyphcache::{GlyphCache, SizedBlockKey};
use crate::utilsprites::RenderMetrics;
use ::window::bitmaps::atlas::Sprite;
use ::window::color::{LinearRgba, SrgbaPixel};
@ -3661,17 +3661,21 @@ impl<T: Texture2d> GlyphCache<T> {
}
}
pub fn cursor_sprite(&mut self, shape: Option<CursorShape>) -> anyhow::Result<Sprite<T>> {
pub fn cursor_sprite(
&mut self,
shape: Option<CursorShape>,
metrics: &RenderMetrics,
) -> anyhow::Result<Sprite<T>> {
if let Some(sprite) = self.cursor_glyphs.get(&shape) {
return Ok(sprite.clone());
}
let mut buffer = Image::new(
self.metrics.cell_size.width as usize,
self.metrics.cell_size.height as usize,
metrics.cell_size.width as usize,
metrics.cell_size.height as usize,
);
let black = SrgbaPixel::rgba(0, 0, 0, 0);
let cell_rect = Rect::new(Point::new(0, 0), self.metrics.cell_size);
let cell_rect = Rect::new(Point::new(0, 0), metrics.cell_size);
buffer.clear_rect(cell_rect, black);
match shape {
@ -3680,7 +3684,6 @@ impl<T: Texture2d> GlyphCache<T> {
buffer.clear_rect(cell_rect, SrgbaPixel::rgba(0xff, 0xff, 0xff, 0xff));
}
Some(CursorShape::BlinkingBlock | CursorShape::SteadyBlock) => {
let metrics = self.metrics.clone();
self.draw_polys(
&metrics,
&[Poly {
@ -3698,7 +3701,6 @@ impl<T: Texture2d> GlyphCache<T> {
);
}
Some(CursorShape::BlinkingBar | CursorShape::SteadyBar) => {
let metrics = self.metrics.clone();
self.draw_polys(
&metrics,
&[Poly {
@ -3713,7 +3715,6 @@ impl<T: Texture2d> GlyphCache<T> {
);
}
Some(CursorShape::BlinkingUnderline | CursorShape::SteadyUnderline) => {
let metrics = self.metrics.clone();
self.draw_polys(
&metrics,
&[Poly {
@ -3734,8 +3735,12 @@ impl<T: Texture2d> GlyphCache<T> {
Ok(sprite)
}
pub fn block_sprite(&mut self, block: BlockKey) -> anyhow::Result<Sprite<T>> {
let metrics = match &block {
pub fn block_sprite(
&mut self,
render_metrics: &RenderMetrics,
key: SizedBlockKey,
) -> anyhow::Result<Sprite<T>> {
let metrics = match &key.block {
BlockKey::PolyWithCustomMetrics {
underline_height,
cell_size,
@ -3748,7 +3753,7 @@ impl<T: Texture2d> GlyphCache<T> {
strike_row: 0,
cell_size: cell_size.clone(),
},
_ => self.metrics.clone(),
_ => render_metrics.clone(),
};
let mut buffer = Image::new(
@ -3761,7 +3766,7 @@ impl<T: Texture2d> GlyphCache<T> {
buffer.clear_rect(cell_rect, black);
match block {
match key.block {
BlockKey::Upper(num) => {
let lower = metrics.cell_size.height as f32 * (num as f32) / 8.;
let width = metrics.cell_size.width as usize;
@ -3916,7 +3921,7 @@ impl<T: Texture2d> GlyphCache<T> {
*/
let sprite = self.atlas.allocate(&buffer)?;
self.block_glyphs.insert(block, sprite.clone());
self.block_glyphs.insert(key, sprite.clone());
Ok(sprite)
}
}

View File

@ -27,12 +27,40 @@ use wezterm_font::units::*;
use wezterm_font::{FontConfiguration, GlyphInfo, LoadedFont};
use wezterm_term::Underline;
pub fn rc_to_usize<T>(rc: &Rc<T>) -> usize {
Rc::as_ptr(rc) as usize
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct CellMetricKey {
pub pixel_width: u16,
pub pixel_height: u16,
}
impl From<&RenderMetrics> for CellMetricKey {
fn from(metrics: &RenderMetrics) -> CellMetricKey {
CellMetricKey {
pixel_width: metrics.cell_size.width as u16,
pixel_height: metrics.cell_size.height as u16,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct SizedBlockKey {
pub block: BlockKey,
pub size: CellMetricKey,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct GlyphKey {
pub font_idx: usize,
pub glyph_pos: u32,
pub style: TextStyle,
pub followed_by_space: bool,
pub metric: CellMetricKey,
/// as produced by rc_to_usize
pub font_ptr: usize,
}
/// We'd like to avoid allocating when resolving from the cache
@ -46,6 +74,9 @@ pub struct BorrowedGlyphKey<'a> {
pub glyph_pos: u32,
pub style: &'a TextStyle,
pub followed_by_space: bool,
pub metric: CellMetricKey,
/// as produced by rc_to_usize
pub font_ptr: usize,
}
impl<'a> BorrowedGlyphKey<'a> {
@ -55,6 +86,8 @@ impl<'a> BorrowedGlyphKey<'a> {
glyph_pos: self.glyph_pos,
style: self.style.clone(),
followed_by_space: self.followed_by_space,
metric: self.metric,
font_ptr: self.font_ptr,
}
}
}
@ -70,6 +103,8 @@ impl GlyphKeyTrait for GlyphKey {
glyph_pos: self.glyph_pos,
style: &self.style,
followed_by_space: self.followed_by_space,
metric: self.metric,
font_ptr: self.font_ptr,
}
}
}
@ -134,6 +169,7 @@ struct LineKey {
strike_through: bool,
underline: Underline,
overline: bool,
size: CellMetricKey,
}
/// A helper struct to implement BitmapImage for ImageDataType while
@ -219,19 +255,14 @@ pub struct GlyphCache<T: Texture2d> {
pub image_cache: LruCache<usize, DecodedImage>,
frame_cache: HashMap<[u8; 32], Sprite<T>>,
line_glyphs: HashMap<LineKey, Sprite<T>>,
pub block_glyphs: HashMap<BlockKey, Sprite<T>>,
pub block_glyphs: HashMap<SizedBlockKey, Sprite<T>>,
pub cursor_glyphs: HashMap<Option<CursorShape>, Sprite<T>>,
pub color: HashMap<(RgbColor, NotNan<f32>), Sprite<T>>,
pub metrics: RenderMetrics,
}
#[cfg(test)]
impl GlyphCache<ImageTexture> {
pub fn new_in_memory(
fonts: &Rc<FontConfiguration>,
size: usize,
metrics: &RenderMetrics,
) -> anyhow::Result<Self> {
pub fn new_in_memory(fonts: &Rc<FontConfiguration>, size: usize) -> anyhow::Result<Self> {
let surface = Rc::new(ImageTexture::new(size, size));
let atlas = Atlas::new(&surface).expect("failed to create new texture atlas");
@ -245,7 +276,6 @@ impl GlyphCache<ImageTexture> {
),
frame_cache: HashMap::new(),
atlas,
metrics: metrics.clone(),
line_glyphs: HashMap::new(),
block_glyphs: HashMap::new(),
cursor_glyphs: HashMap::new(),
@ -259,7 +289,6 @@ impl GlyphCache<SrgbTexture2d> {
backend: &Rc<GliumContext>,
fonts: &Rc<FontConfiguration>,
size: usize,
metrics: &RenderMetrics,
) -> anyhow::Result<Self> {
let caps = backend.get_capabilities();
// You'd hope that allocating a texture would automatically
@ -298,7 +327,6 @@ impl GlyphCache<SrgbTexture2d> {
),
frame_cache: HashMap::new(),
atlas,
metrics: metrics.clone(),
line_glyphs: HashMap::new(),
block_glyphs: HashMap::new(),
cursor_glyphs: HashMap::new(),
@ -315,13 +343,16 @@ impl<T: Texture2d> GlyphCache<T> {
info: &GlyphInfo,
style: &TextStyle,
followed_by_space: bool,
font: Option<&Rc<LoadedFont>>,
font: &Rc<LoadedFont>,
metrics: &RenderMetrics,
) -> anyhow::Result<Rc<CachedGlyph<T>>> {
let key = BorrowedGlyphKey {
font_idx: info.font_idx,
glyph_pos: info.glyph_pos,
style,
followed_by_space,
metric: metrics.into(),
font_ptr: rc_to_usize(font),
};
if let Some(entry) = self.glyph_cache.get(&key as &dyn GlyphKeyTrait) {
@ -330,7 +361,7 @@ impl<T: Texture2d> GlyphCache<T> {
}
metrics::histogram!("glyph_cache.glyph_cache.miss.rate", 1.);
let glyph = match self.load_glyph(info, style, font, followed_by_space) {
let glyph = match self.load_glyph(info, font, followed_by_space) {
Ok(g) => g,
Err(err) => {
if err
@ -374,8 +405,7 @@ impl<T: Texture2d> GlyphCache<T> {
fn load_glyph(
&mut self,
info: &GlyphInfo,
style: &TextStyle,
font: Option<&Rc<LoadedFont>>,
font: &Rc<LoadedFont>,
followed_by_space: bool,
) -> anyhow::Result<Rc<CachedGlyph<T>>> {
let base_metrics;
@ -384,10 +414,6 @@ impl<T: Texture2d> GlyphCache<T> {
let glyph;
{
let font = match font {
Some(f) => Rc::clone(f),
None => self.fonts.resolve_font(style)?,
};
base_metrics = font.metrics();
glyph = font.rasterize_glyph(info.glyph_pos, info.font_idx)?;
@ -660,33 +686,41 @@ impl<T: Texture2d> GlyphCache<T> {
Ok(sprite)
}
pub fn cached_block(&mut self, block: BlockKey) -> anyhow::Result<Sprite<T>> {
if let Some(s) = self.block_glyphs.get(&block) {
pub fn cached_block(
&mut self,
block: BlockKey,
metrics: &RenderMetrics,
) -> anyhow::Result<Sprite<T>> {
let key = SizedBlockKey {
block,
size: metrics.into(),
};
if let Some(s) = self.block_glyphs.get(&key) {
return Ok(s.clone());
}
self.block_sprite(block)
self.block_sprite(metrics, key)
}
fn line_sprite(&mut self, key: LineKey) -> anyhow::Result<Sprite<T>> {
fn line_sprite(&mut self, key: LineKey, metrics: &RenderMetrics) -> anyhow::Result<Sprite<T>> {
let mut buffer = Image::new(
self.metrics.cell_size.width as usize,
self.metrics.cell_size.height as usize,
metrics.cell_size.width as usize,
metrics.cell_size.height as usize,
);
let black = SrgbaPixel::rgba(0, 0, 0, 0);
let white = SrgbaPixel::rgba(0xff, 0xff, 0xff, 0xff);
let cell_rect = Rect::new(Point::new(0, 0), self.metrics.cell_size);
let cell_rect = Rect::new(Point::new(0, 0), metrics.cell_size);
let draw_single = |buffer: &mut Image| {
for row in 0..self.metrics.underline_height {
for row in 0..metrics.underline_height {
buffer.draw_line(
Point::new(
cell_rect.origin.x,
cell_rect.origin.y + self.metrics.descender_row + row,
cell_rect.origin.y + metrics.descender_row + row,
),
Point::new(
cell_rect.origin.x + self.metrics.cell_size.width,
cell_rect.origin.y + self.metrics.descender_row + row,
cell_rect.origin.x + metrics.cell_size.width,
cell_rect.origin.y + metrics.descender_row + row,
),
white,
);
@ -694,17 +728,17 @@ impl<T: Texture2d> GlyphCache<T> {
};
let draw_dotted = |buffer: &mut Image| {
for row in 0..self.metrics.underline_height {
let y = (cell_rect.origin.y + self.metrics.descender_row + row) as usize;
if y >= self.metrics.cell_size.height as usize {
for row in 0..metrics.underline_height {
let y = (cell_rect.origin.y + metrics.descender_row + row) as usize;
if y >= metrics.cell_size.height as usize {
break;
}
let mut color = white;
let segment_length = (self.metrics.cell_size.width / 4) as usize;
let segment_length = (metrics.cell_size.width / 4) as usize;
let mut count = segment_length;
let range =
buffer.horizontal_pixel_range_mut(0, self.metrics.cell_size.width as usize, y);
buffer.horizontal_pixel_range_mut(0, metrics.cell_size.width as usize, y);
for c in range.iter_mut() {
*c = color.as_srgba32();
count -= 1;
@ -717,16 +751,16 @@ impl<T: Texture2d> GlyphCache<T> {
};
let draw_dashed = |buffer: &mut Image| {
for row in 0..self.metrics.underline_height {
let y = (cell_rect.origin.y + self.metrics.descender_row + row) as usize;
if y >= self.metrics.cell_size.height as usize {
for row in 0..metrics.underline_height {
let y = (cell_rect.origin.y + metrics.descender_row + row) as usize;
if y >= metrics.cell_size.height as usize {
break;
}
let mut color = white;
let third = (self.metrics.cell_size.width / 3) as usize + 1;
let third = (metrics.cell_size.width / 3) as usize + 1;
let mut count = third;
let range =
buffer.horizontal_pixel_range_mut(0, self.metrics.cell_size.width as usize, y);
buffer.horizontal_pixel_range_mut(0, metrics.cell_size.width as usize, y);
for c in range.iter_mut() {
*c = color.as_srgba32();
count -= 1;
@ -739,16 +773,15 @@ impl<T: Texture2d> GlyphCache<T> {
};
let draw_curly = |buffer: &mut Image| {
let max_y = self.metrics.cell_size.height as usize - 1;
let x_factor = (2. * std::f32::consts::PI) / self.metrics.cell_size.width as f32;
let max_y = metrics.cell_size.height as usize - 1;
let x_factor = (2. * std::f32::consts::PI) / metrics.cell_size.width as f32;
// Have the wave go from the descender to the bottom of the cell
let wave_height =
self.metrics.cell_size.height - (cell_rect.origin.y + self.metrics.descender_row);
metrics.cell_size.height - (cell_rect.origin.y + metrics.descender_row);
let half_height = (wave_height as f32 / 4.).max(1.);
let y =
(cell_rect.origin.y + self.metrics.descender_row) as usize - half_height as usize;
let y = (cell_rect.origin.y + metrics.descender_row) as usize - half_height as usize;
fn add(x: usize, y: usize, val: u8, max_y: usize, buffer: &mut Image) {
let y = y.min(max_y);
@ -758,12 +791,12 @@ impl<T: Texture2d> GlyphCache<T> {
*pixel = SrgbaPixel::rgba(value, value, value, value).as_srgba32();
}
for x in 0..self.metrics.cell_size.width as usize {
for x in 0..metrics.cell_size.width as usize {
let vertical = -half_height * (x as f32 * x_factor).sin() + half_height;
let v1 = vertical.floor();
let v2 = vertical.ceil();
for row in 0..self.metrics.underline_height as usize {
for row in 0..metrics.underline_height as usize {
let value = (255. * (vertical - v1).abs()) as u8;
add(x, row + y + v1 as usize, 255 - value, max_y, buffer);
add(x, row + y + v2 as usize, value, max_y, buffer);
@ -772,16 +805,15 @@ impl<T: Texture2d> GlyphCache<T> {
};
let draw_double = |buffer: &mut Image| {
let first_line = self
.metrics
let first_line = metrics
.descender_row
.min(self.metrics.descender_plus_two - 2 * self.metrics.underline_height);
.min(metrics.descender_plus_two - 2 * metrics.underline_height);
for row in 0..self.metrics.underline_height {
for row in 0..metrics.underline_height {
buffer.draw_line(
Point::new(cell_rect.origin.x, cell_rect.origin.y + first_line + row),
Point::new(
cell_rect.origin.x + self.metrics.cell_size.width,
cell_rect.origin.x + metrics.cell_size.width,
cell_rect.origin.y + first_line + row,
),
white,
@ -789,11 +821,11 @@ impl<T: Texture2d> GlyphCache<T> {
buffer.draw_line(
Point::new(
cell_rect.origin.x,
cell_rect.origin.y + self.metrics.descender_plus_two + row,
cell_rect.origin.y + 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,
cell_rect.origin.x + metrics.cell_size.width,
cell_rect.origin.y + metrics.descender_plus_two + row,
),
white,
);
@ -801,15 +833,15 @@ impl<T: Texture2d> GlyphCache<T> {
};
let draw_strike = |buffer: &mut Image| {
for row in 0..self.metrics.underline_height {
for row in 0..metrics.underline_height {
buffer.draw_line(
Point::new(
cell_rect.origin.x,
cell_rect.origin.y + self.metrics.strike_row + row,
cell_rect.origin.y + metrics.strike_row + row,
),
Point::new(
cell_rect.origin.x + self.metrics.cell_size.width,
cell_rect.origin.y + self.metrics.strike_row + row,
cell_rect.origin.x + metrics.cell_size.width,
cell_rect.origin.y + metrics.strike_row + row,
),
white,
);
@ -817,11 +849,11 @@ impl<T: Texture2d> GlyphCache<T> {
};
let draw_overline = |buffer: &mut Image| {
for row in 0..self.metrics.underline_height {
for row in 0..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.x + metrics.cell_size.width,
cell_rect.origin.y + row,
),
white,
@ -858,6 +890,7 @@ impl<T: Texture2d> GlyphCache<T> {
is_strike_through: bool,
underline: Underline,
overline: bool,
metrics: &RenderMetrics,
) -> anyhow::Result<Sprite<T>> {
let effective_underline = match (is_highlited_hyperlink, underline) {
(true, Underline::None) => Underline::Single,
@ -870,12 +903,13 @@ impl<T: Texture2d> GlyphCache<T> {
strike_through: is_strike_through,
overline,
underline: effective_underline,
size: metrics.into(),
};
if let Some(s) = self.line_glyphs.get(&key) {
return Ok(s.clone());
}
self.line_sprite(key)
self.line_sprite(key, metrics)
}
}

View File

@ -118,8 +118,7 @@ impl RenderState {
mut atlas_size: usize,
) -> anyhow::Result<Self> {
loop {
let glyph_cache =
RefCell::new(GlyphCache::new_gl(&context, fonts, atlas_size, metrics)?);
let glyph_cache = RefCell::new(GlyphCache::new_gl(&context, fonts, atlas_size)?);
let result = UtilSprites::new(&mut *glyph_cache.borrow_mut(), metrics);
match result {
Ok(util_sprites) => {
@ -291,7 +290,7 @@ impl RenderState {
size: Option<usize>,
) -> anyhow::Result<()> {
let size = size.unwrap_or_else(|| self.glyph_cache.borrow().atlas.size());
let mut new_glyph_cache = GlyphCache::new_gl(&self.context, fonts, size, metrics)?;
let mut new_glyph_cache = GlyphCache::new_gl(&self.context, fonts, size)?;
self.util_sprites = UtilSprites::new(&mut new_glyph_cache, metrics)?;
let mut glyph_cache = self.glyph_cache.borrow_mut();

View File

@ -288,7 +288,7 @@ mod test {
};
glyph_cache
.cached_glyph(info, &style, followed_by_space, None)
.cached_glyph(info, &style, followed_by_space, font, render_metrics)
.unwrap()
})
.collect::<Vec<_>>();
@ -331,7 +331,7 @@ mod test {
.unwrap(),
);
let render_metrics = RenderMetrics::new(&fonts).unwrap();
let mut glyph_cache = GlyphCache::new_in_memory(&fonts, 128, &render_metrics).unwrap();
let mut glyph_cache = GlyphCache::new_in_memory(&fonts, 128).unwrap();
let style = TextStyle::default();
let font = fonts.resolve_font(&style).unwrap();
@ -416,7 +416,7 @@ mod test {
.unwrap(),
);
let render_metrics = RenderMetrics::new(&fonts).unwrap();
let mut glyph_cache = GlyphCache::new_in_memory(&fonts, 128, &render_metrics).unwrap();
let mut glyph_cache = GlyphCache::new_in_memory(&fonts, 128).unwrap();
let style = TextStyle::default();
let font = fonts.resolve_font(&style).unwrap();

View File

@ -9,6 +9,7 @@ use crate::termwindow::{
BorrowedShapeCacheKey, MappedQuads, RenderState, ScrollHit, ShapedInfo, TermWindowNotif,
UIItem, UIItemType,
};
use crate::utilsprites::RenderMetrics;
use ::window::bitmaps::atlas::OutOfTextureSpace;
use ::window::bitmaps::{TextureCoord, TextureRect, TextureSize};
use ::window::glium;
@ -21,6 +22,7 @@ use anyhow::anyhow;
use config::{
ConfigHandle, DimensionContext, HsbTransform, TabBarColors, TextStyle, VisualBellTarget,
};
use euclid::num::Zero;
use mux::pane::Pane;
use mux::renderable::{RenderableDimensions, StableCursorPosition};
use mux::tab::{PositionedPane, PositionedSplit, SplitDirection};
@ -31,7 +33,7 @@ use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
use termwiz::cell::{unicode_column_width, Blink};
use termwiz::cellcluster::CellCluster;
use termwiz::surface::{CursorShape, CursorVisibility};
use wezterm_font::units::IntPixelLength;
use wezterm_font::units::{IntPixelLength, PixelLength};
use wezterm_font::{ClearShapeCache, FontMetrics, GlyphInfo, LoadedFont};
use wezterm_term::color::{ColorAttribute, ColorPalette, RgbColor};
use wezterm_term::{CellAttributes, Line, StableRowIndex};
@ -80,6 +82,8 @@ pub struct RenderScreenLineOpenGLParams<'a> {
/// rather than using monospace cell based positions.
pub use_pixel_positioning: bool,
pub pre_shaped: Option<&'a Vec<ShapedCluster<'a>>>,
pub render_metrics: RenderMetrics,
}
pub struct ComputeCellFgBgParams<'a> {
@ -347,11 +351,14 @@ impl super::TermWindow {
let sprite = gl_state
.glyph_cache
.borrow_mut()
.cached_block(BlockKey::PolyWithCustomMetrics {
polys,
underline_height,
cell_size,
})?
.cached_block(
BlockKey::PolyWithCustomMetrics {
polys,
underline_height,
cell_size,
},
&self.render_metrics,
)?
.texture_coords();
let mut quad = layer.allocate()?;
@ -509,6 +516,7 @@ impl super::TermWindow {
style: Some(&self.config.window_frame.font),
use_pixel_positioning: true,
pre_shaped: None,
render_metrics: RenderMetrics::with_font_metrics(metrics),
};
let cell_clusters = item.title.cluster();
let shaped = self.cluster_and_shape(&cell_clusters, &params)?;
@ -689,6 +697,7 @@ impl super::TermWindow {
))
}
TabBarItem::None => {
// log::info!("{:#?}", shaped);
// Right align to window width
let tab_bounding_rect: Rect = euclid::rect(
tab_bounding_rect.min_x(),
@ -941,6 +950,7 @@ impl super::TermWindow {
font: None,
use_pixel_positioning: false,
pre_shaped: None,
render_metrics: self.render_metrics,
},
&mut layers,
)?;
@ -1290,6 +1300,7 @@ impl super::TermWindow {
style: None,
use_pixel_positioning: false,
pre_shaped: None,
render_metrics: self.render_metrics,
},
&mut layers,
)?;
@ -1458,7 +1469,7 @@ impl super::TermWindow {
let sprite = gl_state
.glyph_cache
.borrow_mut()
.cached_block(block)?
.cached_block(block, &self.render_metrics)?
.texture_coords();
let mut quad = quads.allocate()?;
@ -1578,6 +1589,7 @@ impl super::TermWindow {
attrs.strikethrough(),
attrs.underline(),
attrs.overline(),
&params.render_metrics,
)?
.texture_coords();
let bg_is_default = attrs.background() == ColorAttribute::Default;
@ -1670,6 +1682,7 @@ impl super::TermWindow {
&gl_state,
params.line,
params.font.as_ref(),
&params.render_metrics,
)?;
let pixel_width = glyph_info
.iter()
@ -1709,14 +1722,8 @@ impl super::TermWindow {
Some(params.config.inactive_pane_hsb)
};
let metrics = params.font.as_ref().map(|f| f.metrics());
let cell_width = metrics
.map(|m| m.cell_width.get() as isize)
.unwrap_or(self.render_metrics.cell_size.width) as f32;
let cell_height = metrics
.map(|m| m.cell_height.get() as isize)
.unwrap_or(self.render_metrics.cell_size.height) as f32;
let cell_width = params.render_metrics.cell_size.width as f32;
let cell_height = params.render_metrics.cell_size.height as f32;
let pos_y = (self.dimensions.pixel_height as f32 / -2.) + params.top_pixel_y;
let start = Instant::now();
@ -1855,7 +1862,7 @@ impl super::TermWindow {
.texture
.as_ref()
.map(|t| {
let width = self.render_metrics.cell_size.width as f32;
let width = params.render_metrics.cell_size.width as f32;
if t.coords.size.width as f32 > width * 1.5 {
// Glyph is wider than the cell
true
@ -1869,11 +1876,7 @@ impl super::TermWindow {
.unwrap_or(false);
*/
let top = cell_height
+ (metrics
.map(|m| m.descender)
.unwrap_or(self.render_metrics.descender)
.get() as f32)
let top = cell_height + params.render_metrics.descender.get() as f32
- (glyph.y_offset + glyph.bearing_y).get() as f32;
// We use this to remember the `left` offset value to use for glyph_idx > 0
@ -2012,7 +2015,7 @@ impl super::TermWindow {
gl_state
.glyph_cache
.borrow_mut()
.cursor_sprite(cursor_shape)?
.cursor_sprite(cursor_shape, &params.render_metrics)?
.texture_coords(),
);
@ -2070,7 +2073,7 @@ impl super::TermWindow {
block,
gl_state,
&mut layers[0],
cell_idx,
pos_x,
&params,
hsv,
glyph_color,
@ -2119,7 +2122,7 @@ impl super::TermWindow {
let slice = SpriteSlice {
cell_idx: glyph_idx,
num_cells: info.pos.num_cells as usize,
cell_width: self.render_metrics.cell_size.width as usize,
cell_width: params.render_metrics.cell_size.width as usize,
scale: glyph.scale as f32,
left_offset: left,
};
@ -2131,9 +2134,9 @@ impl super::TermWindow {
let left = if glyph_idx == 0 { left } else { slice_left };
let bottom =
(pixel_rect.size.height as f32 * glyph.scale as f32) + top
- self.render_metrics.cell_size.height as f32;
- params.render_metrics.cell_size.height as f32;
let right = pixel_rect.size.width as f32 + left
- self.render_metrics.cell_size.width as f32;
- params.render_metrics.cell_size.width as f32;
// Save the `right` position; we'll use it for the `left` adjust for
// the next slice that comprises this glyph.
@ -2285,7 +2288,7 @@ impl super::TermWindow {
gl_state
.glyph_cache
.borrow_mut()
.cursor_sprite(cursor_shape)?
.cursor_sprite(cursor_shape, &params.render_metrics)?
.texture_coords(),
);
quad.set_fg_color(cursor_border_color);
@ -2312,7 +2315,7 @@ impl super::TermWindow {
block: BlockKey,
gl_state: &RenderState,
quads: &mut MappedQuads,
cell_idx: usize,
pos_x: f32,
params: &RenderScreenLineOpenGLParams,
hsv: Option<config::HsbTransform>,
glyph_color: LinearRgba,
@ -2320,16 +2323,13 @@ impl super::TermWindow {
let sprite = gl_state
.glyph_cache
.borrow_mut()
.cached_block(block)?
.cached_block(block, &params.render_metrics)?
.texture_coords();
let mut quad = quads.allocate()?;
let cell_width = self.render_metrics.cell_size.width as f32;
let cell_height = self.render_metrics.cell_size.height as f32;
let cell_width = params.render_metrics.cell_size.width as f32;
let cell_height = params.render_metrics.cell_size.height as f32;
let pos_y = (self.dimensions.pixel_height as f32 / -2.) + params.top_pixel_y;
let pos_x = (self.dimensions.pixel_width as f32 / -2.)
+ params.left_pixel_x
+ (cell_idx as f32 * cell_width);
quad.set_position(pos_x, pos_y, pos_x + cell_width, pos_y + cell_height);
quad.set_hsv(hsv);
quad.set_fg_color(glyph_color);
@ -2358,7 +2358,7 @@ impl super::TermWindow {
.render_metrics
.cell_size
.height
.max(self.render_metrics.cell_size.width) as usize;
.max(params.render_metrics.cell_size.width) as usize;
let padding = if padding.is_power_of_two() {
padding
} else {
@ -2396,8 +2396,8 @@ impl super::TermWindow {
let texture_rect = TextureRect::new(origin, size);
let mut quad = quads.allocate()?;
let cell_width = self.render_metrics.cell_size.width as f32;
let cell_height = self.render_metrics.cell_size.height as f32;
let cell_width = params.render_metrics.cell_size.width as f32;
let cell_height = params.render_metrics.cell_size.height as f32;
let pos_y = (self.dimensions.pixel_height as f32 / -2.) + params.top_pixel_y;
let pos_x = (self.dimensions.pixel_width as f32 / -2.)
@ -2580,17 +2580,47 @@ impl super::TermWindow {
style: &TextStyle,
glyph_cache: &mut GlyphCache<SrgbTexture2d>,
infos: &[GlyphInfo],
font: Option<&Rc<LoadedFont>>,
font: &Rc<LoadedFont>,
metrics: &RenderMetrics,
) -> anyhow::Result<Vec<Rc<CachedGlyph<SrgbTexture2d>>>> {
let mut glyphs = Vec::with_capacity(infos.len());
for info in infos {
let cell_idx = cluster.byte_to_cell_idx(info.cluster as usize);
if self.config.custom_block_glyphs {
if let Some(cell) = line.cells().get(cell_idx) {
if BlockKey::from_cell(cell).is_some() {
// Don't bother rendering the glyph from the font, as it can
// have incorrect advance metrics.
// Instead, just use our pixel-perfect cell metrics
glyphs.push(Rc::new(CachedGlyph {
brightness_adjust: 1.0,
has_color: false,
texture: None,
x_advance: PixelLength::new(metrics.cell_size.width as f64),
x_offset: PixelLength::zero(),
y_offset: PixelLength::zero(),
bearing_x: PixelLength::zero(),
bearing_y: PixelLength::zero(),
scale: 1.0,
}));
continue;
}
}
}
let followed_by_space = match line.cells().get(cell_idx + 1) {
Some(cell) => cell.str() == " ",
None => false,
};
glyphs.push(glyph_cache.cached_glyph(info, &style, followed_by_space, font)?);
glyphs.push(glyph_cache.cached_glyph(
info,
&style,
followed_by_space,
font,
metrics,
)?);
}
Ok(glyphs)
}
@ -2603,6 +2633,7 @@ impl super::TermWindow {
gl_state: &RenderState,
line: &Line,
font: Option<&Rc<LoadedFont>>,
metrics: &RenderMetrics,
) -> anyhow::Result<Rc<Vec<ShapedInfo<SrgbTexture2d>>>> {
let shape_resolve_start = Instant::now();
let key = BorrowedShapeCacheKey {
@ -2631,14 +2662,10 @@ impl super::TermWindow {
&style,
&mut gl_state.glyph_cache.borrow_mut(),
&info,
Some(&font),
&font,
metrics,
)?;
let shaped = Rc::new(ShapedInfo::process(
&self.render_metrics,
cluster,
&info,
&glyphs,
));
let shaped = Rc::new(ShapedInfo::process(metrics, cluster, &info, &glyphs));
self.shape_cache
.borrow_mut()

View File

@ -6,7 +6,7 @@ use ::window::{Point, Rect, Size};
use anyhow::Context;
use std::rc::Rc;
use wezterm_font::units::*;
use wezterm_font::FontConfiguration;
use wezterm_font::{FontConfiguration, FontMetrics};
#[derive(Copy, Clone, Debug)]
pub struct RenderMetrics {
@ -19,6 +19,30 @@ pub struct RenderMetrics {
}
impl RenderMetrics {
pub fn with_font_metrics(metrics: &FontMetrics) -> Self {
let (cell_height, cell_width) = (
metrics.cell_height.get().ceil() as usize,
metrics.cell_width.get().ceil() as usize,
);
let underline_height = metrics.underline_thickness.get().round().max(1.) as isize;
let descender_row =
(cell_height as f64 + (metrics.descender - metrics.underline_position).get()) as isize;
let descender_plus_two =
(2 * underline_height + descender_row).min(cell_height as isize - underline_height);
let strike_row = descender_row / 2;
Self {
descender: metrics.descender,
descender_row,
descender_plus_two,
strike_row,
cell_size: Size::new(cell_width as isize, cell_height as isize),
underline_height,
}
}
pub fn new(fonts: &Rc<FontConfiguration>) -> anyhow::Result<Self> {
let metrics = fonts
.default_font_metrics()