From bc953df0d69357aa450e8d5243086a64d6b17e30 Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Wed, 5 Oct 2022 10:43:59 -0600 Subject: [PATCH] Store original text as lines, reshape only changed lines --- examples/text/src/font/layout.rs | 1 + examples/text/src/font/matches.rs | 33 ++++---------------- examples/text/src/font/shape.rs | 2 ++ examples/text/src/main.rs | 50 +++++++++++++++++++++++-------- 4 files changed, 46 insertions(+), 40 deletions(-) diff --git a/examples/text/src/font/layout.rs b/examples/text/src/font/layout.rs index 0b0047dc..992ad3c6 100644 --- a/examples/text/src/font/layout.rs +++ b/examples/text/src/font/layout.rs @@ -1,6 +1,7 @@ use core::marker::PhantomData; pub struct FontLayoutGlyph<'a, T: 'a> { + pub line_i: usize, pub start: usize, pub end: usize, pub x: f32, diff --git a/examples/text/src/font/matches.rs b/examples/text/src/font/matches.rs index 4f5675c0..ac7b42d4 100644 --- a/examples/text/src/font/matches.rs +++ b/examples/text/src/font/matches.rs @@ -5,8 +5,8 @@ pub struct FontMatches<'a> { } impl<'a> FontMatches<'a> { - fn shape_span(&self, string: &str, start_span: usize, end_span: usize) -> FontShapeSpan { - let span = &string[start_span..end_span]; + fn shape_span(&self, line_i: usize, line: &str, start_span: usize, end_span: usize) -> FontShapeSpan { + let span = &line[start_span..end_span]; let mut spans_by_font = Vec::with_capacity(self.fonts.len()); for (font_i, font) in self.fonts.iter().enumerate() { @@ -44,6 +44,7 @@ impl<'a> FontMatches<'a> { let inner = font.rusttype.glyph(rusttype::GlyphId(info.glyph_id as u16)); glyphs.push(FontShapeGlyph { + line_i, start: start_span + info.cluster as usize, end: end_span, // Set later x_advance, @@ -113,11 +114,9 @@ impl<'a> FontMatches<'a> { spans_by_font.remove(least_misses_i).1 } - fn shape_line(&self, string: &str, start_line: usize, end_line: usize) -> FontShapeLine { + pub fn shape_line(&self, line_i: usize, line: &str) -> FontShapeLine { use unicode_script::{Script, UnicodeScript}; - let line = &string[start_line..end_line]; - //TODO: more special handling of characters let mut spans = Vec::new(); @@ -131,7 +130,7 @@ impl<'a> FontMatches<'a> { let cur = c.script(); if prev != cur && prev != Script::Unknown { // No combination, start new span - spans.push(self.shape_span(string, start_line + start, start_line + i)); + spans.push(self.shape_span(line_i, line, start, i)); start = i; prev = Script::Unknown; } else { @@ -139,7 +138,7 @@ impl<'a> FontMatches<'a> { } } - spans.push(self.shape_span(string, start_line + start, start_line + line.len())); + spans.push(self.shape_span(line_i, line, start, line.len())); let bidi = unicode_bidi::BidiInfo::new(line, None); let rtl = if bidi.paragraphs.is_empty() { @@ -154,24 +153,4 @@ impl<'a> FontMatches<'a> { spans, } } - - pub fn shape(&self, string: &str) -> Vec { - let mut lines = Vec::new(); - - let mut start = 0; - for (i, c) in string.char_indices() { - if ! string.is_char_boundary(i) { - continue; - } - - if c == '\n' { - lines.push(self.shape_line(string, start, i)); - start = i + 1; - } - } - - lines.push(self.shape_line(string, start, string.len())); - - lines - } } diff --git a/examples/text/src/font/shape.rs b/examples/text/src/font/shape.rs index 877ae336..060fd1c9 100644 --- a/examples/text/src/font/shape.rs +++ b/examples/text/src/font/shape.rs @@ -5,6 +5,7 @@ use core::marker::PhantomData; use super::{FontLayoutGlyph, FontLayoutLine}; pub struct FontShapeGlyph<'a> { + pub line_i: usize, pub start: usize, pub end: usize, pub x_advance: f32, @@ -98,6 +99,7 @@ impl<'a> FontShapeLine<'a> { )); glyphs.push(FontLayoutGlyph { + line_i: glyph.line_i, start: glyph.start, end: glyph.end, x, diff --git a/examples/text/src/main.rs b/examples/text/src/main.rs index a95e6e06..033ab0b1 100644 --- a/examples/text/src/main.rs +++ b/examples/text/src/main.rs @@ -37,10 +37,11 @@ fn main() { #[cfg(not(feature = "mono"))] let default_text = include_str!("../res/proportional.txt"); - let mut text = if let Some(arg) = env::args().nth(1) { - fs::read_to_string(&arg).expect("failed to open file") + let mut text_lines: Vec = if let Some(arg) = env::args().nth(1) { + let text = fs::read_to_string(&arg).expect("failed to open file"); + text.lines().map(String::from).collect() } else { - default_text.to_string() + default_text.lines().map(String::from).collect() }; let bg_color = Color::rgb(0x34, 0x34, 0x34); @@ -91,6 +92,7 @@ fn main() { let mut rehit = false; let mut relayout = true; let mut reshape = true; + let mut reshape_lines = Vec::new(); let mut scroll = 0; loop { let (mut font_size, mut line_height) = font_sizes[font_size_i]; @@ -100,7 +102,10 @@ fn main() { if reshape { let instant = Instant::now(); - shape_lines = font_matches.shape(&text); + shape_lines.clear(); + for (line_i, text_line) in text_lines.iter().enumerate() { + shape_lines.push(font_matches.shape_line(line_i, text_line)); + } reshape = false; relayout = true; @@ -109,6 +114,17 @@ fn main() { println!("reshape: {:?}", duration); } + for line_i in reshape_lines.drain(..) { + let instant = Instant::now(); + + shape_lines[line_i] = font_matches.shape_line(line_i, &text_lines[line_i]); + + relayout = true; + + let duration = instant.elapsed(); + println!("reshape line {}: {:?}", line_i, duration); + } + if relayout { let instant = Instant::now(); @@ -210,7 +226,9 @@ fn main() { line_height as u32, Color::rgba(0xFF, 0xFF, 0xFF, 0x20) ); - println!("{}, {}: '{}'", glyph.start, glyph.end, &text[glyph.start..glyph.end]); + + let text_line = &text_lines[glyph.line_i]; + println!("{}, {}: '{}'", glyph.start, glyph.end, &text_line[glyph.start..glyph.end]); } } @@ -274,15 +292,19 @@ fn main() { } if cursor_glyph > 0 { cursor_glyph -= 1; - text.remove(line.glyphs[cursor_glyph].start); - reshape = true; + let glyph = &line.glyphs[cursor_glyph]; + let text_line = &mut text_lines[glyph.line_i]; + text_line.remove(glyph.start); + reshape_lines.push(cursor_line); } }, orbclient::K_DEL => { let line = &layout_lines[cursor_line]; if cursor_glyph < line.glyphs.len() { - text.remove(line.glyphs[cursor_glyph].start); - reshape = true; + let glyph = &line.glyphs[cursor_glyph]; + let text_line = &mut text_lines[glyph.line_i]; + text_line.remove(glyph.start); + reshape_lines.push(cursor_line); } }, orbclient::K_0 => { @@ -305,17 +327,19 @@ fn main() { if cursor_glyph >= line.glyphs.len() { match line.glyphs.last() { Some(glyph) => { - text.insert(glyph.end, event.character); + let text_line = &mut text_lines[glyph.line_i]; + text_line.insert(glyph.end, event.character); cursor_glyph += 1; - reshape = true; + reshape_lines.push(cursor_line); }, None => () // TODO } } else { let glyph = &line.glyphs[cursor_glyph]; - text.insert(glyph.start, event.character); + let text_line = &mut text_lines[glyph.line_i]; + text_line.insert(glyph.start, event.character); cursor_glyph += 1; - reshape = true; + reshape_lines.push(cursor_line); } }, EventOption::Mouse(event) => {