浏览代码

Direct2D1 caching

Benedikt Schroeder 7 年之前
父节点
当前提交
1ace429342

+ 0 - 1
samples/ControlCatalog.Desktop/Program.cs

@@ -16,7 +16,6 @@ namespace ControlCatalog
         {
             // TODO: Make this work with GTK/Skia/Cairo depending on command-line args
             // again.
-            AvaloniaLocator.CurrentMutable.Bind<IFontFamilyCache>().ToConstant(new FontFamilyCache());
             BuildAvaloniaApp().Start<MainWindow>();
         }
 

二进制
samples/ControlCatalog/Assets/octin_stencil_free.ttf


+ 1 - 0
samples/ControlCatalog/ControlCatalog.csproj

@@ -13,6 +13,7 @@
   </ItemGroup>
   <ItemGroup>
     <None Remove="Assets\kooten.ttf" />
+    <None Remove="Assets\octin_stencil_free.ttf" />
   </ItemGroup>
 
   <ItemGroup>

+ 2 - 2
samples/ControlCatalog/Pages/BorderPage.xaml

@@ -27,8 +27,8 @@
               Padding="16">
                 <StackPanel>
                     <TextBlock FontSize="14">Rounded Corners</TextBlock>
-                    <TextBlock FontWeight="Bold" FontSize="14" FontFamily="Arial">Rounded Corners</TextBlock>
-                    <TextBlock FontSize="14" FontFamily="res:///ControlCatalog/Assets/kooten.ttf#Kootenay">Rounded Corners</TextBlock>
+                    <TextBlock FontSize="14" FontFamily="resm:ControlCatalog.Assets.octin_stencil_free.ttf#Octin Stencil Free">Rounded Corners</TextBlock>
+                    <TextBlock FontSize="14" FontFamily="resm:ControlCatalog.Assets.kooten.ttf#Kootenay">Rounded Corners</TextBlock>
                 </StackPanel>
             </Border>
         </StackPanel>

+ 107 - 100
src/Avalonia.Visuals/Media/FontFamily.cs

@@ -2,50 +2,33 @@
 // 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
     {
-        public FontFamily(string familyName) : this(familyName, null) { }
-
-        public FontFamily(string familyName, Uri baseUri)
+        public FontFamily(string familyName = "Courier New", Uri baseUri = null)
         {
             if (familyName == null) throw new ArgumentNullException(nameof(familyName));
 
-            FontFamilyKey = new FontFamilyKey(familyName, baseUri);
+            Key = new FontFamilyKey(familyName, baseUri);
         }
 
-        public string Name => FontFamilyKey.FriendlyName;
+        public string Name => Key.FriendlyName;
 
-        public Uri BaseUri => FontFamilyKey.BaseUri;
+        public Uri BaseUri => Key.BaseUri;
 
-        internal FontFamilyKey FontFamilyKey { get; }
-    }
+        public FontFamilyKey Key { get; }
 
-    public class FamilyTypeface
-    {
-        public FamilyTypeface(Uri resourceUri = null, FontWeight fontWeight = FontWeight.Normal, FontStyle fontStyle = FontStyle.Normal)
+        public override string ToString()
         {
-            ResourceUri = resourceUri;
-            FontStyle = fontStyle;
-            FontWeight = fontWeight;
+            return Key.ToString();
         }
-
-        public Uri ResourceUri { get; }
-        public FontWeight FontWeight { get; }
-        public FontStyle FontStyle { get; }
     }
 
     public class FontFamilyKey
     {
-        public FontFamilyKey(string friendlyName) : this(friendlyName, null) { }
-
-        public FontFamilyKey(string friendlyName, Uri baseUri)
+        public FontFamilyKey(string friendlyName, Uri baseUri = null)
         {
             FriendlyName = friendlyName;
             BaseUri = baseUri;
@@ -85,87 +68,111 @@ namespace Avalonia.Media
 
             return true;
         }
-    }
 
-    public class FamilyTypefaceKey
-    {
-        public FamilyTypefaceKey(FontWeight fontWeight = FontWeight.Normal, FontStyle fontStyle = FontStyle.Normal)
-        {
-            FontWeight = fontWeight;
-            FontStyle = fontStyle;
-        }
-
-        public FontWeight FontWeight { get; }
-
-        public FontStyle FontStyle { get; }
-
-        public override int GetHashCode()
+        public override string ToString()
         {
-            unchecked
+            if (BaseUri != null)
             {
-                var hash = (int)2166136261;
-
-                hash = (hash * 16777619) ^ FontWeight.GetHashCode();
-
-                hash = (hash * 16777619) ^ FontStyle.GetHashCode();
-
-                return hash;
+                return BaseUri + "#" + FriendlyName;
             }
-        }
-
-        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;
+            return FriendlyName;
         }
     }
 
-    public class CachedFontFamily
-    {
-        private readonly ConcurrentDictionary<FamilyTypefaceKey, FamilyTypeface> _typefaces =
-            new ConcurrentDictionary<FamilyTypefaceKey, FamilyTypeface>();
-
-        public IEnumerable<FamilyTypeface> 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 FamilyTypeface GetOrAddFamilyTypeface(Uri resourceUri, FontWeight fontWeight = FontWeight.Normal, FontStyle fontStyle = FontStyle.Normal)
-        {
-            return _typefaces.GetOrAdd(new FamilyTypefaceKey(fontWeight, fontStyle), x => CreateFamilyTypeface(x, resourceUri));
-        }
-
-        private static FamilyTypeface CreateFamilyTypeface(FamilyTypefaceKey familyTypefaceKey, Uri resourceUri)
-        {
-            return new FamilyTypeface(resourceUri, familyTypefaceKey.FontWeight, familyTypefaceKey.FontStyle);
-        }
-    }
-
-    public interface IFontFamilyCache
-    {
-        CachedFontFamily GetOrAddFontFamily(FontFamily fontFamily);
-    }
-
-    public class FontFamilyCache : IFontFamilyCache
-    {
-        private readonly ConcurrentDictionary<FontFamilyKey, CachedFontFamily> _cachedFontFamilies = new ConcurrentDictionary<FontFamilyKey, CachedFontFamily>();
-
-        public CachedFontFamily GetOrAddFontFamily(FontFamily fontFamily)
-        {
-            return _cachedFontFamilies.GetOrAdd(fontFamily.FontFamilyKey, CreateCachedFontFamily);
-        }
-
-        private static CachedFontFamily CreateCachedFontFamily(FontFamilyKey fontFamilyKey)
-        {
-            return new CachedFontFamily();
-        }
-    }
+    //public class FamilyTypeface
+    //{
+    //    public FamilyTypeface(Uri resourceUri = null, FontWeight fontWeight = FontWeight.Normal, FontStyle fontStyle = FontStyle.Normal)
+    //    {
+    //        ResourceUri = resourceUri;
+    //        FontStyle = fontStyle;
+    //        FontWeight = fontWeight;
+    //    }
+
+    //    public Uri ResourceUri { get; }
+    //    public FontWeight FontWeight { get; }
+    //    public FontStyle FontStyle { get; }
+    //}
+
+    //public class FamilyTypefaceKey
+    //{
+    //    public FamilyTypefaceKey(FontWeight fontWeight = FontWeight.Normal, FontStyle fontStyle = FontStyle.Normal)
+    //    {
+    //        FontWeight = fontWeight;
+    //        FontStyle = fontStyle;
+    //    }
+
+    //    public FontWeight FontWeight { get; }
+
+    //    public FontStyle FontStyle { get; }
+
+    //    public override int GetHashCode()
+    //    {
+    //        unchecked
+    //        {
+    //            var hash = (int)2166136261;
+
+    //            hash = (hash * 16777619) ^ FontWeight.GetHashCode();
+
+    //            hash = (hash * 16777619) ^ FontStyle.GetHashCode();
+
+    //            return hash;
+    //        }
+    //    }
+
+    //    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;
+    //    }
+    //}
+
+    //public class CachedFontFamily
+    //{
+    //    private readonly ConcurrentDictionary<FamilyTypefaceKey, FamilyTypeface> _typefaces =
+    //        new ConcurrentDictionary<FamilyTypefaceKey, FamilyTypeface>();
+
+    //    public bool TryGetFamilyTypeface(out FamilyTypeface typeface, FontWeight fontWeight = FontWeight.Normal,
+    //        FontStyle fontStyle = FontStyle.Normal)
+    //    {
+    //        return _typefaces.TryGetValue(new FamilyTypefaceKey(fontWeight, fontStyle), out typeface);
+    //    }
+
+    //    public bool TryAddFamilyTypeface(Uri resourceUri, FontWeight fontWeight = FontWeight.Normal, FontStyle fontStyle = FontStyle.Normal)
+    //    {
+    //        var familyTypefaceKeytypefaceKey = new FamilyTypefaceKey(fontWeight, fontStyle);
+
+    //        return _typefaces.TryAdd(familyTypefaceKeytypefaceKey, CreateFamilyTypeface(familyTypefaceKeytypefaceKey, resourceUri));
+    //    }
+
+    //    private static FamilyTypeface CreateFamilyTypeface(FamilyTypefaceKey familyTypefaceKey, Uri resourceUri)
+    //    {
+    //        return new FamilyTypeface(resourceUri, familyTypefaceKey.FontWeight, familyTypefaceKey.FontStyle);
+    //    }
+    //}
+
+    //public class FontFamilyCache
+    //{
+    //    private readonly ConcurrentDictionary<FontFamilyKey, CachedFontFamily> _cachedFontFamilies = new ConcurrentDictionary<FontFamilyKey, CachedFontFamily>();
+
+    //    public bool TryGetCachedFontFamily(FontFamily fontFamily, out CachedFontFamily cachedFontFamily)
+    //    {
+    //        return _cachedFontFamilies.TryGetValue(fontFamily.Key, out cachedFontFamily);
+    //    }
+
+    //    public CachedFontFamily GetOrAddCachedFontFamily(FontFamily fontFamily)
+    //    {
+    //        return _cachedFontFamilies.GetOrAdd(fontFamily.Key, CreateCachedFontFamily);
+    //    }
+
+    //    private static CachedFontFamily CreateCachedFontFamily(FontFamilyKey fontFamilyKey)
+    //    {
+    //        return new CachedFontFamily();
+    //    }
+    //}
 }

+ 3 - 26
src/Markup/Avalonia.Markup.Xaml/Converters/FontFamilyTypeConverter.cs

@@ -18,16 +18,14 @@ namespace Avalonia.Markup.Xaml.Converters
 
             if (string.IsNullOrEmpty(s)) throw new ArgumentException("Specified family is not supported.");
 
+            if (!s.StartsWith("resm:")) return new FontFamily(s);
+
             var fontFamilyExpression = s.Split('#');
 
             string familyName;
 
             Uri baseUri = null;
 
-            var fontWeight = FontWeight.Normal;
-
-            var fontStyle = FontStyle.Normal;
-
             switch (fontFamilyExpression.Length)
             {
                 case 1:
@@ -41,34 +39,13 @@ namespace Avalonia.Markup.Xaml.Converters
                         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<IFontFamilyCache>().GetOrAddFontFamily(fontFamily);
-
-            cachedFontFamily.GetOrAddFamilyTypeface(baseUri, fontWeight, fontStyle);
-
-            return fontFamily;
+            return new FontFamily(familyName, baseUri);
         }
     }
 }

+ 68 - 84
src/Windows/Avalonia.Direct2D1/Media/FormattedTextImpl.cs

@@ -2,8 +2,8 @@
 // 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.IO;
 using System.Linq;
 using Avalonia.Media;
 using Avalonia.Platform;
@@ -26,59 +26,46 @@ namespace Avalonia.Direct2D1.Media
 
             var factory = AvaloniaLocator.Current.GetService<DWrite.Factory>();
 
+            DWrite.TextFormat textFormat;
+
             if (typeface.FontFamily.BaseUri != null)
             {
-                var fontFamily = AvaloniaLocator.Current.GetService<IFontFamilyCache>().GetOrAddFontFamily(typeface.FontFamily);
-
-                fontFamily.TryGetFamilyTypeface(out var familyTypeface);
-
-                var fontLoader = new ResourceFontLoader(factory, familyTypeface.ResourceUri);
+                var fontCollection = Direct2D1CustomFontResourceCache.GetOrAddCustomFontResource(typeface.FontFamily, factory);
 
-                var fontCollection = new DWrite.FontCollection(factory, fontLoader, fontLoader.Key);
-
-                using (var textFormat =
-                    new DWrite.TextFormat(factory, typeface.FontFamily.Name, fontCollection, DWrite.FontWeight.Normal,
-                        DWrite.FontStyle.Normal, DWrite.FontStretch.Normal, (float)typeface.FontSize))
-                {
-                    textFormat.TextAlignment = DWrite.TextAlignment.Center;
-                    textFormat.ParagraphAlignment = DWrite.ParagraphAlignment.Center;
-
-                    textFormat.WordWrapping = wrapping == TextWrapping.Wrap ?
-                        DWrite.WordWrapping.Wrap :
-                        DWrite.WordWrapping.NoWrap;
-
-                    TextLayout = new DWrite.TextLayout(factory, Text ?? string.Empty, textFormat, (float)constraint.Width,
-                        (float)constraint.Height)
-                    {
-                        TextAlignment = textAlignment.ToDirect2D()
-                    };
-                }
+                textFormat = new DWrite.TextFormat(
+                        factory,
+                        typeface.FontFamily.Name,
+                        fontCollection,
+                        (DWrite.FontWeight)typeface.Weight,
+                        (DWrite.FontStyle)typeface.Style,
+                        DWrite.FontStretch.Normal,
+                        (float)typeface.FontSize);
             }
             else
             {
-                using (var format = new DWrite.TextFormat(
+                textFormat = new DWrite.TextFormat(
                     factory,
-                    typeface?.FontFamily.Name ?? "Courier New",
-                    (DWrite.FontWeight)(typeface.Weight),
-                    (DWrite.FontStyle)(typeface.Style),
-                    (float)typeface.FontSize))
-                {
-                    format.WordWrapping = wrapping == TextWrapping.Wrap ?
-                        DWrite.WordWrapping.Wrap :
-                        DWrite.WordWrapping.NoWrap;
-
-                    TextLayout = new DWrite.TextLayout(
-                        factory,
-                        text ?? string.Empty,
-                        format,
-                        (float)constraint.Width,
-                        (float)constraint.Height)
-                    {
-                        TextAlignment = textAlignment.ToDirect2D()
-                    };
-                }
+                    typeface.FontFamily.Name,
+                    (DWrite.FontWeight)typeface.Weight,
+                    (DWrite.FontStyle)typeface.Style,
+                    (float)typeface.FontSize);
             }
 
+            textFormat.TextAlignment = DWrite.TextAlignment.Center;
+            textFormat.ParagraphAlignment = DWrite.ParagraphAlignment.Center;
+
+            textFormat.WordWrapping = wrapping == TextWrapping.Wrap ?
+                DWrite.WordWrapping.Wrap :
+                DWrite.WordWrapping.NoWrap;
+
+            TextLayout = new DWrite.TextLayout(factory, Text ?? string.Empty, textFormat, (float)constraint.Width,
+                (float)constraint.Height)
+            {
+                TextAlignment = textAlignment.ToDirect2D()
+            };
+
+            textFormat.Dispose();
+
             if (spans != null)
             {
                 foreach (var span in spans)
@@ -98,10 +85,10 @@ namespace Avalonia.Direct2D1.Media
 
         public DWrite.TextLayout TextLayout { get; }
 
-        public void Dispose()
-        {
-            TextLayout.Dispose();
-        }
+        //public void Dispose()
+        //{
+        //    TextLayout.Dispose();
+        //}
 
         public IEnumerable<FormattedTextLine> GetLines()
         {
@@ -111,14 +98,11 @@ namespace Avalonia.Direct2D1.Media
 
         public TextHitTestResult HitTestPoint(Point point)
         {
-            SharpDX.Mathematics.Interop.RawBool isTrailingHit;
-            SharpDX.Mathematics.Interop.RawBool isInside;
-
             var result = TextLayout.HitTestPoint(
                 (float)point.X,
                 (float)point.Y,
-                out isTrailingHit,
-                out isInside);
+                out var isTrailingHit,
+                out var isInside);
 
             return new TextHitTestResult
             {
@@ -130,14 +114,7 @@ namespace Avalonia.Direct2D1.Media
 
         public Rect HitTestTextPosition(int index)
         {
-            float x;
-            float y;
-
-            var result = TextLayout.HitTestTextPosition(
-                index,
-                false,
-                out x,
-                out y);
+            var result = TextLayout.HitTestTextPosition(index, false, out _, out _);
 
             return new Rect(result.Left, result.Top, result.Width, result.Height);
         }
@@ -175,12 +152,29 @@ namespace Avalonia.Direct2D1.Media
         }
     }
 
+    internal static class Direct2D1CustomFontResourceCache
+    {
+        private static readonly ConcurrentDictionary<FontFamilyKey, DWrite.FontCollection> s_cachedFonts =
+            new ConcurrentDictionary<FontFamilyKey, DWrite.FontCollection>();
+
+        public static DWrite.FontCollection GetOrAddCustomFontResource(FontFamily fontFamily, DWrite.Factory factory)
+        {
+            return s_cachedFonts.GetOrAdd(fontFamily.Key, x => CreateCustomFontResource(x, factory));
+        }
+
+        private static DWrite.FontCollection CreateCustomFontResource(FontFamilyKey fontFamilyKey, DWrite.Factory factory)
+        {
+            var fontLoader = new ResourceFontLoader(factory, fontFamilyKey.BaseUri);
+
+            return new DWrite.FontCollection(factory, fontLoader, fontLoader.Key);
+        }
+    }
+
     public class ResourceFontLoader : CallbackBase, DWrite.FontCollectionLoader, DWrite.FontFileLoader
     {
         private readonly List<ResourceFontFileStream> _fontStreams = new List<ResourceFontFileStream>();
         private readonly List<ResourceFontFileEnumerator> _enumerators = new List<ResourceFontFileEnumerator>();
         private readonly DataStream _keyStream;
-        private readonly DWrite.Factory _factory;
 
 
         /// <summary>
@@ -190,7 +184,7 @@ namespace Avalonia.Direct2D1.Media
         /// <param name="fontResource"></param>
         public ResourceFontLoader(DWrite.Factory factory, Uri fontResource)
         {
-            _factory = factory;
+            var factory1 = factory;
 
             var assets = AvaloniaLocator.Current.GetService<IAssetLoader>();
 
@@ -215,8 +209,8 @@ namespace Avalonia.Direct2D1.Media
             _keyStream.Position = 0;
 
             // Register the 
-            _factory.RegisterFontFileLoader(this);
-            _factory.RegisterFontCollectionLoader(this);
+            factory1.RegisterFontFileLoader(this);
+            factory1.RegisterFontCollectionLoader(this);
         }
 
 
@@ -224,13 +218,7 @@ namespace Avalonia.Direct2D1.Media
         /// Gets the key used to identify the FontCollection as well as storing index for fonts.
         /// </summary>
         /// <value>The key.</value>
-        public DataStream Key
-        {
-            get
-            {
-                return _keyStream;
-            }
-        }
+        public DataStream Key => _keyStream;
 
         /// <summary>
         /// Creates a font file enumerator object that encapsulates a collection of font files. The font system calls back to this interface to create a font collection.
@@ -282,7 +270,7 @@ namespace Avalonia.Direct2D1.Media
         /// <param name="stream">The stream.</param>
         public ResourceFontFileStream(DataStream stream)
         {
-            this._stream = stream;
+            _stream = stream;
         }
 
         /// <summary>
@@ -384,19 +372,15 @@ namespace Avalonia.Direct2D1.Media
         {
             bool moveNext = keyStream.RemainingLength != 0;
 
-            if (moveNext)
-            {
-                if (_currentFontFile != null)
-                {
-                    _currentFontFile.Dispose();
-                }
+            if (!moveNext) return false;
 
-                _currentFontFile = new DWrite.FontFile(_factory, keyStream.PositionPointer, 4, _loader);
+            _currentFontFile?.Dispose();
 
-                keyStream.Position += 4;
-            }
+            _currentFontFile = new DWrite.FontFile(_factory, keyStream.PositionPointer, 4, _loader);
+
+            keyStream.Position += 4;
 
-            return moveNext;
+            return true;
         }
 
         /// <summary>