1
1
mirror of https://github.com/wez/wezterm.git synced 2024-09-22 04:07:31 +03:00

simplify shape preprocessing

Manual test scenario:

```
wezterm -n --config adjust_window_size_when_changing_font_size=false --config 'exit_behavior="Hold"' start -- sh -c "echo '(...)'"
```

then CTRL +/- to change font size; the first cell of the `...` was
previously random garbage, now is more consistent.

refs: https://github.com/wez/wezterm/issues/478
This commit is contained in:
Wez Furlong 2021-03-06 10:19:59 -08:00
parent a3429d189b
commit f0527f646c
2 changed files with 99 additions and 87 deletions

View File

@ -1,4 +1,5 @@
use crate::glyphcache::CachedGlyph;
use crate::utilsprites::RenderMetrics;
use ::window::bitmaps::Texture2d;
use config::TextStyle;
use std::rc::Rc;
@ -44,81 +45,80 @@ where
/// found for the resultant grapheme.
/// This function's goal is to handle those two cases.
pub fn process(
render_metrics: &RenderMetrics,
cluster: &CellCluster,
infos: &[GlyphInfo],
glyphs: &[Rc<CachedGlyph<T>>],
) -> Vec<ShapedInfo<T>> {
let mut pos: Vec<ShapedInfo<T>> = vec![];
let mut run = None;
let mut pos: Vec<Option<ShapedInfo<T>>> = vec![];
let mut x = 0.;
let mut prior_info: Option<&GlyphInfo> = None;
for (info, glyph) in infos.iter().zip(glyphs.iter()) {
if !info.is_space && glyph.texture.is_none() {
if let Some(shaped) = pos.last_mut() {
// if bearing_x == 0, then the glyph could be a blank
// in a <blank>, <blank>, "..." sequence, and it shouldn't
// be swept into the "a" in a sequence like "a..."
if shaped.pos.bearing_x > 0.
&& shaped.pos.bitmap_pixel_width as f32 > shaped.pos.bearing_x
{
// This space-like glyph belongs to the preceding glyph
shaped.pos.num_cells += 1;
continue;
}
}
if run.is_none() {
run.replace(ShapedInfo {
pos: GlyphPosition {
glyph_idx: info.glyph_pos,
cluster: info.cluster,
num_cells: info.num_cells,
x_offset: info.x_advance,
bearing_x: 0.,
bitmap_pixel_width: 0,
},
glyph: Rc::clone(glyph),
});
continue;
}
let run = run.as_mut().unwrap();
run.pos.num_cells += info.num_cells;
run.pos.x_offset += info.x_advance;
continue;
let idx = ((x + info.x_offset.get() + glyph.bearing_x.get())
/ render_metrics.cell_size.width as f64)
.floor() as usize;
if idx >= pos.len() {
pos.resize_with(idx + 1, || None);
}
if let Some(mut run) = run.take() {
run.glyph = Rc::clone(glyph);
run.pos.glyph_idx = info.glyph_pos;
run.pos.num_cells += info.num_cells;
run.pos.bitmap_pixel_width = glyph
if let Some(prior) = prior_info.take() {
if prior.cluster == info.cluster {
// This is a tricky case: if we have a cluster such as
// 1F470 1F3FF 200D 2640 (woman with veil: dark skin tone)
// and the font doesn't define a glyph for it, the shaper
// may give us a sequence of three output clusters, each
// comprising: veil, skin tone and female respectively.
// Those all have the same info.cluster which
// means that they all resolve to the same cell_idx.
// In this case, the cluster is logically a single cell,
// and the best presentation is of the veil, so we pick
// that one and ignore the rest of the glyphs that map to
// this same cell.
// Ideally we'd overlay this with a "something is broken"
// glyph in the corner.
prior_info.replace(info);
continue;
}
}
prior_info.replace(info);
if glyph.texture.is_some() {
assert!(pos[idx].is_none());
let bitmap_pixel_width = glyph
.texture
.as_ref()
.map_or(0, |t| t.coords.width() as u32);
run.pos.bearing_x = (run.pos.x_offset.get() + glyph.bearing_x.get() as f64) as f32;
run.pos.x_offset = PixelLength::new(0.);
pos.push(run);
} else {
let cell_idx = cluster.byte_to_cell_idx[info.cluster as usize];
if let Some(prior) = pos.last() {
let prior_cell_idx = cluster.byte_to_cell_idx[prior.pos.cluster as usize];
if cell_idx <= prior_cell_idx {
// This is a tricky case: if we have a cluster such as
// 1F470 1F3FF 200D 2640 (woman with veil: dark skin tone)
// and the font doesn't define a glyph for it, the shaper
// may give us a sequence of three output clusters, each
// comprising: veil, skin tone and female respectively.
// Those all have the same info.cluster which
// means that they all resolve to the same cell_idx.
// In this case, the cluster is logically a single cell,
// and the best presentation is of the veil, so we pick
// that one and ignore the rest of the glyphs that map to
// this same cell.
// Ideally we'd overlay this with a "something is broken"
// glyph in the corner.
continue;
}
}
pos.push(ShapedInfo {
let glyph_width = (info.x_advance - glyph.bearing_x).get().ceil();
let num_cells =
if info.num_cells == 1 && glyph_width > render_metrics.cell_size.width as f64 {
(glyph_width / render_metrics.cell_size.width as f64).ceil() as u8
} else {
info.num_cells
};
let cluster = if num_cells > info.num_cells {
info.cluster - (num_cells - info.num_cells) as u32
} else {
info.cluster
};
let bearing_x = if num_cells > info.num_cells && glyph.bearing_x.get() < 0. {
((num_cells - info.num_cells) as f64 * render_metrics.cell_size.width as f64)
+ glyph.bearing_x.get()
} else {
glyph.bearing_x.get()
};
pos[idx].replace(ShapedInfo {
glyph: Rc::clone(&glyph),
pos: GlyphPosition {
glyph_idx: info.glyph_pos,
cluster,
num_cells,
x_offset: info.x_offset,
bearing_x: bearing_x as f32,
bitmap_pixel_width,
},
});
} else if info.is_space {
pos[idx].replace(ShapedInfo {
pos: GlyphPosition {
glyph_idx: info.glyph_pos,
bitmap_pixel_width: glyph
@ -133,16 +133,10 @@ where
glyph: Rc::clone(glyph),
});
}
x += info.x_advance.get();
}
if let Some(run) = run.take() {
log::warn!(
"leftovers when shaping {:#?} {:#?} -> {:?}",
infos,
glyphs,
run
);
}
pos
pos.into_iter().filter_map(|n| n).collect()
}
}
@ -228,6 +222,7 @@ mod test {
use wezterm_font::LoadedFont;
fn cluster_and_shape<T>(
render_metrics: &RenderMetrics,
glyph_cache: &mut GlyphCache<T>,
style: &TextStyle,
font: &Rc<LoadedFont>,
@ -261,7 +256,7 @@ mod test {
eprintln!("infos: {:#?}", infos);
eprintln!("glyphs: {:#?}", glyphs);
ShapedInfo::process(&cluster, &infos, &glyphs)
ShapedInfo::process(render_metrics, &cluster, &infos, &glyphs)
.into_iter()
.map(|p| p.pos)
.collect()
@ -293,7 +288,7 @@ mod test {
let font = fonts.resolve_font(&style).unwrap();
assert_eq!(
cluster_and_shape(&mut glyph_cache, &style, &font, "a..."),
cluster_and_shape(&render_metrics, &mut glyph_cache, &style, &font, "a..."),
vec![
GlyphPosition {
glyph_idx: 180,
@ -331,7 +326,7 @@ mod test {
let font = fonts.resolve_font(&style).unwrap();
assert_eq!(
cluster_and_shape(&mut glyph_cache, &style, &font, "ab"),
cluster_and_shape(&render_metrics, &mut glyph_cache, &style, &font, "ab"),
vec![
GlyphPosition {
glyph_idx: 180,
@ -353,7 +348,7 @@ mod test {
);
assert_eq!(
cluster_and_shape(&mut glyph_cache, &style, &font, "a b"),
cluster_and_shape(&render_metrics, &mut glyph_cache, &style, &font, "a b"),
vec![
GlyphPosition {
glyph_idx: 180,
@ -383,7 +378,7 @@ mod test {
);
assert_eq!(
cluster_and_shape(&mut glyph_cache, &style, &font, "a..."),
cluster_and_shape(&render_metrics, &mut glyph_cache, &style, &font, "a..."),
vec![
GlyphPosition {
glyph_idx: 180,
@ -405,7 +400,7 @@ mod test {
);
assert_eq!(
cluster_and_shape(&mut glyph_cache, &style, &font, "a b"),
cluster_and_shape(&render_metrics, &mut glyph_cache, &style, &font, "a b"),
vec![
GlyphPosition {
glyph_idx: 180,
@ -443,7 +438,7 @@ mod test {
);
assert_eq!(
cluster_and_shape(&mut glyph_cache, &style, &font, "<-"),
cluster_and_shape(&render_metrics, &mut glyph_cache, &style, &font, "<-"),
vec![GlyphPosition {
glyph_idx: 1065,
cluster: 0,
@ -455,7 +450,7 @@ mod test {
);
assert_eq!(
cluster_and_shape(&mut glyph_cache, &style, &font, "<>"),
cluster_and_shape(&render_metrics, &mut glyph_cache, &style, &font, "<>"),
vec![GlyphPosition {
glyph_idx: 1089,
cluster: 0,
@ -467,7 +462,7 @@ mod test {
);
assert_eq!(
cluster_and_shape(&mut glyph_cache, &style, &font, "|=>"),
cluster_and_shape(&render_metrics, &mut glyph_cache, &style, &font, "|=>"),
vec![GlyphPosition {
glyph_idx: 1040,
cluster: 0,
@ -479,7 +474,7 @@ mod test {
);
assert_eq!(
cluster_and_shape(&mut glyph_cache, &style, &font, "<!--"),
cluster_and_shape(&render_metrics, &mut glyph_cache, &style, &font, "<!--"),
vec![GlyphPosition {
glyph_idx: 1071,
cluster: 0,
@ -497,6 +492,7 @@ mod test {
);
assert_eq!(
cluster_and_shape(
&render_metrics,
&mut glyph_cache,
&style,
&font,
@ -515,7 +511,13 @@ mod test {
let england_flag = "\u{1F3F4}\u{E0067}\u{E0062}\u{E0065}\u{E006E}\u{E0067}\u{E007F}";
println!("england_flag: {}", england_flag);
assert_eq!(
cluster_and_shape(&mut glyph_cache, &style, &font, england_flag),
cluster_and_shape(
&render_metrics,
&mut glyph_cache,
&style,
&font,
england_flag
),
vec![GlyphPosition {
glyph_idx: 1583,
cluster: 0,

View File

@ -458,7 +458,12 @@ impl super::TermWindow {
&mut gl_state.glyph_cache.borrow_mut(),
&info,
)?;
let shaped = ShapedInfo::process(&clusters[0], &info, &glyphs);
let shaped = ShapedInfo::process(
&self.render_metrics,
&clusters[0],
&info,
&glyphs,
);
self.shape_cache
.borrow_mut()
.put(key.to_owned(), Ok(Rc::new(shaped)));
@ -693,7 +698,12 @@ impl super::TermWindow {
&mut gl_state.glyph_cache.borrow_mut(),
&info,
)?;
let shaped = ShapedInfo::process(cluster, &info, &glyphs);
let shaped = ShapedInfo::process(
&self.render_metrics,
cluster,
&info,
&glyphs,
);
self.shape_cache
.borrow_mut()