Skip to main content

i_slint_renderer_software/fonts/
vectorfont.rs

1// Copyright © SixtyFPS GmbH <info@slint.dev>
2// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
3
4use core::num::NonZeroU16;
5
6use alloc::rc::Rc;
7use skrifa::MetadataProvider;
8
9use crate::PhysicalLength;
10use crate::fixed::Fixed;
11use i_slint_common::sharedfontique::fontique;
12use i_slint_core::lengths::PhysicalPx;
13use i_slint_core::textlayout::{Glyph, TextShaper};
14
15use super::RenderableVectorGlyph;
16
17// A length in font design space.
18struct FontUnit;
19type FontLength = euclid::Length<i32, FontUnit>;
20type FontScaleFactor = euclid::Scale<f32, FontUnit, PhysicalPx>;
21
22type GlyphCacheKey = (u64, u32, PhysicalLength, core::num::NonZeroU16);
23
24struct RenderableGlyphWeightScale;
25
26impl clru::WeightScale<GlyphCacheKey, RenderableVectorGlyph> for RenderableGlyphWeightScale {
27    fn weight(&self, _: &GlyphCacheKey, value: &RenderableVectorGlyph) -> usize {
28        value.alpha_map.len()
29    }
30}
31
32type GlyphCache = clru::CLruCache<
33    GlyphCacheKey,
34    RenderableVectorGlyph,
35    std::collections::hash_map::RandomState,
36    RenderableGlyphWeightScale,
37>;
38
39i_slint_core::thread_local!(static GLYPH_CACHE: core::cell::RefCell<GlyphCache>  =
40    core::cell::RefCell::new(
41        clru::CLruCache::with_config(
42            clru::CLruCacheConfig::new(core::num::NonZeroUsize::new(1024 * 1024).unwrap())
43                .with_scale(RenderableGlyphWeightScale)
44        )
45    )
46);
47
48pub struct VectorFont {
49    font_index: u32,
50    font_blob: fontique::Blob<u8>,
51    fontdue_font: Rc<fontdue::Font>,
52    ascender: PhysicalLength,
53    descender: PhysicalLength,
54    height: PhysicalLength,
55    pixel_size: PhysicalLength,
56    x_height: PhysicalLength,
57    cap_height: PhysicalLength,
58}
59
60impl VectorFont {
61    pub fn new(
62        font: fontique::QueryFont,
63        fontdue_font: Rc<fontdue::Font>,
64        pixel_size: PhysicalLength,
65    ) -> Self {
66        Self::new_from_blob_and_index(font.blob, font.index, fontdue_font, pixel_size)
67    }
68
69    pub fn new_from_blob_and_index(
70        font_blob: fontique::Blob<u8>,
71        font_index: u32,
72        fontdue_font: Rc<fontdue::Font>,
73        pixel_size: PhysicalLength,
74    ) -> Self {
75        let face = skrifa::FontRef::from_index(font_blob.data(), font_index).unwrap();
76
77        let metrics = face
78            .metrics(skrifa::instance::Size::unscaled(), skrifa::instance::LocationRef::new(&[]));
79
80        let ascender = FontLength::new(metrics.ascent as _);
81        let descender = FontLength::new(metrics.descent as _);
82        let height = FontLength::new((metrics.ascent - metrics.descent) as _);
83        let x_height = FontLength::new(metrics.x_height.unwrap_or_default() as _);
84        let cap_height = FontLength::new(metrics.cap_height.unwrap_or_default() as _);
85        let units_per_em = metrics.units_per_em;
86        let scale = FontScaleFactor::new(pixel_size.get() as f32 / units_per_em as f32);
87        Self {
88            font_index,
89            font_blob,
90            fontdue_font,
91            ascender: (ascender.cast() * scale).cast(),
92            descender: (descender.cast() * scale).cast(),
93            height: (height.cast() * scale).cast(),
94            pixel_size,
95            x_height: (x_height.cast() * scale).cast(),
96            cap_height: (cap_height.cast() * scale).cast(),
97        }
98    }
99
100    pub fn render_vector_glyph(
101        &self,
102        glyph_id: core::num::NonZeroU16,
103    ) -> Option<RenderableVectorGlyph> {
104        GLYPH_CACHE.with(|cache| {
105            let mut cache = cache.borrow_mut();
106
107            let cache_key = (self.font_blob.id(), self.font_index, self.pixel_size, glyph_id);
108
109            if let Some(entry) = cache.get(&cache_key) {
110                Some(entry.clone())
111            } else {
112                let (metrics, alpha_map) =
113                    self.fontdue_font.rasterize_indexed(glyph_id.get(), self.pixel_size.get() as _);
114
115                let alpha_map: Rc<[u8]> = alpha_map.into();
116
117                let glyph = super::RenderableVectorGlyph {
118                    x: Fixed::from_integer(metrics.xmin.try_into().unwrap()),
119                    y: Fixed::from_integer(metrics.ymin.try_into().unwrap()),
120                    width: PhysicalLength::new(metrics.width.try_into().unwrap()),
121                    height: PhysicalLength::new(metrics.height.try_into().unwrap()),
122                    alpha_map,
123                    pixel_stride: metrics.width.try_into().unwrap(),
124                    bounds: metrics.bounds,
125                };
126
127                cache.put_with_weight(cache_key, glyph.clone()).ok();
128                Some(glyph)
129            }
130        })
131    }
132}
133
134impl TextShaper for VectorFont {
135    type LengthPrimitive = i16;
136    type Length = PhysicalLength;
137    fn shape_text<GlyphStorage: core::iter::Extend<Glyph<PhysicalLength>>>(
138        &self,
139        text: &str,
140        glyphs: &mut GlyphStorage,
141    ) {
142        glyphs.extend(text.char_indices().map(|(byte_offset, char)| {
143            let glyph_id = NonZeroU16::try_from(self.fontdue_font.lookup_glyph_index(char)).ok();
144            let x_advance = glyph_id.map_or_else(
145                || self.pixel_size.get(),
146                |id| {
147                    self.fontdue_font
148                        .metrics_indexed(id.get(), self.pixel_size.get() as _)
149                        .advance_width as _
150                },
151            );
152
153            Glyph {
154                glyph_id,
155                advance: PhysicalLength::new(x_advance),
156                text_byte_offset: byte_offset,
157                ..Default::default()
158            }
159        }));
160    }
161
162    fn glyph_for_char(&self, ch: char) -> Option<Glyph<PhysicalLength>> {
163        NonZeroU16::try_from(self.fontdue_font.lookup_glyph_index(ch)).ok().map(|glyph_id| {
164            let mut out_glyph = Glyph::default();
165            out_glyph.glyph_id = Some(glyph_id);
166            out_glyph.advance = PhysicalLength::new(
167                self.fontdue_font
168                    .metrics_indexed(glyph_id.get(), self.pixel_size.get() as _)
169                    .advance_width as _,
170            );
171            out_glyph
172        })
173    }
174
175    fn max_lines(&self, max_height: PhysicalLength) -> usize {
176        (max_height / self.height).get() as _
177    }
178}
179
180impl i_slint_core::textlayout::FontMetrics<PhysicalLength> for VectorFont {
181    fn ascent(&self) -> PhysicalLength {
182        self.ascender
183    }
184
185    fn height(&self) -> PhysicalLength {
186        self.height
187    }
188
189    fn descent(&self) -> PhysicalLength {
190        self.descender
191    }
192
193    fn x_height(&self) -> PhysicalLength {
194        self.x_height
195    }
196
197    fn cap_height(&self) -> PhysicalLength {
198        self.cap_height
199    }
200}
201
202impl super::GlyphRenderer for VectorFont {
203    fn render_glyph(&self, glyph_id: core::num::NonZeroU16) -> Option<super::RenderableGlyph> {
204        self.render_vector_glyph(glyph_id).map(|glyph| super::RenderableGlyph {
205            x: glyph.x,
206            y: glyph.y,
207            width: glyph.width,
208            height: glyph.height,
209            alpha_map: glyph.alpha_map.into(),
210            pixel_stride: glyph.pixel_stride,
211            sdf: false,
212        })
213    }
214
215    fn scale_delta(&self) -> super::Fixed<u16, 8> {
216        super::Fixed::from_integer(1)
217    }
218}