diff --git a/gpui/src/elements/label.rs b/gpui/src/elements/label.rs index 4236240548..149d065712 100644 --- a/gpui/src/elements/label.rs +++ b/gpui/src/elements/label.rs @@ -1,6 +1,7 @@ use crate::{ color::ColorU, - fonts::{FamilyId, Properties}, + font_cache::FamilyId, + fonts::Properties, geometry::vector::{vec2f, Vector2F}, text_layout::Line, AfterLayoutContext, Element, Event, EventContext, LayoutContext, PaintContext, SizeConstraint, diff --git a/gpui/src/elements/line_box.rs b/gpui/src/elements/line_box.rs index 0c9dbada2a..599a0a830d 100644 --- a/gpui/src/elements/line_box.rs +++ b/gpui/src/elements/line_box.rs @@ -1,5 +1,6 @@ use crate::{ - fonts::{FamilyId, FontId, Properties}, + font_cache::FamilyId, + fonts::{FontId, Properties}, geometry::vector::{vec2f, Vector2F}, AfterLayoutContext, Element, ElementBox, Event, EventContext, LayoutContext, PaintContext, SizeConstraint, diff --git a/gpui/src/font_cache.rs b/gpui/src/font_cache.rs new file mode 100644 index 0000000000..fea35971e6 --- /dev/null +++ b/gpui/src/font_cache.rs @@ -0,0 +1,154 @@ +use crate::{ + fonts::{FontId, Metrics, Properties}, + geometry::vector::{vec2f, Vector2F}, + platform, +}; +use anyhow::{anyhow, Result}; +use parking_lot::{RwLock, RwLockUpgradableReadGuard}; +use std::{collections::HashMap, sync::Arc}; + +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +pub struct FamilyId(usize); + +struct Family { + name: String, + font_ids: Vec, +} + +pub struct FontCache(RwLock); + +pub struct FontCacheState { + fonts: Arc, + families: Vec, + font_selections: HashMap>, + metrics: HashMap, +} + +unsafe impl Send for FontCache {} + +impl FontCache { + pub fn new(fonts: Arc) -> Self { + Self(RwLock::new(FontCacheState { + fonts, + families: Vec::new(), + font_selections: HashMap::new(), + metrics: HashMap::new(), + })) + } + + pub fn load_family(&self, names: &[&str]) -> Result { + for name in names { + let state = self.0.upgradable_read(); + + if let Some(ix) = state.families.iter().position(|f| f.name == *name) { + return Ok(FamilyId(ix)); + } + + let mut state = RwLockUpgradableReadGuard::upgrade(state); + + if let Ok(font_ids) = state.fonts.load_family(name) { + if font_ids.is_empty() { + continue; + } + + let family_id = FamilyId(state.families.len()); + for font_id in &font_ids { + if state.fonts.glyph_for_char(*font_id, 'm').is_none() { + return Err(anyhow!("font must contain a glyph for the 'm' character")); + } + } + + state.families.push(Family { + name: String::from(*name), + font_ids, + }); + return Ok(family_id); + } + } + + Err(anyhow!( + "could not find a non-empty font family matching one of the given names" + )) + } + + pub fn default_font(&self, family_id: FamilyId) -> FontId { + self.select_font(family_id, &Properties::default()).unwrap() + } + + pub fn select_font(&self, family_id: FamilyId, properties: &Properties) -> Result { + let inner = self.0.upgradable_read(); + if let Some(font_id) = inner + .font_selections + .get(&family_id) + .and_then(|f| f.get(properties)) + { + Ok(*font_id) + } else { + let mut inner = RwLockUpgradableReadGuard::upgrade(inner); + let family = &inner.families[family_id.0]; + let font_id = inner + .fonts + .select_font(&family.font_ids, properties) + .unwrap_or(family.font_ids[0]); + + inner + .font_selections + .entry(family_id) + .or_default() + .insert(properties.clone(), font_id); + Ok(font_id) + } + } + + pub fn metric(&self, font_id: FontId, f: F) -> T + where + F: FnOnce(&Metrics) -> T, + T: 'static, + { + let state = self.0.upgradable_read(); + if let Some(metrics) = state.metrics.get(&font_id) { + f(metrics) + } else { + let metrics = state.fonts.font_metrics(font_id); + let metric = f(&metrics); + let mut state = RwLockUpgradableReadGuard::upgrade(state); + state.metrics.insert(font_id, metrics); + metric + } + } + + pub fn bounding_box(&self, font_id: FontId, font_size: f32) -> Vector2F { + let bounding_box = self.metric(font_id, |m| m.bounding_box); + let width = self.scale_metric(bounding_box.width(), font_id, font_size); + let height = self.scale_metric(bounding_box.height(), font_id, font_size); + vec2f(width, height) + } + + pub fn em_width(&self, font_id: FontId, font_size: f32) -> f32 { + let state = self.0.read(); + let glyph_id = state.fonts.glyph_for_char(font_id, 'm').unwrap(); + let bounds = state.fonts.typographic_bounds(font_id, glyph_id).unwrap(); + self.scale_metric(bounds.width(), font_id, font_size) + } + + pub fn line_height(&self, font_id: FontId, font_size: f32) -> f32 { + let bounding_box = self.metric(font_id, |m| m.bounding_box); + self.scale_metric(bounding_box.height(), font_id, font_size) + } + + pub fn cap_height(&self, font_id: FontId, font_size: f32) -> f32 { + self.scale_metric(self.metric(font_id, |m| m.cap_height), font_id, font_size) + } + + pub fn ascent(&self, font_id: FontId, font_size: f32) -> f32 { + self.scale_metric(self.metric(font_id, |m| m.ascent), font_id, font_size) + } + + pub fn descent(&self, font_id: FontId, font_size: f32) -> f32 { + self.scale_metric(self.metric(font_id, |m| m.descent), font_id, font_size) + } + + pub fn scale_metric(&self, metric: f32, font_id: FontId, font_size: f32) -> f32 { + metric * font_size / self.metric(font_id, |m| m.units_per_em as f32) + } +} diff --git a/gpui/src/fonts.rs b/gpui/src/fonts.rs index 7924558d31..599f018526 100644 --- a/gpui/src/fonts.rs +++ b/gpui/src/fonts.rs @@ -1,160 +1,7 @@ -use crate::{ - geometry::vector::{vec2f, Vector2F}, - platform, -}; -use anyhow::{anyhow, Result}; -use font_kit::metrics::Metrics; +pub use font_kit::metrics::Metrics; pub use font_kit::properties::{Properties, Weight}; -use parking_lot::{RwLock, RwLockUpgradableReadGuard}; -use std::{collections::HashMap, sync::Arc}; - -pub type GlyphId = u32; - -#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] -pub struct FamilyId(usize); #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] pub struct FontId(pub usize); -pub struct FontCache(RwLock); - -pub struct FontCacheState { - fonts: Arc, - families: Vec, - font_selections: HashMap>, - metrics: HashMap, -} - -unsafe impl Send for FontCache {} - -struct Family { - name: String, - font_ids: Vec, -} - -impl FontCache { - pub fn new(fonts: Arc) -> Self { - Self(RwLock::new(FontCacheState { - fonts, - families: Vec::new(), - font_selections: HashMap::new(), - metrics: HashMap::new(), - })) - } - - pub fn load_family(&self, names: &[&str]) -> Result { - for name in names { - let state = self.0.upgradable_read(); - - if let Some(ix) = state.families.iter().position(|f| f.name == *name) { - return Ok(FamilyId(ix)); - } - - let mut state = RwLockUpgradableReadGuard::upgrade(state); - - if let Ok(font_ids) = state.fonts.load_family(name) { - if font_ids.is_empty() { - continue; - } - - let family_id = FamilyId(state.families.len()); - for font_id in &font_ids { - if state.fonts.glyph_for_char(*font_id, 'm').is_none() { - return Err(anyhow!("font must contain a glyph for the 'm' character")); - } - } - - state.families.push(Family { - name: String::from(*name), - font_ids, - }); - return Ok(family_id); - } - } - - Err(anyhow!( - "could not find a non-empty font family matching one of the given names" - )) - } - - pub fn default_font(&self, family_id: FamilyId) -> FontId { - self.select_font(family_id, &Properties::default()).unwrap() - } - - pub fn select_font(&self, family_id: FamilyId, properties: &Properties) -> Result { - let inner = self.0.upgradable_read(); - if let Some(font_id) = inner - .font_selections - .get(&family_id) - .and_then(|f| f.get(properties)) - { - Ok(*font_id) - } else { - let mut inner = RwLockUpgradableReadGuard::upgrade(inner); - let family = &inner.families[family_id.0]; - let font_id = inner - .fonts - .select_font(&family.font_ids, properties) - .unwrap_or(family.font_ids[0]); - - inner - .font_selections - .entry(family_id) - .or_default() - .insert(properties.clone(), font_id); - Ok(font_id) - } - } - - pub fn metric(&self, font_id: FontId, f: F) -> T - where - F: FnOnce(&Metrics) -> T, - T: 'static, - { - let state = self.0.upgradable_read(); - if let Some(metrics) = state.metrics.get(&font_id) { - f(metrics) - } else { - let metrics = state.fonts.font_metrics(font_id); - let metric = f(&metrics); - let mut state = RwLockUpgradableReadGuard::upgrade(state); - state.metrics.insert(font_id, metrics); - metric - } - } - - pub fn bounding_box(&self, font_id: FontId, font_size: f32) -> Vector2F { - let bounding_box = self.metric(font_id, |m| m.bounding_box); - let width = self.scale_metric(bounding_box.width(), font_id, font_size); - let height = self.scale_metric(bounding_box.height(), font_id, font_size); - vec2f(width, height) - } - - pub fn em_width(&self, font_id: FontId, font_size: f32) -> f32 { - let state = self.0.read(); - let glyph_id = state.fonts.glyph_for_char(font_id, 'm').unwrap(); - let bounds = state.fonts.typographic_bounds(font_id, glyph_id).unwrap(); - self.scale_metric(bounds.width(), font_id, font_size) - } - - pub fn line_height(&self, font_id: FontId, font_size: f32) -> f32 { - let bounding_box = self.metric(font_id, |m| m.bounding_box); - self.scale_metric(bounding_box.height(), font_id, font_size) - } - - pub fn cap_height(&self, font_id: FontId, font_size: f32) -> f32 { - self.scale_metric(self.metric(font_id, |m| m.cap_height), font_id, font_size) - } - - pub fn ascent(&self, font_id: FontId, font_size: f32) -> f32 { - self.scale_metric(self.metric(font_id, |m| m.ascent), font_id, font_size) - } - - pub fn descent(&self, font_id: FontId, font_size: f32) -> f32 { - self.scale_metric(self.metric(font_id, |m| m.descent), font_id, font_size) - } - - pub fn scale_metric(&self, metric: f32, font_id: FontId, font_size: f32) -> f32 { - metric * font_size / self.metric(font_id, |m| m.units_per_em as f32) - } -} +pub type GlyphId = u32; diff --git a/gpui/src/lib.rs b/gpui/src/lib.rs index 0aa504abae..5d62d04edf 100644 --- a/gpui/src/lib.rs +++ b/gpui/src/lib.rs @@ -3,8 +3,9 @@ pub use app::*; mod assets; pub use assets::*; pub mod elements; +pub mod font_cache; +pub use font_cache::FontCache; pub mod fonts; -pub use fonts::FontCache; mod presenter; mod scene; pub use scene::{Border, Scene}; diff --git a/gpui/src/platform/mac/fonts.rs b/gpui/src/platform/mac/fonts.rs index f1b94e5ef4..80f005f3ba 100644 --- a/gpui/src/platform/mac/fonts.rs +++ b/gpui/src/platform/mac/fonts.rs @@ -1,5 +1,5 @@ use crate::{ - fonts::{FontId, GlyphId}, + fonts::{FontId, GlyphId, Metrics, Properties}, geometry::{ rect::{RectF, RectI}, transform2d::Transform2F, @@ -19,10 +19,7 @@ use core_graphics::{ base::CGGlyph, color_space::CGColorSpace, context::CGContext, geometry::CGAffineTransform, }; use core_text::{line::CTLine, string_attributes::kCTFontAttributeName}; -use font_kit::{ - canvas::RasterizationOptions, hinting::HintingOptions, metrics::Metrics, - properties::Properties, source::SystemSource, -}; +use font_kit::{canvas::RasterizationOptions, hinting::HintingOptions, source::SystemSource}; use parking_lot::RwLock; use std::{char, convert::TryFrom}; diff --git a/gpui/src/platform/mod.rs b/gpui/src/platform/mod.rs index 61a8ab5874..016cdba7da 100644 --- a/gpui/src/platform/mod.rs +++ b/gpui/src/platform/mod.rs @@ -8,7 +8,7 @@ pub mod current { use crate::{ executor, - fonts::{FontId, GlyphId}, + fonts::{FontId, GlyphId, Metrics as FontMetrics, Properties as FontProperties}, geometry::{ rect::{RectF, RectI}, vector::Vector2F, @@ -19,7 +19,6 @@ use crate::{ use anyhow::Result; use async_task::Runnable; pub use event::Event; -use font_kit::{metrics::Metrics as FontMetrics, properties::Properties as FontProperties}; use std::{ops::Range, path::PathBuf, rc::Rc, sync::Arc}; pub trait Runner { diff --git a/gpui/src/presenter.rs b/gpui/src/presenter.rs index 900c9750c2..8df368b247 100644 --- a/gpui/src/presenter.rs +++ b/gpui/src/presenter.rs @@ -1,7 +1,7 @@ use crate::{ app::{AppContext, MutableAppContext, WindowInvalidation}, elements::Element, - fonts::FontCache, + font_cache::FontCache, platform::{self, Event}, text_layout::TextLayoutCache, AssetCache, ElementBox, Scene, diff --git a/zed/src/editor/buffer_view.rs b/zed/src/editor/buffer_view.rs index 6c41426d9f..bf050ef5e5 100644 --- a/zed/src/editor/buffer_view.rs +++ b/zed/src/editor/buffer_view.rs @@ -6,10 +6,8 @@ use crate::{settings::Settings, watch, workspace}; use anyhow::Result; use easy_parallel::Parallel; use gpui::{ - fonts::{FontCache, Properties as FontProperties}, - keymap::Binding, - text_layout, App, AppContext, Element, ElementBox, Entity, ModelHandle, View, ViewContext, - WeakViewHandle, + fonts::Properties as FontProperties, keymap::Binding, text_layout, App, AppContext, Element, + ElementBox, Entity, FontCache, ModelHandle, View, ViewContext, WeakViewHandle, }; use gpui::{geometry::vector::Vector2F, TextLayoutCache}; use parking_lot::Mutex; diff --git a/zed/src/settings.rs b/zed/src/settings.rs index f3ad978b4b..cd90842fd0 100644 --- a/zed/src/settings.rs +++ b/zed/src/settings.rs @@ -1,6 +1,6 @@ use crate::watch; use anyhow::Result; -use gpui::fonts::{FamilyId, FontCache}; +use gpui::font_cache::{FamilyId, FontCache}; #[derive(Clone)] pub struct Settings {