Add skip_shaping flag to avoid expensive shaping when not needed

This commit is contained in:
Héctor Ramón Jiménez 2023-04-19 00:24:43 +02:00
parent bfb5eefbfa
commit ad111a1df1
No known key found for this signature in database
GPG key ID: 140CC052C94F138E
5 changed files with 126 additions and 29 deletions

View file

@ -331,7 +331,7 @@ impl Buffer {
redraw: false,
wrap: Wrap::Word,
};
buffer.set_text(font_system, "", Attrs::new());
buffer.set_text(font_system, "", Attrs::new(), true);
buffer
}
@ -562,16 +562,28 @@ impl Buffer {
}
/// Set text of buffer, using provided attributes for each line by default
pub fn set_text(&mut self, font_system: &mut FontSystem, text: &str, attrs: Attrs) {
pub fn set_text(
&mut self,
font_system: &mut FontSystem,
text: &str,
attrs: Attrs,
skip_shaping: bool,
) {
self.lines.clear();
for line in text.lines() {
self.lines
.push(BufferLine::new(line.to_string(), AttrsList::new(attrs)));
self.lines.push(BufferLine::new(
line.to_string(),
AttrsList::new(attrs),
skip_shaping,
));
}
// Make sure there is always one line
if self.lines.is_empty() {
self.lines
.push(BufferLine::new(String::new(), AttrsList::new(attrs)));
self.lines.push(BufferLine::new(
String::new(),
AttrsList::new(attrs),
skip_shaping,
));
}
self.scroll = 0;
@ -769,8 +781,9 @@ impl<'a> BorrowedWithFontSystem<'a, Buffer> {
}
/// Set text of buffer, using provided attributes for each line by default
pub fn set_text(&mut self, text: &str, attrs: Attrs) {
self.inner.set_text(self.font_system, text, attrs);
pub fn set_text(&mut self, text: &str, attrs: Attrs, skip_shaping: bool) {
self.inner
.set_text(self.font_system, text, attrs, skip_shaping);
}
/// Draw the buffer

View file

@ -12,13 +12,14 @@ pub struct BufferLine {
align: Option<Align>,
shape_opt: Option<ShapeLine>,
layout_opt: Option<Vec<LayoutLine>>,
skip_shaping: bool,
}
impl BufferLine {
/// Create a new line with the given text and attributes list
/// Cached shaping and layout can be done using the [`Self::shape`] and
/// [`Self::layout`] functions
pub fn new<T: Into<String>>(text: T, attrs_list: AttrsList) -> Self {
pub fn new<T: Into<String>>(text: T, attrs_list: AttrsList, skip_shaping: bool) -> Self {
Self {
text: text.into(),
attrs_list,
@ -26,6 +27,7 @@ impl BufferLine {
align: None,
shape_opt: None,
layout_opt: None,
skip_shaping,
}
}
@ -142,7 +144,7 @@ impl BufferLine {
let attrs_list = self.attrs_list.split_off(index);
self.reset();
let mut new = Self::new(text, attrs_list);
let mut new = Self::new(text, attrs_list, self.skip_shaping);
new.wrap = self.wrap;
new
}
@ -167,7 +169,12 @@ impl BufferLine {
/// Shape line, will cache results
pub fn shape(&mut self, font_system: &mut FontSystem) -> &ShapeLine {
if self.shape_opt.is_none() {
self.shape_opt = Some(ShapeLine::new(font_system, &self.text, &self.attrs_list));
self.shape_opt = Some(ShapeLine::new(
font_system,
&self.text,
&self.attrs_list,
self.skip_shaping,
));
self.layout_opt = None;
}
self.shape_opt.as_ref().expect("shape not found")

View file

@ -245,6 +245,7 @@ impl Edit for Editor {
.strip_suffix(char::is_control)
.unwrap_or(data_line),
these_attrs,
false,
));
} else {
panic!("str::lines() did not yield any elements");
@ -256,6 +257,7 @@ impl Edit for Editor {
.strip_suffix(char::is_control)
.unwrap_or(data_line),
final_attrs.split_off(remaining_split_len),
false,
);
tmp.append(after);
self.buffer.lines.insert(insert_line, tmp);
@ -270,6 +272,7 @@ impl Edit for Editor {
.strip_suffix(char::is_control)
.unwrap_or(data_line),
final_attrs.split_off(remaining_split_len),
false,
);
self.buffer.lines.insert(insert_line, tmp);
self.cursor.line += 1;

View file

@ -36,7 +36,7 @@
//! let attrs = Attrs::new();
//!
//! // Add some text!
//! buffer.set_text("Hello, Rust! 🦀\n", attrs);
//! buffer.set_text("Hello, Rust! 🦀\n", attrs, false);
//!
//! // Perform shaping as desired
//! buffer.shape_until_scroll();

View file

@ -213,6 +213,49 @@ fn shape_run(
glyphs
}
fn shape_skip(
font_system: &mut FontSystem,
line: &str,
attrs_list: &AttrsList,
start_run: usize,
end_run: usize,
) -> Vec<ShapeGlyph> {
let attrs = attrs_list.get_span(start_run);
let fonts = font_system.get_font_matches(attrs);
let default_families = [&attrs.family];
let mut font_iter = FontFallbackIter::new(font_system, &fonts, &default_families, vec![]);
let font = font_iter.next().expect("no default font found");
let font_id = font.id();
let font = font.as_swash();
let charmap = font.charmap();
let glyph_metrics = font.glyph_metrics(&[]).scale(1.0);
line[start_run..end_run]
.chars()
.enumerate()
.map(|(i, codepoint)| {
let glyph_id = charmap.map(codepoint);
let x_advance = glyph_metrics.advance_width(glyph_id);
ShapeGlyph {
start: i,
end: i + 1,
x_advance,
y_advance: 0.0,
x_offset: 0.0,
y_offset: 0.0,
font_id,
glyph_id,
color_opt: attrs.color_opt,
metadata: attrs.metadata,
}
})
.collect()
}
/// A shaped glyph
pub struct ShapeGlyph {
pub start: usize,
@ -278,6 +321,7 @@ impl ShapeWord {
word_range: Range<usize>,
level: unicode_bidi::Level,
blank: bool,
skip_shaping: bool,
) -> Self {
let word = &line[word_range.clone()];
@ -297,14 +341,24 @@ impl ShapeWord {
let attrs_egc = attrs_list.get_span(start_egc);
if !attrs.compatible(&attrs_egc) {
//TODO: more efficient
glyphs.append(&mut shape_run(
font_system,
line,
attrs_list,
start_run,
start_egc,
span_rtl,
));
if skip_shaping {
glyphs.append(&mut shape_skip(
font_system,
line,
attrs_list,
start_run,
start_egc,
));
} else {
glyphs.append(&mut shape_run(
font_system,
line,
attrs_list,
start_run,
start_egc,
span_rtl,
));
};
start_run = start_egc;
attrs = attrs_egc;
@ -312,14 +366,24 @@ impl ShapeWord {
}
if start_run < word_range.end {
//TODO: more efficient
glyphs.append(&mut shape_run(
font_system,
line,
attrs_list,
start_run,
word_range.end,
span_rtl,
));
if skip_shaping {
glyphs.append(&mut shape_skip(
font_system,
line,
attrs_list,
start_run,
word_range.end,
));
} else {
glyphs.append(&mut shape_run(
font_system,
line,
attrs_list,
start_run,
word_range.end,
span_rtl,
));
}
}
let mut x_advance = 0.0;
@ -352,6 +416,7 @@ impl ShapeSpan {
span_range: Range<usize>,
line_rtl: bool,
level: unicode_bidi::Level,
skip_shaping: bool,
) -> Self {
let span = &line[span_range.start..span_range.end];
@ -382,6 +447,7 @@ impl ShapeSpan {
(span_range.start + start_word)..(span_range.start + start_lb),
level,
false,
skip_shaping,
));
}
if start_lb < end_lb {
@ -395,6 +461,7 @@ impl ShapeSpan {
..(span_range.start + start_lb + i + c.len_utf8()),
level,
true,
skip_shaping,
));
}
}
@ -437,7 +504,12 @@ impl ShapeLine {
/// # Panics
///
/// Will panic if `line` contains more than one paragraph.
pub fn new(font_system: &mut FontSystem, line: &str, attrs_list: &AttrsList) -> Self {
pub fn new(
font_system: &mut FontSystem,
line: &str,
attrs_list: &AttrsList,
skip_shaping: bool,
) -> Self {
let mut spans = Vec::new();
let bidi = unicode_bidi::BidiInfo::new(line, None);
@ -473,6 +545,7 @@ impl ShapeLine {
start..i,
line_rtl,
run_level,
skip_shaping,
));
start = i;
run_level = new_level;
@ -485,6 +558,7 @@ impl ShapeLine {
start..line_range.end,
line_rtl,
run_level,
skip_shaping,
));
line_rtl
};