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

View File

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

View File

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

View File

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

View File

@ -6,7 +6,7 @@ use ::window::{Point, Rect, Size};
use anyhow::Context; use anyhow::Context;
use std::rc::Rc; use std::rc::Rc;
use wezterm_font::units::*; use wezterm_font::units::*;
use wezterm_font::FontConfiguration; use wezterm_font::{FontConfiguration, FontMetrics};
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
pub struct RenderMetrics { pub struct RenderMetrics {
@ -19,6 +19,30 @@ pub struct RenderMetrics {
} }
impl 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> { pub fn new(fonts: &Rc<FontConfiguration>) -> anyhow::Result<Self> {
let metrics = fonts let metrics = fonts
.default_font_metrics() .default_font_metrics()