1#![deny(unsafe_code)]
41
42use std::{cell::RefCell, collections::HashMap, ops::Deref, path::Path};
43
44use fontdue::{FontResult, FontSettings};
45use macroquad::prelude::{
46 draw_texture_ex, vec2, Color, DrawTextureParams, FilterMode, Image, TextDimensions,
47};
48
49use crate::{
50 atlas::Atlas,
51 misc::{read_file, IoError, IoErrorKind, IoResult},
52};
53
54pub(crate) mod atlas;
55pub(crate) mod misc;
56
57pub type ScalingMode = FilterMode;
58pub type FontdueFont = fontdue::Font;
59
60#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
64pub enum DrawFrom {
65 BottomLeft,
67 TopLeft,
71}
72
73impl Default for DrawFrom {
74 fn default() -> Self {
75 Self::TopLeft
76 }
77}
78
79#[derive(Default, Debug, Copy, Clone, PartialEq, PartialOrd)]
80pub(crate) struct CharacterInfo {
81 pub id: u64,
82 pub offset_x: f32,
83 pub offset_y: f32,
84 pub advance: f32,
85}
86
87#[derive(Debug, Copy, Clone, PartialEq)]
89pub struct TextParams<'a> {
90 pub text: &'a str,
92 pub x: f32,
94 pub y: f32,
96 pub size: u16,
98 pub color: Color,
100 pub draw: DrawFrom,
102}
103
104impl<'a> Default for TextParams<'a> {
105 fn default() -> Self {
106 Self {
107 text: "",
108 x: 0.0,
109 y: 0.0,
110 size: 22,
111 color: Color::from_rgba(255, 255, 255, 255),
112 draw: DrawFrom::TopLeft,
113 }
114 }
115}
116
117#[derive(Debug)]
119pub struct Font<'a> {
120 pub name: &'a str,
121 font: FontdueFont,
122 atlas: RefCell<Atlas>,
123 chars: RefCell<HashMap<(char, u16), CharacterInfo>>,
124}
125
126impl<'a> Deref for Font<'a> {
127 type Target = FontdueFont;
128
129 fn deref(&self) -> &Self::Target {
130 &self.font
131 }
132}
133
134impl<'a> Font<'a> {
135 fn new(name: &'a str, font: FontdueFont, mode: ScalingMode) -> Self {
137 Self {
138 name,
139 font,
140 atlas: RefCell::new(Atlas::new(mode)),
141 chars: RefCell::default(),
142 }
143 }
144
145 pub fn contains(&self, c: char) -> bool {
147 self.lookup_glyph_index(c) != 0
148 }
149
150 fn _cache_glyph(&self, c: char, size: u16) -> CharacterInfo {
151 let (matrix, bitmap) = self.rasterize(c, size as f32);
152 let (width, height) = (matrix.width as u16, matrix.height as u16);
153
154 let id = self.atlas.borrow_mut().new_unique_id();
155 let bytes = bitmap
156 .iter()
157 .flat_map(|coverage| vec![255, 255, 255, *coverage])
158 .collect::<Vec<_>>();
159
160 self.atlas.borrow_mut().cache_sprite(
161 id,
162 Image {
163 width,
164 height,
165 bytes,
166 },
167 );
168
169 CharacterInfo {
170 id,
171 offset_x: matrix.xmin as f32,
172 offset_y: matrix.ymin as f32,
173 advance: matrix.advance_width,
174 }
175 }
176
177 pub fn cache_glyph(&self, c: char, size: u16) {
181 if !self.chars.borrow().contains_key(&(c, size)) {
182 let info = self._cache_glyph(c, size);
183
184 self.chars.borrow_mut().insert((c, size), info);
185 }
186 }
187
188 pub fn recache_glyphs(&self) {
192 for ((c, size), info) in self.chars.borrow_mut().iter_mut() {
193 *info = self._cache_glyph(*c, *size);
194 }
195 }
196}
197
198#[derive(Debug)]
199pub struct Fonts<'a> {
200 fonts: Vec<Font<'a>>,
201 index_by_name: HashMap<&'a str, usize>,
202 default_sm: ScalingMode,
203}
204
205impl<'a> Default for Fonts<'a> {
206 fn default() -> Self {
210 Self::new(ScalingMode::Linear)
211 }
212}
213
214impl<'a> Fonts<'a> {
215 pub fn new(default_sm: ScalingMode) -> Self {
230 Self {
231 fonts: Vec::default(),
232 index_by_name: HashMap::default(),
233 default_sm,
234 }
235 }
236
237 pub fn fonts(&self) -> &Vec<Font> {
240 &self.fonts
241 }
242
243 pub fn cache_glyph(&self, c: char, size: u16) {
247 for font in self.fonts.iter() {
248 font.cache_glyph(c, size);
249 }
250 }
251
252 pub fn load_font_from_bytes_with_scale(
265 &mut self,
266 name: &'a str,
267 bytes: &[u8],
268 scale: f32,
269 ) -> FontResult<()> {
270 let settings = FontSettings {
271 collection_index: 0,
272 scale,
273 };
274 let font = FontdueFont::from_bytes(bytes, settings)?;
275
276 self.index_by_name.insert(name, self.fonts.len());
277 self.fonts.push(Font::new(name, font, self.default_sm));
278
279 Ok(())
280 }
281
282 pub fn load_font_from_bytes(&mut self, name: &'a str, bytes: &[u8]) -> FontResult<()> {
286 self.load_font_from_bytes_with_scale(name, bytes, 100.0)
287 }
288
289 pub fn load_font_from_file(&mut self, name: &'a str, path: impl AsRef<Path>) -> IoResult<()> {
293 self.load_font_from_file_with_scale(name, path, 100.0)
294 }
295
296 pub fn load_font_from_file_with_scale(
300 &mut self,
301 name: &'a str,
302 path: impl AsRef<Path>,
303 scale: f32,
304 ) -> IoResult<()> {
305 let bytes = read_file(path)?;
306
307 self
308 .load_font_from_bytes_with_scale(name, &bytes, scale)
309 .map_err(|err| IoError::new(IoErrorKind::InvalidData, err))
310 }
311
312 pub fn unload_font_by_index(&mut self, index: usize) {
316 if self.fonts.len() <= index {
317 return;
318 }
319
320 self.fonts.remove(index);
321 self.index_by_name.clear();
322
323 for (index, font) in self.fonts.iter().enumerate() {
324 self.index_by_name.insert(font.name, index);
325 }
326 }
327
328 pub fn unload_font_by_name(&mut self, name: &str) {
332 self.unload_font_by_index(self.get_index_by_name(name).unwrap_or(self.fonts.len()));
333 }
334
335 pub fn get_font_by_index(&self, index: usize) -> Option<&Font> {
337 self.fonts.get(index)
338 }
339
340 pub fn get_index_by_char(&self, c: char) -> Option<usize> {
342 self.fonts.iter().position(|it| it.contains(c))
343 }
344
345 pub fn get_index_by_name(&self, name: &str) -> Option<usize> {
347 self.index_by_name.get(name).copied()
348 }
349
350 pub fn get_font_by_name(&self, name: &str) -> Option<&Font> {
352 self.get_font_by_index(self.get_index_by_name(name)?)
353 }
354
355 pub fn get_font_by_char(&self, c: char) -> Option<&Font> {
357 self.get_font_by_index(self.get_index_by_char(c)?)
358 }
359
360 pub fn get_font_by_char_or_panic(&self, c: char) -> &Font {
364 self
365 .get_font_by_char(c)
366 .or_else(|| self.fonts.first())
367 .expect("There is no font currently loaded")
368 }
369
370 pub fn contains(&self, c: char) -> bool {
372 self.fonts.iter().any(|f| f.contains(c))
373 }
374
375 pub fn measure_text(&self, text: &str, size: u16) -> TextDimensions {
390 let mut width = 0f32;
391 let mut min_y = f32::MAX;
392 let mut max_y = f32::MIN;
393
394 for c in text.chars() {
395 let font = self.get_font_by_char_or_panic(c);
396
397 font.cache_glyph(c, size);
398
399 let info = font.chars.borrow()[&(c, size)];
400 let glyph = font.atlas.borrow().get(info.id).unwrap().rect;
401
402 width += info.advance;
403
404 if min_y > info.offset_y {
405 min_y = info.offset_y;
406 }
407
408 if max_y < glyph.h + info.offset_y {
409 max_y = glyph.h + info.offset_y;
410 }
411 }
412
413 TextDimensions {
414 width,
415 height: max_y - min_y,
416 offset_y: max_y,
417 }
418 }
419
420 pub fn draw_text(&self, text: &str, x: f32, y: f32, size: u16, color: Color) -> TextDimensions {
429 self.draw_text_ex(&TextParams {
430 text,
431 x,
432 y,
433 size,
434 color,
435 draw: Default::default(),
436 })
437 }
438
439 pub fn draw_text_ex(&self, params: &TextParams) -> TextDimensions {
466 let mut total_width = 0f32;
467
468 for c in params.text.chars() {
469 let font = self.get_font_by_char_or_panic(c);
470 font.cache_glyph(c, params.size);
471 }
472
473 for c in params.text.chars() {
474 let font = self.get_font_by_char_or_panic(c);
475 let mut atlas = font.atlas.borrow_mut();
476 let info = &font.chars.borrow()[&(c, params.size)];
477 let glyph = atlas.get(info.id).unwrap().rect;
478 let mut y = 0.0 - glyph.h - info.offset_y + params.y;
479
480 if let DrawFrom::TopLeft = params.draw {
481 y += params.size as f32;
482 }
483
484 draw_texture_ex(
485 atlas.texture(),
486 info.offset_x + total_width + params.x,
487 y,
488 params.color,
489 DrawTextureParams {
490 dest_size: Some(vec2(glyph.w, glyph.h)),
491 source: Some(glyph),
492 ..Default::default()
493 },
494 );
495
496 total_width += info.advance;
497 }
498
499 self.measure_text(params.text, params.size)
500 }
501}