i_slint_renderer_software/fonts/
vectorfont.rs1use core::num::NonZeroU16;
5
6use alloc::rc::Rc;
7use alloc::vec::Vec;
8use skrifa::MetadataProvider;
9
10use crate::PhysicalLength;
11use crate::fixed::Fixed;
12use i_slint_common::sharedfontique::fontique;
13use i_slint_core::lengths::PhysicalPx;
14use i_slint_core::textlayout::{Glyph, TextShaper};
15
16use super::RenderableVectorGlyph;
17
18struct FontUnit;
20type FontLength = euclid::Length<i32, FontUnit>;
21type FontScaleFactor = euclid::Scale<f32, FontUnit, PhysicalPx>;
22
23type GlyphCacheKey = (u64, u32, PhysicalLength, core::num::NonZeroU16, u64);
26
27struct RenderableGlyphWeightScale;
28
29impl clru::WeightScale<GlyphCacheKey, RenderableVectorGlyph> for RenderableGlyphWeightScale {
30 fn weight(&self, _: &GlyphCacheKey, value: &RenderableVectorGlyph) -> usize {
31 value.alpha_map.len()
32 }
33}
34
35type GlyphCache = clru::CLruCache<
36 GlyphCacheKey,
37 RenderableVectorGlyph,
38 std::collections::hash_map::RandomState,
39 RenderableGlyphWeightScale,
40>;
41
42i_slint_core::thread_local!(static GLYPH_CACHE: core::cell::RefCell<GlyphCache> =
43 core::cell::RefCell::new(
44 clru::CLruCache::with_config(
45 clru::CLruCacheConfig::new(core::num::NonZeroUsize::new(1024 * 1024).unwrap())
46 .with_scale(RenderableGlyphWeightScale)
47 )
48 )
49);
50
51pub struct VectorFont {
52 font_index: u32,
53 font_blob: fontique::Blob<u8>,
54 swash_key: swash::CacheKey,
55 swash_offset: u32,
56 ascender: PhysicalLength,
57 descender: PhysicalLength,
58 height: PhysicalLength,
59 pixel_size: PhysicalLength,
60 x_height: PhysicalLength,
61 cap_height: PhysicalLength,
62 normalized_coords: Vec<i16>,
64 coords_hash: u64,
66}
67
68fn hash_coords(coords: &[i16]) -> u64 {
69 use core::hash::{Hash, Hasher};
70 let mut hasher = std::collections::hash_map::DefaultHasher::new();
71 coords.hash(&mut hasher);
72 hasher.finish()
73}
74
75impl VectorFont {
76 fn swash_font_ref(&self) -> swash::FontRef<'_> {
77 swash::FontRef {
78 data: self.font_blob.data(),
79 offset: self.swash_offset,
80 key: self.swash_key,
81 }
82 }
83
84 pub fn new(
85 font: fontique::QueryFont,
86 swash_key: swash::CacheKey,
87 swash_offset: u32,
88 pixel_size: PhysicalLength,
89 ) -> Self {
90 Self::new_from_blob_and_index(font.blob, font.index, swash_key, swash_offset, pixel_size)
91 }
92
93 pub fn new_from_blob_and_index(
94 font_blob: fontique::Blob<u8>,
95 font_index: u32,
96 swash_key: swash::CacheKey,
97 swash_offset: u32,
98 pixel_size: PhysicalLength,
99 ) -> Self {
100 Self::new_from_blob_and_index_with_coords(
101 font_blob,
102 font_index,
103 swash_key,
104 swash_offset,
105 pixel_size,
106 &[],
107 )
108 }
109
110 pub fn new_from_blob_and_index_with_coords(
111 font_blob: fontique::Blob<u8>,
112 font_index: u32,
113 swash_key: swash::CacheKey,
114 swash_offset: u32,
115 pixel_size: PhysicalLength,
116 normalized_coords: &[i16],
117 ) -> Self {
118 let face = skrifa::FontRef::from_index(font_blob.data(), font_index).unwrap();
119
120 let skrifa_coords: Vec<skrifa::instance::NormalizedCoord> = normalized_coords
121 .iter()
122 .map(|&c| skrifa::instance::NormalizedCoord::from_bits(c))
123 .collect();
124 let location = skrifa::instance::LocationRef::new(&skrifa_coords);
125
126 let metrics = face.metrics(skrifa::instance::Size::unscaled(), location);
127
128 let ascender = FontLength::new(metrics.ascent as _);
129 let descender = FontLength::new(metrics.descent as _);
130 let height = FontLength::new((metrics.ascent - metrics.descent) as _);
131 let x_height = FontLength::new(metrics.x_height.unwrap_or_default() as _);
132 let cap_height = FontLength::new(metrics.cap_height.unwrap_or_default() as _);
133 let units_per_em = metrics.units_per_em;
134 let scale = FontScaleFactor::new(pixel_size.get() as f32 / units_per_em as f32);
135 let coords_hash = hash_coords(normalized_coords);
136 Self {
137 font_index,
138 font_blob,
139 swash_key,
140 swash_offset,
141 ascender: (ascender.cast() * scale).cast(),
142 descender: (descender.cast() * scale).cast(),
143 height: (height.cast() * scale).cast(),
144 pixel_size,
145 x_height: (x_height.cast() * scale).cast(),
146 cap_height: (cap_height.cast() * scale).cast(),
147 normalized_coords: normalized_coords.to_vec(),
148 coords_hash,
149 }
150 }
151
152 pub fn render_vector_glyph(
153 &self,
154 glyph_id: core::num::NonZeroU16,
155 slint_context: &i_slint_core::SlintContext,
156 ) -> Option<RenderableVectorGlyph> {
157 GLYPH_CACHE.with(|cache| {
158 let mut cache = cache.borrow_mut();
159
160 let cache_key =
161 (self.font_blob.id(), self.font_index, self.pixel_size, glyph_id, self.coords_hash);
162
163 if let Some(entry) = cache.get(&cache_key) {
164 return Some(entry.clone());
165 }
166
167 let glyph = {
168 let font_ref = self.swash_font_ref();
169 let mut ctx = slint_context.swash_scale_context().borrow_mut();
170 let mut scaler = ctx
171 .builder(font_ref)
172 .size(self.pixel_size.get() as f32)
173 .normalized_coords(&self.normalized_coords)
174 .build();
175 let image = swash::scale::Render::new(&[swash::scale::Source::Outline])
176 .format(swash::zeno::Format::Alpha)
177 .render(&mut scaler, glyph_id.get())?;
178
179 let placement = image.placement;
180 let alpha_map: Rc<[u8]> = image.data.into();
181
182 Some(RenderableVectorGlyph {
183 x: Fixed::from_integer(placement.left),
184 y: Fixed::from_integer(placement.top - placement.height as i32),
185 width: PhysicalLength::new(placement.width.try_into().unwrap()),
186 height: PhysicalLength::new(placement.height.try_into().unwrap()),
187 alpha_map,
188 pixel_stride: placement.width.try_into().unwrap(),
189 glyph_origin_x: placement.left as f32,
190 })
191 };
192
193 if let Some(ref glyph) = glyph {
194 cache.put_with_weight(cache_key, glyph.clone()).ok();
195 }
196 glyph
197 })
198 }
199}
200
201impl TextShaper for VectorFont {
202 type LengthPrimitive = i16;
203 type Length = PhysicalLength;
204 fn shape_text<GlyphStorage: core::iter::Extend<Glyph<PhysicalLength>>>(
205 &self,
206 text: &str,
207 glyphs: &mut GlyphStorage,
208 ) {
209 let font_ref = self.swash_font_ref();
210 let charmap = font_ref.charmap();
211 let gm = font_ref.glyph_metrics(&[]);
212 let metrics = font_ref.metrics(&[]);
213 let scale = self.pixel_size.get() as f32 / metrics.units_per_em as f32;
214
215 glyphs.extend(text.char_indices().map(|(byte_offset, char)| {
216 let glyph_id = NonZeroU16::try_from(charmap.map(char)).ok();
217 let x_advance = glyph_id.map_or_else(
218 || self.pixel_size.get(),
219 |id| (gm.advance_width(id.get()) * scale) as _,
220 );
221
222 Glyph {
223 glyph_id,
224 advance: PhysicalLength::new(x_advance),
225 text_byte_offset: byte_offset,
226 ..Default::default()
227 }
228 }));
229 }
230
231 fn glyph_for_char(&self, ch: char) -> Option<Glyph<PhysicalLength>> {
232 let font_ref = self.swash_font_ref();
233 let charmap = font_ref.charmap();
234 let gm = font_ref.glyph_metrics(&[]);
235 let metrics = font_ref.metrics(&[]);
236 let scale = self.pixel_size.get() as f32 / metrics.units_per_em as f32;
237
238 NonZeroU16::try_from(charmap.map(ch)).ok().map(|glyph_id| Glyph {
239 glyph_id: Some(glyph_id),
240 advance: PhysicalLength::new((gm.advance_width(glyph_id.get()) * scale) as _),
241 ..Default::default()
242 })
243 }
244
245 fn max_lines(&self, max_height: PhysicalLength) -> usize {
246 (max_height / self.height).get() as _
247 }
248}
249
250impl i_slint_core::textlayout::FontMetrics<PhysicalLength> for VectorFont {
251 fn ascent(&self) -> PhysicalLength {
252 self.ascender
253 }
254
255 fn height(&self) -> PhysicalLength {
256 self.height
257 }
258
259 fn descent(&self) -> PhysicalLength {
260 self.descender
261 }
262
263 fn x_height(&self) -> PhysicalLength {
264 self.x_height
265 }
266
267 fn cap_height(&self) -> PhysicalLength {
268 self.cap_height
269 }
270}
271
272impl super::GlyphRenderer for VectorFont {
273 fn render_glyph(
274 &self,
275 glyph_id: core::num::NonZeroU16,
276 slint_context: &i_slint_core::SlintContext,
277 ) -> Option<super::RenderableGlyph> {
278 self.render_vector_glyph(glyph_id, slint_context).map(|glyph| super::RenderableGlyph {
279 x: glyph.x,
280 y: glyph.y,
281 width: glyph.width,
282 height: glyph.height,
283 alpha_map: glyph.alpha_map.into(),
284 pixel_stride: glyph.pixel_stride,
285 sdf: false,
286 })
287 }
288
289 fn scale_delta(&self) -> super::Fixed<u16, 8> {
290 super::Fixed::from_integer(1)
291 }
292}