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:
parent
5a6317ebe4
commit
64271288ac
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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, ¶ms)?;
|
||||
@ -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(),
|
||||
¶ms.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(),
|
||||
¶ms.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, ¶ms.render_metrics)?
|
||||
.texture_coords(),
|
||||
);
|
||||
|
||||
@ -2070,7 +2073,7 @@ impl super::TermWindow {
|
||||
block,
|
||||
gl_state,
|
||||
&mut layers[0],
|
||||
cell_idx,
|
||||
pos_x,
|
||||
¶ms,
|
||||
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, ¶ms.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, ¶ms.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()
|
||||
|
@ -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()
|
||||
|
Loading…
Reference in New Issue
Block a user