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