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]
526 pub fn row_height(&self, font_id: &FontId) -> f32 {
527 self.lock().fonts.row_height(font_id)
528 }
529
530 pub fn families(&self) -> Vec<FontFamily> {
532 self.lock()
533 .fonts
534 .definitions
535 .families
536 .keys()
537 .cloned()
538 .collect()
539 }
540
541 #[inline]
549 pub fn layout_job(&self, job: LayoutJob) -> Arc<Galley> {
550 self.lock().layout_job(job)
551 }
552
553 pub fn num_galleys_in_cache(&self) -> usize {
554 self.lock().galley_cache.num_galleys_in_cache()
555 }
556
557 pub fn font_atlas_fill_ratio(&self) -> f32 {
562 self.lock().fonts.atlas.lock().fill_ratio()
563 }
564
565 pub fn layout(
569 &self,
570 text: String,
571 font_id: FontId,
572 color: crate::Color32,
573 wrap_width: f32,
574 ) -> Arc<Galley> {
575 let job = LayoutJob::simple(text, font_id, color, wrap_width);
576 self.layout_job(job)
577 }
578
579 pub fn layout_no_wrap(
583 &self,
584 text: String,
585 font_id: FontId,
586 color: crate::Color32,
587 ) -> Arc<Galley> {
588 let job = LayoutJob::simple(text, font_id, color, f32::INFINITY);
589 self.layout_job(job)
590 }
591
592 pub fn layout_delayed_color(
596 &self,
597 text: String,
598 font_id: FontId,
599 wrap_width: f32,
600 ) -> Arc<Galley> {
601 self.layout(text, font_id, crate::Color32::PLACEHOLDER, wrap_width)
602 }
603}
604
605pub struct FontsAndCache {
608 pub fonts: FontsImpl,
609 galley_cache: GalleyCache,
610}
611
612impl FontsAndCache {
613 fn layout_job(&mut self, job: LayoutJob) -> Arc<Galley> {
614 self.galley_cache.layout(&mut self.fonts, job)
615 }
616}
617
618pub struct FontsImpl {
624 pixels_per_point: f32,
625 max_texture_side: usize,
626 definitions: FontDefinitions,
627 atlas: Arc<Mutex<TextureAtlas>>,
628 font_impl_cache: FontImplCache,
629 sized_family: ahash::HashMap<(OrderedFloat<f32>, FontFamily), Font>,
630}
631
632impl FontsImpl {
633 pub fn new(
636 pixels_per_point: f32,
637 max_texture_side: usize,
638 definitions: FontDefinitions,
639 ) -> Self {
640 assert!(
641 0.0 < pixels_per_point && pixels_per_point < 100.0,
642 "pixels_per_point out of range: {pixels_per_point}"
643 );
644
645 let texture_width = max_texture_side.at_most(16 * 1024);
646 let initial_height = 32; let atlas = TextureAtlas::new([texture_width, initial_height]);
648
649 let atlas = Arc::new(Mutex::new(atlas));
650
651 let font_impl_cache =
652 FontImplCache::new(atlas.clone(), pixels_per_point, &definitions.font_data);
653
654 Self {
655 pixels_per_point,
656 max_texture_side,
657 definitions,
658 atlas,
659 font_impl_cache,
660 sized_family: Default::default(),
661 }
662 }
663
664 #[inline(always)]
665 pub fn pixels_per_point(&self) -> f32 {
666 self.pixels_per_point
667 }
668
669 #[inline]
670 pub fn definitions(&self) -> &FontDefinitions {
671 &self.definitions
672 }
673
674 pub fn font(&mut self, font_id: &FontId) -> &mut Font {
676 let FontId { mut size, family } = font_id;
677 size = size.at_least(0.1).at_most(2048.0);
678
679 self.sized_family
680 .entry((OrderedFloat(size), family.clone()))
681 .or_insert_with(|| {
682 let fonts = &self.definitions.families.get(family);
683 let fonts = fonts
684 .unwrap_or_else(|| panic!("FontFamily::{family:?} is not bound to any fonts"));
685
686 let fonts: Vec<Arc<FontImpl>> = fonts
687 .iter()
688 .map(|font_name| self.font_impl_cache.font_impl(size, font_name))
689 .collect();
690
691 Font::new(fonts)
692 })
693 }
694
695 fn glyph_width(&mut self, font_id: &FontId, c: char) -> f32 {
697 self.font(font_id).glyph_width(c)
698 }
699
700 pub fn has_glyph(&mut self, font_id: &FontId, c: char) -> bool {
702 self.font(font_id).has_glyph(c)
703 }
704
705 pub fn has_glyphs(&mut self, font_id: &FontId, s: &str) -> bool {
707 self.font(font_id).has_glyphs(s)
708 }
709
710 fn row_height(&mut self, font_id: &FontId) -> f32 {
714 self.font(font_id).row_height()
715 }
716}
717
718struct CachedGalley {
721 last_used: u32,
723 galley: Arc<Galley>,
724}
725
726#[derive(Default)]
727struct GalleyCache {
728 generation: u32,
730 cache: nohash_hasher::IntMap<u64, CachedGalley>,
731}
732
733impl GalleyCache {
734 fn layout(&mut self, fonts: &mut FontsImpl, mut job: LayoutJob) -> Arc<Galley> {
735 if job.wrap.max_width.is_finite() {
736 job.wrap.max_width = job.wrap.max_width.round();
758 }
759
760 let hash = crate::util::hash(&job); match self.cache.entry(hash) {
763 std::collections::hash_map::Entry::Occupied(entry) => {
764 let cached = entry.into_mut();
765 cached.last_used = self.generation;
766 cached.galley.clone()
767 }
768 std::collections::hash_map::Entry::Vacant(entry) => {
769 let galley = super::layout(fonts, job.into());
770 let galley = Arc::new(galley);
771 entry.insert(CachedGalley {
772 last_used: self.generation,
773 galley: galley.clone(),
774 });
775 galley
776 }
777 }
778 }
779
780 pub fn num_galleys_in_cache(&self) -> usize {
781 self.cache.len()
782 }
783
784 pub fn flush_cache(&mut self) {
786 let current_generation = self.generation;
787 self.cache.retain(|_key, cached| {
788 cached.last_used == current_generation });
790 self.generation = self.generation.wrapping_add(1);
791 }
792}
793
794struct FontImplCache {
797 atlas: Arc<Mutex<TextureAtlas>>,
798 pixels_per_point: f32,
799 ab_glyph_fonts: BTreeMap<String, (FontTweak, ab_glyph::FontArc)>,
800
801 cache: ahash::HashMap<(u32, String), Arc<FontImpl>>,
803}
804
805impl FontImplCache {
806 pub fn new(
807 atlas: Arc<Mutex<TextureAtlas>>,
808 pixels_per_point: f32,
809 font_data: &BTreeMap<String, Arc<FontData>>,
810 ) -> Self {
811 let ab_glyph_fonts = font_data
812 .iter()
813 .map(|(name, font_data)| {
814 let tweak = font_data.tweak;
815 let ab_glyph = ab_glyph_font_from_font_data(name, font_data);
816 (name.clone(), (tweak, ab_glyph))
817 })
818 .collect();
819
820 Self {
821 atlas,
822 pixels_per_point,
823 ab_glyph_fonts,
824 cache: Default::default(),
825 }
826 }
827
828 pub fn font_impl(&mut self, scale_in_points: f32, font_name: &str) -> Arc<FontImpl> {
829 use ab_glyph::Font as _;
830
831 let (tweak, ab_glyph_font) = self
832 .ab_glyph_fonts
833 .get(font_name)
834 .unwrap_or_else(|| panic!("No font data found for {font_name:?}"))
835 .clone();
836
837 let scale_in_pixels = self.pixels_per_point * scale_in_points;
838
839 let units_per_em = ab_glyph_font.units_per_em().unwrap_or_else(|| {
841 panic!("The font unit size of {font_name:?} exceeds the expected range (16..=16384)")
842 });
843 let font_scaling = ab_glyph_font.height_unscaled() / units_per_em;
844 let scale_in_pixels = scale_in_pixels * font_scaling;
845
846 self.cache
847 .entry((
848 (scale_in_pixels * tweak.scale).round() as u32,
849 font_name.to_owned(),
850 ))
851 .or_insert_with(|| {
852 Arc::new(FontImpl::new(
853 self.atlas.clone(),
854 self.pixels_per_point,
855 font_name.to_owned(),
856 ab_glyph_font,
857 scale_in_pixels,
858 tweak,
859 ))
860 })
861 .clone()
862 }
863}