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, Result, SharedString, Size,
 14    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 raster_bounds(&self, params: &RenderGlyphParams) -> Result<Bounds<DevicePixels>> {
219        self.platform_text_system.glyph_raster_bounds(params)
220    }
221
222    pub fn rasterize_glyph(
223        &self,
224        glyph_id: &RenderGlyphParams,
225    ) -> Result<(Size<DevicePixels>, Vec<u8>)> {
226        self.platform_text_system.rasterize_glyph(glyph_id)
227    }
228}
229
230#[derive(Hash, Eq, PartialEq)]
231struct FontIdWithSize {
232    font_id: FontId,
233    font_size: Pixels,
234}
235
236pub struct LineWrapperHandle {
237    wrapper: Option<LineWrapper>,
238    text_system: Arc<TextSystem>,
239}
240
241impl Drop for LineWrapperHandle {
242    fn drop(&mut self) {
243        let mut state = self.text_system.wrapper_pool.lock();
244        let wrapper = self.wrapper.take().unwrap();
245        state
246            .get_mut(&FontIdWithSize {
247                font_id: wrapper.font_id.clone(),
248                font_size: wrapper.font_size,
249            })
250            .unwrap()
251            .push(wrapper);
252    }
253}
254
255impl Deref for LineWrapperHandle {
256    type Target = LineWrapper;
257
258    fn deref(&self) -> &Self::Target {
259        self.wrapper.as_ref().unwrap()
260    }
261}
262
263impl DerefMut for LineWrapperHandle {
264    fn deref_mut(&mut self) -> &mut Self::Target {
265        self.wrapper.as_mut().unwrap()
266    }
267}
268
269/// The degree of blackness or stroke thickness of a font. This value ranges from 100.0 to 900.0,
270/// with 400.0 as normal.
271#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
272pub struct FontWeight(pub f32);
273
274impl Default for FontWeight {
275    #[inline]
276    fn default() -> FontWeight {
277        FontWeight::NORMAL
278    }
279}
280
281impl Hash for FontWeight {
282    fn hash<H: Hasher>(&self, state: &mut H) {
283        state.write_u32(u32::from_be_bytes(self.0.to_be_bytes()));
284    }
285}
286
287impl Eq for FontWeight {}
288
289impl FontWeight {
290    /// Thin weight (100), the thinnest value.
291    pub const THIN: FontWeight = FontWeight(100.0);
292    /// Extra light weight (200).
293    pub const EXTRA_LIGHT: FontWeight = FontWeight(200.0);
294    /// Light weight (300).
295    pub const LIGHT: FontWeight = FontWeight(300.0);
296    /// Normal (400).
297    pub const NORMAL: FontWeight = FontWeight(400.0);
298    /// Medium weight (500, higher than normal).
299    pub const MEDIUM: FontWeight = FontWeight(500.0);
300    /// Semibold weight (600).
301    pub const SEMIBOLD: FontWeight = FontWeight(600.0);
302    /// Bold weight (700).
303    pub const BOLD: FontWeight = FontWeight(700.0);
304    /// Extra-bold weight (800).
305    pub const EXTRA_BOLD: FontWeight = FontWeight(800.0);
306    /// Black weight (900), the thickest value.
307    pub const BLACK: FontWeight = FontWeight(900.0);
308}
309
310/// Allows italic or oblique faces to be selected.
311#[derive(Clone, Copy, Eq, PartialEq, Debug, Hash)]
312pub enum FontStyle {
313    /// A face that is neither italic not obliqued.
314    Normal,
315    /// A form that is generally cursive in nature.
316    Italic,
317    /// A typically-sloped version of the regular face.
318    Oblique,
319}
320
321impl Default for FontStyle {
322    fn default() -> FontStyle {
323        FontStyle::Normal
324    }
325}
326
327impl Display for FontStyle {
328    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
329        Debug::fmt(self, f)
330    }
331}
332
333#[derive(Clone, Debug, PartialEq, Eq)]
334pub struct RunStyle {
335    pub font: Font,
336    pub color: Hsla,
337    pub underline: Option<UnderlineStyle>,
338}
339
340#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
341#[repr(C)]
342pub struct GlyphId(u32);
343
344impl From<GlyphId> for u32 {
345    fn from(value: GlyphId) -> Self {
346        value.0
347    }
348}
349
350impl From<u16> for GlyphId {
351    fn from(num: u16) -> Self {
352        GlyphId(num as u32)
353    }
354}
355
356impl From<u32> for GlyphId {
357    fn from(num: u32) -> Self {
358        GlyphId(num)
359    }
360}
361
362#[derive(Default, Debug)]
363pub struct ShapedLine {
364    pub font_size: Pixels,
365    pub width: Pixels,
366    pub ascent: Pixels,
367    pub descent: Pixels,
368    pub runs: Vec<ShapedRun>,
369    pub len: usize,
370}
371
372#[derive(Debug)]
373pub struct ShapedRun {
374    pub font_id: FontId,
375    pub glyphs: Vec<ShapedGlyph>,
376}
377
378#[derive(Clone, Debug)]
379pub struct ShapedGlyph {
380    pub id: GlyphId,
381    pub position: Point<Pixels>,
382    pub index: usize,
383    pub is_emoji: bool,
384}
385
386#[derive(Clone, Debug, PartialEq)]
387pub struct RenderGlyphParams {
388    pub(crate) font_id: FontId,
389    pub(crate) glyph_id: GlyphId,
390    pub(crate) font_size: Pixels,
391    pub(crate) subpixel_variant: Point<u8>,
392    pub(crate) scale_factor: f32,
393}
394
395impl Eq for RenderGlyphParams {}
396
397impl Hash for RenderGlyphParams {
398    fn hash<H: Hasher>(&self, state: &mut H) {
399        self.font_id.0.hash(state);
400        self.glyph_id.0.hash(state);
401        self.font_size.0.to_bits().hash(state);
402        self.subpixel_variant.hash(state);
403        self.scale_factor.to_bits().hash(state);
404    }
405}
406
407#[derive(Clone, Debug, PartialEq)]
408pub struct RenderEmojiParams {
409    pub(crate) font_id: FontId,
410    pub(crate) glyph_id: GlyphId,
411    pub(crate) font_size: Pixels,
412    pub(crate) scale_factor: f32,
413}
414
415impl Eq for RenderEmojiParams {}
416
417impl Hash for RenderEmojiParams {
418    fn hash<H: Hasher>(&self, state: &mut H) {
419        self.font_id.0.hash(state);
420        self.glyph_id.0.hash(state);
421        self.font_size.0.to_bits().hash(state);
422        self.scale_factor.to_bits().hash(state);
423    }
424}
425
426#[derive(Clone, Debug, Eq, PartialEq, Hash)]
427pub struct Font {
428    pub family: SharedString,
429    pub features: FontFeatures,
430    pub weight: FontWeight,
431    pub style: FontStyle,
432}
433
434pub fn font(family: impl Into<SharedString>) -> Font {
435    Font {
436        family: family.into(),
437        features: FontFeatures::default(),
438        weight: FontWeight::default(),
439        style: FontStyle::default(),
440    }
441}
442
443impl Font {
444    pub fn bold(mut self) -> Self {
445        self.weight = FontWeight::BOLD;
446        self
447    }
448}
449
450/// A struct for storing font metrics.
451/// It is used to define the measurements of a typeface.
452#[derive(Clone, Copy, Debug)]
453pub struct FontMetrics {
454    /// The number of font units that make up the "em square",
455    /// a scalable grid for determining the size of a typeface.
456    pub(crate) units_per_em: u32,
457
458    /// The vertical distance from the baseline of the font to the top of the glyph covers.
459    pub(crate) ascent: f32,
460
461    /// The vertical distance from the baseline of the font to the bottom of the glyph covers.
462    pub(crate) descent: f32,
463
464    /// The recommended additional space to add between lines of type.
465    pub(crate) line_gap: f32,
466
467    /// The suggested position of the underline.
468    pub(crate) underline_position: f32,
469
470    /// The suggested thickness of the underline.
471    pub(crate) underline_thickness: f32,
472
473    /// The height of a capital letter measured from the baseline of the font.
474    pub(crate) cap_height: f32,
475
476    /// The height of a lowercase x.
477    pub(crate) x_height: f32,
478
479    /// The outer limits of the area that the font covers.
480    pub(crate) bounding_box: Bounds<f32>,
481}
482
483impl FontMetrics {
484    /// Returns the vertical distance from the baseline of the font to the top of the glyph covers in pixels.
485    pub fn ascent(&self, font_size: Pixels) -> Pixels {
486        Pixels((self.ascent / self.units_per_em as f32) * font_size.0)
487    }
488
489    /// Returns the vertical distance from the baseline of the font to the bottom of the glyph covers in pixels.
490    pub fn descent(&self, font_size: Pixels) -> Pixels {
491        Pixels((self.descent / self.units_per_em as f32) * font_size.0)
492    }
493
494    /// Returns the recommended additional space to add between lines of type in pixels.
495    pub fn line_gap(&self, font_size: Pixels) -> Pixels {
496        Pixels((self.line_gap / self.units_per_em as f32) * font_size.0)
497    }
498
499    /// Returns the suggested position of the underline in pixels.
500    pub fn underline_position(&self, font_size: Pixels) -> Pixels {
501        Pixels((self.underline_position / self.units_per_em as f32) * font_size.0)
502    }
503
504    /// Returns the suggested thickness of the underline in pixels.
505    pub fn underline_thickness(&self, font_size: Pixels) -> Pixels {
506        Pixels((self.underline_thickness / self.units_per_em as f32) * font_size.0)
507    }
508
509    /// Returns the height of a capital letter measured from the baseline of the font in pixels.
510    pub fn cap_height(&self, font_size: Pixels) -> Pixels {
511        Pixels((self.cap_height / self.units_per_em as f32) * font_size.0)
512    }
513
514    /// Returns the height of a lowercase x in pixels.
515    pub fn x_height(&self, font_size: Pixels) -> Pixels {
516        Pixels((self.x_height / self.units_per_em as f32) * font_size.0)
517    }
518
519    /// Returns the outer limits of the area that the font covers in pixels.
520    pub fn bounding_box(&self, font_size: Pixels) -> Bounds<Pixels> {
521        (self.bounding_box / self.units_per_em as f32 * font_size.0).map(px)
522    }
523}