committed by
GitHub
15 changed files with 419 additions and 445 deletions
@ -0,0 +1,259 @@ |
|||||
|
using System; |
||||
|
using System.Collections; |
||||
|
using System.Collections.Concurrent; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Diagnostics.CodeAnalysis; |
||||
|
using System.Globalization; |
||||
|
using Avalonia.Platform; |
||||
|
|
||||
|
namespace Avalonia.Media.Fonts |
||||
|
{ |
||||
|
public abstract class FontCollectionBase : IFontCollection |
||||
|
{ |
||||
|
protected readonly ConcurrentDictionary<string, ConcurrentDictionary<FontCollectionKey, IGlyphTypeface?>> _glyphTypefaceCache = new(); |
||||
|
|
||||
|
public abstract Uri Key { get; } |
||||
|
|
||||
|
public abstract int Count { get; } |
||||
|
|
||||
|
public abstract FontFamily this[int index] { get; } |
||||
|
|
||||
|
public abstract bool TryGetGlyphTypeface(string familyName, FontStyle style, FontWeight weight, FontStretch stretch, |
||||
|
[NotNullWhen(true)] out IGlyphTypeface? glyphTypeface); |
||||
|
|
||||
|
public bool TryMatchCharacter(int codepoint, FontStyle style, FontWeight weight, FontStretch stretch, |
||||
|
string? familyName, CultureInfo? culture, out Typeface match) |
||||
|
{ |
||||
|
match = default; |
||||
|
|
||||
|
if (string.IsNullOrEmpty(familyName)) |
||||
|
{ |
||||
|
foreach (var typefaces in _glyphTypefaceCache.Values) |
||||
|
{ |
||||
|
if (TryGetNearestMatch(typefaces, new FontCollectionKey { Style = style, Weight = weight, Stretch = stretch }, out var glyphTypeface)) |
||||
|
{ |
||||
|
if (glyphTypeface.TryGetGlyph((uint)codepoint, out _)) |
||||
|
{ |
||||
|
match = new Typeface(glyphTypeface.FamilyName, style, weight, stretch); |
||||
|
|
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
if (TryGetGlyphTypeface(familyName, style, weight, stretch, out var glyphTypeface)) |
||||
|
{ |
||||
|
if (glyphTypeface.TryGetGlyph((uint)codepoint, out _)) |
||||
|
{ |
||||
|
match = new Typeface(familyName, style, weight, stretch); |
||||
|
|
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
public abstract void Initialize(IFontManagerImpl fontManager); |
||||
|
|
||||
|
public abstract IEnumerator<FontFamily> GetEnumerator(); |
||||
|
|
||||
|
void IDisposable.Dispose() |
||||
|
{ |
||||
|
foreach (var glyphTypefaces in _glyphTypefaceCache.Values) |
||||
|
{ |
||||
|
foreach (var pair in glyphTypefaces) |
||||
|
{ |
||||
|
pair.Value?.Dispose(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
GC.SuppressFinalize(this); |
||||
|
} |
||||
|
|
||||
|
IEnumerator IEnumerable.GetEnumerator() |
||||
|
{ |
||||
|
return GetEnumerator(); |
||||
|
} |
||||
|
|
||||
|
internal static bool TryGetNearestMatch( |
||||
|
ConcurrentDictionary<FontCollectionKey, |
||||
|
IGlyphTypeface?> glyphTypefaces, |
||||
|
FontCollectionKey key, |
||||
|
[NotNullWhen(true)] out IGlyphTypeface? glyphTypeface) |
||||
|
{ |
||||
|
if (glyphTypefaces.TryGetValue(key, out glyphTypeface) && glyphTypeface != null) |
||||
|
{ |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
if (key.Style != FontStyle.Normal) |
||||
|
{ |
||||
|
key = key with { Style = FontStyle.Normal }; |
||||
|
} |
||||
|
|
||||
|
if (key.Stretch != FontStretch.Normal) |
||||
|
{ |
||||
|
if (TryFindStretchFallback(glyphTypefaces, key, out glyphTypeface)) |
||||
|
{ |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
if (key.Weight != FontWeight.Normal) |
||||
|
{ |
||||
|
if (TryFindStretchFallback(glyphTypefaces, key with { Weight = FontWeight.Normal }, out glyphTypeface)) |
||||
|
{ |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
key = key with { Stretch = FontStretch.Normal }; |
||||
|
} |
||||
|
|
||||
|
if (TryFindWeightFallback(glyphTypefaces, key, out glyphTypeface)) |
||||
|
{ |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
if (TryFindStretchFallback(glyphTypefaces, key, out glyphTypeface)) |
||||
|
{ |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
//Take the first glyph typeface we can find.
|
||||
|
foreach (var typeface in glyphTypefaces.Values) |
||||
|
{ |
||||
|
if(typeface != null) |
||||
|
{ |
||||
|
glyphTypeface = typeface; |
||||
|
|
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
internal static bool TryFindStretchFallback( |
||||
|
ConcurrentDictionary<FontCollectionKey, |
||||
|
IGlyphTypeface?> glyphTypefaces, |
||||
|
FontCollectionKey key, |
||||
|
[NotNullWhen(true)] out IGlyphTypeface? glyphTypeface) |
||||
|
{ |
||||
|
glyphTypeface = null; |
||||
|
|
||||
|
var stretch = (int)key.Stretch; |
||||
|
|
||||
|
if (stretch < 5) |
||||
|
{ |
||||
|
for (var i = 0; stretch + i < 9; i++) |
||||
|
{ |
||||
|
if (glyphTypefaces.TryGetValue(key with { Stretch = (FontStretch)(stretch + i) }, out glyphTypeface) && glyphTypeface != null) |
||||
|
{ |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
for (var i = 0; stretch - i > 1; i++) |
||||
|
{ |
||||
|
if (glyphTypefaces.TryGetValue(key with { Stretch = (FontStretch)(stretch - i) }, out glyphTypeface) && glyphTypeface != null) |
||||
|
{ |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
internal static bool TryFindWeightFallback( |
||||
|
ConcurrentDictionary<FontCollectionKey, |
||||
|
IGlyphTypeface?> glyphTypefaces, |
||||
|
FontCollectionKey key, |
||||
|
[NotNullWhen(true)] out IGlyphTypeface? glyphTypeface) |
||||
|
{ |
||||
|
glyphTypeface = null; |
||||
|
var weight = (int)key.Weight; |
||||
|
|
||||
|
//If the target weight given is between 400 and 500 inclusive
|
||||
|
if (weight >= 400 && weight <= 500) |
||||
|
{ |
||||
|
//Look for available weights between the target and 500, in ascending order.
|
||||
|
for (var i = 0; weight + i <= 500; i += 50) |
||||
|
{ |
||||
|
if (glyphTypefaces.TryGetValue(key with { Weight = (FontWeight)(weight + i) }, out glyphTypeface) && glyphTypeface != null) |
||||
|
{ |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
//If no match is found, look for available weights less than the target, in descending order.
|
||||
|
for (var i = 0; weight - i >= 100; i += 50) |
||||
|
{ |
||||
|
if (glyphTypefaces.TryGetValue(key with { Weight = (FontWeight)(weight - i) }, out glyphTypeface) && glyphTypeface != null) |
||||
|
{ |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
//If no match is found, look for available weights greater than 500, in ascending order.
|
||||
|
for (var i = 0; weight + i <= 900; i += 50) |
||||
|
{ |
||||
|
if (glyphTypefaces.TryGetValue(key with { Weight = (FontWeight)(weight + i) }, out glyphTypeface) && glyphTypeface != null) |
||||
|
{ |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
//If a weight less than 400 is given, look for available weights less than the target, in descending order.
|
||||
|
if (weight < 400) |
||||
|
{ |
||||
|
for (var i = 0; weight - i >= 100; i += 50) |
||||
|
{ |
||||
|
if (glyphTypefaces.TryGetValue(key with { Weight = (FontWeight)(weight - i) }, out glyphTypeface) && glyphTypeface != null) |
||||
|
{ |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
//If no match is found, look for available weights less than the target, in descending order.
|
||||
|
for (var i = 0; weight + i <= 900; i += 50) |
||||
|
{ |
||||
|
if (glyphTypefaces.TryGetValue(key with { Weight = (FontWeight)(weight + i) }, out glyphTypeface) && glyphTypeface != null) |
||||
|
{ |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
//If a weight greater than 500 is given, look for available weights greater than the target, in ascending order.
|
||||
|
if (weight > 500) |
||||
|
{ |
||||
|
for (var i = 0; weight + i <= 900; i += 50) |
||||
|
{ |
||||
|
if (glyphTypefaces.TryGetValue(key with { Weight = (FontWeight)(weight + i) }, out glyphTypeface) && glyphTypeface != null) |
||||
|
{ |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
//If no match is found, look for available weights less than the target, in descending order.
|
||||
|
for (var i = 0; weight - i >= 100; i += 50) |
||||
|
{ |
||||
|
if (glyphTypefaces.TryGetValue(key with { Weight = (FontWeight)(weight - i) }, out glyphTypeface) && glyphTypeface != null) |
||||
|
{ |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue