1- using SkiaSharp ;
2- using System . Collections . Concurrent ;
1+ using Sandbox . Engine ;
2+ using SkiaSharp ;
33using System . Diagnostics . CodeAnalysis ;
44using Topten . RichTextKit ;
55
@@ -24,11 +24,24 @@ internal class FontManager : FontMapper
2424{
2525 public static FontManager Instance = new FontManager ( ) ;
2626
27- static ConcurrentDictionary < int , SKTypeface > LoadedFonts = new ( ) ;
28-
29- static Dictionary < int , SKTypeface > Cache = new ( ) ;
27+ public IEnumerable < string > FontFamilies
28+ {
29+ get
30+ {
31+ lock ( LoadedFonts )
32+ {
33+ return LoadedFonts . Values . Select ( x => x . Typeface . FamilyName ) . Distinct ( ) . ToList ( ) ;
34+ }
35+ }
36+ }
3037
31- public static IEnumerable < string > FontFamilies => LoadedFonts . Values . Select ( x => x . FamilyName ) . Distinct ( ) ;
38+ struct LoadedTypeface
39+ {
40+ public SKTypeface Typeface ;
41+ public bool IsMenu ;
42+ }
43+ Dictionary < int , LoadedTypeface > LoadedFonts = new ( ) ;
44+ Dictionary < int , SKTypeface > Cache = new ( ) ;
3245
3346 private void Load ( System . IO . Stream stream )
3447 {
@@ -37,19 +50,36 @@ private void Load( System.IO.Stream stream )
3750 var face = SKTypeface . FromStream ( stream ) ;
3851 if ( face is null ) return ;
3952
40- var hash = HashCode . Combine ( face . FamilyName , face . FontWeight , face . FontSlant ) ;
53+ bool isMenu = GlobalContext . Current == GlobalContext . Menu ;
4154
42- LoadedFonts [ hash ] = face ;
55+ var hash = HashCode . Combine ( face . FamilyName , face . FontWeight , face . FontSlant ) ;
56+ lock ( LoadedFonts )
57+ {
58+ if ( LoadedFonts . ContainsKey ( hash ) )
59+ {
60+ face . Dispose ( ) ;
61+ return ;
62+ }
63+
64+ LoadedFonts [ hash ] = new ( )
65+ {
66+ Typeface = face ,
67+ IsMenu = isMenu
68+ } ;
69+ }
4370
44- Log . Trace ( $ "Loaded font { face . FamilyName } weight { face . FontWeight } " ) ;
71+ Log . Trace ( $ "Loaded font { face . FamilyName } weight { face . FontWeight } (IsMenu: { isMenu } ) " ) ;
4572 }
4673
4774 List < FileWatch > watchers = new ( ) ;
4875
4976 public void LoadAll ( BaseFileSystem fileSystem )
5077 {
51- // If we're loading new fonts, we may have cached it already
52- Cache . Clear ( ) ;
78+ lock ( Cache )
79+ {
80+ // empty cache new fonts can replace fallbacks and best-matches
81+ Cache . Clear ( ) ;
82+ }
5383
5484 var fontFiles = fileSystem . FindFile ( "/fonts/" , "*.ttf" , true )
5585 . Union ( fileSystem . FindFile ( "/fonts/" , "*.otf" , true ) ) ;
@@ -71,7 +101,10 @@ public void LoadAll( BaseFileSystem fileSystem )
71101
72102 private void OnFontFilesChanged ( FileWatch w , BaseFileSystem fs )
73103 {
74- Cache . Clear ( ) ;
104+ lock ( Cache )
105+ {
106+ Cache . Clear ( ) ;
107+ }
75108
76109 foreach ( var file in w . Changes )
77110 {
@@ -85,18 +118,22 @@ private void OnFontFilesChanged( FileWatch w, BaseFileSystem fs )
85118 /// </summary>
86119 private SKTypeface GetBestTypeface ( IStyle style )
87120 {
88- // Must be of same family
89- var familyFonts = LoadedFonts . Values . Where ( x => string . Equals ( x . FamilyName , style . FontFamily , StringComparison . OrdinalIgnoreCase ) ) ;
90- if ( ! familyFonts . Any ( ) ) return null ;
91-
92- // Get matching slants, if no matching fallback to regular
93- var slantFonts = familyFonts . Where ( x => x . IsItalic == style . FontItalic ) ;
94- if ( slantFonts . Any ( ) ) familyFonts = slantFonts ;
95-
96- // Finally get the closest font weight
97- return familyFonts . Select ( x => new { x , distance = Math . Abs ( x . FontWeight - style . FontWeight ) } )
98- . OrderBy ( x => x . distance )
99- . First ( ) . x ;
121+ lock ( LoadedFonts )
122+ {
123+ // Must be of same family
124+ var familyFonts = LoadedFonts . Values . Select ( x => x . Typeface )
125+ . Where ( x => string . Equals ( x . FamilyName , style . FontFamily , StringComparison . OrdinalIgnoreCase ) ) ;
126+ if ( ! familyFonts . Any ( ) ) return null ;
127+
128+ // Get matching slants, if no matching fallback to regular
129+ var slantFonts = familyFonts . Where ( x => x . IsItalic == style . FontItalic ) ;
130+ if ( slantFonts . Any ( ) ) familyFonts = slantFonts ;
131+
132+ // Finally get the closest font weight
133+ return familyFonts . Select ( x => new { x , distance = Math . Abs ( x . FontWeight - style . FontWeight ) } )
134+ . OrderBy ( x => x . distance )
135+ . First ( ) . x ;
136+ }
100137 }
101138
102139 public override SKTypeface TypefaceFromStyle ( IStyle style , bool ignoreFontVariants )
@@ -121,26 +158,30 @@ public override SKTypeface TypefaceFromStyle( IStyle style, bool ignoreFontVaria
121158 return f ;
122159 }
123160
124- public void Reset ( )
161+ public void Clear ( bool removeMenu )
125162 {
126163 foreach ( var watcher in watchers )
127164 {
128165 watcher . Dispose ( ) ;
129166 }
130167 watchers . Clear ( ) ;
131168
132- foreach ( var ( _ , font ) in LoadedFonts )
169+ lock ( LoadedFonts )
133170 {
134- font ? . Dispose ( ) ;
171+ foreach ( var ( hash , font ) in LoadedFonts . ToArray ( ) )
172+ {
173+ if ( ! removeMenu && font . IsMenu )
174+ continue ;
175+
176+ LoadedFonts . Remove ( hash ) ;
177+ font . Typeface ? . Dispose ( ) ;
178+ }
135179 }
136180
137- foreach ( var ( _ , font ) in Cache )
181+ lock ( Cache )
138182 {
139- font ? . Dispose ( ) ;
183+ Cache . Clear ( ) ;
140184 }
141-
142- LoadedFonts . Clear ( ) ;
143- Cache . Clear ( ) ;
144185 }
145186}
146187
0 commit comments