i_slint_renderer_software/fonts/
vectorfont.rs1use 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
17struct 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}