Reduce allocations when caching fonts

Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
Antonio Scandurra 2022-04-13 19:06:34 +02:00
parent cdcdccfb89
commit 8e89074714

View File

@ -9,6 +9,7 @@ use crate::{
text_layout::{Glyph, LineLayout, Run, RunStyle},
};
use cocoa::appkit::{CGFloat, CGPoint};
use collections::HashMap;
use core_foundation::{
array::CFIndex,
attributed_string::{CFAttributedStringRef, CFMutableAttributedString},
@ -36,19 +37,19 @@ struct FontSystemState {
memory_source: MemSource,
system_source: SystemSource,
fonts: Vec<font_kit::font::Font>,
emoji_font_id: FontId,
font_ids_by_postscript_name: HashMap<String, FontId>,
postscript_names_by_font_id: HashMap<FontId, String>,
}
impl FontSystem {
pub fn new() -> Self {
let mut state = FontSystemState {
Self(RwLock::new(FontSystemState {
memory_source: MemSource::empty(),
system_source: SystemSource::new(),
fonts: Vec::new(),
emoji_font_id: FontId(0), // This will be the first font that we load.
};
state.load_family("Apple Color Emoji").unwrap();
Self(RwLock::new(state))
font_ids_by_postscript_name: Default::default(),
postscript_names_by_font_id: Default::default(),
}))
}
}
@ -128,7 +129,13 @@ impl FontSystemState {
.or_else(|_| self.system_source.select_family_by_name(name))?;
for font in family.fonts() {
let font = font.load()?;
font_ids.push(FontId(self.fonts.len()));
let font_id = FontId(self.fonts.len());
font_ids.push(font_id);
let postscript_name = font.postscript_name().unwrap();
self.font_ids_by_postscript_name
.insert(postscript_name.clone(), font_id);
self.postscript_names_by_font_id
.insert(font_id, postscript_name);
self.fonts.push(font);
}
Ok(font_ids)
@ -159,17 +166,30 @@ impl FontSystemState {
self.fonts[font_id.0].glyph_for_char(ch)
}
fn id_for_font(&mut self, requested_font: font_kit::font::Font) -> FontId {
// TODO: don't allocate the postscript name
// Note: Coretext always returns a Some option for postscript_name
let requested_font_name = requested_font.postscript_name();
for (id, font) in self.fonts.iter().enumerate() {
if font.postscript_name() == requested_font_name {
return FontId(id);
}
fn id_for_native_font(&mut self, requested_font: CTFont) -> FontId {
let postscript_name = requested_font.postscript_name();
if let Some(font_id) = self.font_ids_by_postscript_name.get(&postscript_name) {
*font_id
} else {
let font_id = FontId(self.fonts.len());
self.font_ids_by_postscript_name
.insert(postscript_name.clone(), font_id);
self.postscript_names_by_font_id
.insert(font_id, postscript_name);
self.fonts
.push(font_kit::font::Font::from_core_graphics_font(
requested_font.copy_to_CGFont(),
));
font_id
}
self.fonts.push(requested_font);
FontId(self.fonts.len() - 1)
}
fn is_emoji(&self, font_id: FontId) -> bool {
self.postscript_names_by_font_id
.get(&font_id)
.map_or(false, |postscript_name| {
postscript_name == "AppleColorEmoji"
})
}
fn rasterize_glyph(
@ -340,13 +360,12 @@ impl FontSystemState {
for run in line.glyph_runs().into_iter() {
let attributes = run.attributes().unwrap();
let font = unsafe {
let native_font = attributes
attributes
.get(kCTFontAttributeName)
.downcast::<CTFont>()
.unwrap();
font_kit::font::Font::from_native_font(native_font)
.unwrap()
};
let font_id = self.id_for_font(font);
let font_id = self.id_for_native_font(font);
let mut ix_converter = StringIndexConverter::new(text);
let mut glyphs = Vec::new();
@ -362,7 +381,7 @@ impl FontSystemState {
id: *glyph_id as GlyphId,
position: vec2f(position.x as f32, position.y as f32),
index: ix_converter.utf8_ix,
is_emoji: font_id == self.emoji_font_id,
is_emoji: self.is_emoji(font_id),
});
}