зеркало из https://github.com/SixLabors/Fonts.git
Make lazy values thread safe.
This commit is contained in:
Родитель
c5e4cefb85
Коммит
3bf24e4169
|
@ -33,7 +33,7 @@ internal sealed class FileFontMetrics : FontMetrics
|
|||
{
|
||||
this.Description = description;
|
||||
this.Path = path;
|
||||
this.fontMetrics = new Lazy<StreamFontMetrics>(() => StreamFontMetrics.LoadFont(path, offset));
|
||||
this.fontMetrics = new Lazy<StreamFontMetrics>(() => StreamFontMetrics.LoadFont(path, offset), true);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="FontMetrics.Description"/>
|
||||
|
|
|
@ -42,8 +42,8 @@ public sealed class Font
|
|||
this.Family = family;
|
||||
this.RequestedStyle = style;
|
||||
this.Size = size;
|
||||
this.metrics = new Lazy<FontMetrics?>(this.LoadInstanceInternal);
|
||||
this.fontName = new Lazy<string>(this.LoadFontName);
|
||||
this.metrics = new Lazy<FontMetrics?>(this.LoadInstanceInternal, true);
|
||||
this.fontName = new Lazy<string>(this.LoadFontName, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -95,6 +95,7 @@ public sealed class Font
|
|||
/// <summary>
|
||||
/// Gets the font metrics.
|
||||
/// </summary>
|
||||
/// <exception cref="FontException">Font instance not found.</exception>
|
||||
public FontMetrics FontMetrics => this.metrics.Value ?? throw new FontException("Font instance not found.");
|
||||
|
||||
/// <summary>
|
||||
|
@ -194,7 +195,7 @@ public sealed class Font
|
|||
/// </summary>
|
||||
/// <param name="codePoint">The code point of the character.</param>
|
||||
/// <param name="textAttributes">The text attributes to apply to the glyphs.</param>
|
||||
/// <param name="layoutMode">The layout mode to apply to thte glyphs.</param>
|
||||
/// <param name="layoutMode">The layout mode to apply to the glyphs.</param>
|
||||
/// <param name="support">Options for enabling color font support during layout and rendering.</param>
|
||||
/// <param name="glyphs">
|
||||
/// When this method returns, contains the glyphs for the given codepoint, attributes, and color support if the glyphs
|
||||
|
@ -217,7 +218,7 @@ public sealed class Font
|
|||
/// <param name="codePoint">The code point of the character.</param>
|
||||
/// <param name="textAttributes">The text attributes to apply to the glyphs.</param>
|
||||
/// <param name="textDecorations">The text decorations to apply to the glyphs.</param>
|
||||
/// <param name="layoutMode">The layout mode to apply to thte glyphs.</param>
|
||||
/// <param name="layoutMode">The layout mode to apply to the glyphs.</param>
|
||||
/// <param name="support">Options for enabling color font support during layout and rendering.</param>
|
||||
/// <param name="glyphs">
|
||||
/// When this method returns, contains the glyphs for the given codepoint, attributes, and color support if the glyphs
|
||||
|
|
|
@ -66,7 +66,7 @@ internal partial class StreamFontMetrics
|
|||
}
|
||||
|
||||
private GlyphMetrics CreateCffGlyphMetrics(
|
||||
CodePoint codePoint,
|
||||
in CodePoint codePoint,
|
||||
ushort glyphId,
|
||||
GlyphType glyphType,
|
||||
TextAttributes textAttributes,
|
||||
|
|
|
@ -115,7 +115,7 @@ internal partial class StreamFontMetrics
|
|||
}
|
||||
|
||||
private GlyphMetrics CreateTrueTypeGlyphMetrics(
|
||||
CodePoint codePoint,
|
||||
in CodePoint codePoint,
|
||||
ushort glyphId,
|
||||
GlyphType glyphType,
|
||||
TextAttributes textAttributes,
|
||||
|
|
|
@ -31,6 +31,7 @@ internal partial class StreamFontMetrics : FontMetrics
|
|||
// https://docs.microsoft.com/en-us/typography/opentype/spec/otff#font-tables
|
||||
private readonly ConcurrentDictionary<(int CodePoint, ushort Id, TextAttributes Attributes, bool IsVerticalLayout), GlyphMetrics[]> glyphCache;
|
||||
private readonly ConcurrentDictionary<(int CodePoint, ushort Id, TextAttributes Attributes, bool IsVerticalLayout), GlyphMetrics[]>? colorGlyphCache;
|
||||
private readonly ConcurrentDictionary<(int CodePoint, int NextCodePoint), (bool Success, ushort GlyphId, bool SkipNextCodePoint)> glyphIdCache;
|
||||
private readonly FontDescription description;
|
||||
private readonly HorizontalMetrics horizontalMetrics;
|
||||
private readonly VerticalMetrics verticalMetrics;
|
||||
|
@ -59,6 +60,7 @@ internal partial class StreamFontMetrics : FontMetrics
|
|||
this.trueTypeFontTables = tables;
|
||||
this.outlineType = OutlineType.TrueType;
|
||||
this.description = new FontDescription(tables.Name, tables.Os2, tables.Head);
|
||||
this.glyphIdCache = new();
|
||||
this.glyphCache = new();
|
||||
if (tables.Colr is not null)
|
||||
{
|
||||
|
@ -79,6 +81,7 @@ internal partial class StreamFontMetrics : FontMetrics
|
|||
this.compactFontTables = tables;
|
||||
this.outlineType = OutlineType.CFF;
|
||||
this.description = new FontDescription(tables.Name, tables.Os2, tables.Head);
|
||||
this.glyphIdCache = new();
|
||||
this.glyphCache = new();
|
||||
if (tables.Colr is not null)
|
||||
{
|
||||
|
@ -157,7 +160,18 @@ internal partial class StreamFontMetrics : FontMetrics
|
|||
? this.trueTypeFontTables!.Cmap
|
||||
: this.compactFontTables!.Cmap;
|
||||
|
||||
return cmap.TryGetGlyphId(codePoint, nextCodePoint, out glyphId, out skipNextCodePoint);
|
||||
(bool success, ushort id, bool skip) = this.glyphIdCache.GetOrAdd(
|
||||
(codePoint.Value, nextCodePoint?.Value ?? -1),
|
||||
static (_, arg) =>
|
||||
{
|
||||
bool success = arg.cmap.TryGetGlyphId(arg.codePoint, arg.nextCodePoint, out ushort id, out bool skip);
|
||||
return (success, id, skip);
|
||||
},
|
||||
(cmap, codePoint, nextCodePoint));
|
||||
|
||||
glyphId = id;
|
||||
skipNextCodePoint = skip;
|
||||
return success;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
@ -206,14 +220,6 @@ internal partial class StreamFontMetrics : FontMetrics
|
|||
LayoutMode layoutMode,
|
||||
ColorFontSupport support)
|
||||
{
|
||||
GlyphType glyphType = GlyphType.Standard;
|
||||
if (glyphId == 0)
|
||||
{
|
||||
// A glyph was not found in this face for the previously matched
|
||||
// codepoint. Set to fallback.
|
||||
glyphType = GlyphType.Fallback;
|
||||
}
|
||||
|
||||
if (support == ColorFontSupport.MicrosoftColrFormat
|
||||
&& this.TryGetColoredMetrics(codePoint, glyphId, textAttributes, textDecorations, layoutMode, out GlyphMetrics[]? metrics))
|
||||
{
|
||||
|
@ -222,17 +228,18 @@ internal partial class StreamFontMetrics : FontMetrics
|
|||
|
||||
// We overwrite the cache entry for this type should the attributes change.
|
||||
return this.glyphCache.GetOrAdd(
|
||||
CreateCacheKey(codePoint, glyphId, textAttributes, layoutMode),
|
||||
key => new[]
|
||||
{
|
||||
this.CreateGlyphMetrics(
|
||||
codePoint,
|
||||
key.Id,
|
||||
glyphType,
|
||||
key.Attributes,
|
||||
textDecorations,
|
||||
key.IsVerticalLayout)
|
||||
});
|
||||
CreateCacheKey(in codePoint, glyphId, textAttributes, layoutMode),
|
||||
static (key, arg) => new[]
|
||||
{
|
||||
arg.Item3.CreateGlyphMetrics(
|
||||
in arg.codePoint,
|
||||
key.Id,
|
||||
key.Id == 0 ? GlyphType.Fallback : GlyphType.Standard,
|
||||
key.Attributes,
|
||||
arg.textDecorations,
|
||||
key.IsVerticalLayout)
|
||||
},
|
||||
(textDecorations, codePoint, this));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
@ -530,7 +537,7 @@ internal partial class StreamFontMetrics : FontMetrics
|
|||
}
|
||||
|
||||
private static (int CodePoint, ushort Id, TextAttributes Attributes, bool IsVerticalLayout) CreateCacheKey(
|
||||
CodePoint codePoint,
|
||||
in CodePoint codePoint,
|
||||
ushort glyphId,
|
||||
TextAttributes textAttributes,
|
||||
LayoutMode layoutMode)
|
||||
|
@ -555,28 +562,31 @@ internal partial class StreamFontMetrics : FontMetrics
|
|||
}
|
||||
|
||||
// We overwrite the cache entry for this type should the attributes change.
|
||||
metrics = this.colorGlyphCache.GetOrAdd(CreateCacheKey(codePoint, glyphId, textAttributes, layoutMode), key =>
|
||||
{
|
||||
GlyphMetrics[] m = Array.Empty<GlyphMetrics>();
|
||||
Span<LayerRecord> indexes = colr.GetLayers(key.Id);
|
||||
if (indexes.Length > 0)
|
||||
metrics = this.colorGlyphCache.GetOrAdd(
|
||||
CreateCacheKey(in codePoint, glyphId, textAttributes, layoutMode),
|
||||
(key, args) =>
|
||||
{
|
||||
m = new GlyphMetrics[indexes.Length];
|
||||
for (int i = 0; i < indexes.Length; i++)
|
||||
GlyphMetrics[] m = Array.Empty<GlyphMetrics>();
|
||||
Span<LayerRecord> indexes = colr.GetLayers(key.Id);
|
||||
if (indexes.Length > 0)
|
||||
{
|
||||
LayerRecord layer = indexes[i];
|
||||
m[i] = this.CreateGlyphMetrics(codePoint, layer.GlyphId, GlyphType.ColrLayer, key.Attributes, textDecorations, key.IsVerticalLayout, layer.PaletteIndex);
|
||||
m = new GlyphMetrics[indexes.Length];
|
||||
for (int i = 0; i < indexes.Length; i++)
|
||||
{
|
||||
LayerRecord layer = indexes[i];
|
||||
m[i] = args.Item2.CreateGlyphMetrics(in args.codePoint, layer.GlyphId, GlyphType.ColrLayer, key.Attributes, textDecorations, key.IsVerticalLayout, layer.PaletteIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return m;
|
||||
});
|
||||
return m;
|
||||
},
|
||||
(codePoint, this));
|
||||
|
||||
return metrics.Length > 0;
|
||||
}
|
||||
|
||||
private GlyphMetrics CreateGlyphMetrics(
|
||||
CodePoint codePoint,
|
||||
in CodePoint codePoint,
|
||||
ushort glyphId,
|
||||
GlyphType glyphType,
|
||||
TextAttributes textAttributes,
|
||||
|
@ -585,8 +595,8 @@ internal partial class StreamFontMetrics : FontMetrics
|
|||
ushort paletteIndex = 0)
|
||||
=> this.outlineType switch
|
||||
{
|
||||
OutlineType.TrueType => this.CreateTrueTypeGlyphMetrics(codePoint, glyphId, glyphType, textAttributes, textDecorations, isVerticalLayout, paletteIndex),
|
||||
OutlineType.CFF => this.CreateCffGlyphMetrics(codePoint, glyphId, glyphType, textAttributes, textDecorations, isVerticalLayout, paletteIndex),
|
||||
OutlineType.TrueType => this.CreateTrueTypeGlyphMetrics(in codePoint, glyphId, glyphType, textAttributes, textDecorations, isVerticalLayout, paletteIndex),
|
||||
OutlineType.CFF => this.CreateCffGlyphMetrics(in codePoint, glyphId, glyphType, textAttributes, textDecorations, isVerticalLayout, paletteIndex),
|
||||
_ => throw new NotSupportedException(),
|
||||
};
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ namespace SixLabors.Fonts;
|
|||
/// </summary>
|
||||
public static class SystemFonts
|
||||
{
|
||||
private static readonly Lazy<SystemFontCollection> LazySystemFonts = new(() => new SystemFontCollection());
|
||||
private static readonly Lazy<SystemFontCollection> LazySystemFonts = new(() => new SystemFontCollection(), true);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the collection containing the globally installed system fonts.
|
||||
|
|
|
@ -11,7 +11,7 @@ namespace SixLabors.Fonts.Tables.AdvancedTypographic;
|
|||
/// </summary>
|
||||
internal sealed class UnicodeScriptTagMap : Dictionary<ScriptClass, Tag[]>
|
||||
{
|
||||
private static readonly Lazy<UnicodeScriptTagMap> Lazy = new(() => CreateMap());
|
||||
private static readonly Lazy<UnicodeScriptTagMap> Lazy = new(() => CreateMap(), true);
|
||||
|
||||
/// <summary>
|
||||
/// Prevents a default instance of the <see cref="UnicodeScriptTagMap"/> class from being created.
|
||||
|
|
|
@ -11,7 +11,6 @@ namespace SixLabors.Fonts.Tables.Cff;
|
|||
/// </summary>
|
||||
internal class CffGlyphMetrics : GlyphMetrics
|
||||
{
|
||||
private static readonly Vector2 YInverter = new(1, -1);
|
||||
private CffGlyphData glyphData;
|
||||
|
||||
internal CffGlyphMetrics(
|
||||
|
|
|
@ -5,7 +5,7 @@ namespace SixLabors.Fonts.Tables.Cff;
|
|||
|
||||
internal sealed class CFFOperator
|
||||
{
|
||||
private static readonly Lazy<Dictionary<int, CFFOperator>> RegisteredOperators = new(() => CreateDictionary());
|
||||
private static readonly Lazy<Dictionary<int, CFFOperator>> RegisteredOperators = new(() => CreateDictionary(), true);
|
||||
private readonly byte b0;
|
||||
private readonly byte b1;
|
||||
private readonly OperatorOperandKind operatorOperandKind;
|
||||
|
|
|
@ -9,18 +9,18 @@ namespace SixLabors.Fonts.Unicode;
|
|||
|
||||
internal static class UnicodeData
|
||||
{
|
||||
private static readonly Lazy<UnicodeTrie> LazyBidiTrie = new(() => GetBidiTrie());
|
||||
private static readonly Lazy<UnicodeTrie> LazyBidiMirrorTrie = new(() => GetBidiMirrorTrie());
|
||||
private static readonly Lazy<UnicodeTrie> LazyGraphemeTrie = new(() => GetGraphemeTrie());
|
||||
private static readonly Lazy<UnicodeTrie> LazyLineBreakTrie = new(() => GetLineBreakTrie());
|
||||
private static readonly Lazy<UnicodeTrie> LazyScriptTrie = new(() => GetScriptTrie());
|
||||
private static readonly Lazy<UnicodeTrie> LazyCategoryTrie = new(() => GetCategoryTrie());
|
||||
private static readonly Lazy<UnicodeTrie> LazyArabicShapingTrie = new(() => GetArabicShapingTrie());
|
||||
private static readonly Lazy<UnicodeTrie> LazyIndicSyllabicCategoryTrie = new(() => GetIndicSyllabicCategoryTrie());
|
||||
private static readonly Lazy<UnicodeTrie> LazyIndicPositionalCategoryTrie = new(() => GetIndicPositionalCategoryTrie());
|
||||
private static readonly Lazy<UnicodeTrie> LazyVerticalOrientationTrie = new(() => GetVerticalOrientationTrie());
|
||||
private static readonly Lazy<UnicodeTrie> LazyUniversalShapingTrie = new(() => GetUniversalShapingTrie());
|
||||
private static readonly Lazy<UnicodeTrie> LazyIndicShapingTrie = new(() => GetIndicShapingTrie());
|
||||
private static readonly Lazy<UnicodeTrie> LazyBidiTrie = new(() => GetBidiTrie(), true);
|
||||
private static readonly Lazy<UnicodeTrie> LazyBidiMirrorTrie = new(() => GetBidiMirrorTrie(), true);
|
||||
private static readonly Lazy<UnicodeTrie> LazyGraphemeTrie = new(() => GetGraphemeTrie(), true);
|
||||
private static readonly Lazy<UnicodeTrie> LazyLineBreakTrie = new(() => GetLineBreakTrie(), true);
|
||||
private static readonly Lazy<UnicodeTrie> LazyScriptTrie = new(() => GetScriptTrie(), true);
|
||||
private static readonly Lazy<UnicodeTrie> LazyCategoryTrie = new(() => GetCategoryTrie(), true);
|
||||
private static readonly Lazy<UnicodeTrie> LazyArabicShapingTrie = new(() => GetArabicShapingTrie(), true);
|
||||
private static readonly Lazy<UnicodeTrie> LazyIndicSyllabicCategoryTrie = new(() => GetIndicSyllabicCategoryTrie(), true);
|
||||
private static readonly Lazy<UnicodeTrie> LazyIndicPositionalCategoryTrie = new(() => GetIndicPositionalCategoryTrie(), true);
|
||||
private static readonly Lazy<UnicodeTrie> LazyVerticalOrientationTrie = new(() => GetVerticalOrientationTrie(), true);
|
||||
private static readonly Lazy<UnicodeTrie> LazyUniversalShapingTrie = new(() => GetUniversalShapingTrie(), true);
|
||||
private static readonly Lazy<UnicodeTrie> LazyIndicShapingTrie = new(() => GetIndicShapingTrie(), true);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static uint GetBidiData(uint codePoint) => LazyBidiTrie.Value.Get(codePoint);
|
||||
|
|
|
@ -14,7 +14,7 @@ namespace SixLabors.Fonts.Benchmarks;
|
|||
/// </para>
|
||||
/// <para>We should see if we can include the Skia HarfBuzz extensions to see how we compare.</para>
|
||||
/// </summary>
|
||||
[MediumRunJob]
|
||||
[ShortRunJob]
|
||||
public class MeasureTextBenchmark : IDisposable
|
||||
{
|
||||
private readonly TextOptions textOptions;
|
||||
|
@ -22,12 +22,42 @@ public class MeasureTextBenchmark : IDisposable
|
|||
private readonly SKFont font;
|
||||
private readonly SKPaint paint;
|
||||
|
||||
private const string LoremIpsum = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. "
|
||||
+ "Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. "
|
||||
+ "Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper congue, euismod non, mi. "
|
||||
+ "Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. "
|
||||
+ "Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. "
|
||||
+ "Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. "
|
||||
+ "Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. "
|
||||
+ "Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; "
|
||||
+ "Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. "
|
||||
+ "\n\n"
|
||||
+ "Ut velit mauris, egestas sed, gravida nec, ornare ut, mi. Aenean ut orci vel massa suscipit pulvinar. "
|
||||
+ "Nulla sollicitudin. Fusce varius, ligula non tempus aliquam, nunc turpis ullamcorper nibh, in tempus sapien eros vitae ligula. "
|
||||
+ "Pellentesque rhoncus nunc et augue. Integer id felis. Curabitur aliquet pellentesque diam. "
|
||||
+ "Integer quis metus vitae elit lobortis egestas. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. "
|
||||
+ "Morbi vel erat non mauris convallis vehicula. Nulla et sapien. Integer tortor tellus, aliquam faucibus, "
|
||||
+ "convallis id, congue eu, quam. Mauris ullamcorper felis vitae erat. Proin feugiat, augue non elementum posuere, "
|
||||
+ "metus purus iaculis lectus, et tristique ligula justo vitae magna.\n\n"
|
||||
+ "Aliquam convallis sollicitudin purus. Praesent aliquam, enim at fermentum mollis, ligula massa adipiscing nisl, "
|
||||
+ "ac euismod nibh nisl eu lectus. Fusce vulputate eleifend sapien. Vestibulum purus quam, scelerisque ut, mollis sed, "
|
||||
+ "nonummy id, metus. Nullam accumsan lorem in dui. Cras ultricies mi eu turpis hendrerit fringilla. "
|
||||
+ "Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; In ac dui quis mi consectetuer lacinia. "
|
||||
+ "Nam pretium turpis et arcu. Duis arcu tortor, suscipit eget, imperdiet nec, imperdiet iaculis, ipsum. "
|
||||
+ "Sed aliquam ultrices mauris. Integer ante arcu, accumsan a, consectetuer eget, posuere ut, mauris. "
|
||||
+ "Praesent adipiscing. Phasellus ullamcorper ipsum rutrum nunc. Nunc nonummy metus. "
|
||||
+ "Vestibulum volutpat pretium libero. Cras id dui. Aenean ut eros et nisl sagittis vestibulum. "
|
||||
+ "Nullam nulla eros, ultricies sit amet, nonummy id, imperdiet feugiat, pede. Sed lectus.";
|
||||
|
||||
public MeasureTextBenchmark()
|
||||
{
|
||||
const string fontFamilyName = "Arial";
|
||||
const int fontSize = 16;
|
||||
|
||||
this.textOptions = new TextOptions(SystemFonts.Get(fontFamilyName).CreateFont(fontSize, FontStyle.Regular));
|
||||
this.textOptions = new TextOptions(SystemFonts.Get(fontFamilyName).CreateFont(fontSize, FontStyle.Regular))
|
||||
{
|
||||
WrappingLength = 400
|
||||
};
|
||||
|
||||
this.arialTypeface = SKTypeface.FromFamilyName(fontFamilyName, SKFontStyle.Normal);
|
||||
this.font = new SKFont(this.arialTypeface, fontSize);
|
||||
|
@ -41,12 +71,12 @@ public class MeasureTextBenchmark : IDisposable
|
|||
this.paint.Dispose();
|
||||
}
|
||||
|
||||
[Params("a", "Hello world", "The quick brown fox jumps over the lazy dog")]
|
||||
[Params("a", "Hello world", "The quick brown fox jumps over the lazy dog", LoremIpsum)]
|
||||
public string Text { get; set; } = string.Empty;
|
||||
|
||||
[Benchmark]
|
||||
public void SixLaborsFonts() => TextMeasurer.MeasureSize(this.Text, this.textOptions);
|
||||
|
||||
[Benchmark]
|
||||
public void SkiaSharp() => this.paint.MeasureText(this.Text);
|
||||
// [Benchmark]
|
||||
// public void SkiaSharp() => this.paint.MeasureText(this.Text);
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче