From 04c781ca1431b3bc3d741d9ea864ad00569c33a9 Mon Sep 17 00:00:00 2001 From: Benedikt Schroeder Date: Tue, 8 Sep 2020 14:55:10 +0200 Subject: [PATCH] Make sure GlyphTypefaces are always cached --- Avalonia.sln | 28 +++++++- .../Presenters/TextPresenter.cs | 4 +- src/Avalonia.Controls/TextBlock.cs | 2 +- .../HeadlessPlatformStubs.cs | 4 +- src/Avalonia.Visuals/ApiCompatBaseline.txt | 12 +++- src/Avalonia.Visuals/Media/FontManager.cs | 67 ++++++------------- src/Avalonia.Visuals/Media/Fonts/FontKey.cs | 40 ----------- .../Media/TextFormatting/TextCharacters.cs | 7 +- src/Avalonia.Visuals/Media/Typeface.cs | 31 ++------- .../Platform/IFontManagerImpl.cs | 5 +- .../Rendering/RendererBase.cs | 2 +- src/Skia/Avalonia.Skia/FontManagerImpl.cs | 7 +- .../Avalonia.Skia/SKTypefaceCollection.cs | 19 +++--- .../SKTypefaceCollectionCache.cs | 2 +- .../Media/FontManagerImpl.cs | 7 +- .../Media/FormattedTextImplTests.cs | 2 +- .../Media/CustomFontManagerImpl.cs | 8 +-- .../TextFormatting/TextFormatterTests.cs | 2 +- .../Avalonia.UnitTests/MockFontManagerImpl.cs | 5 +- .../Media/FontManagerTests.cs | 7 +- 20 files changed, 103 insertions(+), 158 deletions(-) delete mode 100644 src/Avalonia.Visuals/Media/Fonts/FontKey.cs diff --git a/Avalonia.sln b/Avalonia.sln index 66777f33eb..ddcd61408d 100644 --- a/Avalonia.sln +++ b/Avalonia.sln @@ -222,9 +222,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Headless.Vnc", "sr EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Markup.Xaml.Loader", "src\Markup\Avalonia.Markup.Xaml.Loader\Avalonia.Markup.Xaml.Loader.csproj", "{909A8CBD-7D0E-42FD-B841-022AD8925820}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.ReactiveUI.Events", "src\Avalonia.ReactiveUI.Events\Avalonia.ReactiveUI.Events.csproj", "{28F18757-C3E6-4BBE-A37D-11BA2AB9177C}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.ReactiveUI.Events", "src\Avalonia.ReactiveUI.Events\Avalonia.ReactiveUI.Events.csproj", "{28F18757-C3E6-4BBE-A37D-11BA2AB9177C}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.ReactiveUI.Events.UnitTests", "tests\Avalonia.ReactiveUI.Events.UnitTests\Avalonia.ReactiveUI.Events.UnitTests.csproj", "{780AC0FE-8DD2-419F-A1DC-AC7E3EB393F7}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.ReactiveUI.Events.UnitTests", "tests\Avalonia.ReactiveUI.Events.UnitTests\Avalonia.ReactiveUI.Events.UnitTests.csproj", "{780AC0FE-8DD2-419F-A1DC-AC7E3EB393F7}" EndProject Global GlobalSection(SharedMSBuildProjectFiles) = preSolution @@ -2040,6 +2040,30 @@ Global {28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.Release|iPhone.Build.0 = Release|Any CPU {28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU {28F18757-C3E6-4BBE-A37D-11BA2AB9177C}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {780AC0FE-8DD2-419F-A1DC-AC7E3EB393F7}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU + {780AC0FE-8DD2-419F-A1DC-AC7E3EB393F7}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU + {780AC0FE-8DD2-419F-A1DC-AC7E3EB393F7}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {780AC0FE-8DD2-419F-A1DC-AC7E3EB393F7}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {780AC0FE-8DD2-419F-A1DC-AC7E3EB393F7}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {780AC0FE-8DD2-419F-A1DC-AC7E3EB393F7}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {780AC0FE-8DD2-419F-A1DC-AC7E3EB393F7}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU + {780AC0FE-8DD2-419F-A1DC-AC7E3EB393F7}.AppStore|Any CPU.Build.0 = Debug|Any CPU + {780AC0FE-8DD2-419F-A1DC-AC7E3EB393F7}.AppStore|iPhone.ActiveCfg = Debug|Any CPU + {780AC0FE-8DD2-419F-A1DC-AC7E3EB393F7}.AppStore|iPhone.Build.0 = Debug|Any CPU + {780AC0FE-8DD2-419F-A1DC-AC7E3EB393F7}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {780AC0FE-8DD2-419F-A1DC-AC7E3EB393F7}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {780AC0FE-8DD2-419F-A1DC-AC7E3EB393F7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {780AC0FE-8DD2-419F-A1DC-AC7E3EB393F7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {780AC0FE-8DD2-419F-A1DC-AC7E3EB393F7}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {780AC0FE-8DD2-419F-A1DC-AC7E3EB393F7}.Debug|iPhone.Build.0 = Debug|Any CPU + {780AC0FE-8DD2-419F-A1DC-AC7E3EB393F7}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {780AC0FE-8DD2-419F-A1DC-AC7E3EB393F7}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {780AC0FE-8DD2-419F-A1DC-AC7E3EB393F7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {780AC0FE-8DD2-419F-A1DC-AC7E3EB393F7}.Release|Any CPU.Build.0 = Release|Any CPU + {780AC0FE-8DD2-419F-A1DC-AC7E3EB393F7}.Release|iPhone.ActiveCfg = Release|Any CPU + {780AC0FE-8DD2-419F-A1DC-AC7E3EB393F7}.Release|iPhone.Build.0 = Release|Any CPU + {780AC0FE-8DD2-419F-A1DC-AC7E3EB393F7}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {780AC0FE-8DD2-419F-A1DC-AC7E3EB393F7}.Release|iPhoneSimulator.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/Avalonia.Controls/Presenters/TextPresenter.cs b/src/Avalonia.Controls/Presenters/TextPresenter.cs index 37490c3ef3..cb7bee1d33 100644 --- a/src/Avalonia.Controls/Presenters/TextPresenter.cs +++ b/src/Avalonia.Controls/Presenters/TextPresenter.cs @@ -282,7 +282,7 @@ namespace Avalonia.Controls.Presenters return new FormattedText { Constraint = constraint, - Typeface = FontManager.Current?.GetOrAddTypeface(FontFamily, FontStyle, FontWeight), + Typeface = new Typeface(FontFamily, FontStyle, FontWeight), FontSize = FontSize, Text = text ?? string.Empty, TextAlignment = TextAlignment, @@ -499,7 +499,7 @@ namespace Avalonia.Controls.Presenters return new FormattedText { Text = "X", - Typeface = FontManager.Current?.GetOrAddTypeface(FontFamily, FontStyle, FontWeight), + Typeface = new Typeface(FontFamily, FontStyle, FontWeight), FontSize = FontSize, TextAlignment = TextAlignment, Constraint = availableSize, diff --git a/src/Avalonia.Controls/TextBlock.cs b/src/Avalonia.Controls/TextBlock.cs index 7e5287f81f..3b9e9c4751 100644 --- a/src/Avalonia.Controls/TextBlock.cs +++ b/src/Avalonia.Controls/TextBlock.cs @@ -452,7 +452,7 @@ namespace Avalonia.Controls return new TextLayout( text ?? string.Empty, - FontManager.Current?.GetOrAddTypeface(FontFamily, FontStyle, FontWeight), + new Typeface(FontFamily, FontStyle, FontWeight), FontSize, Foreground, TextAlignment, diff --git a/src/Avalonia.Headless/HeadlessPlatformStubs.cs b/src/Avalonia.Headless/HeadlessPlatformStubs.cs index 763d192693..4c0e2982f4 100644 --- a/src/Avalonia.Headless/HeadlessPlatformStubs.cs +++ b/src/Avalonia.Headless/HeadlessPlatformStubs.cs @@ -155,9 +155,9 @@ namespace Avalonia.Headless return new List { "Arial" }; } - public bool TryMatchCharacter(int codepoint, FontStyle fontStyle, FontWeight fontWeight, FontFamily fontFamily, CultureInfo culture, out FontKey fontKey) + public bool TryMatchCharacter(int codepoint, FontStyle fontStyle, FontWeight fontWeight, FontFamily fontFamily, CultureInfo culture, out Typeface typeface) { - fontKey = new FontKey("Arial", fontStyle, fontWeight); + typeface = new Typeface("Arial", fontStyle, fontWeight); return true; } } diff --git a/src/Avalonia.Visuals/ApiCompatBaseline.txt b/src/Avalonia.Visuals/ApiCompatBaseline.txt index 5058cff26d..5aa497861d 100644 --- a/src/Avalonia.Visuals/ApiCompatBaseline.txt +++ b/src/Avalonia.Visuals/ApiCompatBaseline.txt @@ -1,5 +1,15 @@ Compat issues with assembly Avalonia.Visuals: +MembersMustExist : Member 'public Avalonia.Media.Typeface Avalonia.Media.FontManager.GetOrAddTypeface(Avalonia.Media.FontFamily, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight)' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public Avalonia.Media.Typeface Avalonia.Media.FontManager.MatchCharacter(System.Int32, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight, Avalonia.Media.FontFamily, System.Globalization.CultureInfo)' does not exist in the implementation but it does exist in the contract. +CannotSealType : Type 'Avalonia.Media.Typeface' is actually (has the sealed modifier) sealed in the implementation but not sealed in the contract. +TypeCannotChangeClassification : Type 'Avalonia.Media.Typeface' is a 'struct' in the implementation but is a 'class' in the contract. +CannotMakeMemberNonVirtual : Member 'public System.Boolean Avalonia.Media.Typeface.Equals(System.Object)' is non-virtual in the implementation but is virtual in the contract. +CannotMakeMemberNonVirtual : Member 'public System.Int32 Avalonia.Media.Typeface.GetHashCode()' is non-virtual in the implementation but is virtual in the contract. +TypesMustExist : Type 'Avalonia.Media.Fonts.FontKey' does not exist in the implementation but it does exist in the contract. CannotAddAbstractMembers : Member 'public Avalonia.Media.TextFormatting.TextLineBreak Avalonia.Media.TextFormatting.TextLine.TextLineBreak' is abstract in the implementation but is missing in the contract. MembersMustExist : Member 'public Avalonia.Media.TextFormatting.TextLineBreak Avalonia.Media.TextFormatting.TextLine.LineBreak.get()' does not exist in the implementation but it does exist in the contract. CannotAddAbstractMembers : Member 'public Avalonia.Media.TextFormatting.TextLineBreak Avalonia.Media.TextFormatting.TextLine.TextLineBreak.get()' is abstract in the implementation but is missing in the contract. -Total Issues: 3 +InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalonia.Platform.IFontManagerImpl.TryMatchCharacter(System.Int32, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight, Avalonia.Media.FontFamily, System.Globalization.CultureInfo, Avalonia.Media.Fonts.FontKey)' is present in the contract but not in the implementation. +MembersMustExist : Member 'public System.Boolean Avalonia.Platform.IFontManagerImpl.TryMatchCharacter(System.Int32, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight, Avalonia.Media.FontFamily, System.Globalization.CultureInfo, Avalonia.Media.Fonts.FontKey)' does not exist in the implementation but it does exist in the contract. +InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalonia.Platform.IFontManagerImpl.TryMatchCharacter(System.Int32, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight, Avalonia.Media.FontFamily, System.Globalization.CultureInfo, Avalonia.Media.Typeface)' is present in the implementation but not in the contract. +Total Issues: 13 diff --git a/src/Avalonia.Visuals/Media/FontManager.cs b/src/Avalonia.Visuals/Media/FontManager.cs index ad3fee7eb7..db87c7c6c4 100644 --- a/src/Avalonia.Visuals/Media/FontManager.cs +++ b/src/Avalonia.Visuals/Media/FontManager.cs @@ -13,8 +13,8 @@ namespace Avalonia.Media /// public sealed class FontManager { - private readonly ConcurrentDictionary _typefaceCache = - new ConcurrentDictionary(); + private readonly ConcurrentDictionary _glyphTypefaceCache = + new ConcurrentDictionary(); private readonly FontFamily _defaultFontFamily; public FontManager(IFontManagerImpl platformImpl) @@ -76,79 +76,52 @@ namespace Avalonia.Media PlatformImpl.GetInstalledFontFamilyNames(checkForUpdates); /// - /// Returns a new typeface, or an existing one if a matching typeface exists. + /// Returns a new , or an existing one if a matching exists. /// - /// The font family. - /// The font style. - /// The font weight. + /// The typeface. /// - /// The typeface. + /// The . /// - public Typeface GetOrAddTypeface(FontFamily fontFamily, FontStyle fontStyle = FontStyle.Normal, - FontWeight fontWeight = FontWeight.Normal) + public GlyphTypeface GetOrAddGlyphTypeface(Typeface typeface) { while (true) { - if (fontFamily.IsDefault) + if (_glyphTypefaceCache.TryGetValue(typeface, out var glyphTypeface)) { - fontFamily = _defaultFontFamily; + return glyphTypeface; } - var key = new FontKey(fontFamily.Name, fontStyle, fontWeight); + glyphTypeface = new GlyphTypeface(typeface); - if (_typefaceCache.TryGetValue(key, out var typeface)) + if (_glyphTypefaceCache.TryAdd(typeface, glyphTypeface)) { - return typeface; + return glyphTypeface; } - typeface = new Typeface(fontFamily, fontStyle, fontWeight); - - if (_typefaceCache.TryAdd(key, typeface)) - { - return typeface; - } - - if (fontFamily == _defaultFontFamily) + if (typeface.FontFamily == _defaultFontFamily) { return null; } - fontFamily = _defaultFontFamily; + typeface = new Typeface(_defaultFontFamily, typeface.Style, typeface.Weight); } } /// - /// Tries to match a specified character to a typeface that supports specified font properties. - /// Returns null if no fallback was found. + /// Tries to match a specified character to a that supports specified font properties. /// /// The codepoint to match against. /// The font style. /// The font weight. /// The font family. This is optional and used for fallback lookup. /// The culture. + /// The matching . /// - /// The matched typeface. + /// True, if the could match the character to specified parameters, False otherwise. /// - public Typeface MatchCharacter(int codepoint, - FontStyle fontStyle = FontStyle.Normal, - FontWeight fontWeight = FontWeight.Normal, - FontFamily fontFamily = null, CultureInfo culture = null) - { - foreach (var cachedTypeface in _typefaceCache.Values) - { - // First try to find a cached typeface by style and weight to avoid redundant glyph index lookup. - if (cachedTypeface.Style == fontStyle && cachedTypeface.Weight == fontWeight - && cachedTypeface.GlyphTypeface.GetGlyph((uint)codepoint) != 0) - { - return cachedTypeface; - } - } - - var matchedTypeface = PlatformImpl.TryMatchCharacter(codepoint, fontStyle, fontWeight, fontFamily, culture, out var key) ? - _typefaceCache.GetOrAdd(key, new Typeface(key.FamilyName, key.Style, key.Weight)) : - null; - - return matchedTypeface; - } + public bool TryMatchCharacter(int codepoint, FontStyle fontStyle, + FontWeight fontWeight, + FontFamily fontFamily, CultureInfo culture, out Typeface typeface) => + PlatformImpl.TryMatchCharacter(codepoint, fontStyle, fontWeight, fontFamily, culture, out typeface); } } diff --git a/src/Avalonia.Visuals/Media/Fonts/FontKey.cs b/src/Avalonia.Visuals/Media/Fonts/FontKey.cs deleted file mode 100644 index b330db8462..0000000000 --- a/src/Avalonia.Visuals/Media/Fonts/FontKey.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System; - -namespace Avalonia.Media.Fonts -{ - public readonly struct FontKey : IEquatable - { - public FontKey(string familyName, FontStyle style, FontWeight weight) - { - FamilyName = familyName; - Style = style; - Weight = weight; - } - - public string FamilyName { get; } - public FontStyle Style { get; } - public FontWeight Weight { get; } - - public override int GetHashCode() - { - var hash = FamilyName.GetHashCode(); - - hash = hash * 31 + (int)Style; - hash = hash * 31 + (int)Weight; - - return hash; - } - - public override bool Equals(object other) - { - return other is FontKey key && Equals(key); - } - - public bool Equals(FontKey other) - { - return FamilyName == other.FamilyName && - Style == other.Style && - Weight == other.Weight; - } - } -} diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextCharacters.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextCharacters.cs index 47e716982c..b91a50a27c 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/TextCharacters.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/TextCharacters.cs @@ -70,10 +70,11 @@ namespace Avalonia.Media.TextFormatting var codepoint = Codepoint.ReadAt(text, count, out _); //ToDo: Fix FontFamily fallback - currentTypeface = - FontManager.Current.MatchCharacter(codepoint, defaultTypeface.Style, defaultTypeface.Weight, defaultTypeface.FontFamily); + var matchFound = + FontManager.Current.TryMatchCharacter(codepoint, defaultTypeface.Style, defaultTypeface.Weight, + defaultTypeface.FontFamily, defaultProperties.CultureInfo, out currentTypeface); - if (currentTypeface != null && TryGetRunProperties(text, currentTypeface, defaultTypeface, out count)) + if (matchFound && TryGetRunProperties(text, currentTypeface, defaultTypeface, out count)) { //Fallback found return new ShapeableTextCharacters(text.Take(count), diff --git a/src/Avalonia.Visuals/Media/Typeface.cs b/src/Avalonia.Visuals/Media/Typeface.cs index 677e930804..17824c3c5e 100644 --- a/src/Avalonia.Visuals/Media/Typeface.cs +++ b/src/Avalonia.Visuals/Media/Typeface.cs @@ -8,17 +8,15 @@ namespace Avalonia.Media /// Represents a typeface. /// [DebuggerDisplay("Name = {FontFamily.Name}, Weight = {Weight}, Style = {Style}")] - public class Typeface : IEquatable + public readonly struct Typeface : IEquatable { - private GlyphTypeface _glyphTypeface; - /// /// Initializes a new instance of the class. /// /// The font family. /// The font style. /// The font weight. - public Typeface([NotNull]FontFamily fontFamily, + public Typeface([NotNull] FontFamily fontFamily, FontStyle style = FontStyle.Normal, FontWeight weight = FontWeight.Normal) { @@ -45,7 +43,7 @@ namespace Avalonia.Media { } - public static Typeface Default => FontManager.Current?.GetOrAddTypeface(FontFamily.Default); + public static Typeface Default { get; } = new Typeface(FontFamily.Default); /// /// Gets the font family. @@ -68,7 +66,7 @@ namespace Avalonia.Media /// /// The glyph typeface. /// - public GlyphTypeface GlyphTypeface => _glyphTypeface ?? (_glyphTypeface = new GlyphTypeface(this)); + public GlyphTypeface GlyphTypeface => FontManager.Current.GetOrAddGlyphTypeface(this); public static bool operator !=(Typeface a, Typeface b) { @@ -77,32 +75,17 @@ namespace Avalonia.Media public static bool operator ==(Typeface a, Typeface b) { - if (ReferenceEquals(a, b)) - { - return true; - } - - return !(a is null) && a.Equals(b); + return a.Equals(b); } public override bool Equals(object obj) { - if (obj is Typeface typeface) - { - return Equals(typeface); - } - - return false; + return obj is Typeface typeface && Equals(typeface); } public bool Equals(Typeface other) { - if (other is null) - { - return false; - } - - return FontFamily.Equals(other.FontFamily) && Style == other.Style && Weight == other.Weight; + return FontFamily == other.FontFamily && Style == other.Style && Weight == other.Weight; } public override int GetHashCode() diff --git a/src/Avalonia.Visuals/Platform/IFontManagerImpl.cs b/src/Avalonia.Visuals/Platform/IFontManagerImpl.cs index 59b08aae0a..e562b45ca8 100644 --- a/src/Avalonia.Visuals/Platform/IFontManagerImpl.cs +++ b/src/Avalonia.Visuals/Platform/IFontManagerImpl.cs @@ -1,7 +1,6 @@ using System.Collections.Generic; using System.Globalization; using Avalonia.Media; -using Avalonia.Media.Fonts; namespace Avalonia.Platform { @@ -26,13 +25,13 @@ namespace Avalonia.Platform /// The font weight. /// The font family. This is optional and used for fallback lookup. /// The culture. - /// The matching font key. + /// The matching typeface. /// /// True, if the could match the character to specified parameters, False otherwise. /// bool TryMatchCharacter(int codepoint, FontStyle fontStyle, FontWeight fontWeight, - FontFamily fontFamily, CultureInfo culture, out FontKey fontKey); + FontFamily fontFamily, CultureInfo culture, out Typeface typeface); /// /// Creates a glyph typeface. diff --git a/src/Avalonia.Visuals/Rendering/RendererBase.cs b/src/Avalonia.Visuals/Rendering/RendererBase.cs index b37d5d660b..5c9cace4cd 100644 --- a/src/Avalonia.Visuals/Rendering/RendererBase.cs +++ b/src/Avalonia.Visuals/Rendering/RendererBase.cs @@ -20,7 +20,7 @@ namespace Avalonia.Rendering _useManualFpsCounting = useManualFpsCounting; _fpsText = new FormattedText { - Typeface = FontManager.Current?.GetOrAddTypeface(FontFamily.Default), + Typeface = new Typeface(FontFamily.Default), FontSize = s_fontSize }; } diff --git a/src/Skia/Avalonia.Skia/FontManagerImpl.cs b/src/Skia/Avalonia.Skia/FontManagerImpl.cs index 91bc937475..62ec39346a 100644 --- a/src/Skia/Avalonia.Skia/FontManagerImpl.cs +++ b/src/Skia/Avalonia.Skia/FontManagerImpl.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Globalization; using Avalonia.Media; -using Avalonia.Media.Fonts; using Avalonia.Platform; using SkiaSharp; @@ -31,7 +30,7 @@ namespace Avalonia.Skia public bool TryMatchCharacter(int codepoint, FontStyle fontStyle, FontWeight fontWeight, - FontFamily fontFamily, CultureInfo culture, out FontKey fontKey) + FontFamily fontFamily, CultureInfo culture, out Typeface fontKey) { SKFontStyle skFontStyle; @@ -81,7 +80,7 @@ namespace Avalonia.Skia continue; } - fontKey = new FontKey(skTypeface.FamilyName, fontStyle, fontWeight); + fontKey = new Typeface(skTypeface.FamilyName, fontStyle, fontWeight); return true; } @@ -92,7 +91,7 @@ namespace Avalonia.Skia if (skTypeface != null) { - fontKey = new FontKey(skTypeface.FamilyName, fontStyle, fontWeight); + fontKey = new Typeface(skTypeface.FamilyName, fontStyle, fontWeight); return true; } diff --git a/src/Skia/Avalonia.Skia/SKTypefaceCollection.cs b/src/Skia/Avalonia.Skia/SKTypefaceCollection.cs index 6c2ac17923..71deb1235f 100644 --- a/src/Skia/Avalonia.Skia/SKTypefaceCollection.cs +++ b/src/Skia/Avalonia.Skia/SKTypefaceCollection.cs @@ -2,31 +2,28 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using Avalonia.Media; -using Avalonia.Media.Fonts; using SkiaSharp; namespace Avalonia.Skia { internal class SKTypefaceCollection { - private readonly ConcurrentDictionary _typefaces = - new ConcurrentDictionary(); + private readonly ConcurrentDictionary _typefaces = + new ConcurrentDictionary(); - public void AddTypeface(FontKey key, SKTypeface typeface) + public void AddTypeface(Typeface key, SKTypeface typeface) { _typefaces.TryAdd(key, typeface); } public SKTypeface Get(Typeface typeface) { - var key = new FontKey(typeface.FontFamily.Name, typeface.Style, typeface.Weight); - - return GetNearestMatch(_typefaces, key); + return GetNearestMatch(_typefaces, typeface); } - private static SKTypeface GetNearestMatch(IDictionary typefaces, FontKey key) + private static SKTypeface GetNearestMatch(IDictionary typefaces, Typeface key) { - if (typefaces.TryGetValue(new FontKey(key.FamilyName, key.Style, key.Weight), out var typeface)) + if (typefaces.TryGetValue(key, out var typeface)) { return typeface; } @@ -42,7 +39,7 @@ namespace Avalonia.Skia { if (weight - j >= 100) { - if (typefaces.TryGetValue(new FontKey(key.FamilyName, (FontStyle)i, (FontWeight)(weight - j)), out typeface)) + if (typefaces.TryGetValue(new Typeface(key.FontFamily, (FontStyle)i, (FontWeight)(weight - j)), out typeface)) { return typeface; } @@ -53,7 +50,7 @@ namespace Avalonia.Skia continue; } - if (typefaces.TryGetValue(new FontKey(key.FamilyName, (FontStyle)i, (FontWeight)(weight + j)), out typeface)) + if (typefaces.TryGetValue(new Typeface(key.FontFamily, (FontStyle)i, (FontWeight)(weight + j)), out typeface)) { return typeface; } diff --git a/src/Skia/Avalonia.Skia/SKTypefaceCollectionCache.cs b/src/Skia/Avalonia.Skia/SKTypefaceCollectionCache.cs index 7ca44e7282..de77c9186e 100644 --- a/src/Skia/Avalonia.Skia/SKTypefaceCollectionCache.cs +++ b/src/Skia/Avalonia.Skia/SKTypefaceCollectionCache.cs @@ -56,7 +56,7 @@ namespace Avalonia.Skia continue; } - var key = new FontKey(fontFamily.Name, typeface.FontSlant.ToAvalonia(), + var key = new Typeface(fontFamily, typeface.FontSlant.ToAvalonia(), (FontWeight)typeface.FontWeight); typeFaceCollection.AddTypeface(key, typeface); diff --git a/src/Windows/Avalonia.Direct2D1/Media/FontManagerImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/FontManagerImpl.cs index 33af15076d..6d95d759ec 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/FontManagerImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/FontManagerImpl.cs @@ -1,7 +1,6 @@ using System.Collections.Generic; using System.Globalization; using Avalonia.Media; -using Avalonia.Media.Fonts; using Avalonia.Platform; using SharpDX.DirectWrite; using FontFamily = Avalonia.Media.FontFamily; @@ -34,7 +33,7 @@ namespace Avalonia.Direct2D1.Media public bool TryMatchCharacter(int codepoint, FontStyle fontStyle, FontWeight fontWeight, - FontFamily fontFamily, CultureInfo culture, out FontKey fontKey) + FontFamily fontFamily, CultureInfo culture, out Typeface typeface) { var familyCount = Direct2D1FontCollectionCache.InstalledFontCollection.FontFamilyCount; @@ -51,12 +50,12 @@ namespace Avalonia.Direct2D1.Media var fontFamilyName = font.FontFamily.FamilyNames.GetString(0); - fontKey = new FontKey(fontFamilyName, fontStyle, fontWeight); + typeface = new Typeface(fontFamilyName, fontStyle, fontWeight); return true; } - fontKey = default; + typeface = default; return false; } diff --git a/tests/Avalonia.RenderTests/Media/FormattedTextImplTests.cs b/tests/Avalonia.RenderTests/Media/FormattedTextImplTests.cs index 8683da9a01..7528424521 100644 --- a/tests/Avalonia.RenderTests/Media/FormattedTextImplTests.cs +++ b/tests/Avalonia.RenderTests/Media/FormattedTextImplTests.cs @@ -51,7 +51,7 @@ namespace Avalonia.Direct2D1.RenderTests.Media { var r = AvaloniaLocator.Current.GetService(); return r.CreateFormattedText(text, - FontManager.Current.GetOrAddTypeface(fontFamily, fontStyle, fontWeight), + new Typeface(fontFamily, fontStyle, fontWeight), fontSize, textAlignment, wrapping, diff --git a/tests/Avalonia.Skia.UnitTests/Media/CustomFontManagerImpl.cs b/tests/Avalonia.Skia.UnitTests/Media/CustomFontManagerImpl.cs index f36d6d9e4a..a0fe348166 100644 --- a/tests/Avalonia.Skia.UnitTests/Media/CustomFontManagerImpl.cs +++ b/tests/Avalonia.Skia.UnitTests/Media/CustomFontManagerImpl.cs @@ -39,7 +39,7 @@ namespace Avalonia.Skia.UnitTests.Media private readonly string[] _bcp47 = { CultureInfo.CurrentCulture.ThreeLetterISOLanguageName, CultureInfo.CurrentCulture.TwoLetterISOLanguageName }; public bool TryMatchCharacter(int codepoint, FontStyle fontStyle, FontWeight fontWeight, FontFamily fontFamily, - CultureInfo culture, out FontKey fontKey) + CultureInfo culture, out Typeface typeface) { foreach (var customTypeface in _customTypefaces) { @@ -48,7 +48,7 @@ namespace Avalonia.Skia.UnitTests.Media continue; } - fontKey = new FontKey(customTypeface.FontFamily.Name, fontStyle, fontWeight); + typeface = new Typeface(customTypeface.FontFamily.Name, fontStyle, fontWeight); return true; } @@ -56,7 +56,7 @@ namespace Avalonia.Skia.UnitTests.Media var fallback = SKFontManager.Default.MatchCharacter(fontFamily?.Name, (SKFontStyleWeight)fontWeight, SKFontStyleWidth.Normal, (SKFontStyleSlant)fontStyle, _bcp47, codepoint); - fontKey = new FontKey(fallback?.FamilyName ?? _defaultFamilyName, fontStyle, fontWeight); + typeface = new Typeface(fallback?.FamilyName ?? _defaultFamilyName, fontStyle, fontWeight); return true; } @@ -73,13 +73,13 @@ namespace Avalonia.Skia.UnitTests.Media skTypeface = typefaceCollection.Get(typeface); break; } - case "Noto Sans": { var typefaceCollection = SKTypefaceCollectionCache.GetOrAddTypefaceCollection(_italicTypeface.FontFamily); skTypeface = typefaceCollection.Get(typeface); break; } + case FontFamily.DefaultFontFamilyName: case "Noto Mono": { var typefaceCollection = SKTypefaceCollectionCache.GetOrAddTypefaceCollection(_defaultTypeface.FontFamily); diff --git a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextFormatterTests.cs b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextFormatterTests.cs index 4a88b259bc..ea806e01fe 100644 --- a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextFormatterTests.cs +++ b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextFormatterTests.cs @@ -130,7 +130,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting { using (Start()) { - const string text = "1234الدولي"; + const string text = "ぁぁぁぁالدولي"; var defaultProperties = new GenericTextRunProperties(Typeface.Default); diff --git a/tests/Avalonia.UnitTests/MockFontManagerImpl.cs b/tests/Avalonia.UnitTests/MockFontManagerImpl.cs index e614c60310..ba3b346f1b 100644 --- a/tests/Avalonia.UnitTests/MockFontManagerImpl.cs +++ b/tests/Avalonia.UnitTests/MockFontManagerImpl.cs @@ -1,7 +1,6 @@ using System.Collections.Generic; using System.Globalization; using Avalonia.Media; -using Avalonia.Media.Fonts; using Avalonia.Platform; namespace Avalonia.UnitTests @@ -26,9 +25,9 @@ namespace Avalonia.UnitTests } public bool TryMatchCharacter(int codepoint, FontStyle fontStyle, FontWeight fontWeight, FontFamily fontFamily, - CultureInfo culture, out FontKey fontKey) + CultureInfo culture, out Typeface fontKey) { - fontKey = default; + fontKey = new Typeface(_defaultFamilyName); return false; } diff --git a/tests/Avalonia.Visuals.UnitTests/Media/FontManagerTests.cs b/tests/Avalonia.Visuals.UnitTests/Media/FontManagerTests.cs index 81a4ca6495..2b0ffa4ed6 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/FontManagerTests.cs +++ b/tests/Avalonia.Visuals.UnitTests/Media/FontManagerTests.cs @@ -1,6 +1,5 @@ using System; using Avalonia.Media; -using Avalonia.Platform; using Avalonia.UnitTests; using Xunit; @@ -15,9 +14,11 @@ namespace Avalonia.Visuals.UnitTests.Media { var fontFamily = new FontFamily("MyFont"); - var typeface = FontManager.Current.GetOrAddTypeface(fontFamily); + var typeface = new Typeface(fontFamily); - Assert.Same(typeface, FontManager.Current.GetOrAddTypeface(fontFamily)); + var glyphTypeface = FontManager.Current.GetOrAddGlyphTypeface(typeface); + + Assert.Same(glyphTypeface, FontManager.Current.GetOrAddGlyphTypeface(typeface)); } }