Skip to content

Commit 3e01a53

Browse files
authored
Reuse single HarfBuzz buffer in TextShaperImpl (#18892)
1 parent c0bd507 commit 3e01a53

File tree

1 file changed

+42
-38
lines changed

1 file changed

+42
-38
lines changed

src/Skia/Avalonia.Skia/TextShaperImpl.cs

Lines changed: 42 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ namespace Avalonia.Skia
1414
{
1515
internal class TextShaperImpl : ITextShaperImpl
1616
{
17+
[ThreadStatic]
18+
private static Buffer? s_buffer;
19+
1720
private static readonly ConcurrentDictionary<int, Language> s_cachedLanguage = new();
1821

1922
public ShapedBuffer ShapeText(ReadOnlyMemory<char> text, TextShaperOptions options)
@@ -24,69 +27,70 @@ public ShapedBuffer ShapeText(ReadOnlyMemory<char> text, TextShaperOptions optio
2427
var bidiLevel = options.BidiLevel;
2528
var culture = options.Culture;
2629

27-
using (var buffer = new Buffer())
28-
{
29-
// HarfBuzz needs the surrounding characters to correctly shape the text
30-
var containingText = GetContainingMemory(text, out var start, out var length).Span;
31-
buffer.AddUtf16(containingText, start, length);
30+
var buffer = s_buffer ??= new Buffer();
3231

33-
MergeBreakPair(buffer);
32+
buffer.Reset();
3433

35-
buffer.GuessSegmentProperties();
34+
// HarfBuzz needs the surrounding characters to correctly shape the text
35+
var containingText = GetContainingMemory(text, out var start, out var length).Span;
36+
buffer.AddUtf16(containingText, start, length);
3637

37-
buffer.Direction = (bidiLevel & 1) == 0 ? Direction.LeftToRight : Direction.RightToLeft;
38+
MergeBreakPair(buffer);
3839

39-
var usedCulture = culture ?? CultureInfo.CurrentCulture;
40+
buffer.GuessSegmentProperties();
4041

41-
buffer.Language = s_cachedLanguage.GetOrAdd(usedCulture.LCID, _ => new Language(usedCulture));
42+
buffer.Direction = (bidiLevel & 1) == 0 ? Direction.LeftToRight : Direction.RightToLeft;
4243

43-
var font = ((GlyphTypefaceImpl)typeface).Font;
44+
var usedCulture = culture ?? CultureInfo.CurrentCulture;
4445

45-
font.Shape(buffer, GetFeatures(options));
46+
buffer.Language = s_cachedLanguage.GetOrAdd(usedCulture.LCID, _ => new Language(usedCulture));
4647

47-
if (buffer.Direction == Direction.RightToLeft)
48-
{
49-
buffer.Reverse();
50-
}
48+
var font = ((GlyphTypefaceImpl)typeface).Font;
5149

52-
font.GetScale(out var scaleX, out _);
50+
font.Shape(buffer, GetFeatures(options));
5351

54-
var textScale = fontRenderingEmSize / scaleX;
52+
if (buffer.Direction == Direction.RightToLeft)
53+
{
54+
buffer.Reverse();
55+
}
5556

56-
var bufferLength = buffer.Length;
57+
font.GetScale(out var scaleX, out _);
5758

58-
var shapedBuffer = new ShapedBuffer(text, bufferLength, typeface, fontRenderingEmSize, bidiLevel);
59+
var textScale = fontRenderingEmSize / scaleX;
5960

60-
var glyphInfos = buffer.GetGlyphInfoSpan();
61+
var bufferLength = buffer.Length;
6162

62-
var glyphPositions = buffer.GetGlyphPositionSpan();
63+
var shapedBuffer = new ShapedBuffer(text, bufferLength, typeface, fontRenderingEmSize, bidiLevel);
6364

64-
for (var i = 0; i < bufferLength; i++)
65-
{
66-
var sourceInfo = glyphInfos[i];
65+
var glyphInfos = buffer.GetGlyphInfoSpan();
6766

68-
var glyphIndex = (ushort)sourceInfo.Codepoint;
67+
var glyphPositions = buffer.GetGlyphPositionSpan();
6968

70-
var glyphCluster = (int)sourceInfo.Cluster;
69+
for (var i = 0; i < bufferLength; i++)
70+
{
71+
var sourceInfo = glyphInfos[i];
7172

72-
var glyphAdvance = GetGlyphAdvance(glyphPositions, i, textScale) + options.LetterSpacing;
73+
var glyphIndex = (ushort)sourceInfo.Codepoint;
7374

74-
var glyphOffset = GetGlyphOffset(glyphPositions, i, textScale);
75+
var glyphCluster = (int)sourceInfo.Cluster;
7576

76-
if (glyphCluster < containingText.Length && containingText[glyphCluster] == '\t')
77-
{
78-
glyphIndex = typeface.GetGlyph(' ');
77+
var glyphAdvance = GetGlyphAdvance(glyphPositions, i, textScale) + options.LetterSpacing;
7978

80-
glyphAdvance = options.IncrementalTabWidth > 0 ?
81-
options.IncrementalTabWidth :
82-
4 * typeface.GetGlyphAdvance(glyphIndex) * textScale;
83-
}
79+
var glyphOffset = GetGlyphOffset(glyphPositions, i, textScale);
8480

85-
shapedBuffer[i] = new Media.TextFormatting.GlyphInfo(glyphIndex, glyphCluster, glyphAdvance, glyphOffset);
81+
if (glyphCluster < containingText.Length && containingText[glyphCluster] == '\t')
82+
{
83+
glyphIndex = typeface.GetGlyph(' ');
84+
85+
glyphAdvance = options.IncrementalTabWidth > 0 ?
86+
options.IncrementalTabWidth :
87+
4 * typeface.GetGlyphAdvance(glyphIndex) * textScale;
8688
}
8789

88-
return shapedBuffer;
90+
shapedBuffer[i] = new Media.TextFormatting.GlyphInfo(glyphIndex, glyphCluster, glyphAdvance, glyphOffset);
8991
}
92+
93+
return shapedBuffer;
9094
}
9195

9296
private static void MergeBreakPair(Buffer buffer)

0 commit comments

Comments
 (0)