diff --git a/src/Skia/Avalonia.Skia/SKTypefaceCollection.cs b/src/Skia/Avalonia.Skia/SKTypefaceCollection.cs index 687fe7feca..17c51dbb6e 100644 --- a/src/Skia/Avalonia.Skia/SKTypefaceCollection.cs +++ b/src/Skia/Avalonia.Skia/SKTypefaceCollection.cs @@ -1,72 +1,115 @@ +// Copyright (c) The Avalonia Project. All rights reserved. +// Licensed under the MIT license. See licence.md file in the project root for full license information. + using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; + using Avalonia.Media; + using SkiaSharp; namespace Avalonia.Skia { internal class SKTypefaceCollection { - private readonly ConcurrentDictionary _cachedTypefaces = - new ConcurrentDictionary(); + private readonly ConcurrentDictionary> _fontFamilies = + new ConcurrentDictionary>(); public void AddTypeFace(SKTypeface typeface) { - var key = new FontKey(typeface.FamilyName, (SKFontStyleWeight)typeface.FontWeight, typeface.FontSlant); + var key = new FontKey((SKFontStyleWeight)typeface.FontWeight, typeface.FontSlant); - _cachedTypefaces.TryAdd(key, typeface); + if (!_fontFamilies.TryGetValue(typeface.FamilyName, out var fontFamily)) + { + fontFamily = new ConcurrentDictionary(); + + _fontFamilies.TryAdd(typeface.FamilyName, fontFamily); + } + + fontFamily.TryAdd(key, typeface); } public SKTypeface GetTypeFace(Typeface typeface) { - SKFontStyleSlant skStyle = SKFontStyleSlant.Upright; + var styleSlant = SKFontStyleSlant.Upright; switch (typeface.Style) { case FontStyle.Italic: - skStyle = SKFontStyleSlant.Italic; + styleSlant = SKFontStyleSlant.Italic; break; case FontStyle.Oblique: - skStyle = SKFontStyleSlant.Oblique; + styleSlant = SKFontStyleSlant.Oblique; break; } - var key = new FontKey(typeface.FontFamily.Name, (SKFontStyleWeight)typeface.Weight, skStyle); + if (!_fontFamilies.TryGetValue(typeface.FontFamily.Name, out var fontFamily)) + { + return TypefaceCache.Default; + } + + var weight = (SKFontStyleWeight)typeface.Weight; + + var key = new FontKey(weight, styleSlant); + + return fontFamily.GetOrAdd(key, GetFallback(fontFamily, key)); + } + + private static SKTypeface GetFallback(IDictionary fontFamily, FontKey key) + { + var keys = fontFamily.Keys.Where( + x => ((int)x.Weight <= (int)key.Weight || (int)x.Weight > (int)key.Weight) && x.Slant == key.Slant).ToArray(); + + if (!keys.Any()) + { + keys = fontFamily.Keys.Where( + x => x.Weight == key.Weight && (x.Slant >= key.Slant || x.Slant < key.Slant)).ToArray(); + + if (!keys.Any()) + { + keys = fontFamily.Keys.Where( + x => ((int)x.Weight <= (int)key.Weight || (int)x.Weight > (int)key.Weight) && + (x.Slant >= key.Slant || x.Slant < key.Slant)).ToArray(); + } + } + + key = keys.FirstOrDefault(); + + fontFamily.TryGetValue(key, out var typeface); - return _cachedTypefaces.TryGetValue(key, out var skTypeface) ? skTypeface : TypefaceCache.Default; + return typeface; } private struct FontKey { - public readonly string Name; public readonly SKFontStyleSlant Slant; public readonly SKFontStyleWeight Weight; - public FontKey(string name, SKFontStyleWeight weight, SKFontStyleSlant slant) + public FontKey(SKFontStyleWeight weight, SKFontStyleSlant slant) { - Name = name; Slant = slant; Weight = weight; } public override int GetHashCode() { - int hash = 17; - hash = hash * 31 + Name.GetHashCode(); - hash = hash * 31 + (int)Slant; - hash = hash * 31 + (int)Weight; + var hash = 17; + hash = (hash * 31) + (int)Slant; + hash = (hash * 31) + (int)Weight; return hash; } public override bool Equals(object other) { - return other is FontKey ? Equals((FontKey)other) : false; + return other is FontKey key && this.Equals(key); } private bool Equals(FontKey other) { - return Name == other.Name && Slant == other.Slant && + return Slant == other.Slant && Weight == other.Weight; } } diff --git a/src/Skia/Avalonia.Skia/SKTypefaceCollectionCache.cs b/src/Skia/Avalonia.Skia/SKTypefaceCollectionCache.cs index b9a8c9b98b..ab8ee85a54 100644 --- a/src/Skia/Avalonia.Skia/SKTypefaceCollectionCache.cs +++ b/src/Skia/Avalonia.Skia/SKTypefaceCollectionCache.cs @@ -1,3 +1,6 @@ +// Copyright (c) The Avalonia Project. All rights reserved. +// Licensed under the MIT license. See licence.md file in the project root for full license information. + using System.Collections.Concurrent; using Avalonia.Media; using Avalonia.Media.Fonts;