i_slint_renderer_software/
fonts.rs1use alloc::rc::Rc;
5use alloc::vec::Vec;
6use core::cell::RefCell;
7
8use super::{Fixed, PhysicalLength, PhysicalSize};
9use i_slint_core::Coord;
10use i_slint_core::graphics::{BitmapFont, FontRequest};
11use i_slint_core::lengths::{LogicalLength, ScaleFactor};
12use i_slint_core::textlayout::TextLayout;
13
14i_slint_core::thread_local! {
15 static BITMAP_FONTS: RefCell<Vec<&'static BitmapFont>> = RefCell::default()
16}
17
18#[derive(derive_more::From, Clone)]
19pub enum GlyphAlphaMap {
20 Static(&'static [u8]),
21 Shared(Rc<[u8]>),
22}
23
24#[derive(Clone)]
25pub struct RenderableGlyph {
26 pub x: Fixed<i32, 8>,
27 pub y: Fixed<i32, 8>,
28 pub width: PhysicalLength,
29 pub height: PhysicalLength,
30 pub alpha_map: GlyphAlphaMap,
31 pub pixel_stride: u16,
32 pub sdf: bool,
33}
34
35impl RenderableGlyph {
36 pub fn size(&self) -> PhysicalSize {
37 PhysicalSize::from_lengths(self.width, self.height)
38 }
39}
40
41#[cfg(feature = "systemfonts")]
43#[derive(Clone)]
44pub struct RenderableVectorGlyph {
45 pub x: Fixed<i32, 8>,
46 pub y: Fixed<i32, 8>,
47 pub width: PhysicalLength,
48 pub height: PhysicalLength,
49 pub alpha_map: Rc<[u8]>,
50 pub pixel_stride: u16,
51 pub bounds: fontdue::OutlineBounds,
52}
53
54#[cfg(feature = "systemfonts")]
55impl RenderableVectorGlyph {
56 pub fn size(&self) -> PhysicalSize {
57 PhysicalSize::from_lengths(self.width, self.height)
58 }
59}
60
61pub trait GlyphRenderer {
62 fn render_glyph(&self, glyph_id: core::num::NonZeroU16) -> Option<RenderableGlyph>;
63 fn scale_delta(&self) -> Fixed<u16, 8>;
65}
66
67pub(super) const DEFAULT_FONT_SIZE: LogicalLength = LogicalLength::new(12 as Coord);
68
69mod pixelfont;
70#[cfg(feature = "systemfonts")]
71pub mod vectorfont;
72
73#[cfg(feature = "systemfonts")]
74pub mod systemfonts;
75
76#[derive(derive_more::From)]
77pub enum Font {
78 PixelFont(pixelfont::PixelFont),
79 #[cfg(feature = "systemfonts")]
80 VectorFont(vectorfont::VectorFont),
81}
82
83pub fn pixel_size(glyphs: &i_slint_core::graphics::BitmapGlyphs) -> PhysicalLength {
85 PhysicalLength::new(glyphs.pixel_size)
86}
87
88impl i_slint_core::textlayout::FontMetrics<PhysicalLength> for Font {
89 fn ascent(&self) -> PhysicalLength {
90 match self {
91 Font::PixelFont(pixel_font) => pixel_font.ascent(),
92 #[cfg(feature = "systemfonts")]
93 Font::VectorFont(vector_font) => vector_font.ascent(),
94 }
95 }
96
97 fn height(&self) -> PhysicalLength {
98 match self {
99 Font::PixelFont(pixel_font) => pixel_font.height(),
100 #[cfg(feature = "systemfonts")]
101 Font::VectorFont(vector_font) => vector_font.height(),
102 }
103 }
104
105 fn descent(&self) -> PhysicalLength {
106 match self {
107 Font::PixelFont(pixel_font) => pixel_font.descent(),
108 #[cfg(feature = "systemfonts")]
109 Font::VectorFont(vector_font) => vector_font.descent(),
110 }
111 }
112
113 fn x_height(&self) -> PhysicalLength {
114 match self {
115 Font::PixelFont(pixel_font) => pixel_font.x_height(),
116 #[cfg(feature = "systemfonts")]
117 Font::VectorFont(vector_font) => vector_font.x_height(),
118 }
119 }
120
121 fn cap_height(&self) -> PhysicalLength {
122 match self {
123 Font::PixelFont(pixel_font) => pixel_font.cap_height(),
124 #[cfg(feature = "systemfonts")]
125 Font::VectorFont(vector_font) => vector_font.cap_height(),
126 }
127 }
128}
129
130pub fn match_font(request: &FontRequest, scale_factor: ScaleFactor) -> Font {
131 let requested_weight = request
132 .weight
133 .and_then(|weight| weight.try_into().ok())
134 .unwrap_or(400);
135
136 let bitmap_font = BITMAP_FONTS.with(|fonts| {
137 let fonts = fonts.borrow();
138
139 request.family.as_ref().and_then(|requested_family| {
140 fonts
141 .iter()
142 .filter(|bitmap_font| {
143 core::str::from_utf8(bitmap_font.family_name.as_slice()).unwrap()
144 == requested_family.as_str()
145 && bitmap_font.italic == request.italic
146 })
147 .min_by_key(|bitmap_font| bitmap_font.weight.abs_diff(requested_weight))
148 .copied()
149 })
150 });
151
152 let font = match bitmap_font {
153 Some(bitmap_font) => bitmap_font,
154 None => {
155 #[cfg(feature = "systemfonts")]
156 if let Some(vectorfont) = systemfonts::match_font(request, scale_factor) {
157 return vectorfont.into();
158 }
159 if let Some(fallback_bitmap_font) = BITMAP_FONTS.with(|fonts| {
160 let fonts = fonts.borrow();
161 fonts
162 .iter()
163 .cloned()
164 .filter(|bitmap_font| bitmap_font.italic == request.italic)
165 .min_by_key(|bitmap_font| bitmap_font.weight.abs_diff(requested_weight))
166 .or_else(|| fonts.first().cloned())
167 }) {
168 fallback_bitmap_font
169 } else {
170 #[cfg(feature = "systemfonts")]
171 return systemfonts::fallbackfont(request, scale_factor).into();
172 #[cfg(not(feature = "systemfonts"))]
173 panic!(
174 "No font fallback found. The software renderer requires enabling the `EmbedForSoftwareRenderer` option when compiling slint files."
175 )
176 }
177 }
178 };
179
180 let requested_pixel_size: PhysicalLength =
181 (request.pixel_size.unwrap_or(DEFAULT_FONT_SIZE).cast() * scale_factor).cast();
182
183 let nearest_pixel_size = font
184 .glyphs
185 .partition_point(|glyphs| pixel_size(glyphs) <= requested_pixel_size)
186 .saturating_sub(1);
187 let matching_glyphs = &font.glyphs[nearest_pixel_size];
188
189 let pixel_size = if font.sdf { requested_pixel_size } else { pixel_size(matching_glyphs) };
190
191 pixelfont::PixelFont { bitmap_font: font, glyphs: matching_glyphs, pixel_size }.into()
192}
193
194pub fn text_layout_for_font<'a, Font>(
195 font: &'a Font,
196 font_request: &FontRequest,
197 scale_factor: ScaleFactor,
198) -> TextLayout<'a, Font>
199where
200 Font: i_slint_core::textlayout::AbstractFont
201 + i_slint_core::textlayout::TextShaper<Length = PhysicalLength>,
202{
203 let letter_spacing =
204 font_request.letter_spacing.map(|spacing| (spacing.cast() * scale_factor).cast());
205
206 TextLayout { font, letter_spacing }
207}
208
209pub fn register_bitmap_font(font_data: &'static BitmapFont) {
210 BITMAP_FONTS.with(|fonts| fonts.borrow_mut().push(font_data))
211}