Cache font matches, use usize for line index, use font system for swash

This commit is contained in:
Jeremy Soller 2022-10-26 12:23:03 -06:00
parent 94576fb682
commit 119a570ee9
No known key found for this signature in database
GPG key ID: 87F211AF2BE4C2FE
8 changed files with 105 additions and 120 deletions

View file

@ -64,7 +64,7 @@ pub struct Window {
theme: Theme, theme: Theme,
path_opt: Option<PathBuf>, path_opt: Option<PathBuf>,
buffer: Mutex<TextBuffer<'static>>, buffer: Mutex<TextBuffer<'static>>,
cache: Mutex<SwashCache>, cache: Mutex<SwashCache<'static>>,
bold: bool, bold: bool,
italic: bool, italic: bool,
monospaced: bool, monospaced: bool,
@ -117,11 +117,13 @@ impl Application for Window {
FONT_SIZES[font_size_i], FONT_SIZES[font_size_i],
); );
let cache = SwashCache::new(&FONT_SYSTEM);
let mut window = Window { let mut window = Window {
theme: Theme::Dark, theme: Theme::Dark,
path_opt: None, path_opt: None,
buffer: Mutex::new(buffer), buffer: Mutex::new(buffer),
cache: Mutex::new(SwashCache::new()), cache: Mutex::new(cache),
bold: false, bold: false,
italic: false, italic: false,
monospaced: true, monospaced: true,
@ -178,7 +180,7 @@ impl Application for Window {
} else { } else {
cosmic_text::Weight::NORMAL cosmic_text::Weight::NORMAL
}); });
buffer.set_attrs(&FONT_SYSTEM, attrs); buffer.set_attrs(attrs);
}, },
Message::Italic(italic) => { Message::Italic(italic) => {
self.italic = italic; self.italic = italic;
@ -189,7 +191,7 @@ impl Application for Window {
} else { } else {
cosmic_text::Style::Normal cosmic_text::Style::Normal
}); });
buffer.set_attrs(&FONT_SYSTEM, attrs); buffer.set_attrs(attrs);
}, },
Message::Monospaced(monospaced) => { Message::Monospaced(monospaced) => {
self.monospaced = monospaced; self.monospaced = monospaced;
@ -202,7 +204,7 @@ impl Application for Window {
cosmic_text::Family::SansSerif cosmic_text::Family::SansSerif
}) })
.monospaced(monospaced); .monospaced(monospaced);
buffer.set_attrs(&FONT_SYSTEM, attrs); buffer.set_attrs(attrs);
}, },
Message::MetricsChanged(metrics) => { Message::MetricsChanged(metrics) => {
let mut buffer = self.buffer.lock().unwrap(); let mut buffer = self.buffer.lock().unwrap();

View file

@ -63,16 +63,16 @@ impl StyleSheet for Theme {
pub struct TextBox<'a> { pub struct TextBox<'a> {
buffer: &'a Mutex<TextBuffer<'static>>, buffer: &'a Mutex<TextBuffer<'static>>,
cache: &'a Mutex<SwashCache>, cache: &'a Mutex<SwashCache<'static>>,
} }
impl<'a> TextBox<'a> { impl<'a> TextBox<'a> {
pub fn new(buffer: &'a Mutex<TextBuffer<'static>>, cache: &'a Mutex<SwashCache>) -> Self { pub fn new(buffer: &'a Mutex<TextBuffer<'static>>, cache: &'a Mutex<SwashCache<'static>>) -> Self {
Self { buffer, cache } Self { buffer, cache }
} }
} }
pub fn text_box<'a>(buffer: &'a Mutex<TextBuffer<'static>>, cache: &'a Mutex<SwashCache>) -> TextBox<'a> { pub fn text_box<'a>(buffer: &'a Mutex<TextBuffer<'static>>, cache: &'a Mutex<SwashCache<'static>>) -> TextBox<'a> {
TextBox::new(buffer, cache) TextBox::new(buffer, cache)
} }

View file

@ -53,7 +53,7 @@ fn main() {
default_text.to_string() default_text.to_string()
}; };
let mut swash_cache = SwashCache::new(); let mut swash_cache = SwashCache::new(&font_system);
let line_x = 8 * display_scale; let line_x = 8 * display_scale;
let attrs = cosmic_text::Attrs::new().monospaced(cfg!(feature = "mono")); let attrs = cosmic_text::Attrs::new().monospaced(cfg!(feature = "mono"));

View file

@ -71,7 +71,7 @@ fn main() {
window.height() as i32 window.height() as i32
); );
let mut swash_cache = SwashCache::new(); let mut swash_cache = SwashCache::new(&font_system);
let text = if let Some(arg) = env::args().nth(1) { let text = if let Some(arg) = env::args().nth(1) {
fs::read_to_string(&arg).expect("failed to open file") fs::read_to_string(&arg).expect("failed to open file")

View file

@ -2,7 +2,7 @@
pub use fontdb::{Family, Stretch, Style, Weight}; pub use fontdb::{Family, Stretch, Style, Weight};
#[derive(Clone, Debug, Eq, PartialEq)] #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct Attrs<'a> { pub struct Attrs<'a> {
pub family: Family<'a>, pub family: Family<'a>,
pub monospaced: bool, pub monospaced: bool,

View file

@ -3,6 +3,7 @@
use std::{ use std::{
cmp, cmp,
fmt, fmt,
sync::Arc,
time::Instant, time::Instant,
}; };
use unicode_segmentation::UnicodeSegmentation; use unicode_segmentation::UnicodeSegmentation;
@ -52,32 +53,32 @@ pub enum TextAction {
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct TextCursor { pub struct TextCursor {
/// Text line the cursor is on /// Text line the cursor is on
pub line: TextLineIndex, pub line: usize,
/// Index of glyph at cursor (will insert behind this glyph) /// Index of glyph at cursor (will insert behind this glyph)
pub index: usize, pub index: usize,
} }
impl TextCursor { impl TextCursor {
pub const fn new(line: TextLineIndex, index: usize) -> Self { pub const fn new(line: usize, index: usize) -> Self {
Self { line, index } Self { line, index }
} }
} }
struct TextLayoutCursor { struct TextLayoutCursor {
line: TextLineIndex, line: usize,
layout: usize, layout: usize,
glyph: usize, glyph: usize,
} }
impl TextLayoutCursor { impl TextLayoutCursor {
fn new(line: TextLineIndex, layout: usize, glyph: usize) -> Self { fn new(line: usize, layout: usize, glyph: usize) -> Self {
Self { line, layout, glyph } Self { line, layout, glyph }
} }
} }
pub struct TextLayoutRun<'a> { pub struct TextLayoutRun<'a> {
/// The index of the original text line /// The index of the original text line
pub line_i: TextLineIndex, pub line_i: usize,
/// The original text line /// The original text line
pub text: &'a str, pub text: &'a str,
/// True if the original paragraph direction is RTL /// True if the original paragraph direction is RTL
@ -130,7 +131,7 @@ impl<'a, 'b> Iterator for TextLayoutRunIter<'a, 'b> {
} }
return Some(TextLayoutRun { return Some(TextLayoutRun {
line_i: TextLineIndex::new(self.line_i), line_i: self.line_i,
text: line.text.as_str(), text: line.text.as_str(),
rtl: shape.rtl, rtl: shape.rtl,
glyphs: &layout_line.glyphs, glyphs: &layout_line.glyphs,
@ -145,20 +146,6 @@ impl<'a, 'b> Iterator for TextLayoutRunIter<'a, 'b> {
} }
} }
/// Index of a text line
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Ord, PartialOrd)]
pub struct TextLineIndex(usize);
impl TextLineIndex {
pub fn new(index: usize) -> Self {
Self(index)
}
pub fn get(&self) -> usize {
self.0
}
}
/// Metrics of text /// Metrics of text
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct TextMetrics { pub struct TextMetrics {
@ -237,8 +224,10 @@ impl TextBufferLine {
/// A buffer of text that is shaped and laid out /// A buffer of text that is shaped and laid out
pub struct TextBuffer<'a> { pub struct TextBuffer<'a> {
font_matches: FontMatches<'a>, font_system: &'a FontSystem<'a>,
font_matches: Arc<FontMatches<'a>>,
attrs: Attrs<'a>, attrs: Attrs<'a>,
attr_spans: Vec<(TextCursor, usize, Attrs<'a>)>,
lines: Vec<TextBufferLine>, lines: Vec<TextBufferLine>,
metrics: TextMetrics, metrics: TextMetrics,
width: i32, width: i32,
@ -256,10 +245,12 @@ impl<'a> TextBuffer<'a> {
attrs: Attrs<'a>, attrs: Attrs<'a>,
metrics: TextMetrics, metrics: TextMetrics,
) -> Self { ) -> Self {
let font_matches = font_system.matches_attrs(&attrs); let font_matches = font_system.get_font_matches(attrs);
let mut buffer = Self { let mut buffer = Self {
font_system,
font_matches, font_matches,
attrs, attrs,
attr_spans: Vec::new(),
lines: Vec::new(), lines: Vec::new(),
metrics, metrics,
width: 0, width: 0,
@ -312,7 +303,7 @@ impl<'a> TextBuffer<'a> {
let mut reshaped = 0; let mut reshaped = 0;
let mut layout_i = 0; let mut layout_i = 0;
for (line_i, line) in self.lines.iter_mut().enumerate() { for (line_i, line) in self.lines.iter_mut().enumerate() {
if line_i > self.cursor.line.get() { if line_i > self.cursor.line {
break; break;
} }
@ -324,7 +315,7 @@ impl<'a> TextBuffer<'a> {
self.metrics.font_size, self.metrics.font_size,
self.width self.width
); );
if line_i == self.cursor.line.get() { if line_i == self.cursor.line {
let layout_cursor = self.layout_cursor(&self.cursor); let layout_cursor = self.layout_cursor(&self.cursor);
layout_i += layout_cursor.layout as i32; layout_i += layout_cursor.layout as i32;
break; break;
@ -385,7 +376,7 @@ impl<'a> TextBuffer<'a> {
} }
fn layout_cursor(&self, cursor: &TextCursor) -> TextLayoutCursor { fn layout_cursor(&self, cursor: &TextCursor) -> TextLayoutCursor {
let line = &self.lines[cursor.line.get()]; let line = &self.lines[cursor.line];
let layout = line.layout_opt.as_ref().unwrap(); //TODO: ensure layout is done? let layout = line.layout_opt.as_ref().unwrap(); //TODO: ensure layout is done?
for (layout_i, layout_line) in layout.iter().enumerate() { for (layout_i, layout_line) in layout.iter().enumerate() {
@ -428,7 +419,7 @@ impl<'a> TextBuffer<'a> {
} }
fn set_layout_cursor(&mut self, cursor: TextLayoutCursor) { fn set_layout_cursor(&mut self, cursor: TextLayoutCursor) {
let line = &mut self.lines[cursor.line.get()]; let line = &mut self.lines[cursor.line];
let layout = line.layout( let layout = line.layout(
&self.font_matches, &self.font_matches,
self.metrics.font_size, self.metrics.font_size,
@ -516,9 +507,9 @@ impl<'a> TextBuffer<'a> {
} }
/// Set attributes /// Set attributes
pub fn set_attrs(&mut self, font_system: &'a FontSystem<'a>, attrs: Attrs<'a>) { pub fn set_attrs(&mut self, attrs: Attrs<'a>) {
if attrs != self.attrs { if attrs != self.attrs {
self.font_matches = font_system.matches_attrs(&attrs); self.font_matches = self.font_system.get_font_matches(attrs);
self.attrs = attrs; self.attrs = attrs;
for line in self.lines.iter_mut() { for line in self.lines.iter_mut() {
@ -529,6 +520,10 @@ impl<'a> TextBuffer<'a> {
} }
} }
pub fn add_attr_span(&mut self, cursor: TextCursor, len: usize, attrs: Attrs<'a>) {
self.attr_spans.push((cursor, len, attrs));
}
/// Set text of buffer /// Set text of buffer
pub fn set_text(&mut self, text: &str) { pub fn set_text(&mut self, text: &str) {
self.lines.clear(); self.lines.clear();
@ -558,7 +553,7 @@ impl<'a> TextBuffer<'a> {
match action { match action {
TextAction::Previous => { TextAction::Previous => {
let line = &mut self.lines[self.cursor.line.get()]; let line = &mut self.lines[self.cursor.line];
if self.cursor.index > 0 { if self.cursor.index > 0 {
// Find previous character index // Find previous character index
@ -573,14 +568,14 @@ impl<'a> TextBuffer<'a> {
self.cursor.index = prev_index; self.cursor.index = prev_index;
self.redraw = true; self.redraw = true;
} else if self.cursor.line.get() > 0 { } else if self.cursor.line > 0 {
self.cursor.line = TextLineIndex::new(self.cursor.line.get() - 1); self.cursor.line -= 1;
self.cursor.index = self.lines[self.cursor.line.get()].text.len(); self.cursor.index = self.lines[self.cursor.line].text.len();
self.redraw = true; self.redraw = true;
} }
}, },
TextAction::Next => { TextAction::Next => {
let line = &mut self.lines[self.cursor.line.get()]; let line = &mut self.lines[self.cursor.line];
if self.cursor.index < line.text.len() { if self.cursor.index < line.text.len() {
for (i, c) in line.text.grapheme_indices(true) { for (i, c) in line.text.grapheme_indices(true) {
@ -590,14 +585,14 @@ impl<'a> TextBuffer<'a> {
break; break;
} }
} }
} else if self.cursor.line.get() + 1 < self.lines.len() { } else if self.cursor.line + 1 < self.lines.len() {
self.cursor.line = TextLineIndex::new(self.cursor.line.get() + 1); self.cursor.line += 1;
self.cursor.index = 0; self.cursor.index = 0;
self.redraw = true; self.redraw = true;
} }
}, },
TextAction::Left => { TextAction::Left => {
let rtl_opt = self.lines[self.cursor.line.get()].shape_opt.as_ref().map(|shape| shape.rtl); let rtl_opt = self.lines[self.cursor.line].shape_opt.as_ref().map(|shape| shape.rtl);
if let Some(rtl) = rtl_opt { if let Some(rtl) = rtl_opt {
if rtl { if rtl {
self.action(TextAction::Next); self.action(TextAction::Next);
@ -607,7 +602,7 @@ impl<'a> TextBuffer<'a> {
} }
}, },
TextAction::Right => { TextAction::Right => {
let rtl_opt = self.lines[self.cursor.line.get()].shape_opt.as_ref().map(|shape| shape.rtl); let rtl_opt = self.lines[self.cursor.line].shape_opt.as_ref().map(|shape| shape.rtl);
if let Some(rtl) = rtl_opt { if let Some(rtl) = rtl_opt {
if rtl { if rtl {
self.action(TextAction::Previous); self.action(TextAction::Previous);
@ -621,8 +616,8 @@ impl<'a> TextBuffer<'a> {
let mut cursor = self.layout_cursor(&self.cursor); let mut cursor = self.layout_cursor(&self.cursor);
if cursor.layout > 0 { if cursor.layout > 0 {
cursor.layout -= 1; cursor.layout -= 1;
} else if cursor.line.get() > 0 { } else if cursor.line > 0 {
cursor.line = TextLineIndex::new(cursor.line.get() - 1); cursor.line -= 1;
cursor.layout = usize::max_value(); cursor.layout = usize::max_value();
} }
self.set_layout_cursor(cursor); self.set_layout_cursor(cursor);
@ -631,7 +626,7 @@ impl<'a> TextBuffer<'a> {
//TODO: make this preserve X as best as possible! //TODO: make this preserve X as best as possible!
let mut cursor = self.layout_cursor(&self.cursor); let mut cursor = self.layout_cursor(&self.cursor);
let layout_len = { let layout_len = {
let line = &mut self.lines[cursor.line.get()]; let line = &mut self.lines[cursor.line];
let layout = line.layout( let layout = line.layout(
&self.font_matches, &self.font_matches,
self.metrics.font_size, self.metrics.font_size,
@ -641,8 +636,8 @@ impl<'a> TextBuffer<'a> {
}; };
if cursor.layout + 1 < layout_len { if cursor.layout + 1 < layout_len {
cursor.layout += 1; cursor.layout += 1;
} else if cursor.line.get() + 1 < self.lines.len() { } else if cursor.line + 1 < self.lines.len() {
cursor.line = TextLineIndex::new(cursor.line.get() + 1); cursor.line += 1;
cursor.layout = 0; cursor.layout = 0;
} }
self.set_layout_cursor(cursor); self.set_layout_cursor(cursor);
@ -678,7 +673,7 @@ impl<'a> TextBuffer<'a> {
// Filter out special chars (except for tab), use TextAction instead // Filter out special chars (except for tab), use TextAction instead
log::debug!("Refusing to insert control character {:?}", character); log::debug!("Refusing to insert control character {:?}", character);
} else { } else {
let line = &mut self.lines[self.cursor.line.get()]; let line = &mut self.lines[self.cursor.line];
line.reset(); line.reset();
line.text.insert(self.cursor.index, character); line.text.insert(self.cursor.index, character);
@ -687,20 +682,20 @@ impl<'a> TextBuffer<'a> {
}, },
TextAction::Enter => { TextAction::Enter => {
let new_line = { let new_line = {
let line = &mut self.lines[self.cursor.line.get()]; let line = &mut self.lines[self.cursor.line];
line.reset(); line.reset();
line.text.split_off(self.cursor.index) line.text.split_off(self.cursor.index)
}; };
let next_line = self.cursor.line.get() + 1; let next_line = self.cursor.line + 1;
self.lines.insert(next_line, TextBufferLine::new(new_line)); self.lines.insert(next_line, TextBufferLine::new(new_line));
self.cursor.line = TextLineIndex::new(next_line); self.cursor.line = next_line;
self.cursor.index = 0; self.cursor.index = 0;
}, },
TextAction::Backspace => { TextAction::Backspace => {
if self.cursor.index > 0 { if self.cursor.index > 0 {
let line = &mut self.lines[self.cursor.line.get()]; let line = &mut self.lines[self.cursor.line];
line.reset(); line.reset();
// Find previous character index // Find previous character index
@ -716,23 +711,23 @@ impl<'a> TextBuffer<'a> {
self.cursor.index = prev_index; self.cursor.index = prev_index;
line.text.remove(self.cursor.index); line.text.remove(self.cursor.index);
} else if self.cursor.line.get() > 0 { } else if self.cursor.line > 0 {
let mut line_index = self.cursor.line.get(); let mut line_index = self.cursor.line;
let old_line = self.lines.remove(line_index); let old_line = self.lines.remove(line_index);
line_index -= 1; line_index -= 1;
let line = &mut self.lines[line_index]; let line = &mut self.lines[line_index];
line.reset(); line.reset();
self.cursor.line = TextLineIndex::new(line_index); self.cursor.line = line_index;
self.cursor.index = line.text.len(); self.cursor.index = line.text.len();
line.text.push_str(&old_line.text); line.text.push_str(&old_line.text);
} }
}, },
TextAction::Delete => { TextAction::Delete => {
if self.cursor.index < self.lines[self.cursor.line.get()].text.len() { if self.cursor.index < self.lines[self.cursor.line].text.len() {
let line = &mut self.lines[self.cursor.line.get()]; let line = &mut self.lines[self.cursor.line];
line.reset(); line.reset();
if let Some((i, c)) = line if let Some((i, c)) = line
@ -744,10 +739,10 @@ impl<'a> TextBuffer<'a> {
line.text.replace_range(i..(i + c.len()), ""); line.text.replace_range(i..(i + c.len()), "");
self.cursor.index = i; self.cursor.index = i;
} }
} else if self.cursor.line.get() + 1 < self.lines.len() { } else if self.cursor.line + 1 < self.lines.len() {
let old_line = self.lines.remove(self.cursor.line.get() + 1); let old_line = self.lines.remove(self.cursor.line + 1);
let line = &mut self.lines[self.cursor.line.get()]; let line = &mut self.lines[self.cursor.line];
line.reset(); line.reset();
line.text.push_str(&old_line.text); line.text.push_str(&old_line.text);
@ -860,7 +855,7 @@ impl<'a> TextBuffer<'a> {
let text_glyph = &run.text[glyph.start..glyph.end]; let text_glyph = &run.text[glyph.start..glyph.end];
log::debug!( log::debug!(
"{}, {}: '{}' ('{}'): '{}' ({:?})", "{}, {}: '{}' ('{}'): '{}' ({:?})",
self.cursor.line.get(), self.cursor.line,
self.cursor.index, self.cursor.index,
font_opt.as_ref().map_or("?", |font| font.info.family.as_str()), font_opt.as_ref().map_or("?", |font| font.info.family.as_str()),
font_opt.as_ref().map_or("?", |font| font.info.post_script_name.as_str()), font_opt.as_ref().map_or("?", |font| font.info.post_script_name.as_str()),
@ -1045,7 +1040,7 @@ impl<'a> TextBuffer<'a> {
for glyph in run.glyphs.iter() { for glyph in run.glyphs.iter() {
let (cache_key, x_int, y_int) = (glyph.cache_key, glyph.x_int, glyph.y_int); let (cache_key, x_int, y_int) = (glyph.cache_key, glyph.x_int, glyph.y_int);
cache.with_pixels(&self.font_matches, cache_key, color, |x, y, color| { cache.with_pixels(cache_key, color, |x, y, color| {
f(x_int + x, line_y + y_int + y, 1, 1, color) f(x_int + x, line_y + y_int + y, 1, 1, color)
}); });
} }

View file

@ -5,13 +5,14 @@ use std::{
sync::{Arc, Mutex}, sync::{Arc, Mutex},
}; };
use crate::{Attrs, Family, Font, FontMatches}; use crate::{Attrs, Font, FontMatches};
/// Access system fonts /// Access system fonts
pub struct FontSystem<'a> { pub struct FontSystem<'a> {
pub locale: String, pub locale: String,
pub db: fontdb::Database, pub db: fontdb::Database,
pub font_cache: Mutex<HashMap<fontdb::ID, Option<Arc<Font<'a>>>>>, pub font_cache: Mutex<HashMap<fontdb::ID, Option<Arc<Font<'a>>>>>,
pub font_matches_cache: Mutex<HashMap<Attrs<'a>, Arc<FontMatches<'a>>>>,
} }
impl<'a> FontSystem<'a> { impl<'a> FontSystem<'a> {
@ -60,6 +61,7 @@ impl<'a> FontSystem<'a> {
locale, locale,
db, db,
font_cache: Mutex::new(HashMap::new()), font_cache: Mutex::new(HashMap::new()),
font_matches_cache: Mutex::new(HashMap::new()),
} }
} }
@ -77,48 +79,33 @@ impl<'a> FontSystem<'a> {
}).clone() }).clone()
} }
pub fn matches<F: Fn(&fontdb::FaceInfo) -> bool>( pub fn get_font_matches(&'a self, attrs: Attrs<'a>) -> Arc<FontMatches<'a>> {
&'a self, let mut font_matches_cache = self.font_matches_cache.lock().unwrap();
default_family: &Family, font_matches_cache.entry(attrs).or_insert_with(|| {
f: F, let now = std::time::Instant::now();
) -> FontMatches<'_> {
let mut fonts = Vec::new(); let mut fonts = Vec::new();
for face in self.db.faces() { for face in self.db.faces() {
if !f(face) { if !attrs.matches(face) {
continue; continue;
}
match self.get_font(face.id) {
Some(font) => fonts.push(font),
None => (),
}
} }
match self.get_font(face.id) { let font_matches = Arc::new(FontMatches {
Some(font) => fonts.push(font), locale: &self.locale,
None => (), default_family: self.db.family_name(&attrs.family).to_string(),
} fonts
} });
FontMatches { let elapsed = now.elapsed();
locale: &self.locale, log::debug!("font matches for {:?} in {:?}", attrs, elapsed);
default_family: self.db.family_name(default_family).to_string(),
fonts
}
}
pub fn matches_attrs(&'a self, attrs: &Attrs) -> FontMatches<'_> { font_matches
self.matches(&attrs.family, |face| { }).clone()
let matched = attrs.matches(face);
if matched {
log::debug!(
"{:?}: family '{}' postscript name '{}' style {:?} weight {:?} stretch {:?} monospaced {:?}",
face.id,
face.family,
face.post_script_name,
face.style,
face.weight,
face.stretch,
face.monospaced
);
}
matched
})
} }
} }

View file

@ -5,12 +5,12 @@ use swash::scale::{ScaleContext, image::Content};
use swash::scale::{Render, Source, StrikeWith}; use swash::scale::{Render, Source, StrikeWith};
use swash::zeno::{Format, Vector}; use swash::zeno::{Format, Vector};
use crate::{CacheKey, FontMatches}; use crate::{CacheKey, FontSystem};
pub use swash::scale::image::{Content as SwashContent, Image as SwashImage}; pub use swash::scale::image::{Content as SwashContent, Image as SwashImage};
fn swash_image(context: &mut ScaleContext, matches: &FontMatches, cache_key: CacheKey) -> Option<SwashImage> { fn swash_image<'a>(font_system: &'a FontSystem<'a>, context: &mut ScaleContext, cache_key: CacheKey) -> Option<SwashImage> {
let font = match matches.get_font(&cache_key.font_id) { let font = match font_system.get_font(cache_key.font_id) {
Some(some) => some, Some(some) => some,
None => { None => {
log::warn!("did not find font {:?}", cache_key.font_id); log::warn!("did not find font {:?}", cache_key.font_id);
@ -47,41 +47,42 @@ fn swash_image(context: &mut ScaleContext, matches: &FontMatches, cache_key: Cac
.render(&mut scaler, cache_key.glyph_id) .render(&mut scaler, cache_key.glyph_id)
} }
pub struct SwashCache { pub struct SwashCache<'a> {
font_system: &'a FontSystem<'a>,
context: ScaleContext, context: ScaleContext,
pub image_cache: HashMap<CacheKey, Option<SwashImage>>, pub image_cache: HashMap<CacheKey, Option<SwashImage>>,
} }
impl SwashCache { impl<'a> SwashCache<'a> {
/// Create a new swash cache /// Create a new swash cache
pub fn new() -> Self { pub fn new(font_system: &'a FontSystem<'a>) -> Self {
Self { Self {
font_system: font_system,
context: ScaleContext::new(), context: ScaleContext::new(),
image_cache: HashMap::new() image_cache: HashMap::new()
} }
} }
/// Create a swash Image from a cache key, without caching results /// Create a swash Image from a cache key, without caching results
pub fn get_image_uncached(&mut self, matches: &FontMatches, cache_key: CacheKey) -> Option<SwashImage> { pub fn get_image_uncached(&mut self, cache_key: CacheKey) -> Option<SwashImage> {
swash_image(&mut self.context, matches, cache_key) swash_image(self.font_system, &mut self.context, cache_key)
} }
/// Create a swash Image from a cache key, caching results /// Create a swash Image from a cache key, caching results
pub fn get_image(&mut self, matches: &FontMatches, cache_key: CacheKey) -> &Option<SwashImage> { pub fn get_image(&mut self, cache_key: CacheKey) -> &Option<SwashImage> {
self.image_cache.entry(cache_key).or_insert_with(|| { self.image_cache.entry(cache_key).or_insert_with(|| {
swash_image(&mut self.context, matches, cache_key) swash_image(self.font_system, &mut self.context, cache_key)
}) })
} }
/// Enumerate pixels in an Image, use `with_image` for better performance /// Enumerate pixels in an Image, use `with_image` for better performance
pub fn with_pixels<F: FnMut(i32, i32, u32)>( pub fn with_pixels<F: FnMut(i32, i32, u32)>(
&mut self, &mut self,
matches: &FontMatches<'_>,
cache_key: CacheKey, cache_key: CacheKey,
base: u32, base: u32,
mut f: F mut f: F
) { ) {
if let Some(image) = self.get_image(matches, cache_key) { if let Some(image) = self.get_image(cache_key) {
let x = image.placement.left; let x = image.placement.left;
let y = -image.placement.top; let y = -image.placement.top;