1#![allow(clippy::unnecessary_to_owned)] use crate::core::{
24 algebra::Vector2, rectpack::RectPacker, reflect::prelude::*, uuid::Uuid, uuid_provider,
25 visitor::prelude::*, TypeUuidProvider,
26};
27use fxhash::FxHashMap;
28use fyrox_core::math::Rect;
29use fyrox_resource::{
30 embedded_data_source, io::ResourceIo, manager::BuiltInResource, untyped::UntypedResource,
31 Resource, ResourceData,
32};
33use lazy_static::lazy_static;
34use std::{
35 error::Error,
36 fmt::{Debug, Formatter},
37 hash::{Hash, Hasher},
38 ops::Deref,
39 path::Path,
40};
41
42pub mod loader;
43
44#[derive(Debug)]
45pub struct FontGlyph {
46 pub bitmap_top: f32,
47 pub bitmap_left: f32,
48 pub bitmap_width: f32,
49 pub bitmap_height: f32,
50 pub advance: f32,
51 pub tex_coords: [Vector2<f32>; 4],
52 pub page_index: usize,
53 pub bounds: Rect<f32>,
54}
55
56pub struct Page {
58 pub pixels: Vec<u8>,
59 pub texture: Option<UntypedResource>,
60 pub rect_packer: RectPacker<usize>,
61 pub modified: bool,
62}
63
64impl Debug for Page {
65 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
66 f.debug_struct("Page")
67 .field("Pixels", &self.pixels)
68 .field("Texture", &self.texture)
69 .field("Modified", &self.modified)
70 .finish()
71 }
72}
73
74#[derive(Default, Debug)]
77pub struct Atlas {
78 pub glyphs: Vec<FontGlyph>,
79 pub char_map: FxHashMap<char, usize>,
80 pub pages: Vec<Page>,
81}
82
83impl Atlas {
84 fn glyph(
85 &mut self,
86 font: &fontdue::Font,
87 unicode: char,
88 height: FontHeight,
89 page_size: usize,
90 ) -> Option<&FontGlyph> {
91 let border = 2;
92
93 match self.char_map.get(&unicode) {
94 Some(glyph_index) => self.glyphs.get(*glyph_index),
95 None => {
96 if let Some(char_index) = font.chars().get(&unicode) {
100 let (metrics, glyph_raster) =
101 font.rasterize_indexed(char_index.get(), height.0);
102
103 let mut placement_info =
106 self.pages
107 .iter_mut()
108 .enumerate()
109 .find_map(|(page_index, page)| {
110 page.rect_packer
111 .find_free(metrics.width + border, metrics.height + border)
112 .map(|bounds| (page_index, bounds))
113 });
114
115 if placement_info.is_none() {
117 let mut page = Page {
118 pixels: vec![0; page_size * page_size],
119 texture: None,
120 rect_packer: RectPacker::new(page_size, page_size),
121 modified: true,
122 };
123
124 let page_index = self.pages.len();
125
126 match page
127 .rect_packer
128 .find_free(metrics.width + border, metrics.height + border)
129 {
130 Some(bounds) => {
131 placement_info = Some((page_index, bounds));
132
133 self.pages.push(page);
134 }
135 None => {
136 return None;
138 }
139 }
140 }
141
142 let (page_index, placement_rect) = placement_info?;
143 let page = &mut self.pages[page_index];
144 let glyph_index = self.glyphs.len();
145
146 page.modified = true;
149
150 let mut glyph = FontGlyph {
151 bitmap_left: metrics.xmin as f32,
152 bitmap_top: metrics.ymin as f32,
153 advance: metrics.advance_width,
154 tex_coords: Default::default(),
155 bitmap_width: metrics.width as f32,
156 bitmap_height: metrics.height as f32,
157 bounds: Rect::new(
158 metrics.bounds.xmin,
159 metrics.bounds.ymin,
160 metrics.bounds.width,
161 metrics.bounds.height,
162 ),
163 page_index,
164 };
165
166 let k = 1.0 / page_size as f32;
167
168 let bw = placement_rect.w().saturating_sub(border);
169 let bh = placement_rect.h().saturating_sub(border);
170 let bx = placement_rect.x() + border / 2;
171 let by = placement_rect.y() + border / 2;
172
173 let tw = bw as f32 * k;
174 let th = bh as f32 * k;
175 let tx = bx as f32 * k;
176 let ty = by as f32 * k;
177
178 glyph.tex_coords[0] = Vector2::new(tx, ty);
179 glyph.tex_coords[1] = Vector2::new(tx + tw, ty);
180 glyph.tex_coords[2] = Vector2::new(tx + tw, ty + th);
181 glyph.tex_coords[3] = Vector2::new(tx, ty + th);
182
183 let row_end = by + bh;
184 let col_end = bx + bw;
185
186 for (src_row, row) in (by..row_end).enumerate() {
188 for (src_col, col) in (bx..col_end).enumerate() {
189 page.pixels[row * page_size + col] =
190 glyph_raster[src_row * bw + src_col];
191 }
192 }
193
194 self.glyphs.push(glyph);
195
196 self.char_map.insert(unicode, glyph_index);
198
199 self.glyphs.get(glyph_index)
200 } else {
201 None
202 }
203 }
204 }
205 }
206}
207
208#[derive(Default, Debug, Reflect, Visit)]
209#[reflect(hide_all)]
210pub struct Font {
211 #[visit(skip)]
212 pub inner: Option<fontdue::Font>,
213 #[visit(skip)]
214 pub atlases: FxHashMap<FontHeight, Atlas>,
215 #[visit(skip)]
216 pub page_size: usize,
217}
218
219uuid_provider!(Font = "692fec79-103a-483c-bb0b-9fc3a349cb48");
220
221impl ResourceData for Font {
222 fn type_uuid(&self) -> Uuid {
223 <Self as TypeUuidProvider>::type_uuid()
224 }
225
226 fn save(&mut self, _path: &Path) -> Result<(), Box<dyn Error>> {
227 Ok(())
228 }
229
230 fn can_be_saved(&self) -> bool {
231 false
232 }
233}
234
235#[derive(Copy, Clone, Default, Debug)]
236pub struct FontHeight(pub f32);
237
238impl From<f32> for FontHeight {
239 fn from(value: f32) -> Self {
240 Self(value)
241 }
242}
243
244impl PartialEq for FontHeight {
245 fn eq(&self, other: &Self) -> bool {
246 fyrox_core::value_as_u8_slice(&self.0) == fyrox_core::value_as_u8_slice(&other.0)
247 }
248}
249
250impl Eq for FontHeight {}
251
252impl Hash for FontHeight {
253 fn hash<H: Hasher>(&self, state: &mut H) {
254 fyrox_core::hash_as_bytes(&self.0, state)
257 }
258}
259
260pub type FontResource = Resource<Font>;
261
262lazy_static! {
263 pub static ref BUILT_IN_FONT: BuiltInResource<Font> =
264 BuiltInResource::new(embedded_data_source!("./built_in_font.ttf"), |data| {
265 FontResource::new_ok(
266 "__BUILT_IN_FONT__".into(),
267 Font::from_memory(data.to_vec(), 1024).unwrap(),
268 )
269 });
270}
271
272impl Font {
273 pub fn from_memory(
274 data: impl Deref<Target = [u8]>,
275 page_size: usize,
276 ) -> Result<Self, &'static str> {
277 let fontdue_font = fontdue::Font::from_bytes(data, fontdue::FontSettings::default())?;
278 Ok(Font {
279 inner: Some(fontdue_font),
280 atlases: Default::default(),
281 page_size,
282 })
283 }
284
285 pub async fn from_file<P: AsRef<Path>>(
286 path: P,
287 page_size: usize,
288 io: &dyn ResourceIo,
289 ) -> Result<Self, &'static str> {
290 if let Ok(file_content) = io.load_file(path.as_ref()).await {
291 Self::from_memory(file_content, page_size)
292 } else {
293 Err("Unable to read file")
294 }
295 }
296
297 #[inline]
305 pub fn glyph(&mut self, unicode: char, height: f32) -> Option<&FontGlyph> {
306 self.atlases
307 .entry(FontHeight(height))
308 .or_insert_with(|| Atlas {
309 glyphs: Default::default(),
310 char_map: Default::default(),
311 pages: Default::default(),
312 })
313 .glyph(
314 self.inner
315 .as_ref()
316 .expect("Font reader must be initialized!"),
317 unicode,
318 FontHeight(height),
319 self.page_size,
320 )
321 }
322
323 #[inline]
324 pub fn ascender(&self, height: f32) -> f32 {
325 self.inner
326 .as_ref()
327 .unwrap()
328 .horizontal_line_metrics(height)
329 .map(|m| m.ascent)
330 .unwrap_or_default()
331 }
332
333 #[inline]
334 pub fn descender(&self, height: f32) -> f32 {
335 self.inner
336 .as_ref()
337 .unwrap()
338 .horizontal_line_metrics(height)
339 .map(|m| m.descent)
340 .unwrap_or_default()
341 }
342
343 #[inline]
344 pub fn horizontal_kerning(&self, height: f32, left: char, right: char) -> Option<f32> {
345 self.inner
346 .as_ref()
347 .unwrap()
348 .horizontal_kern(left, right, height)
349 }
350
351 #[inline]
352 pub fn page_size(&self) -> usize {
353 self.page_size
354 }
355
356 #[inline]
357 pub fn glyph_advance(&mut self, unicode: char, height: f32) -> f32 {
358 self.glyph(unicode, height)
359 .map_or(height, |glyph| glyph.advance)
360 }
361}
362
363pub struct FontBuilder {
365 page_size: usize,
366}
367
368impl FontBuilder {
369 pub fn new() -> Self {
371 Self { page_size: 1024 }
372 }
373
374 pub async fn build_from_file(
376 self,
377 path: impl AsRef<Path>,
378 io: &dyn ResourceIo,
379 ) -> Result<Font, &'static str> {
380 Font::from_file(path, self.page_size, io).await
381 }
382
383 pub fn build_from_memory(self, data: impl Deref<Target = [u8]>) -> Result<Font, &'static str> {
385 Font::from_memory(data, self.page_size)
386 }
387}