Ver código fonte

Merge pull request #2162 from Gillibald/feature/SkiaTypefaceFallback

Skia: FontWeight and FontStyle fallback for embedded fonts
Jumar Macato 6 anos atrás
pai
commit
b4fae84e81

+ 61 - 18
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<FontKey, SKTypeface> _cachedTypefaces =
-            new ConcurrentDictionary<FontKey, SKTypeface>();
+        private readonly ConcurrentDictionary<string, ConcurrentDictionary<FontKey, SKTypeface>> _fontFamilies =
+            new ConcurrentDictionary<string, ConcurrentDictionary<FontKey, SKTypeface>>();
 
         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<FontKey, SKTypeface>();
+
+                _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<FontKey, SKTypeface> 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;
             }
         }

+ 3 - 0
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;