diff --git a/samples/ControlCatalog.Desktop/Program.cs b/samples/ControlCatalog.Desktop/Program.cs index a2048005a4..df17aabb00 100644 --- a/samples/ControlCatalog.Desktop/Program.cs +++ b/samples/ControlCatalog.Desktop/Program.cs @@ -3,6 +3,7 @@ using System.Linq; using Avalonia; using Avalonia.Controls; using Avalonia.Logging.Serilog; +using Avalonia.Media; using Avalonia.Platform; using Serilog; @@ -15,6 +16,7 @@ namespace ControlCatalog { // TODO: Make this work with GTK/Skia/Cairo depending on command-line args // again. + AvaloniaLocator.CurrentMutable.Bind().ToConstant(new FontFamilyCache()); BuildAvaloniaApp().Start(); } @@ -28,7 +30,7 @@ namespace ControlCatalog { AvaloniaLocator.CurrentMutable .GetService() - .SetDefaultAssembly(typeof(App).Assembly); + .SetDefaultAssembly(typeof(App).Assembly); } } } diff --git a/src/Avalonia.Visuals/Media/FontFamily.cs b/src/Avalonia.Visuals/Media/FontFamily.cs index d702a311a2..9293432461 100644 --- a/src/Avalonia.Visuals/Media/FontFamily.cs +++ b/src/Avalonia.Visuals/Media/FontFamily.cs @@ -2,15 +2,15 @@ // Licensed under the MIT license. See licence.md file in the project root for full license information. using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Linq; namespace Avalonia.Media { public class FontFamily { - private IFontFamily _loadedFamily; - public FontFamily(string familyName) : this(familyName, null) { } public FontFamily(string familyName, Uri baseUri) @@ -25,30 +25,20 @@ namespace Avalonia.Media public Uri BaseUri => FontFamilyKey.BaseUri; internal FontFamilyKey FontFamilyKey { get; } - - internal IFontFamily LoadedFamily => _loadedFamily ?? (_loadedFamily = AvaloniaLocator.Current - .GetService() - .LoadFontFamily(FontFamilyKey)); - - public IEnumerable AvailableTypefaces => LoadedFamily.SupportedTypefaces; } public class FamilyTypeface { - public FamilyTypeface() - { - FontStyle = FontStyle.Normal; - FontWeight = FontWeight.Normal; - } - - public FamilyTypeface(Typeface typeface) + public FamilyTypeface(Uri resourceUri = null, FontWeight fontWeight = FontWeight.Normal, FontStyle fontStyle = FontStyle.Normal) { - FontStyle = typeface.Style; - FontWeight = typeface.Weight; + ResourceUri = resourceUri; + FontStyle = fontStyle; + FontWeight = fontWeight; } - public FontStyle FontStyle { get; } + public Uri ResourceUri { get; } public FontWeight FontWeight { get; } + public FontStyle FontStyle { get; } } public class FontFamilyKey @@ -64,39 +54,118 @@ namespace Avalonia.Media public string FriendlyName { get; } public Uri BaseUri { get; } - } - internal interface IFontFamily - { - IEnumerable SupportedTypefaces { get; } + public override int GetHashCode() + { + unchecked + { + var hash = (int)2166136261; + + if (FriendlyName != null) + { + hash = (hash * 16777619) ^ FriendlyName.GetHashCode(); + } + + if (BaseUri != null) + { + hash = (hash * 16777619) ^ BaseUri.GetHashCode(); + } + + return hash; + } + } + + public override bool Equals(object obj) + { + if (!(obj is FontFamilyKey other)) return false; + + if (FriendlyName != other.FriendlyName) return false; + + if (BaseUri != other.BaseUri) return false; + + return true; + } } - internal class SystemFont : IFontFamily + public class FamilyTypefaceKey { - public SystemFont() : this(new List { new FamilyTypeface() }) { } + public FamilyTypefaceKey(FontWeight fontWeight = FontWeight.Normal, FontStyle fontStyle = FontStyle.Normal) + { + FontWeight = fontWeight; + FontStyle = fontStyle; + } + + public FontWeight FontWeight { get; } + + public FontStyle FontStyle { get; } - public SystemFont(IEnumerable supportedTypefaces) + public override int GetHashCode() { - SupportedTypefaces = new ReadOnlyCollection(new List(supportedTypefaces)); + unchecked + { + var hash = (int)2166136261; + + hash = (hash * 16777619) ^ FontWeight.GetHashCode(); + + hash = (hash * 16777619) ^ FontStyle.GetHashCode(); + + return hash; + } } - public IEnumerable SupportedTypefaces { get; } + public override bool Equals(object obj) + { + if (!(obj is FamilyTypefaceKey other)) return false; + + if (FontWeight != other.FontWeight) return false; + + if (FontStyle != other.FontStyle) return false; + + return true; + } } - internal class CustomFont : IFontFamily + public class CachedFontFamily { - public CustomFont() : this(new List { new FamilyTypeface() }) { } + private readonly ConcurrentDictionary _typefaces = + new ConcurrentDictionary(); + + public IEnumerable SupportedTypefaces => _typefaces.Values; + + public bool TryGetFamilyTypeface(out FamilyTypeface typeface, FontWeight fontWeight = FontWeight.Normal, + FontStyle fontStyle = FontStyle.Normal) + { + return _typefaces.TryGetValue(new FamilyTypefaceKey(fontWeight, fontStyle), out typeface); + } - public CustomFont(IEnumerable supportedTypefaces) + public FamilyTypeface GetOrAddFamilyTypeface(Uri resourceUri, FontWeight fontWeight = FontWeight.Normal, FontStyle fontStyle = FontStyle.Normal) { - SupportedTypefaces = new ReadOnlyCollection(new List(supportedTypefaces)); + return _typefaces.GetOrAdd(new FamilyTypefaceKey(fontWeight, fontStyle), x => CreateFamilyTypeface(x, resourceUri)); } - public IEnumerable SupportedTypefaces { get; } + private static FamilyTypeface CreateFamilyTypeface(FamilyTypefaceKey familyTypefaceKey, Uri resourceUri) + { + return new FamilyTypeface(resourceUri, familyTypefaceKey.FontWeight, familyTypefaceKey.FontStyle); + } + } + + public interface IFontFamilyCache + { + CachedFontFamily GetOrAddFontFamily(FontFamily fontFamily); } - internal interface IFontFamilyLoader + public class FontFamilyCache : IFontFamilyCache { - IFontFamily LoadFontFamily(FontFamilyKey fontFamilyKey); + private readonly ConcurrentDictionary _cachedFontFamilies = new ConcurrentDictionary(); + + public CachedFontFamily GetOrAddFontFamily(FontFamily fontFamily) + { + return _cachedFontFamilies.GetOrAdd(fontFamily.FontFamilyKey, CreateCachedFontFamily); + } + + private static CachedFontFamily CreateCachedFontFamily(FontFamilyKey fontFamilyKey) + { + return new CachedFontFamily(); + } } } diff --git a/src/Markup/Avalonia.Markup.Xaml/Converters/FontFamilyTypeConverter.cs b/src/Markup/Avalonia.Markup.Xaml/Converters/FontFamilyTypeConverter.cs index 51d35fabff..6689b47c2f 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Converters/FontFamilyTypeConverter.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Converters/FontFamilyTypeConverter.cs @@ -20,21 +20,55 @@ namespace Avalonia.Markup.Xaml.Converters var fontFamilyExpression = s.Split('#'); + string familyName; + + Uri baseUri = null; + + var fontWeight = FontWeight.Normal; + + var fontStyle = FontStyle.Normal; + switch (fontFamilyExpression.Length) { case 1: { - return new FontFamily(fontFamilyExpression[0]); + familyName = fontFamilyExpression[0]; + break; } case 2: { - return new FontFamily(fontFamilyExpression[1], new Uri(fontFamilyExpression[0], UriKind.RelativeOrAbsolute)); + baseUri = new Uri(fontFamilyExpression[0], UriKind.RelativeOrAbsolute); + familyName = fontFamilyExpression[1]; + break; } + //case 3: + // { + // baseUri = new Uri(fontFamilyExpression[0], UriKind.RelativeOrAbsolute); + // familyName = fontFamilyExpression[1]; + // fontWeight = (FontWeight)Enum.Parse(typeof(FontWeight), fontFamilyExpression[2]); + // break; + // } + //case 4: + // { + // baseUri = new Uri(fontFamilyExpression[0], UriKind.RelativeOrAbsolute); + // familyName = fontFamilyExpression[1]; + // fontWeight = (FontWeight)Enum.Parse(typeof(FontWeight), fontFamilyExpression[2]); + // fontStyle = (FontStyle)Enum.Parse(typeof(FontStyle), fontFamilyExpression[3]); + // break; + // } default: { throw new ArgumentException("Specified family is not supported."); } } + + var fontFamily = new FontFamily(familyName, baseUri); + + var cachedFontFamily = AvaloniaLocator.Current.GetService().GetOrAddFontFamily(fontFamily); + + cachedFontFamily.GetOrAddFamilyTypeface(baseUri, fontWeight, fontStyle); + + return fontFamily; } } } \ No newline at end of file diff --git a/src/Windows/Avalonia.Direct2D1/Media/FormattedTextImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/FormattedTextImpl.cs index 2b3aa31c64..20fc956b5d 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/FormattedTextImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/FormattedTextImpl.cs @@ -28,7 +28,11 @@ namespace Avalonia.Direct2D1.Media if (typeface.FontFamily.BaseUri != null) { - var fontLoader = new ResourceFontLoader(factory, typeface.FontFamily.BaseUri); + var fontFamily = AvaloniaLocator.Current.GetService().GetOrAddFontFamily(typeface.FontFamily); + + fontFamily.TryGetFamilyTypeface(out var familyTypeface); + + var fontLoader = new ResourceFontLoader(factory, familyTypeface.ResourceUri); var fontCollection = new DWrite.FontCollection(factory, fontLoader, fontLoader.Key);