text_system.rs

  1mod font_features;
  2mod line;
  3mod line_wrapper;
  4mod text_layout_cache;
  5
  6use anyhow::anyhow;
  7pub use font_features::*;
  8pub use line::*;
  9use line_wrapper::*;
 10pub use text_layout_cache::*;
 11
 12use crate::{
 13    px, Bounds, DevicePixels, Hsla, Pixels, PlatformTextSystem, Point, RasterizationOptions,
 14    Result, SharedString, Size, UnderlineStyle,
 15};
 16use collections::HashMap;
 17use core::fmt;
 18use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard};
 19use std::{
 20    fmt::{Debug, Display, Formatter},
 21    hash::{Hash, Hasher},
 22    ops::{Deref, DerefMut},
 23    sync::Arc,
 24};
 25
 26#[derive(Hash, PartialEq, Eq, Clone, Copy, Debug)]
 27#[repr(C)]
 28pub struct FontId(pub usize);
 29
 30#[derive(Hash, PartialEq, Eq, Clone, Copy, Debug)]
 31pub struct FontFamilyId(pub usize);
 32
 33pub const SUBPIXEL_VARIANTS: u8 = 4;
 34
 35pub struct TextSystem {
 36    text_layout_cache: Arc<TextLayoutCache>,
 37    platform_text_system: Arc<dyn PlatformTextSystem>,
 38    font_ids_by_font: RwLock<HashMap<Font, FontId>>,
 39    fonts_by_font_id: RwLock<HashMap<FontId, Font>>,
 40    font_metrics: RwLock<HashMap<Font, FontMetrics>>,
 41    wrapper_pool: Mutex<HashMap<FontIdWithSize, Vec<LineWrapper>>>,
 42    font_runs_pool: Mutex<Vec<Vec<(usize, FontId)>>>,
 43}
 44
 45impl TextSystem {
 46    pub fn new(platform_text_system: Arc<dyn PlatformTextSystem>) -> Self {
 47        TextSystem {
 48            text_layout_cache: Arc::new(TextLayoutCache::new(platform_text_system.clone())),
 49            platform_text_system,
 50            font_metrics: RwLock::new(HashMap::default()),
 51            font_ids_by_font: RwLock::new(HashMap::default()),
 52            fonts_by_font_id: RwLock::new(HashMap::default()),
 53            wrapper_pool: Mutex::new(HashMap::default()),
 54            font_runs_pool: Default::default(),
 55        }
 56    }
 57
 58    pub fn font_id(&self, font: &Font) -> Result<FontId> {
 59        let font_id = self.font_ids_by_font.read().get(font).copied();
 60        if let Some(font_id) = font_id {
 61            Ok(font_id)
 62        } else {
 63            let font_id = self.platform_text_system.font_id(font)?;
 64            self.font_ids_by_font.write().insert(font.clone(), font_id);
 65            self.fonts_by_font_id.write().insert(font_id, font.clone());
 66            Ok(font_id)
 67        }
 68    }
 69
 70    pub fn with_font<T>(&self, font_id: FontId, f: impl FnOnce(&Self, &Font) -> T) -> Result<T> {
 71        self.fonts_by_font_id
 72            .read()
 73            .get(&font_id)
 74            .ok_or_else(|| anyhow!("font not found"))
 75            .map(|font| f(self, font))
 76    }
 77
 78    pub fn bounding_box(&self, font: &Font, font_size: Pixels) -> Result<Bounds<Pixels>> {
 79        self.read_metrics(&font, |metrics| metrics.bounding_box(font_size))
 80    }
 81
 82    pub fn typographic_bounds(
 83        &self,
 84        font: &Font,
 85        font_size: Pixels,
 86        character: char,
 87    ) -> Result<Bounds<Pixels>> {
 88        let font_id = self.font_id(font)?;
 89        let glyph_id = self
 90            .platform_text_system
 91            .glyph_for_char(font_id, character)
 92            .ok_or_else(|| anyhow!("glyph not found for character '{}'", character))?;
 93        let bounds = self
 94            .platform_text_system
 95            .typographic_bounds(font_id, glyph_id)?;
 96        self.read_metrics(font, |metrics| {
 97            (bounds / metrics.units_per_em as f32 * font_size.0).map(px)
 98        })
 99    }
100
101    pub fn advance(&self, font: &Font, font_size: Pixels, ch: char) -> Result<Size<Pixels>> {
102        let font_id = self.font_id(font)?;
103        let glyph_id = self
104            .platform_text_system
105            .glyph_for_char(font_id, ch)
106            .ok_or_else(|| anyhow!("glyph not found for character '{}'", ch))?;
107        let result =
108            self.platform_text_system.advance(font_id, glyph_id)? / self.units_per_em(font)? as f32;
109
110        Ok(result * font_size)
111    }
112
113    pub fn units_per_em(&self, font: &Font) -> Result<u32> {
114        self.read_metrics(font, |metrics| metrics.units_per_em as u32)
115    }
116
117    pub fn cap_height(&self, font: &Font, font_size: Pixels) -> Result<Pixels> {
118        self.read_metrics(font, |metrics| metrics.cap_height(font_size))
119    }
120
121    pub fn x_height(&self, font: &Font, font_size: Pixels) -> Result<Pixels> {
122        self.read_metrics(font, |metrics| metrics.x_height(font_size))
123    }
124
125    pub fn ascent(&self, font: &Font, font_size: Pixels) -> Result<Pixels> {
126        self.read_metrics(font, |metrics| metrics.ascent(font_size))
127    }
128
129    pub fn descent(&self, font: &Font, font_size: Pixels) -> Result<Pixels> {
130        self.read_metrics(font, |metrics| metrics.descent(font_size))
131    }
132
133    pub fn baseline_offset(
134        &self,
135        font: &Font,
136        font_size: Pixels,
137        line_height: Pixels,
138    ) -> Result<Pixels> {
139        let ascent = self.ascent(font, font_size)?;
140        let descent = self.descent(font, font_size)?;
141        let padding_top = (line_height - ascent - descent) / 2.;
142        Ok(padding_top + ascent)
143    }
144
145    fn read_metrics<T>(&self, font: &Font, read: impl FnOnce(&FontMetrics) -> T) -> Result<T> {
146        let lock = self.font_metrics.upgradable_read();
147
148        if let Some(metrics) = lock.get(font) {
149            Ok(read(metrics))
150        } else {
151            let font_id = self.platform_text_system.font_id(&font)?;
152            let mut lock = RwLockUpgradableReadGuard::upgrade(lock);
153            let metrics = lock
154                .entry(font.clone())
155                .or_insert_with(|| self.platform_text_system.font_metrics(font_id));
156            Ok(read(metrics))
157        }
158    }
159
160    pub fn layout_line(
161        &self,
162        text: &str,
163        font_size: Pixels,
164        runs: &[(usize, RunStyle)],
165    ) -> Result<Line> {
166        let mut font_runs = self.font_runs_pool.lock().pop().unwrap_or_default();
167
168        let mut last_font: Option<&Font> = None;
169        for (len, style) in runs {
170            if let Some(last_font) = last_font.as_ref() {
171                if **last_font == style.font {
172                    font_runs.last_mut().unwrap().0 += len;
173                    continue;
174                }
175            }
176            last_font = Some(&style.font);
177            font_runs.push((*len, self.font_id(&style.font)?));
178        }
179
180        let layout = self
181            .text_layout_cache
182            .layout_line(text, font_size, &font_runs);
183
184        font_runs.clear();
185        self.font_runs_pool.lock().push(font_runs);
186
187        Ok(Line::new(layout.clone(), runs))
188    }
189
190    pub fn finish_frame(&self) {
191        self.text_layout_cache.finish_frame()
192    }
193
194    pub fn line_wrapper(
195        self: &Arc<Self>,
196        font: Font,
197        font_size: Pixels,
198    ) -> Result<LineWrapperHandle> {
199        let lock = &mut self.wrapper_pool.lock();
200        let font_id = self.font_id(&font)?;
201        let wrappers = lock
202            .entry(FontIdWithSize { font_id, font_size })
203            .or_default();
204        let wrapper = wrappers.pop().map(anyhow::Ok).unwrap_or_else(|| {
205            Ok(LineWrapper::new(
206                font_id,
207                font_size,
208                self.platform_text_system.clone(),
209            ))
210        })?;
211
212        Ok(LineWrapperHandle {
213            wrapper: Some(wrapper),
214            text_system: self.clone(),
215        })
216    }
217
218    pub fn rasterize_glyph(
219        &self,
220        glyph_id: &RasterizedGlyphId,
221    ) -> Result<(Bounds<DevicePixels>, Vec<u8>)> {
222        self.platform_text_system.rasterize_glyph(glyph_id)
223    }
224}
225
226#[derive(Hash, Eq, PartialEq)]
227struct FontIdWithSize {
228    font_id: FontId,
229    font_size: Pixels,
230}
231
232pub struct LineWrapperHandle {
233    wrapper: Option<LineWrapper>,
234    text_system: Arc<TextSystem>,
235}
236
237impl Drop for LineWrapperHandle {
238    fn drop(&mut self) {
239        let mut state = self.text_system.wrapper_pool.lock();
240        let wrapper = self.wrapper.take().unwrap();
241        state
242            .get_mut(&FontIdWithSize {
243                font_id: wrapper.font_id.clone(),
244                font_size: wrapper.font_size,
245            })
246            .unwrap()
247            .push(wrapper);
248    }
249}
250
251impl Deref for LineWrapperHandle {
252    type Target = LineWrapper;
253
254    fn deref(&self) -> &Self::Target {
255        self.wrapper.as_ref().unwrap()
256    }
257}
258
259impl DerefMut for LineWrapperHandle {
260    fn deref_mut(&mut self) -> &mut Self::Target {
261        self.wrapper.as_mut().unwrap()
262    }
263}
264
265/// The degree of blackness or stroke thickness of a font. This value ranges from 100.0 to 900.0,
266/// with 400.0 as normal.
267#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
268pub struct FontWeight(pub f32);
269
270impl Default for FontWeight {
271    #[inline]
272    fn default() -> FontWeight {
273        FontWeight::NORMAL
274    }
275}
276
277impl Hash for FontWeight {
278    fn hash<H: Hasher>(&self, state: &mut H) {
279        state.write_u32(u32::from_be_bytes(self.0.to_be_bytes()));
280    }
281}
282
283impl Eq for FontWeight {}
284
285impl FontWeight {
286    /// Thin weight (100), the thinnest value.
287    pub const THIN: FontWeight = FontWeight(100.0);
288    /// Extra light weight (200).
289    pub const EXTRA_LIGHT: FontWeight = FontWeight(200.0);
290    /// Light weight (300).
291    pub const LIGHT: FontWeight = FontWeight(300.0);
292    /// Normal (400).
293    pub const NORMAL: FontWeight = FontWeight(400.0);
294    /// Medium weight (500, higher than normal).
295    pub const MEDIUM: FontWeight = FontWeight(500.0);
296    /// Semibold weight (600).
297    pub const SEMIBOLD: FontWeight = FontWeight(600.0);
298    /// Bold weight (700).
299    pub const BOLD: FontWeight = FontWeight(700.0);
300    /// Extra-bold weight (800).
301    pub const EXTRA_BOLD: FontWeight = FontWeight(800.0);
302    /// Black weight (900), the thickest value.
303    pub const BLACK: FontWeight = FontWeight(900.0);
304}
305
306/// Allows italic or oblique faces to be selected.
307#[derive(Clone, Copy, Eq, PartialEq, Debug, Hash)]
308pub enum FontStyle {
309    /// A face that is neither italic not obliqued.
310    Normal,
311    /// A form that is generally cursive in nature.
312    Italic,
313    /// A typically-sloped version of the regular face.
314    Oblique,
315}
316
317impl Default for FontStyle {
318    fn default() -> FontStyle {
319        FontStyle::Normal
320    }
321}
322
323impl Display for FontStyle {
324    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
325        Debug::fmt(self, f)
326    }
327}
328
329#[derive(Clone, Debug, PartialEq, Eq)]
330pub struct RunStyle {
331    pub font: Font,
332    pub color: Hsla,
333    pub underline: Option<UnderlineStyle>,
334}
335
336#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
337#[repr(C)]
338pub struct GlyphId(u32);
339
340impl From<GlyphId> for u32 {
341    fn from(value: GlyphId) -> Self {
342        value.0
343    }
344}
345
346impl From<u16> for GlyphId {
347    fn from(num: u16) -> Self {
348        GlyphId(num as u32)
349    }
350}
351
352impl From<u32> for GlyphId {
353    fn from(num: u32) -> Self {
354        GlyphId(num)
355    }
356}
357
358#[derive(Default, Debug)]
359pub struct ShapedLine {
360    pub font_size: Pixels,
361    pub width: Pixels,
362    pub ascent: Pixels,
363    pub descent: Pixels,
364    pub runs: Vec<ShapedRun>,
365    pub len: usize,
366}
367
368#[derive(Debug)]
369pub struct ShapedRun {
370    pub font_id: FontId,
371    pub glyphs: Vec<ShapedGlyph>,
372}
373
374#[derive(Clone, Debug)]
375pub struct ShapedGlyph {
376    pub id: GlyphId,
377    pub position: Point<Pixels>,
378    pub index: usize,
379    pub is_emoji: bool,
380}
381
382#[derive(Clone, Debug, PartialEq)]
383pub struct RasterizedGlyphId {
384    pub(crate) font_id: FontId,
385    pub(crate) glyph_id: GlyphId,
386    pub(crate) font_size: Pixels,
387    pub(crate) subpixel_variant: Point<u8>,
388    pub(crate) scale_factor: f32,
389}
390
391impl Eq for RasterizedGlyphId {}
392
393impl Hash for RasterizedGlyphId {
394    fn hash<H: Hasher>(&self, state: &mut H) {
395        self.font_id.0.hash(state);
396        self.glyph_id.0.hash(state);
397        self.font_size.0.to_bits().hash(state);
398        self.subpixel_variant.hash(state);
399        self.scale_factor.to_bits().hash(state);
400    }
401}
402
403#[derive(Clone, Debug, Eq, PartialEq, Hash)]
404pub struct Font {
405    pub family: SharedString,
406    pub features: FontFeatures,
407    pub weight: FontWeight,
408    pub style: FontStyle,
409}
410
411pub fn font(family: impl Into<SharedString>) -> Font {
412    Font {
413        family: family.into(),
414        features: FontFeatures::default(),
415        weight: FontWeight::default(),
416        style: FontStyle::default(),
417    }
418}
419
420impl Font {
421    pub fn bold(mut self) -> Self {
422        self.weight = FontWeight::BOLD;
423        self
424    }
425}
426
427/// A struct for storing font metrics.
428/// It is used to define the measurements of a typeface.
429#[derive(Clone, Copy, Debug)]
430pub struct FontMetrics {
431    /// The number of font units that make up the "em square",
432    /// a scalable grid for determining the size of a typeface.
433    pub(crate) units_per_em: u32,
434
435    /// The vertical distance from the baseline of the font to the top of the glyph covers.
436    pub(crate) ascent: f32,
437
438    /// The vertical distance from the baseline of the font to the bottom of the glyph covers.
439    pub(crate) descent: f32,
440
441    /// The recommended additional space to add between lines of type.
442    pub(crate) line_gap: f32,
443
444    /// The suggested position of the underline.
445    pub(crate) underline_position: f32,
446
447    /// The suggested thickness of the underline.
448    pub(crate) underline_thickness: f32,
449
450    /// The height of a capital letter measured from the baseline of the font.
451    pub(crate) cap_height: f32,
452
453    /// The height of a lowercase x.
454    pub(crate) x_height: f32,
455
456    /// The outer limits of the area that the font covers.
457    pub(crate) bounding_box: Bounds<f32>,
458}
459
460impl FontMetrics {
461    /// Returns the vertical distance from the baseline of the font to the top of the glyph covers in pixels.
462    pub fn ascent(&self, font_size: Pixels) -> Pixels {
463        Pixels((self.ascent / self.units_per_em as f32) * font_size.0)
464    }
465
466    /// Returns the vertical distance from the baseline of the font to the bottom of the glyph covers in pixels.
467    pub fn descent(&self, font_size: Pixels) -> Pixels {
468        Pixels((self.descent / self.units_per_em as f32) * font_size.0)
469    }
470
471    /// Returns the recommended additional space to add between lines of type in pixels.
472    pub fn line_gap(&self, font_size: Pixels) -> Pixels {
473        Pixels((self.line_gap / self.units_per_em as f32) * font_size.0)
474    }
475
476    /// Returns the suggested position of the underline in pixels.
477    pub fn underline_position(&self, font_size: Pixels) -> Pixels {
478        Pixels((self.underline_position / self.units_per_em as f32) * font_size.0)
479    }
480
481    /// Returns the suggested thickness of the underline in pixels.
482    pub fn underline_thickness(&self, font_size: Pixels) -> Pixels {
483        Pixels((self.underline_thickness / self.units_per_em as f32) * font_size.0)
484    }
485
486    /// Returns the height of a capital letter measured from the baseline of the font in pixels.
487    pub fn cap_height(&self, font_size: Pixels) -> Pixels {
488        Pixels((self.cap_height / self.units_per_em as f32) * font_size.0)
489    }
490
491    /// Returns the height of a lowercase x in pixels.
492    pub fn x_height(&self, font_size: Pixels) -> Pixels {
493        Pixels((self.x_height / self.units_per_em as f32) * font_size.0)
494    }
495
496    /// Returns the outer limits of the area that the font covers in pixels.
497    pub fn bounding_box(&self, font_size: Pixels) -> Bounds<Pixels> {
498        (self.bounding_box / self.units_per_em as f32 * font_size.0).map(px)
499    }
500}