text_system.rs

  1mod font_features;
  2mod line;
  3mod line_layout;
  4mod line_wrapper;
  5
  6pub use font_features::*;
  7pub use line::*;
  8pub use line_layout::*;
  9pub use line_wrapper::*;
 10
 11use crate::{
 12    px, Bounds, DevicePixels, EntityId, Hsla, Pixels, PlatformTextSystem, Point, Result,
 13    SharedString, Size, UnderlineStyle,
 14};
 15use anyhow::anyhow;
 16use collections::{FxHashMap, FxHashSet};
 17use core::fmt;
 18use itertools::Itertools;
 19use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard};
 20use smallvec::{smallvec, SmallVec};
 21use std::{
 22    cmp,
 23    fmt::{Debug, Display, Formatter},
 24    hash::{Hash, Hasher},
 25    ops::{Deref, DerefMut},
 26    sync::Arc,
 27};
 28
 29#[derive(Hash, PartialEq, Eq, Clone, Copy, Debug)]
 30#[repr(C)]
 31pub struct FontId(pub usize);
 32
 33#[derive(Hash, PartialEq, Eq, Clone, Copy, Debug)]
 34pub struct FontFamilyId(pub usize);
 35
 36pub(crate) const SUBPIXEL_VARIANTS: u8 = 4;
 37
 38pub struct TextSystem {
 39    line_layout_cache: Arc<LineLayoutCache>,
 40    platform_text_system: Arc<dyn PlatformTextSystem>,
 41    font_ids_by_font: RwLock<FxHashMap<Font, FontId>>,
 42    font_metrics: RwLock<FxHashMap<FontId, FontMetrics>>,
 43    raster_bounds: RwLock<FxHashMap<RenderGlyphParams, Bounds<DevicePixels>>>,
 44    wrapper_pool: Mutex<FxHashMap<FontIdWithSize, Vec<LineWrapper>>>,
 45    font_runs_pool: Mutex<Vec<Vec<FontRun>>>,
 46    fallback_font_stack: SmallVec<[Font; 2]>,
 47}
 48
 49impl TextSystem {
 50    pub fn new(platform_text_system: Arc<dyn PlatformTextSystem>) -> Self {
 51        TextSystem {
 52            line_layout_cache: Arc::new(LineLayoutCache::new(platform_text_system.clone())),
 53            platform_text_system,
 54            font_metrics: RwLock::default(),
 55            raster_bounds: RwLock::default(),
 56            font_ids_by_font: RwLock::default(),
 57            wrapper_pool: Mutex::default(),
 58            font_runs_pool: Mutex::default(),
 59            fallback_font_stack: smallvec![
 60                // TODO: This is currently Zed-specific.
 61                // We should allow GPUI users to provide their own fallback font stack.
 62                font("Zed Mono"),
 63                font("Helvetica")
 64            ],
 65        }
 66    }
 67
 68    pub fn all_font_names(&self) -> Vec<String> {
 69        let mut families = self.platform_text_system.all_font_names();
 70        families.append(
 71            &mut self
 72                .fallback_font_stack
 73                .iter()
 74                .map(|font| font.family.to_string())
 75                .collect(),
 76        );
 77        families
 78    }
 79    pub fn add_fonts(&self, fonts: &[Arc<Vec<u8>>]) -> Result<()> {
 80        self.platform_text_system.add_fonts(fonts)
 81    }
 82
 83    pub fn font_id(&self, font: &Font) -> Result<FontId> {
 84        let font_id = self.font_ids_by_font.read().get(font).copied();
 85        if let Some(font_id) = font_id {
 86            Ok(font_id)
 87        } else {
 88            let font_id = self.platform_text_system.font_id(font)?;
 89            self.font_ids_by_font.write().insert(font.clone(), font_id);
 90            Ok(font_id)
 91        }
 92    }
 93
 94    /// Resolves the specified font, falling back to the default font stack if
 95    /// the font fails to load.
 96    ///
 97    /// # Panics
 98    ///
 99    /// Panics if the font and none of the fallbacks can be resolved.
100    pub fn resolve_font(&self, font: &Font) -> FontId {
101        if let Ok(font_id) = self.font_id(font) {
102            return font_id;
103        }
104        for fallback in &self.fallback_font_stack {
105            if let Ok(font_id) = self.font_id(fallback) {
106                return font_id;
107            }
108        }
109
110        panic!(
111            "failed to resolve font '{}' or any of the fallbacks: {}",
112            font.family,
113            self.fallback_font_stack
114                .iter()
115                .map(|fallback| &fallback.family)
116                .join(", ")
117        );
118    }
119
120    pub fn bounding_box(&self, font_id: FontId, font_size: Pixels) -> Bounds<Pixels> {
121        self.read_metrics(font_id, |metrics| metrics.bounding_box(font_size))
122    }
123
124    pub fn typographic_bounds(
125        &self,
126        font_id: FontId,
127        font_size: Pixels,
128        character: char,
129    ) -> Result<Bounds<Pixels>> {
130        let glyph_id = self
131            .platform_text_system
132            .glyph_for_char(font_id, character)
133            .ok_or_else(|| anyhow!("glyph not found for character '{}'", character))?;
134        let bounds = self
135            .platform_text_system
136            .typographic_bounds(font_id, glyph_id)?;
137        Ok(self.read_metrics(font_id, |metrics| {
138            (bounds / metrics.units_per_em as f32 * font_size.0).map(px)
139        }))
140    }
141
142    pub fn advance(&self, font_id: FontId, font_size: Pixels, ch: char) -> Result<Size<Pixels>> {
143        let glyph_id = self
144            .platform_text_system
145            .glyph_for_char(font_id, ch)
146            .ok_or_else(|| anyhow!("glyph not found for character '{}'", ch))?;
147        let result = self.platform_text_system.advance(font_id, glyph_id)?
148            / self.units_per_em(font_id) as f32;
149
150        Ok(result * font_size)
151    }
152
153    pub fn units_per_em(&self, font_id: FontId) -> u32 {
154        self.read_metrics(font_id, |metrics| metrics.units_per_em)
155    }
156
157    pub fn cap_height(&self, font_id: FontId, font_size: Pixels) -> Pixels {
158        self.read_metrics(font_id, |metrics| metrics.cap_height(font_size))
159    }
160
161    pub fn x_height(&self, font_id: FontId, font_size: Pixels) -> Pixels {
162        self.read_metrics(font_id, |metrics| metrics.x_height(font_size))
163    }
164
165    pub fn ascent(&self, font_id: FontId, font_size: Pixels) -> Pixels {
166        self.read_metrics(font_id, |metrics| metrics.ascent(font_size))
167    }
168
169    pub fn descent(&self, font_id: FontId, font_size: Pixels) -> Pixels {
170        self.read_metrics(font_id, |metrics| metrics.descent(font_size))
171    }
172
173    pub fn baseline_offset(
174        &self,
175        font_id: FontId,
176        font_size: Pixels,
177        line_height: Pixels,
178    ) -> Pixels {
179        let ascent = self.ascent(font_id, font_size);
180        let descent = self.descent(font_id, font_size);
181        let padding_top = (line_height - ascent - descent) / 2.;
182        padding_top + ascent
183    }
184
185    fn read_metrics<T>(&self, font_id: FontId, read: impl FnOnce(&FontMetrics) -> T) -> T {
186        let lock = self.font_metrics.upgradable_read();
187
188        if let Some(metrics) = lock.get(&font_id) {
189            read(metrics)
190        } else {
191            let mut lock = RwLockUpgradableReadGuard::upgrade(lock);
192            let metrics = lock
193                .entry(font_id)
194                .or_insert_with(|| self.platform_text_system.font_metrics(font_id));
195            read(metrics)
196        }
197    }
198
199    pub fn with_view<R>(&self, view_id: EntityId, f: impl FnOnce() -> R) -> R {
200        self.line_layout_cache.with_view(view_id, f)
201    }
202
203    pub fn layout_line(
204        &self,
205        text: &str,
206        font_size: Pixels,
207        runs: &[TextRun],
208    ) -> Result<Arc<LineLayout>> {
209        let mut font_runs = self.font_runs_pool.lock().pop().unwrap_or_default();
210        for run in runs.iter() {
211            let font_id = self.resolve_font(&run.font);
212            if let Some(last_run) = font_runs.last_mut() {
213                if last_run.font_id == font_id {
214                    last_run.len += run.len;
215                    continue;
216                }
217            }
218            font_runs.push(FontRun {
219                len: run.len,
220                font_id,
221            });
222        }
223
224        let layout = self
225            .line_layout_cache
226            .layout_line(text, font_size, &font_runs);
227
228        font_runs.clear();
229        self.font_runs_pool.lock().push(font_runs);
230
231        Ok(layout)
232    }
233
234    pub fn shape_line(
235        &self,
236        text: SharedString,
237        font_size: Pixels,
238        runs: &[TextRun],
239    ) -> Result<ShapedLine> {
240        debug_assert!(
241            text.find('\n').is_none(),
242            "text argument should not contain newlines"
243        );
244
245        let mut decoration_runs = SmallVec::<[DecorationRun; 32]>::new();
246        for run in runs {
247            if let Some(last_run) = decoration_runs.last_mut() {
248                if last_run.color == run.color
249                    && last_run.underline == run.underline
250                    && last_run.background_color == run.background_color
251                {
252                    last_run.len += run.len as u32;
253                    continue;
254                }
255            }
256            decoration_runs.push(DecorationRun {
257                len: run.len as u32,
258                color: run.color,
259                background_color: run.background_color,
260                underline: run.underline,
261            });
262        }
263
264        let layout = self.layout_line(text.as_ref(), font_size, runs)?;
265
266        Ok(ShapedLine {
267            layout,
268            text,
269            decoration_runs,
270        })
271    }
272
273    pub fn shape_text(
274        &self,
275        text: SharedString,
276        font_size: Pixels,
277        runs: &[TextRun],
278        wrap_width: Option<Pixels>,
279    ) -> Result<SmallVec<[WrappedLine; 1]>> {
280        let mut runs = runs.iter().cloned().peekable();
281        let mut font_runs = self.font_runs_pool.lock().pop().unwrap_or_default();
282
283        let mut lines = SmallVec::new();
284        let mut line_start = 0;
285
286        let mut process_line = |line_text: SharedString| {
287            let line_end = line_start + line_text.len();
288
289            let mut last_font: Option<Font> = None;
290            let mut decoration_runs = SmallVec::<[DecorationRun; 32]>::new();
291            let mut run_start = line_start;
292            while run_start < line_end {
293                let Some(run) = runs.peek_mut() else {
294                    break;
295                };
296
297                let run_len_within_line = cmp::min(line_end, run_start + run.len) - run_start;
298
299                if last_font == Some(run.font.clone()) {
300                    font_runs.last_mut().unwrap().len += run_len_within_line;
301                } else {
302                    last_font = Some(run.font.clone());
303                    font_runs.push(FontRun {
304                        len: run_len_within_line,
305                        font_id: self.resolve_font(&run.font),
306                    });
307                }
308
309                if decoration_runs.last().map_or(false, |last_run| {
310                    last_run.color == run.color
311                        && last_run.underline == run.underline
312                        && last_run.background_color == run.background_color
313                }) {
314                    decoration_runs.last_mut().unwrap().len += run_len_within_line as u32;
315                } else {
316                    decoration_runs.push(DecorationRun {
317                        len: run_len_within_line as u32,
318                        color: run.color,
319                        background_color: run.background_color,
320                        underline: run.underline,
321                    });
322                }
323
324                if run_len_within_line == run.len {
325                    runs.next();
326                } else {
327                    // Preserve the remainder of the run for the next line
328                    run.len -= run_len_within_line;
329                }
330                run_start += run_len_within_line;
331            }
332
333            let layout = self
334                .line_layout_cache
335                .layout_wrapped_line(&line_text, font_size, &font_runs, wrap_width);
336            lines.push(WrappedLine {
337                layout,
338                decoration_runs,
339                text: line_text,
340            });
341
342            // Skip `\n` character.
343            line_start = line_end + 1;
344            if let Some(run) = runs.peek_mut() {
345                run.len = run.len.saturating_sub(1);
346                if run.len == 0 {
347                    runs.next();
348                }
349            }
350
351            font_runs.clear();
352        };
353
354        let mut split_lines = text.split('\n');
355        let mut processed = false;
356
357        if let Some(first_line) = split_lines.next() {
358            if let Some(second_line) = split_lines.next() {
359                processed = true;
360                process_line(first_line.to_string().into());
361                process_line(second_line.to_string().into());
362                for line_text in split_lines {
363                    process_line(line_text.to_string().into());
364                }
365            }
366        }
367
368        if !processed {
369            process_line(text);
370        }
371
372        self.font_runs_pool.lock().push(font_runs);
373
374        Ok(lines)
375    }
376
377    pub fn finish_frame(&self, reused_views: &FxHashSet<EntityId>) {
378        self.line_layout_cache.finish_frame(reused_views)
379    }
380
381    pub fn line_wrapper(self: &Arc<Self>, font: Font, font_size: Pixels) -> LineWrapperHandle {
382        let lock = &mut self.wrapper_pool.lock();
383        let font_id = self.resolve_font(&font);
384        let wrappers = lock
385            .entry(FontIdWithSize { font_id, font_size })
386            .or_default();
387        let wrapper = wrappers.pop().unwrap_or_else(|| {
388            LineWrapper::new(font_id, font_size, self.platform_text_system.clone())
389        });
390
391        LineWrapperHandle {
392            wrapper: Some(wrapper),
393            text_system: self.clone(),
394        }
395    }
396
397    pub fn raster_bounds(&self, params: &RenderGlyphParams) -> Result<Bounds<DevicePixels>> {
398        let raster_bounds = self.raster_bounds.upgradable_read();
399        if let Some(bounds) = raster_bounds.get(params) {
400            Ok(*bounds)
401        } else {
402            let mut raster_bounds = RwLockUpgradableReadGuard::upgrade(raster_bounds);
403            let bounds = self.platform_text_system.glyph_raster_bounds(params)?;
404            raster_bounds.insert(params.clone(), bounds);
405            Ok(bounds)
406        }
407    }
408
409    pub fn rasterize_glyph(
410        &self,
411        params: &RenderGlyphParams,
412    ) -> Result<(Size<DevicePixels>, Vec<u8>)> {
413        let raster_bounds = self.raster_bounds(params)?;
414        self.platform_text_system
415            .rasterize_glyph(params, raster_bounds)
416    }
417}
418
419#[derive(Hash, Eq, PartialEq)]
420struct FontIdWithSize {
421    font_id: FontId,
422    font_size: Pixels,
423}
424
425pub struct LineWrapperHandle {
426    wrapper: Option<LineWrapper>,
427    text_system: Arc<TextSystem>,
428}
429
430impl Drop for LineWrapperHandle {
431    fn drop(&mut self) {
432        let mut state = self.text_system.wrapper_pool.lock();
433        let wrapper = self.wrapper.take().unwrap();
434        state
435            .get_mut(&FontIdWithSize {
436                font_id: wrapper.font_id,
437                font_size: wrapper.font_size,
438            })
439            .unwrap()
440            .push(wrapper);
441    }
442}
443
444impl Deref for LineWrapperHandle {
445    type Target = LineWrapper;
446
447    fn deref(&self) -> &Self::Target {
448        self.wrapper.as_ref().unwrap()
449    }
450}
451
452impl DerefMut for LineWrapperHandle {
453    fn deref_mut(&mut self) -> &mut Self::Target {
454        self.wrapper.as_mut().unwrap()
455    }
456}
457
458/// The degree of blackness or stroke thickness of a font. This value ranges from 100.0 to 900.0,
459/// with 400.0 as normal.
460#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
461pub struct FontWeight(pub f32);
462
463impl Default for FontWeight {
464    #[inline]
465    fn default() -> FontWeight {
466        FontWeight::NORMAL
467    }
468}
469
470impl Hash for FontWeight {
471    fn hash<H: Hasher>(&self, state: &mut H) {
472        state.write_u32(u32::from_be_bytes(self.0.to_be_bytes()));
473    }
474}
475
476impl Eq for FontWeight {}
477
478impl FontWeight {
479    /// Thin weight (100), the thinnest value.
480    pub const THIN: FontWeight = FontWeight(100.0);
481    /// Extra light weight (200).
482    pub const EXTRA_LIGHT: FontWeight = FontWeight(200.0);
483    /// Light weight (300).
484    pub const LIGHT: FontWeight = FontWeight(300.0);
485    /// Normal (400).
486    pub const NORMAL: FontWeight = FontWeight(400.0);
487    /// Medium weight (500, higher than normal).
488    pub const MEDIUM: FontWeight = FontWeight(500.0);
489    /// Semibold weight (600).
490    pub const SEMIBOLD: FontWeight = FontWeight(600.0);
491    /// Bold weight (700).
492    pub const BOLD: FontWeight = FontWeight(700.0);
493    /// Extra-bold weight (800).
494    pub const EXTRA_BOLD: FontWeight = FontWeight(800.0);
495    /// Black weight (900), the thickest value.
496    pub const BLACK: FontWeight = FontWeight(900.0);
497}
498
499/// Allows italic or oblique faces to be selected.
500#[derive(Clone, Copy, Eq, PartialEq, Debug, Hash, Default)]
501pub enum FontStyle {
502    /// A face that is neither italic not obliqued.
503    #[default]
504    Normal,
505    /// A form that is generally cursive in nature.
506    Italic,
507    /// A typically-sloped version of the regular face.
508    Oblique,
509}
510
511impl Display for FontStyle {
512    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
513        Debug::fmt(self, f)
514    }
515}
516
517#[derive(Clone, Debug, PartialEq, Eq)]
518pub struct TextRun {
519    // number of utf8 bytes
520    pub len: usize,
521    pub font: Font,
522    pub color: Hsla,
523    pub background_color: Option<Hsla>,
524    pub underline: Option<UnderlineStyle>,
525}
526
527#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
528#[repr(C)]
529pub struct GlyphId(u32);
530
531impl From<GlyphId> for u32 {
532    fn from(value: GlyphId) -> Self {
533        value.0
534    }
535}
536
537impl From<u16> for GlyphId {
538    fn from(num: u16) -> Self {
539        GlyphId(num as u32)
540    }
541}
542
543impl From<u32> for GlyphId {
544    fn from(num: u32) -> Self {
545        GlyphId(num)
546    }
547}
548
549#[derive(Clone, Debug, PartialEq)]
550pub struct RenderGlyphParams {
551    pub(crate) font_id: FontId,
552    pub(crate) glyph_id: GlyphId,
553    pub(crate) font_size: Pixels,
554    pub(crate) subpixel_variant: Point<u8>,
555    pub(crate) scale_factor: f32,
556    pub(crate) is_emoji: bool,
557}
558
559impl Eq for RenderGlyphParams {}
560
561impl Hash for RenderGlyphParams {
562    fn hash<H: Hasher>(&self, state: &mut H) {
563        self.font_id.0.hash(state);
564        self.glyph_id.0.hash(state);
565        self.font_size.0.to_bits().hash(state);
566        self.subpixel_variant.hash(state);
567        self.scale_factor.to_bits().hash(state);
568    }
569}
570
571#[derive(Clone, Debug, PartialEq)]
572pub struct RenderEmojiParams {
573    pub(crate) font_id: FontId,
574    pub(crate) glyph_id: GlyphId,
575    pub(crate) font_size: Pixels,
576    pub(crate) scale_factor: f32,
577}
578
579impl Eq for RenderEmojiParams {}
580
581impl Hash for RenderEmojiParams {
582    fn hash<H: Hasher>(&self, state: &mut H) {
583        self.font_id.0.hash(state);
584        self.glyph_id.0.hash(state);
585        self.font_size.0.to_bits().hash(state);
586        self.scale_factor.to_bits().hash(state);
587    }
588}
589
590#[derive(Clone, Debug, Eq, PartialEq, Hash)]
591pub struct Font {
592    pub family: SharedString,
593    pub features: FontFeatures,
594    pub weight: FontWeight,
595    pub style: FontStyle,
596}
597
598pub fn font(family: impl Into<SharedString>) -> Font {
599    Font {
600        family: family.into(),
601        features: FontFeatures::default(),
602        weight: FontWeight::default(),
603        style: FontStyle::default(),
604    }
605}
606
607impl Font {
608    pub fn bold(mut self) -> Self {
609        self.weight = FontWeight::BOLD;
610        self
611    }
612}
613
614/// A struct for storing font metrics.
615/// It is used to define the measurements of a typeface.
616#[derive(Clone, Copy, Debug)]
617pub struct FontMetrics {
618    /// The number of font units that make up the "em square",
619    /// a scalable grid for determining the size of a typeface.
620    pub(crate) units_per_em: u32,
621
622    /// The vertical distance from the baseline of the font to the top of the glyph covers.
623    pub(crate) ascent: f32,
624
625    /// The vertical distance from the baseline of the font to the bottom of the glyph covers.
626    pub(crate) descent: f32,
627
628    /// The recommended additional space to add between lines of type.
629    pub(crate) line_gap: f32,
630
631    /// The suggested position of the underline.
632    pub(crate) underline_position: f32,
633
634    /// The suggested thickness of the underline.
635    pub(crate) underline_thickness: f32,
636
637    /// The height of a capital letter measured from the baseline of the font.
638    pub(crate) cap_height: f32,
639
640    /// The height of a lowercase x.
641    pub(crate) x_height: f32,
642
643    /// The outer limits of the area that the font covers.
644    pub(crate) bounding_box: Bounds<f32>,
645}
646
647impl FontMetrics {
648    /// Returns the vertical distance from the baseline of the font to the top of the glyph covers in pixels.
649    pub fn ascent(&self, font_size: Pixels) -> Pixels {
650        Pixels((self.ascent / self.units_per_em as f32) * font_size.0)
651    }
652
653    /// Returns the vertical distance from the baseline of the font to the bottom of the glyph covers in pixels.
654    pub fn descent(&self, font_size: Pixels) -> Pixels {
655        Pixels((self.descent / self.units_per_em as f32) * font_size.0)
656    }
657
658    /// Returns the recommended additional space to add between lines of type in pixels.
659    pub fn line_gap(&self, font_size: Pixels) -> Pixels {
660        Pixels((self.line_gap / self.units_per_em as f32) * font_size.0)
661    }
662
663    /// Returns the suggested position of the underline in pixels.
664    pub fn underline_position(&self, font_size: Pixels) -> Pixels {
665        Pixels((self.underline_position / self.units_per_em as f32) * font_size.0)
666    }
667
668    /// Returns the suggested thickness of the underline in pixels.
669    pub fn underline_thickness(&self, font_size: Pixels) -> Pixels {
670        Pixels((self.underline_thickness / self.units_per_em as f32) * font_size.0)
671    }
672
673    /// Returns the height of a capital letter measured from the baseline of the font in pixels.
674    pub fn cap_height(&self, font_size: Pixels) -> Pixels {
675        Pixels((self.cap_height / self.units_per_em as f32) * font_size.0)
676    }
677
678    /// Returns the height of a lowercase x in pixels.
679    pub fn x_height(&self, font_size: Pixels) -> Pixels {
680        Pixels((self.x_height / self.units_per_em as f32) * font_size.0)
681    }
682
683    /// Returns the outer limits of the area that the font covers in pixels.
684    pub fn bounding_box(&self, font_size: Pixels) -> Bounds<Pixels> {
685        (self.bounding_box / self.units_per_em as f32 * font_size.0).map(px)
686    }
687}