1use std::sync::{Arc, Mutex, MutexGuard, PoisonError};
4
5use async_channel::{Receiver, Sender};
6use bevy_asset::prelude::*;
7use bevy_ecs::prelude::*;
8use bevy_image::prelude::*;
9use bevy_math::prelude::*;
10use bevy_tasks::IoTaskPool;
11use bevy_utils::HashMap;
12use cosmic_text::{
13 Attrs, Buffer, CacheKey, Family, FontSystem, Metrics, Shaping, SwashCache,
14 fontdb::{Database, Source},
15 ttf_parser::{Face, FaceParsingError},
16};
17use scopeguard::{Always, ScopeGuard};
18use thiserror::Error;
19
20use crate::{
21 atlas::{FontAtlas, FontAtlasKey, FontAtlases},
22 def::{Font, TextAlign, TextFont, TextGlyph, TextGlyphs, TextWrap},
23};
24
25#[derive(Resource)]
27pub struct FontLayout(pub(crate) Mutex<FontLayoutInner>);
28impl FontLayout {
29 #[inline]
31 pub fn get(&self) -> MutexGuard<FontLayoutInner> {
32 self.0.lock().unwrap_or_else(PoisonError::into_inner)
33 }
34
35 #[inline]
37 pub fn get_mut(&mut self) -> &mut FontLayoutInner {
38 self.0.get_mut().unwrap_or_else(PoisonError::into_inner)
39 }
40}
41
42pub struct FontLayoutInner {
44 sys: FontSystem,
45 cache: SwashCache,
46 pending_fonts: Receiver<(Vec<u8>, Sender<Result<Font, FaceParsingError>>)>,
47 font_atlases: HashMap<AssetId<Font>, FontAtlases>,
48 spans: Vec<(&'static str, &'static TextFont)>,
49 glyph_spans: Vec<(AssetId<Font>, FontAtlasKey)>,
50}
51
52impl FontLayoutInner {
53 #[inline]
55 pub(crate) fn new(pending_fonts: Receiver<(Vec<u8>, Sender<Result<Font, FaceParsingError>>)>) -> Self {
56 let locale = sys_locale::get_locale().unwrap_or("en-US".into());
57 Self {
58 sys: FontSystem::new_with_locale_and_db(locale, Database::new()),
59 cache: SwashCache::new(),
60 pending_fonts,
61 font_atlases: HashMap::new(),
62 spans: Vec::new(),
63 glyph_spans: Vec::new(),
64 }
65 }
66}
67
68pub fn load_fonts_to_database(mut fonts: ResMut<FontLayout>) {
71 let fonts = fonts.get_mut();
72 while let Ok((bytes, sender)) = fonts.pending_fonts.try_recv() {
73 if let Err(e) = Face::parse(&bytes, 0) {
74 IoTaskPool::get().spawn(async move { _ = sender.send(Err(e)).await }).detach();
75 continue
76 }
77
78 let src = Arc::new(bytes.into_boxed_slice());
80 let id = fonts.sys.db_mut().load_font_source(Source::Binary(src))[0];
81
82 let info = fonts.sys.db().face(id).unwrap();
83 let name = info
84 .families
85 .first()
86 .map(|(name, _)| name)
87 .cloned()
88 .unwrap_or("Times New Roman".into());
89 let style = info.style;
90 let weight = info.weight;
91 let stretch = info.stretch;
92
93 IoTaskPool::get()
94 .spawn(async move {
95 _ = sender
96 .send(Ok(Font {
97 id,
98 name,
99 style,
100 weight,
101 stretch,
102 }))
103 .await
104 })
105 .detach()
106 }
107}
108
109#[derive(Error, Debug)]
111pub enum FontLayoutError {
112 #[error("required font hasn't been loaded yet or has failed loading")]
114 FontNotLoaded(AssetId<Font>),
115 #[error("couldn't render an image for a glyph")]
117 NoGlyphImage(CacheKey),
118}
119
120impl FontLayoutInner {
121 pub fn compute_glyphs<'a>(
123 &mut self,
124 glyphs: &mut TextGlyphs,
125 (width, height): (Option<f32>, Option<f32>),
126 wrap: TextWrap,
127 align: TextAlign,
128 scale_factor: f32,
129 fonts: &Assets<Font>,
130 images: &mut Assets<Image>,
131 atlases: &mut Assets<FontAtlas>,
132 spans: impl Iterator<Item = (&'a str, &'a TextFont)>,
133 ) -> Result<(), FontLayoutError> {
134 glyphs.size = Vec2::ZERO;
135 glyphs.glyphs.clear();
136
137 let mut glyph_spans = std::mem::take(&mut self.glyph_spans);
138 let spans = spans.inspect(|&(.., font)| {
139 glyph_spans.push((font.font.id(), FontAtlasKey {
140 font_size: font.font_size.to_bits(),
141 antialias: font.antialias,
142 }))
143 });
144
145 let buffer = glyphs.buffer.get_mut().unwrap_or_else(PoisonError::into_inner);
146 if let Err(e) = self.update_buffer(buffer, (width, height), wrap, align, scale_factor, fonts, spans) {
147 glyph_spans.clear();
148 self.glyph_spans = glyph_spans;
149
150 return Err(e);
151 }
152
153 let buffer_size = buffer_size(buffer);
154 if let Err::<(), FontLayoutError>(e) = buffer
155 .layout_runs()
156 .flat_map(|run| run.glyphs.iter().map(move |glyph| (glyph, run.line_y)))
157 .try_for_each(|(glyph, line)| {
158 let (id, key) = glyph_spans[glyph.metadata];
159
160 let mut tmp;
161 let glyph = if !key.antialias {
162 tmp = glyph.clone();
163 tmp.x = tmp.x.round();
164 tmp.y = tmp.y.round();
165 tmp.w = tmp.w.round();
166 tmp.x_offset = tmp.x_offset.round();
167 tmp.y_offset = tmp.y_offset.round();
168 tmp.line_height_opt = tmp.line_height_opt.map(f32::round);
169 &tmp
170 } else {
171 glyph
172 };
173
174 let atlas_set = self.font_atlases.entry(id).or_default();
175 let phys = glyph.physical((0., 0.), 1.);
176 let (atlas_id, atlas) = atlas_set.atlas_mut(key, atlases);
177
178 let (offset, rect, index) = atlas.get_or_create_info(&mut self.sys, &mut self.cache, glyph, images)?;
179 let size = (rect.max - rect.min).as_vec2();
180 let (top, left) = (offset.y as f32, offset.x as f32);
181
182 let x = left + phys.x as f32;
183 let y = buffer_size.y - (line.round() + phys.y as f32 - (top - size.y));
184
185 glyphs.glyphs.push(TextGlyph {
186 origin: Vec2::new(x, y),
187 size,
188 atlas: atlas_id,
189 index,
190 });
191
192 Ok(())
193 })
194 {
195 glyphs.glyphs.clear();
196 glyph_spans.clear();
197 self.glyph_spans = glyph_spans;
198
199 return Err(e);
200 }
201
202 glyph_spans.clear();
203 self.glyph_spans = glyph_spans;
204
205 glyphs.size = buffer_size;
206 Ok(())
207 }
208
209 #[inline]
211 pub fn measure_glyphs<'a>(
212 &mut self,
213 glyphs: &TextGlyphs,
214 (width, height): (Option<f32>, Option<f32>),
215 wrap: TextWrap,
216 align: TextAlign,
217 scale_factor: f32,
218 fonts: &Assets<Font>,
219 spans: impl Iterator<Item = (&'a str, &'a TextFont)>,
220 ) -> Result<Vec2, FontLayoutError> {
221 let mut buffer = glyphs.buffer.lock().unwrap_or_else(PoisonError::into_inner);
222 self.update_buffer(&mut buffer, (width, height), wrap, align, scale_factor, fonts, spans)?;
223
224 Ok(buffer_size(&buffer))
225 }
226
227 #[inline]
229 pub fn size(&self, glyphs: &TextGlyphs) -> Vec2 {
230 buffer_size(&glyphs.buffer.lock().unwrap_or_else(PoisonError::into_inner))
231 }
232
233 fn update_buffer<'a>(
234 &mut self,
235 buffer: &mut Buffer,
236 (width, height): (Option<f32>, Option<f32>),
237 wrap: TextWrap,
238 align: TextAlign,
239 scale_factor: f32,
240 fonts: &Assets<Font>,
241 spans: impl Iterator<Item = (&'a str, &'a TextFont)>,
242 ) -> Result<(), FontLayoutError> {
243 #[inline]
253 #[allow(unsafe_op_in_unsafe_fn)]
254 unsafe fn guard<'a, 'this: 'a>(
255 spans: &'this mut Vec<(&'static str, &'static TextFont)>,
256 ) -> ScopeGuard<&'this mut Vec<(&'a str, &'a TextFont)>, fn(&mut Vec<(&'a str, &'a TextFont)>), Always> {
257 ScopeGuard::with_strategy(
259 std::mem::transmute::<
260 &'this mut Vec<(&'static str, &'static TextFont)>,
261 &'this mut Vec<(&'a str, &'a TextFont)>,
262 >(spans),
263 Vec::clear,
264 )
265 }
266
267 let spans_vec = &mut **unsafe { guard(&mut self.spans) };
269 let sys = &mut self.sys;
270
271 let mut font_size = f32::MIN_POSITIVE;
272 for (span, font) in spans {
273 if span.is_empty() || font.font_size <= 0. || font.line_height <= 0. {
274 continue;
275 }
276
277 if !fonts.contains(&font.font) {
278 return Err(FontLayoutError::FontNotLoaded(font.font.id()));
279 }
280
281 font_size = font_size.max(font.font_size);
282 spans_vec.push((span, font));
283 }
284
285 let mut buffer = buffer.borrow_with(sys);
286 buffer.lines.clear();
287 buffer.set_metrics_and_size(Metrics::relative(font_size, 1.2).scale(scale_factor), width, height);
288 buffer.set_wrap(wrap.into());
289 buffer.set_rich_text(
290 spans_vec.iter().enumerate().map(|(span_index, &(span, font))| {
291 let info = fonts.get(&font.font).unwrap();
293 (
294 span,
295 Attrs::new()
296 .family(Family::Name(&info.name))
297 .stretch(info.stretch)
298 .style(info.style)
299 .weight(info.weight)
300 .metadata(span_index)
301 .metrics(Metrics::relative(font.font_size, font.line_height)),
302 )
303 }),
304 Attrs::new(),
305 Shaping::Advanced,
306 Some(align.into()),
307 );
308
309 buffer.shape_until_scroll(false);
310 Ok(())
311 }
312}
313
314fn buffer_size(buffer: &Buffer) -> Vec2 {
315 let (width, height) = buffer
316 .layout_runs()
317 .map(|run| (run.line_w, run.line_height))
318 .reduce(|(w1, h1), (w2, h2)| (w1.max(w2), h1 + h2))
319 .unwrap_or((0., 0.));
320
321 Vec2::new(width, height).ceil()
322}