Cache platform font manager TryMatchCharacter result (#19987)
This commit is contained in:
@@ -287,6 +287,8 @@ namespace Avalonia.Media
|
||||
}
|
||||
|
||||
if (TryGetFontCollection(source, out var fontCollection) &&
|
||||
// With composite fonts we need to first check if the font collection contains the family if not we skip it
|
||||
fontCollection.TryGetGlyphTypeface(familyName, fontStyle, fontWeight, fontStretch, out _) &&
|
||||
fontCollection.TryMatchCharacter(codepoint, fontStyle, fontWeight, fontStretch, familyName, culture, out typeface))
|
||||
{
|
||||
return true;
|
||||
@@ -306,8 +308,9 @@ namespace Avalonia.Media
|
||||
}
|
||||
}
|
||||
|
||||
//Try to find a match with the system font manager
|
||||
return PlatformImpl.TryMatchCharacter(codepoint, fontStyle, fontWeight, fontStretch, culture, out typeface);
|
||||
//Try to find a match with the system font collection
|
||||
return SystemFonts.TryMatchCharacter(codepoint, fontStyle, fontWeight, fontStretch, fontFamily?.Name,
|
||||
culture, out typeface);
|
||||
}
|
||||
|
||||
internal IReadOnlyList<Typeface> GetFamilyTypefaces(FontFamily fontFamily)
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using Avalonia.Platform;
|
||||
|
||||
@@ -15,7 +16,7 @@ namespace Avalonia.Media.Fonts
|
||||
public SystemFontCollection(FontManager fontManager)
|
||||
{
|
||||
_fontManager = fontManager;
|
||||
_familyNames = fontManager.PlatformImpl.GetInstalledFontFamilyNames().Where(x=> !string.IsNullOrEmpty(x)).ToList();
|
||||
_familyNames = fontManager.PlatformImpl.GetInstalledFontFamilyNames().Where(x => !string.IsNullOrEmpty(x)).ToList();
|
||||
}
|
||||
|
||||
public override Uri Key => FontManager.SystemFontsKey;
|
||||
@@ -144,21 +145,6 @@ namespace Avalonia.Media.Fonts
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
void AddGlyphTypefaceByFamilyName(string familyName, IGlyphTypeface glyphTypeface)
|
||||
{
|
||||
var typefaces = _glyphTypefaceCache.GetOrAdd(familyName,
|
||||
x =>
|
||||
{
|
||||
_familyNames.Insert(0, familyName);
|
||||
|
||||
return new ConcurrentDictionary<FontCollectionKey, IGlyphTypeface?>();
|
||||
});
|
||||
|
||||
typefaces.TryAdd(
|
||||
new FontCollectionKey(glyphTypeface.Style, glyphTypeface.Weight, glyphTypeface.Stretch),
|
||||
glyphTypeface);
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryGetFamilyTypefaces(string familyName, [NotNullWhen(true)] out IReadOnlyList<Typeface>? familyTypefaces)
|
||||
@@ -172,5 +158,49 @@ namespace Avalonia.Media.Fonts
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override bool TryMatchCharacter(int codepoint, FontStyle style, FontWeight weight, FontStretch stretch, string? familyName,
|
||||
CultureInfo? culture, out Typeface match)
|
||||
{
|
||||
//TODO12: Think about removing familyName parameter
|
||||
match = default;
|
||||
|
||||
if (_fontManager.PlatformImpl is IFontManagerImpl2 fontManagerImpl2)
|
||||
{
|
||||
if (fontManagerImpl2.TryMatchCharacter(codepoint, style, weight, stretch, culture, out var glyphTypeface))
|
||||
{
|
||||
AddGlyphTypefaceByFamilyName(glyphTypeface.FamilyName, glyphTypeface);
|
||||
|
||||
match = new Typeface(glyphTypeface.FamilyName, glyphTypeface.Style, glyphTypeface.Weight,
|
||||
glyphTypeface.Stretch);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return _fontManager.PlatformImpl.TryMatchCharacter(codepoint, style, weight, stretch, culture, out match);
|
||||
}
|
||||
}
|
||||
|
||||
private void AddGlyphTypefaceByFamilyName(string familyName, IGlyphTypeface glyphTypeface)
|
||||
{
|
||||
// Add family name to the collection if not exists
|
||||
if (!_familyNames.Contains(familyName))
|
||||
{
|
||||
_familyNames.Add(familyName);
|
||||
}
|
||||
|
||||
// Get or create the typefaces dictionary for the family name
|
||||
if (!_glyphTypefaceCache.TryGetValue(familyName, out var typefaces))
|
||||
{
|
||||
_glyphTypefaceCache[familyName] = typefaces = new ConcurrentDictionary<FontCollectionKey, IGlyphTypeface?>();
|
||||
}
|
||||
|
||||
// Add the glyph typeface to the cache
|
||||
typefaces.TryAdd(new FontCollectionKey(glyphTypeface.Style, glyphTypeface.Weight, glyphTypeface.Stretch), glyphTypeface);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,6 +65,21 @@ namespace Avalonia.Platform
|
||||
|
||||
internal interface IFontManagerImpl2 : IFontManagerImpl
|
||||
{
|
||||
/// <summary>
|
||||
/// Tries to match a specified character to a typeface that supports specified font properties.
|
||||
/// </summary>
|
||||
/// <param name="codepoint">The codepoint to match against.</param>
|
||||
/// <param name="fontStyle">The font style.</param>
|
||||
/// <param name="fontWeight">The font weight.</param>
|
||||
/// <param name="fontStretch">The font stretch.</param>
|
||||
/// <param name="culture">The culture.</param>
|
||||
/// <param name="typeface">The matching typeface.</param>
|
||||
/// <returns>
|
||||
/// <c>True</c>, if the <see cref="IFontManagerImpl"/> could match the character to specified parameters, <c>False</c> otherwise.
|
||||
/// </returns>
|
||||
bool TryMatchCharacter(int codepoint, FontStyle fontStyle,
|
||||
FontWeight fontWeight, FontStretch fontStretch, CultureInfo? culture, [NotNullWhen(true)] out IGlyphTypeface? typeface);
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get a list of typefaces for the specified family name.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
using System;
|
||||
#nullable enable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Text.RegularExpressions;
|
||||
using Avalonia.Media;
|
||||
using Avalonia.Platform;
|
||||
using SkiaSharp;
|
||||
@@ -15,6 +18,7 @@ namespace Avalonia.Skia
|
||||
|
||||
public string GetDefaultFontFamilyName()
|
||||
{
|
||||
|
||||
return SKTypeface.Default.FamilyName;
|
||||
}
|
||||
|
||||
@@ -32,6 +36,53 @@ namespace Avalonia.Skia
|
||||
|
||||
public bool TryMatchCharacter(int codepoint, FontStyle fontStyle,
|
||||
FontWeight fontWeight, FontStretch fontStretch, CultureInfo? culture, out Typeface fontKey)
|
||||
{
|
||||
if (!TryMatchCharacter(codepoint, fontStyle, fontWeight, fontStretch, culture, out SKTypeface? skTypeface))
|
||||
{
|
||||
fontKey = default;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
fontKey = new Typeface(
|
||||
skTypeface.FamilyName,
|
||||
skTypeface.FontStyle.Slant.ToAvalonia(),
|
||||
(FontWeight)skTypeface.FontStyle.Weight,
|
||||
(FontStretch)skTypeface.FontStyle.Width);
|
||||
|
||||
skTypeface.Dispose();
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
public bool TryMatchCharacter(
|
||||
int codepoint,
|
||||
FontStyle fontStyle,
|
||||
FontWeight fontWeight,
|
||||
FontStretch fontStretch,
|
||||
CultureInfo? culture,
|
||||
[NotNullWhen(true)] out IGlyphTypeface? glyphTypeface)
|
||||
{
|
||||
if (!TryMatchCharacter(codepoint, fontStyle, fontWeight, fontStretch, culture, out SKTypeface? skTypeface))
|
||||
{
|
||||
glyphTypeface = null;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
glyphTypeface = new GlyphTypefaceImpl(skTypeface, FontSimulations.None);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool TryMatchCharacter(
|
||||
int codepoint,
|
||||
FontStyle fontStyle,
|
||||
FontWeight fontWeight,
|
||||
FontStretch fontStretch,
|
||||
CultureInfo? culture,
|
||||
[NotNullWhen(true)] out SKTypeface? skTypeface)
|
||||
{
|
||||
SKFontStyle skFontStyle;
|
||||
|
||||
@@ -59,23 +110,9 @@ namespace Avalonia.Skia
|
||||
t_languageTagBuffer ??= new string[1];
|
||||
t_languageTagBuffer[0] = culture.Name;
|
||||
|
||||
using var skTypeface = _skFontManager.MatchCharacter(null, skFontStyle, t_languageTagBuffer, codepoint);
|
||||
skTypeface = _skFontManager.MatchCharacter(null, skFontStyle, t_languageTagBuffer, codepoint);
|
||||
|
||||
if (skTypeface != null)
|
||||
{
|
||||
// ToDo: create glyph typeface here to get the correct style/weight/stretch
|
||||
fontKey = new Typeface(
|
||||
skTypeface.FamilyName,
|
||||
skTypeface.FontStyle.Slant.ToAvalonia(),
|
||||
(FontWeight)skTypeface.FontStyle.Weight,
|
||||
(FontStretch)skTypeface.FontStyle.Width);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
fontKey = default;
|
||||
|
||||
return false;
|
||||
return skTypeface != null;
|
||||
}
|
||||
|
||||
public bool TryCreateGlyphTypeface(string familyName, FontStyle style, FontWeight weight,
|
||||
|
||||
@@ -110,6 +110,27 @@ namespace Avalonia.Skia.UnitTests.Media
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_Cache_MatchCharacter()
|
||||
{
|
||||
var fontManagerImpl = new CustomFontManagerImpl();
|
||||
|
||||
using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface.With(fontManagerImpl: fontManagerImpl)))
|
||||
{
|
||||
var emoji = Codepoint.ReadAt("😀", 0, out _);
|
||||
|
||||
Assert.True(FontManager.Current.TryMatchCharacter((int)emoji, FontStyle.Normal, FontWeight.Normal, FontStretch.Normal, null, null, out var firstMatch));
|
||||
|
||||
var firstGlyphTypeface = firstMatch.GlyphTypeface;
|
||||
|
||||
Assert.True(FontManager.Current.TryMatchCharacter((int)emoji, FontStyle.Normal, FontWeight.Normal, FontStretch.Normal, null, null, out var secondMatch));
|
||||
|
||||
var secondGlyphTypeface = secondMatch.GlyphTypeface;
|
||||
|
||||
Assert.Equal(firstGlyphTypeface, secondGlyphTypeface);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_Load_Embedded_DefaultFontFamily()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user