1use std::{collections::BTreeMap, sync::Arc};
2
3use crate::{
4 mutex::{Mutex, MutexGuard},
5 text::{
6 font::{Font, FontImpl},
7 Galley, LayoutJob,
8 },
9 TextureAtlas,
10};
11use emath::{NumExt as _, OrderedFloat};
12
13#[derive(Clone, Debug, PartialEq)]
17#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
18pub struct FontId {
19 pub size: f32,
21
22 pub family: FontFamily,
24 }
26
27impl Default for FontId {
28 #[inline]
29 fn default() -> Self {
30 Self {
31 size: 14.0,
32 family: FontFamily::Proportional,
33 }
34 }
35}
36
37impl FontId {
38 #[inline]
39 pub const fn new(size: f32, family: FontFamily) -> Self {
40 Self { size, family }
41 }
42
43 #[inline]
44 pub const fn proportional(size: f32) -> Self {
45 Self::new(size, FontFamily::Proportional)
46 }
47
48 #[inline]
49 pub const fn monospace(size: f32) -> Self {
50 Self::new(size, FontFamily::Monospace)
51 }
52}
53
54#[allow(clippy::derived_hash_with_manual_eq)]
55impl std::hash::Hash for FontId {
56 #[inline(always)]
57 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
58 let Self { size, family } = self;
59 emath::OrderedFloat(*size).hash(state);
60 family.hash(state);
61 }
62}
63
64#[derive(Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
71#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
72pub enum FontFamily {
73 #[default]
77 Proportional,
78
79 Monospace,
83
84 Name(Arc<str>),
93}
94
95impl std::fmt::Display for FontFamily {
96 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
97 match self {
98 Self::Monospace => "Monospace".fmt(f),
99 Self::Proportional => "Proportional".fmt(f),
100 Self::Name(name) => (*name).fmt(f),
101 }
102 }
103}
104
105#[derive(Clone, Debug, PartialEq)]
109#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
110pub struct FontData {
111 pub font: std::borrow::Cow<'static, [u8]>,
113
114 pub index: u32,
117
118 pub tweak: FontTweak,
120}
121
122impl FontData {
123 pub fn from_static(font: &'static [u8]) -> Self {
124 Self {
125 font: std::borrow::Cow::Borrowed(font),
126 index: 0,
127 tweak: Default::default(),
128 }
129 }
130
131 pub fn from_owned(font: Vec<u8>) -> Self {
132 Self {
133 font: std::borrow::Cow::Owned(font),
134 index: 0,
135 tweak: Default::default(),
136 }
137 }
138
139 pub fn tweak(self, tweak: FontTweak) -> Self {
140 Self { tweak, ..self }
141 }
142}
143
144#[derive(Copy, Clone, Debug, PartialEq)]
148#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
149pub struct FontTweak {
150 pub scale: f32,
155
156 pub y_offset_factor: f32,
164
165 pub y_offset: f32,
170
171 pub baseline_offset_factor: f32,
177}
178
179impl Default for FontTweak {
180 fn default() -> Self {
181 Self {
182 scale: 1.0,
183 y_offset_factor: 0.0,
184 y_offset: 0.0,
185 baseline_offset_factor: -0.0333, }
187 }
188}
189
190fn ab_glyph_font_from_font_data(name: &str, data: &FontData) -> ab_glyph::FontArc {
193 match &data.font {
194 std::borrow::Cow::Borrowed(bytes) => {
195 ab_glyph::FontRef::try_from_slice_and_index(bytes, data.index)
196 .map(ab_glyph::FontArc::from)
197 }
198 std::borrow::Cow::Owned(bytes) => {
199 ab_glyph::FontVec::try_from_vec_and_index(bytes.clone(), data.index)
200 .map(ab_glyph::FontArc::from)
201 }
202 }
203 .unwrap_or_else(|err| panic!("Error parsing {name:?} TTF/OTF font file: {err}"))
204}
205
206#[derive(Clone, Debug, PartialEq)]
233#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
234#[cfg_attr(feature = "serde", serde(default))]
235pub struct FontDefinitions {
236 pub font_data: BTreeMap<String, FontData>,
240
241 pub families: BTreeMap<FontFamily, Vec<String>>,
248}
249
250impl Default for FontDefinitions {
251 #[cfg(not(feature = "default_fonts"))]
254 fn default() -> Self {
255 Self::empty()
256 }
257
258 #[cfg(feature = "default_fonts")]
261 fn default() -> Self {
262 let mut font_data: BTreeMap<String, FontData> = BTreeMap::new();
263
264 let mut families = BTreeMap::new();
265
266 font_data.insert(
267 "Hack".to_owned(),
268 FontData::from_static(include_bytes!("../../fonts/Hack-Regular.ttf")),
269 );
270 font_data.insert(
271 "Ubuntu-Light".to_owned(),
272 FontData::from_static(include_bytes!("../../fonts/Ubuntu-Light.ttf")),
273 );
274
275 font_data.insert(
277 "NotoEmoji-Regular".to_owned(),
278 FontData::from_static(include_bytes!("../../fonts/NotoEmoji-Regular.ttf")).tweak(
279 FontTweak {
280 scale: 0.81, ..Default::default()
282 },
283 ),
284 );
285
286 font_data.insert(
288 "emoji-icon-font".to_owned(),
289 FontData::from_static(include_bytes!("../../fonts/emoji-icon-font.ttf")).tweak(
290 FontTweak {
291 scale: 0.88, y_offset_factor: 0.11, baseline_offset_factor: -0.11, ..Default::default()
297 },
298 ),
299 );
300
301 families.insert(
302 FontFamily::Monospace,
303 vec![
304 "Hack".to_owned(),
305 "Ubuntu-Light".to_owned(), "NotoEmoji-Regular".to_owned(),
307 "emoji-icon-font".to_owned(),
308 ],
309 );
310 families.insert(
311 FontFamily::Proportional,
312 vec![
313 "Ubuntu-Light".to_owned(),
314 "NotoEmoji-Regular".to_owned(),
315 "emoji-icon-font".to_owned(),
316 ],
317 );
318
319 Self {
320 font_data,
321 families,
322 }
323 }
324}
325
326impl FontDefinitions {
327 pub fn empty() -> Self {
329 let mut families = BTreeMap::new();
330 families.insert(FontFamily::Monospace, vec![]);
331 families.insert(FontFamily::Proportional, vec![]);
332
333 Self {
334 font_data: Default::default(),
335 families,
336 }
337 }
338
339 #[cfg(feature = "default_fonts")]
341 pub fn builtin_font_names() -> &'static [&'static str] {
342 &[
343 "Ubuntu-Light",
344 "NotoEmoji-Regular",
345 "emoji-icon-font",
346 "Hack",
347 ]
348 }
349
350 #[cfg(not(feature = "default_fonts"))]
352 pub fn builtin_font_names() -> &'static [&'static str] {
353 &[]
354 }
355}
356
357#[derive(Clone)]
369pub struct Fonts(Arc<Mutex<FontsAndCache>>);
370
371impl Fonts {
372 pub fn new(
378 pixels_per_point: f32,
379 max_texture_side: usize,
380 definitions: FontDefinitions,
381 ) -> Self {
382 let fonts_and_cache = FontsAndCache {
383 fonts: FontsImpl::new(pixels_per_point, max_texture_side, definitions),
384 galley_cache: Default::default(),
385 };
386 Self(Arc::new(Mutex::new(fonts_and_cache)))
387 }
388
389 pub fn begin_frame(&self, pixels_per_point: f32, max_texture_side: usize) {
397 let mut fonts_and_cache = self.0.lock();
398
399 let pixels_per_point_changed = fonts_and_cache.fonts.pixels_per_point != pixels_per_point;
400 let max_texture_side_changed = fonts_and_cache.fonts.max_texture_side != max_texture_side;
401 let font_atlas_almost_full = fonts_and_cache.fonts.atlas.lock().fill_ratio() > 0.8;
402 let needs_recreate =
403 pixels_per_point_changed || max_texture_side_changed || font_atlas_almost_full;
404
405 if needs_recreate {
406 let definitions = fonts_and_cache.fonts.definitions.clone();
407
408 *fonts_and_cache = FontsAndCache {
409 fonts: FontsImpl::new(pixels_per_point, max_texture_side, definitions),
410 galley_cache: Default::default(),
411 };
412 }
413
414 fonts_and_cache.galley_cache.flush_cache();
415 }
416
417 pub fn font_image_delta(&self) -> Option<crate::ImageDelta> {
419 self.lock().fonts.atlas.lock().take_delta()
420 }
421
422 #[doc(hidden)]
424 #[inline]
425 pub fn lock(&self) -> MutexGuard<'_, FontsAndCache> {
426 self.0.lock()
427 }
428
429 #[inline]
430 pub fn pixels_per_point(&self) -> f32 {
431 self.lock().fonts.pixels_per_point
432 }
433
434 #[inline]
435 pub fn max_texture_side(&self) -> usize {
436 self.lock().fonts.max_texture_side
437 }
438
439 pub fn texture_atlas(&self) -> Arc<Mutex<TextureAtlas>> {
442 self.lock().fonts.atlas.clone()
443 }
444
445 #[inline]
447 pub fn image(&self) -> crate::FontImage {
448 self.lock().fonts.atlas.lock().image().clone()
449 }
450
451 pub fn font_image_size(&self) -> [usize; 2] {
454 self.lock().fonts.atlas.lock().size()
455 }
456
457 #[inline]
459 pub fn glyph_width(&self, font_id: &FontId, c: char) -> f32 {
460 self.lock().fonts.glyph_width(font_id, c)
461 }
462
463 #[inline]
465 pub fn has_glyph(&self, font_id: &FontId, c: char) -> bool {
466 self.lock().fonts.has_glyph(font_id, c)
467 }
468
469 pub fn has_glyphs(&self, font_id: &FontId, s: &str) -> bool {
471 self.lock().fonts.has_glyphs(font_id, s)
472 }
473
474 #[inline]
476 pub fn row_height(&self, font_id: &FontId) -> f32 {
477 self.lock().fonts.row_height(font_id)
478 }
479
480 pub fn families(&self) -> Vec<FontFamily> {
482 self.lock()
483 .fonts
484 .definitions
485 .families
486 .keys()
487 .cloned()
488 .collect()
489 }
490
491 #[inline]
499 pub fn layout_job(&self, job: LayoutJob) -> Arc<Galley> {
500 self.lock().layout_job(job)
501 }
502
503 pub fn num_galleys_in_cache(&self) -> usize {
504 self.lock().galley_cache.num_galleys_in_cache()
505 }
506
507 pub fn font_atlas_fill_ratio(&self) -> f32 {
512 self.lock().fonts.atlas.lock().fill_ratio()
513 }
514
515 pub fn layout(
519 &self,
520 text: String,
521 font_id: FontId,
522 color: crate::Color32,
523 wrap_width: f32,
524 ) -> Arc<Galley> {
525 let job = LayoutJob::simple(text, font_id, color, wrap_width);
526 self.layout_job(job)
527 }
528
529 pub fn layout_no_wrap(
533 &self,
534 text: String,
535 font_id: FontId,
536 color: crate::Color32,
537 ) -> Arc<Galley> {
538 let job = LayoutJob::simple(text, font_id, color, f32::INFINITY);
539 self.layout_job(job)
540 }
541
542 pub fn layout_delayed_color(
546 &self,
547 text: String,
548 font_id: FontId,
549 wrap_width: f32,
550 ) -> Arc<Galley> {
551 self.layout(text, font_id, crate::Color32::PLACEHOLDER, wrap_width)
552 }
553}
554
555pub struct FontsAndCache {
558 pub fonts: FontsImpl,
559 galley_cache: GalleyCache,
560}
561
562impl FontsAndCache {
563 fn layout_job(&mut self, job: LayoutJob) -> Arc<Galley> {
564 self.galley_cache.layout(&mut self.fonts, job)
565 }
566}
567
568pub struct FontsImpl {
574 pixels_per_point: f32,
575 max_texture_side: usize,
576 definitions: FontDefinitions,
577 atlas: Arc<Mutex<TextureAtlas>>,
578 font_impl_cache: FontImplCache,
579 sized_family: ahash::HashMap<(OrderedFloat<f32>, FontFamily), Font>,
580}
581
582impl FontsImpl {
583 pub fn new(
586 pixels_per_point: f32,
587 max_texture_side: usize,
588 definitions: FontDefinitions,
589 ) -> Self {
590 assert!(
591 0.0 < pixels_per_point && pixels_per_point < 100.0,
592 "pixels_per_point out of range: {pixels_per_point}"
593 );
594
595 let texture_width = max_texture_side.at_most(8 * 1024);
596 let initial_height = 32; let atlas = TextureAtlas::new([texture_width, initial_height]);
598
599 let atlas = Arc::new(Mutex::new(atlas));
600
601 let font_impl_cache =
602 FontImplCache::new(atlas.clone(), pixels_per_point, &definitions.font_data);
603
604 Self {
605 pixels_per_point,
606 max_texture_side,
607 definitions,
608 atlas,
609 font_impl_cache,
610 sized_family: Default::default(),
611 }
612 }
613
614 #[inline(always)]
615 pub fn pixels_per_point(&self) -> f32 {
616 self.pixels_per_point
617 }
618
619 #[inline]
620 pub fn definitions(&self) -> &FontDefinitions {
621 &self.definitions
622 }
623
624 pub fn font(&mut self, font_id: &FontId) -> &mut Font {
626 let FontId { size, family } = font_id;
627
628 self.sized_family
629 .entry((OrderedFloat(*size), family.clone()))
630 .or_insert_with(|| {
631 let fonts = &self.definitions.families.get(family);
632 let fonts = fonts
633 .unwrap_or_else(|| panic!("FontFamily::{family:?} is not bound to any fonts"));
634
635 let fonts: Vec<Arc<FontImpl>> = fonts
636 .iter()
637 .map(|font_name| self.font_impl_cache.font_impl(*size, font_name))
638 .collect();
639
640 Font::new(fonts)
641 })
642 }
643
644 fn glyph_width(&mut self, font_id: &FontId, c: char) -> f32 {
646 self.font(font_id).glyph_width(c)
647 }
648
649 pub fn has_glyph(&mut self, font_id: &FontId, c: char) -> bool {
651 self.font(font_id).has_glyph(c)
652 }
653
654 pub fn has_glyphs(&mut self, font_id: &FontId, s: &str) -> bool {
656 self.font(font_id).has_glyphs(s)
657 }
658
659 fn row_height(&mut self, font_id: &FontId) -> f32 {
661 self.font(font_id).row_height()
662 }
663}
664
665struct CachedGalley {
668 last_used: u32,
670 galley: Arc<Galley>,
671}
672
673#[derive(Default)]
674struct GalleyCache {
675 generation: u32,
677 cache: nohash_hasher::IntMap<u64, CachedGalley>,
678}
679
680impl GalleyCache {
681 fn layout(&mut self, fonts: &mut FontsImpl, job: LayoutJob) -> Arc<Galley> {
682 let hash = crate::util::hash(&job); match self.cache.entry(hash) {
685 std::collections::hash_map::Entry::Occupied(entry) => {
686 let cached = entry.into_mut();
687 cached.last_used = self.generation;
688 cached.galley.clone()
689 }
690 std::collections::hash_map::Entry::Vacant(entry) => {
691 let galley = super::layout(fonts, job.into());
692 let galley = Arc::new(galley);
693 entry.insert(CachedGalley {
694 last_used: self.generation,
695 galley: galley.clone(),
696 });
697 galley
698 }
699 }
700 }
701
702 pub fn num_galleys_in_cache(&self) -> usize {
703 self.cache.len()
704 }
705
706 pub fn flush_cache(&mut self) {
708 let current_generation = self.generation;
709 self.cache.retain(|_key, cached| {
710 cached.last_used == current_generation });
712 self.generation = self.generation.wrapping_add(1);
713 }
714}
715
716struct FontImplCache {
719 atlas: Arc<Mutex<TextureAtlas>>,
720 pixels_per_point: f32,
721 ab_glyph_fonts: BTreeMap<String, (FontTweak, ab_glyph::FontArc)>,
722
723 cache: ahash::HashMap<(u32, String), Arc<FontImpl>>,
725}
726
727impl FontImplCache {
728 pub fn new(
729 atlas: Arc<Mutex<TextureAtlas>>,
730 pixels_per_point: f32,
731 font_data: &BTreeMap<String, FontData>,
732 ) -> Self {
733 let ab_glyph_fonts = font_data
734 .iter()
735 .map(|(name, font_data)| {
736 let tweak = font_data.tweak;
737 let ab_glyph = ab_glyph_font_from_font_data(name, font_data);
738 (name.clone(), (tweak, ab_glyph))
739 })
740 .collect();
741
742 Self {
743 atlas,
744 pixels_per_point,
745 ab_glyph_fonts,
746 cache: Default::default(),
747 }
748 }
749
750 pub fn font_impl(&mut self, scale_in_points: f32, font_name: &str) -> Arc<FontImpl> {
751 use ab_glyph::Font as _;
752
753 let (tweak, ab_glyph_font) = self
754 .ab_glyph_fonts
755 .get(font_name)
756 .unwrap_or_else(|| panic!("No font data found for {font_name:?}"))
757 .clone();
758
759 let scale_in_pixels = self.pixels_per_point * scale_in_points;
760
761 let units_per_em = ab_glyph_font.units_per_em().unwrap_or_else(|| {
763 panic!("The font unit size of {font_name:?} exceeds the expected range (16..=16384)")
764 });
765 let font_scaling = ab_glyph_font.height_unscaled() / units_per_em;
766 let scale_in_pixels = scale_in_pixels * font_scaling;
767
768 self.cache
769 .entry((
770 (scale_in_pixels * tweak.scale).round() as u32,
771 font_name.to_owned(),
772 ))
773 .or_insert_with(|| {
774 Arc::new(FontImpl::new(
775 self.atlas.clone(),
776 self.pixels_per_point,
777 font_name.to_owned(),
778 ab_glyph_font,
779 scale_in_pixels,
780 tweak,
781 ))
782 })
783 .clone()
784 }
785}