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#[cfg(feature = "default_fonts")]
14use epaint_default_fonts::{EMOJI_ICON, HACK_REGULAR, NOTO_EMOJI_REGULAR, UBUNTU_LIGHT};
15
16#[derive(Clone, Debug, PartialEq)]
20#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
21pub struct FontId {
22 pub size: f32,
24
25 pub family: FontFamily,
27 }
29
30impl Default for FontId {
31 #[inline]
32 fn default() -> Self {
33 Self {
34 size: 14.0,
35 family: FontFamily::Proportional,
36 }
37 }
38}
39
40impl FontId {
41 #[inline]
42 pub const fn new(size: f32, family: FontFamily) -> Self {
43 Self { size, family }
44 }
45
46 #[inline]
47 pub const fn proportional(size: f32) -> Self {
48 Self::new(size, FontFamily::Proportional)
49 }
50
51 #[inline]
52 pub const fn monospace(size: f32) -> Self {
53 Self::new(size, FontFamily::Monospace)
54 }
55}
56
57#[allow(clippy::derived_hash_with_manual_eq)]
58impl std::hash::Hash for FontId {
59 #[inline(always)]
60 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
61 let Self { size, family } = self;
62 emath::OrderedFloat(*size).hash(state);
63 family.hash(state);
64 }
65}
66
67#[derive(Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
74#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
75pub enum FontFamily {
76 #[default]
80 Proportional,
81
82 Monospace,
86
87 Name(Arc<str>),
96}
97
98impl std::fmt::Display for FontFamily {
99 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
100 match self {
101 Self::Monospace => "Monospace".fmt(f),
102 Self::Proportional => "Proportional".fmt(f),
103 Self::Name(name) => (*name).fmt(f),
104 }
105 }
106}
107
108#[derive(Clone, Debug, PartialEq)]
112#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
113pub struct FontData {
114 pub font: std::borrow::Cow<'static, [u8]>,
116
117 pub index: u32,
120
121 pub tweak: FontTweak,
123}
124
125impl FontData {
126 pub fn from_static(font: &'static [u8]) -> Self {
127 Self {
128 font: std::borrow::Cow::Borrowed(font),
129 index: 0,
130 tweak: Default::default(),
131 }
132 }
133
134 pub fn from_owned(font: Vec<u8>) -> Self {
135 Self {
136 font: std::borrow::Cow::Owned(font),
137 index: 0,
138 tweak: Default::default(),
139 }
140 }
141
142 pub fn tweak(self, tweak: FontTweak) -> Self {
143 Self { tweak, ..self }
144 }
145}
146
147#[derive(Copy, Clone, Debug, PartialEq)]
151#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
152pub struct FontTweak {
153 pub scale: f32,
158
159 pub y_offset_factor: f32,
169
170 pub y_offset: f32,
177
178 pub baseline_offset_factor: f32,
184}
185
186impl Default for FontTweak {
187 fn default() -> Self {
188 Self {
189 scale: 1.0,
190 y_offset_factor: 0.0,
191 y_offset: 0.0,
192 baseline_offset_factor: 0.0,
193 }
194 }
195}
196
197fn ab_glyph_font_from_font_data(name: &str, data: &FontData) -> ab_glyph::FontArc {
200 match &data.font {
201 std::borrow::Cow::Borrowed(bytes) => {
202 ab_glyph::FontRef::try_from_slice_and_index(bytes, data.index)
203 .map(ab_glyph::FontArc::from)
204 }
205 std::borrow::Cow::Owned(bytes) => {
206 ab_glyph::FontVec::try_from_vec_and_index(bytes.clone(), data.index)
207 .map(ab_glyph::FontArc::from)
208 }
209 }
210 .unwrap_or_else(|err| panic!("Error parsing {name:?} TTF/OTF font file: {err}"))
211}
212
213#[derive(Clone, Debug, PartialEq)]
244#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
245#[cfg_attr(feature = "serde", serde(default))]
246pub struct FontDefinitions {
247 pub font_data: BTreeMap<String, Arc<FontData>>,
251
252 pub families: BTreeMap<FontFamily, Vec<String>>,
259}
260
261#[derive(Debug, Clone)]
262pub struct FontInsert {
263 pub name: String,
265
266 pub data: FontData,
268
269 pub families: Vec<InsertFontFamily>,
271}
272
273#[derive(Debug, Clone)]
274pub struct InsertFontFamily {
275 pub family: FontFamily,
277
278 pub priority: FontPriority,
280}
281
282#[derive(Debug, Clone)]
283pub enum FontPriority {
284 Highest,
288
289 Lowest,
293}
294
295impl FontInsert {
296 pub fn new(name: &str, data: FontData, families: Vec<InsertFontFamily>) -> Self {
297 Self {
298 name: name.to_owned(),
299 data,
300 families,
301 }
302 }
303}
304
305impl Default for FontDefinitions {
306 #[cfg(not(feature = "default_fonts"))]
309 fn default() -> Self {
310 Self::empty()
311 }
312
313 #[cfg(feature = "default_fonts")]
316 fn default() -> Self {
317 let mut font_data: BTreeMap<String, Arc<FontData>> = BTreeMap::new();
318
319 let mut families = BTreeMap::new();
320
321 font_data.insert(
322 "Hack".to_owned(),
323 Arc::new(FontData::from_static(HACK_REGULAR)),
324 );
325
326 font_data.insert(
328 "NotoEmoji-Regular".to_owned(),
329 Arc::new(FontData::from_static(NOTO_EMOJI_REGULAR).tweak(FontTweak {
330 scale: 0.81, ..Default::default()
332 })),
333 );
334
335 font_data.insert(
336 "Ubuntu-Light".to_owned(),
337 Arc::new(FontData::from_static(UBUNTU_LIGHT)),
338 );
339
340 font_data.insert(
342 "emoji-icon-font".to_owned(),
343 Arc::new(FontData::from_static(EMOJI_ICON).tweak(FontTweak {
344 scale: 0.90, ..Default::default()
346 })),
347 );
348
349 families.insert(
350 FontFamily::Monospace,
351 vec![
352 "Hack".to_owned(),
353 "Ubuntu-Light".to_owned(), "NotoEmoji-Regular".to_owned(),
355 "emoji-icon-font".to_owned(),
356 ],
357 );
358 families.insert(
359 FontFamily::Proportional,
360 vec![
361 "Ubuntu-Light".to_owned(),
362 "NotoEmoji-Regular".to_owned(),
363 "emoji-icon-font".to_owned(),
364 ],
365 );
366
367 Self {
368 font_data,
369 families,
370 }
371 }
372}
373
374impl FontDefinitions {
375 pub fn empty() -> Self {
377 let mut families = BTreeMap::new();
378 families.insert(FontFamily::Monospace, vec![]);
379 families.insert(FontFamily::Proportional, vec![]);
380
381 Self {
382 font_data: Default::default(),
383 families,
384 }
385 }
386
387 #[cfg(feature = "default_fonts")]
389 pub fn builtin_font_names() -> &'static [&'static str] {
390 &[
391 "Ubuntu-Light",
392 "NotoEmoji-Regular",
393 "emoji-icon-font",
394 "Hack",
395 ]
396 }
397
398 #[cfg(not(feature = "default_fonts"))]
400 pub fn builtin_font_names() -> &'static [&'static str] {
401 &[]
402 }
403}
404
405#[derive(Clone)]
417pub struct Fonts(Arc<Mutex<FontsAndCache>>);
418
419impl Fonts {
420 pub fn new(
426 pixels_per_point: f32,
427 max_texture_side: usize,
428 definitions: FontDefinitions,
429 ) -> Self {
430 let fonts_and_cache = FontsAndCache {
431 fonts: FontsImpl::new(pixels_per_point, max_texture_side, definitions),
432 galley_cache: Default::default(),
433 };
434 Self(Arc::new(Mutex::new(fonts_and_cache)))
435 }
436
437 pub fn begin_pass(&self, pixels_per_point: f32, max_texture_side: usize) {
445 let mut fonts_and_cache = self.0.lock();
446
447 let pixels_per_point_changed = fonts_and_cache.fonts.pixels_per_point != pixels_per_point;
448 let max_texture_side_changed = fonts_and_cache.fonts.max_texture_side != max_texture_side;
449 let font_atlas_almost_full = fonts_and_cache.fonts.atlas.lock().fill_ratio() > 0.8;
450 let needs_recreate =
451 pixels_per_point_changed || max_texture_side_changed || font_atlas_almost_full;
452
453 if needs_recreate {
454 let definitions = fonts_and_cache.fonts.definitions.clone();
455
456 *fonts_and_cache = FontsAndCache {
457 fonts: FontsImpl::new(pixels_per_point, max_texture_side, definitions),
458 galley_cache: Default::default(),
459 };
460 }
461
462 fonts_and_cache.galley_cache.flush_cache();
463 }
464
465 pub fn font_image_delta(&self) -> Option<crate::ImageDelta> {
467 self.lock().fonts.atlas.lock().take_delta()
468 }
469
470 #[doc(hidden)]
472 #[inline]
473 pub fn lock(&self) -> MutexGuard<'_, FontsAndCache> {
474 self.0.lock()
475 }
476
477 #[inline]
478 pub fn pixels_per_point(&self) -> f32 {
479 self.lock().fonts.pixels_per_point
480 }
481
482 #[inline]
483 pub fn max_texture_side(&self) -> usize {
484 self.lock().fonts.max_texture_side
485 }
486
487 pub fn texture_atlas(&self) -> Arc<Mutex<TextureAtlas>> {
490 self.lock().fonts.atlas.clone()
491 }
492
493 #[inline]
495 pub fn image(&self) -> crate::FontImage {
496 self.lock().fonts.atlas.lock().image().clone()
497 }
498
499 pub fn font_image_size(&self) -> [usize; 2] {
502 self.lock().fonts.atlas.lock().size()
503 }
504
505 #[inline]
507 pub fn glyph_width(&self, font_id: &FontId, c: char) -> f32 {
508 self.lock().fonts.glyph_width(font_id, c)
509 }
510
511 #[inline]
513 pub fn has_glyph(&self, font_id: &FontId, c: char) -> bool {
514 self.lock().fonts.has_glyph(font_id, c)
515 }
516
517 pub fn has_glyphs(&self, font_id: &FontId, s: &str) -> bool {
519 self.lock().fonts.has_glyphs(font_id, s)
520 }
521
522 #[inline]
524 pub fn row_height(&self, font_id: &FontId) -> f32 {
525 self.lock().fonts.row_height(font_id)
526 }
527
528 pub fn families(&self) -> Vec<FontFamily> {
530 self.lock()
531 .fonts
532 .definitions
533 .families
534 .keys()
535 .cloned()
536 .collect()
537 }
538
539 #[inline]
547 pub fn layout_job(&self, job: LayoutJob) -> Arc<Galley> {
548 self.lock().layout_job(job)
549 }
550
551 pub fn num_galleys_in_cache(&self) -> usize {
552 self.lock().galley_cache.num_galleys_in_cache()
553 }
554
555 pub fn font_atlas_fill_ratio(&self) -> f32 {
560 self.lock().fonts.atlas.lock().fill_ratio()
561 }
562
563 pub fn layout(
567 &self,
568 text: String,
569 font_id: FontId,
570 color: crate::Color32,
571 wrap_width: f32,
572 ) -> Arc<Galley> {
573 let job = LayoutJob::simple(text, font_id, color, wrap_width);
574 self.layout_job(job)
575 }
576
577 pub fn layout_no_wrap(
581 &self,
582 text: String,
583 font_id: FontId,
584 color: crate::Color32,
585 ) -> Arc<Galley> {
586 let job = LayoutJob::simple(text, font_id, color, f32::INFINITY);
587 self.layout_job(job)
588 }
589
590 pub fn layout_delayed_color(
594 &self,
595 text: String,
596 font_id: FontId,
597 wrap_width: f32,
598 ) -> Arc<Galley> {
599 self.layout(text, font_id, crate::Color32::PLACEHOLDER, wrap_width)
600 }
601}
602
603pub struct FontsAndCache {
606 pub fonts: FontsImpl,
607 galley_cache: GalleyCache,
608}
609
610impl FontsAndCache {
611 fn layout_job(&mut self, job: LayoutJob) -> Arc<Galley> {
612 self.galley_cache.layout(&mut self.fonts, job)
613 }
614}
615
616pub struct FontsImpl {
622 pixels_per_point: f32,
623 max_texture_side: usize,
624 definitions: FontDefinitions,
625 atlas: Arc<Mutex<TextureAtlas>>,
626 font_impl_cache: FontImplCache,
627 sized_family: ahash::HashMap<(OrderedFloat<f32>, FontFamily), Font>,
628}
629
630impl FontsImpl {
631 pub fn new(
634 pixels_per_point: f32,
635 max_texture_side: usize,
636 definitions: FontDefinitions,
637 ) -> Self {
638 assert!(
639 0.0 < pixels_per_point && pixels_per_point < 100.0,
640 "pixels_per_point out of range: {pixels_per_point}"
641 );
642
643 let texture_width = max_texture_side.at_most(16 * 1024);
644 let initial_height = 32; let atlas = TextureAtlas::new([texture_width, initial_height]);
646
647 let atlas = Arc::new(Mutex::new(atlas));
648
649 let font_impl_cache =
650 FontImplCache::new(atlas.clone(), pixels_per_point, &definitions.font_data);
651
652 Self {
653 pixels_per_point,
654 max_texture_side,
655 definitions,
656 atlas,
657 font_impl_cache,
658 sized_family: Default::default(),
659 }
660 }
661
662 #[inline(always)]
663 pub fn pixels_per_point(&self) -> f32 {
664 self.pixels_per_point
665 }
666
667 #[inline]
668 pub fn definitions(&self) -> &FontDefinitions {
669 &self.definitions
670 }
671
672 pub fn font(&mut self, font_id: &FontId) -> &mut Font {
674 let FontId { mut size, family } = font_id;
675 size = size.at_least(0.1).at_most(2048.0);
676
677 self.sized_family
678 .entry((OrderedFloat(size), family.clone()))
679 .or_insert_with(|| {
680 let fonts = &self.definitions.families.get(family);
681 let fonts = fonts
682 .unwrap_or_else(|| panic!("FontFamily::{family:?} is not bound to any fonts"));
683
684 let fonts: Vec<Arc<FontImpl>> = fonts
685 .iter()
686 .map(|font_name| self.font_impl_cache.font_impl(size, font_name))
687 .collect();
688
689 Font::new(fonts)
690 })
691 }
692
693 fn glyph_width(&mut self, font_id: &FontId, c: char) -> f32 {
695 self.font(font_id).glyph_width(c)
696 }
697
698 pub fn has_glyph(&mut self, font_id: &FontId, c: char) -> bool {
700 self.font(font_id).has_glyph(c)
701 }
702
703 pub fn has_glyphs(&mut self, font_id: &FontId, s: &str) -> bool {
705 self.font(font_id).has_glyphs(s)
706 }
707
708 fn row_height(&mut self, font_id: &FontId) -> f32 {
710 self.font(font_id).row_height()
711 }
712}
713
714struct CachedGalley {
717 last_used: u32,
719 galley: Arc<Galley>,
720}
721
722#[derive(Default)]
723struct GalleyCache {
724 generation: u32,
726 cache: nohash_hasher::IntMap<u64, CachedGalley>,
727}
728
729impl GalleyCache {
730 fn layout(&mut self, fonts: &mut FontsImpl, mut job: LayoutJob) -> Arc<Galley> {
731 if job.wrap.max_width.is_finite() {
732 job.wrap.max_width = job.wrap.max_width.round();
754 }
755
756 let hash = crate::util::hash(&job); match self.cache.entry(hash) {
759 std::collections::hash_map::Entry::Occupied(entry) => {
760 let cached = entry.into_mut();
761 cached.last_used = self.generation;
762 cached.galley.clone()
763 }
764 std::collections::hash_map::Entry::Vacant(entry) => {
765 let galley = super::layout(fonts, job.into());
766 let galley = Arc::new(galley);
767 entry.insert(CachedGalley {
768 last_used: self.generation,
769 galley: galley.clone(),
770 });
771 galley
772 }
773 }
774 }
775
776 pub fn num_galleys_in_cache(&self) -> usize {
777 self.cache.len()
778 }
779
780 pub fn flush_cache(&mut self) {
782 let current_generation = self.generation;
783 self.cache.retain(|_key, cached| {
784 cached.last_used == current_generation });
786 self.generation = self.generation.wrapping_add(1);
787 }
788}
789
790struct FontImplCache {
793 atlas: Arc<Mutex<TextureAtlas>>,
794 pixels_per_point: f32,
795 ab_glyph_fonts: BTreeMap<String, (FontTweak, ab_glyph::FontArc)>,
796
797 cache: ahash::HashMap<(u32, String), Arc<FontImpl>>,
799}
800
801impl FontImplCache {
802 pub fn new(
803 atlas: Arc<Mutex<TextureAtlas>>,
804 pixels_per_point: f32,
805 font_data: &BTreeMap<String, Arc<FontData>>,
806 ) -> Self {
807 let ab_glyph_fonts = font_data
808 .iter()
809 .map(|(name, font_data)| {
810 let tweak = font_data.tweak;
811 let ab_glyph = ab_glyph_font_from_font_data(name, font_data);
812 (name.clone(), (tweak, ab_glyph))
813 })
814 .collect();
815
816 Self {
817 atlas,
818 pixels_per_point,
819 ab_glyph_fonts,
820 cache: Default::default(),
821 }
822 }
823
824 pub fn font_impl(&mut self, scale_in_points: f32, font_name: &str) -> Arc<FontImpl> {
825 use ab_glyph::Font as _;
826
827 let (tweak, ab_glyph_font) = self
828 .ab_glyph_fonts
829 .get(font_name)
830 .unwrap_or_else(|| panic!("No font data found for {font_name:?}"))
831 .clone();
832
833 let scale_in_pixels = self.pixels_per_point * scale_in_points;
834
835 let units_per_em = ab_glyph_font.units_per_em().unwrap_or_else(|| {
837 panic!("The font unit size of {font_name:?} exceeds the expected range (16..=16384)")
838 });
839 let font_scaling = ab_glyph_font.height_unscaled() / units_per_em;
840 let scale_in_pixels = scale_in_pixels * font_scaling;
841
842 self.cache
843 .entry((
844 (scale_in_pixels * tweak.scale).round() as u32,
845 font_name.to_owned(),
846 ))
847 .or_insert_with(|| {
848 Arc::new(FontImpl::new(
849 self.atlas.clone(),
850 self.pixels_per_point,
851 font_name.to_owned(),
852 ab_glyph_font,
853 scale_in_pixels,
854 tweak,
855 ))
856 })
857 .clone()
858 }
859}