1use crate::context::{ImageId, TextMetrics};
2use crate::renderer::TextureType;
3use crate::{Align, Bounds, Extent, ImageFlags, Renderer};
4use bitflags::_core::borrow::Borrow;
5use rusttype::gpu_cache::Cache;
6use rusttype::{Font, Glyph, Point, PositionedGlyph, Scale};
7use slab::Slab;
8use std::collections::HashMap;
9
10const TEX_WIDTH: usize = 1024;
11const TEX_HEIGHT: usize = 1024;
12
13pub type FontId = usize;
14
15#[derive(Debug)]
16pub struct LayoutChar {
17 id: FontId,
18 pub x: f32,
19 pub next_x: f32,
20 pub c: char,
21 pub idx: usize,
22 glyph: PositionedGlyph<'static>,
23 pub uv: Bounds,
24 pub bounds: Bounds,
25}
26
27struct FontData {
28 font: Font<'static>,
29 fallback_fonts: Vec<FontId>,
30}
31
32pub struct Fonts {
33 fonts: Slab<FontData>,
34 fonts_by_name: HashMap<String, FontId>,
35 cache: Cache<'static>,
36 pub(crate) img: ImageId,
37}
38
39impl Fonts {
40 pub fn new<R: Renderer>(renderer: &mut R) -> anyhow::Result<Fonts> {
41 Ok(Fonts {
42 fonts: Default::default(),
43 fonts_by_name: Default::default(),
44 img: renderer.create_texture(
45 TextureType::Alpha,
46 TEX_WIDTH,
47 TEX_HEIGHT,
48 ImageFlags::empty(),
49 None,
50 )?,
51 cache: Cache::builder()
52 .multithread(true)
53 .dimensions(TEX_WIDTH as u32, TEX_HEIGHT as u32)
54 .build(),
55 })
56 }
57
58 pub fn add_font<N: Into<String>, D: Into<Vec<u8>>>(
59 &mut self,
60 name: N,
61 data: D,
62 ) -> anyhow::Result<FontId> {
63 let font = Font::<'static>::from_bytes(data.into())?;
64 let fd = FontData {
65 font,
66 fallback_fonts: Default::default(),
67 };
68 let id = self.fonts.insert(fd);
69 self.fonts_by_name.insert(name.into(), id);
70 Ok(id)
71 }
72
73 pub fn find<N: Borrow<str>>(&self, name: N) -> Option<FontId> {
74 self.fonts_by_name.get(name.borrow()).map(ToOwned::to_owned)
75 }
76
77 pub fn add_fallback(&mut self, base: FontId, fallback: FontId) {
78 if let Some(fd) = self.fonts.get_mut(base) {
79 fd.fallback_fonts.push(fallback);
80 }
81 }
82
83 fn glyph(&self, id: FontId, c: char) -> Option<(FontId, Glyph<'static>)> {
84 if let Some(fd) = self.fonts.get(id) {
85 let glyph = fd.font.glyph(c);
86 if glyph.id().0 != 0 {
87 Some((id, glyph))
88 } else {
89 for id in &fd.fallback_fonts {
90 if let Some(fd) = self.fonts.get(*id) {
91 let glyph = fd.font.glyph(c);
92 if glyph.id().0 != 0 {
93 return Some((*id, glyph));
94 }
95 }
96 }
97 None
98 }
99 } else {
100 None
101 }
102 }
103
104 fn render_texture<R: Renderer>(&mut self, renderer: &mut R) -> anyhow::Result<()> {
105 let img = self.img.clone();
106 self.cache.cache_queued(move |rect, data| {
107 renderer
108 .update_texture(
109 img.clone(),
110 rect.min.x as usize,
111 rect.min.y as usize,
112 (rect.max.x - rect.min.x) as usize,
113 (rect.max.y - rect.min.y) as usize,
114 data,
115 )
116 .unwrap();
117 })?;
118 Ok(())
119 }
120
121 pub fn text_metrics(&self, id: FontId, size: f32) -> TextMetrics {
122 if let Some(fd) = self.fonts.get(id) {
123 let scale = Scale::uniform(size);
124 let v_metrics = fd.font.v_metrics(scale);
125 TextMetrics {
126 ascender: v_metrics.descent,
127 descender: v_metrics.descent,
128 line_gap: v_metrics.line_gap,
129 }
130 } else {
131 TextMetrics {
132 ascender: 0.0,
133 descender: 0.0,
134 line_gap: 0.0,
135 }
136 }
137 }
138
139 pub fn text_size(&self, text: &str, id: FontId, size: f32, spacing: f32) -> Extent {
140 if let Some(fd) = self.fonts.get(id) {
141 let scale = Scale::uniform(size);
142 let v_metrics = fd.font.v_metrics(scale);
143 let mut extent = Extent::new(
144 0.0,
145 v_metrics.ascent - v_metrics.descent + v_metrics.line_gap,
146 );
147 let mut last_glyph = None;
148 let mut char_count = 0;
149
150 for c in text.chars() {
151 if let Some((_, glyph)) = self.glyph(id, c) {
152 let glyph = glyph.scaled(scale);
153 let h_metrics = glyph.h_metrics();
154 extent.width += h_metrics.advance_width;
155
156 if let Some(last_glyph) = last_glyph {
157 extent.width += fd.font.pair_kerning(scale, last_glyph, glyph.id());
158 }
159
160 last_glyph = Some(glyph.id());
161 char_count += 1;
162 }
163 }
164
165 if char_count >= 2 {
166 extent.width += spacing * (char_count - 1) as f32;
167 }
168
169 extent
170 } else {
171 Default::default()
172 }
173 }
174
175 pub fn layout_text<R: Renderer>(
176 &mut self,
177 renderer: &mut R,
178 text: &str,
179 id: FontId,
180 position: crate::Point,
181 size: f32,
182 align: Align,
183 spacing: f32,
184 cache: bool,
185 result: &mut Vec<LayoutChar>,
186 ) -> anyhow::Result<()> {
187 result.clear();
188
189 if let Some(fd) = self.fonts.get(id) {
190 let mut offset = Point { x: 0.0, y: 0.0 };
191 let scale = Scale::uniform(size);
192 let v_metrics = fd.font.v_metrics(scale);
193
194 let sz = if align.contains(Align::CENTER)
195 || align.contains(Align::RIGHT)
196 || align.contains(Align::MIDDLE)
197 {
198 self.text_size(text, id, size, spacing)
199 } else {
200 Extent::new(0.0, 0.0)
201 };
202
203 if align.contains(Align::CENTER) {
204 offset.x -= sz.width / 2.0;
205 } else if align.contains(Align::RIGHT) {
206 offset.x -= sz.width;
207 }
208
209 if align.contains(Align::MIDDLE) {
210 offset.y = v_metrics.descent + sz.height / 2.0;
211 } else if align.contains(Align::BOTTOM) {
212 offset.y = v_metrics.descent;
213 } else if align.contains(Align::TOP) {
214 offset.y = v_metrics.ascent;
215 }
216
217 let mut position = Point {
218 x: position.x + offset.x,
219 y: position.y + offset.y,
220 };
221 let mut last_glyph = None;
222
223 for (idx, c) in text.chars().enumerate() {
224 if let Some((id, glyph)) = self.glyph(id, c) {
225 let g = glyph.scaled(scale);
226 let h_metrics = g.h_metrics();
227
228 let glyph = g.positioned(Point {
229 x: position.x,
230 y: position.y,
231 });
232
233 let mut next_x = position.x + h_metrics.advance_width;
234 if let Some(last_glyph) = last_glyph {
235 next_x += fd.font.pair_kerning(scale, last_glyph, glyph.id());
236 }
237
238 if let Some(bb) = glyph.pixel_bounding_box() {
239 self.cache.queue_glyph(id, glyph.clone());
240
241 result.push(LayoutChar {
242 id,
243 idx,
244 c,
245 x: position.x,
246 next_x,
247 glyph: glyph.clone(),
248 uv: Default::default(),
249 bounds: Bounds {
250 min: (bb.min.x, bb.min.y).into(),
251 max: (bb.max.x, bb.max.y).into(),
252 },
253 });
254 }
255
256 position.x = next_x;
257 last_glyph = Some(glyph.id());
258 }
259 }
260
261 if cache {
262 self.render_texture(renderer)?;
263
264 for lc in result {
265 if let Ok(Some((uv, _))) = self.cache.rect_for(lc.id, &lc.glyph) {
266 lc.uv = Bounds {
267 min: crate::Point {
268 x: uv.min.x,
269 y: uv.min.y,
270 },
271 max: crate::Point {
272 x: uv.max.x,
273 y: uv.max.y,
274 },
275 };
276 }
277 }
278 }
279 }
280
281 Ok(())
282 }
283}