Browse Source

Reuse single HarfBuzz buffer in TextShaperImpl (#18892)

release/11.3.1
Compunet 8 months ago
committed by Julien Lebosquain
parent
commit
d05393d3aa
  1. 80
      src/Skia/Avalonia.Skia/TextShaperImpl.cs

80
src/Skia/Avalonia.Skia/TextShaperImpl.cs

@ -14,6 +14,9 @@ namespace Avalonia.Skia
{ {
internal class TextShaperImpl : ITextShaperImpl internal class TextShaperImpl : ITextShaperImpl
{ {
[ThreadStatic]
private static Buffer? s_buffer;
private static readonly ConcurrentDictionary<int, Language> s_cachedLanguage = new(); private static readonly ConcurrentDictionary<int, Language> s_cachedLanguage = new();
public ShapedBuffer ShapeText(ReadOnlyMemory<char> text, TextShaperOptions options) public ShapedBuffer ShapeText(ReadOnlyMemory<char> text, TextShaperOptions options)
@ -24,69 +27,70 @@ namespace Avalonia.Skia
var bidiLevel = options.BidiLevel; var bidiLevel = options.BidiLevel;
var culture = options.Culture; var culture = options.Culture;
using (var buffer = new Buffer()) var buffer = s_buffer ??= new Buffer();
{
// HarfBuzz needs the surrounding characters to correctly shape the text
var containingText = GetContainingMemory(text, out var start, out var length).Span;
buffer.AddUtf16(containingText, start, length);
MergeBreakPair(buffer); buffer.Reset();
buffer.GuessSegmentProperties(); // HarfBuzz needs the surrounding characters to correctly shape the text
var containingText = GetContainingMemory(text, out var start, out var length).Span;
buffer.AddUtf16(containingText, start, length);
buffer.Direction = (bidiLevel & 1) == 0 ? Direction.LeftToRight : Direction.RightToLeft; MergeBreakPair(buffer);
var usedCulture = culture ?? CultureInfo.CurrentCulture; buffer.GuessSegmentProperties();
buffer.Language = s_cachedLanguage.GetOrAdd(usedCulture.LCID, _ => new Language(usedCulture)); buffer.Direction = (bidiLevel & 1) == 0 ? Direction.LeftToRight : Direction.RightToLeft;
var font = ((GlyphTypefaceImpl)typeface).Font; var usedCulture = culture ?? CultureInfo.CurrentCulture;
font.Shape(buffer, GetFeatures(options)); buffer.Language = s_cachedLanguage.GetOrAdd(usedCulture.LCID, _ => new Language(usedCulture));
if (buffer.Direction == Direction.RightToLeft) var font = ((GlyphTypefaceImpl)typeface).Font;
{
buffer.Reverse();
}
font.GetScale(out var scaleX, out _); font.Shape(buffer, GetFeatures(options));
var textScale = fontRenderingEmSize / scaleX; if (buffer.Direction == Direction.RightToLeft)
{
buffer.Reverse();
}
var bufferLength = buffer.Length; font.GetScale(out var scaleX, out _);
var shapedBuffer = new ShapedBuffer(text, bufferLength, typeface, fontRenderingEmSize, bidiLevel); var textScale = fontRenderingEmSize / scaleX;
var glyphInfos = buffer.GetGlyphInfoSpan(); var bufferLength = buffer.Length;
var glyphPositions = buffer.GetGlyphPositionSpan(); var shapedBuffer = new ShapedBuffer(text, bufferLength, typeface, fontRenderingEmSize, bidiLevel);
for (var i = 0; i < bufferLength; i++) var glyphInfos = buffer.GetGlyphInfoSpan();
{
var sourceInfo = glyphInfos[i];
var glyphIndex = (ushort)sourceInfo.Codepoint; var glyphPositions = buffer.GetGlyphPositionSpan();
var glyphCluster = (int)sourceInfo.Cluster; for (var i = 0; i < bufferLength; i++)
{
var sourceInfo = glyphInfos[i];
var glyphAdvance = GetGlyphAdvance(glyphPositions, i, textScale) + options.LetterSpacing; var glyphIndex = (ushort)sourceInfo.Codepoint;
var glyphOffset = GetGlyphOffset(glyphPositions, i, textScale); var glyphCluster = (int)sourceInfo.Cluster;
if (glyphCluster < containingText.Length && containingText[glyphCluster] == '\t') var glyphAdvance = GetGlyphAdvance(glyphPositions, i, textScale) + options.LetterSpacing;
{
glyphIndex = typeface.GetGlyph(' ');
glyphAdvance = options.IncrementalTabWidth > 0 ? var glyphOffset = GetGlyphOffset(glyphPositions, i, textScale);
options.IncrementalTabWidth :
4 * typeface.GetGlyphAdvance(glyphIndex) * textScale;
}
shapedBuffer[i] = new Media.TextFormatting.GlyphInfo(glyphIndex, glyphCluster, glyphAdvance, glyphOffset); if (glyphCluster < containingText.Length && containingText[glyphCluster] == '\t')
{
glyphIndex = typeface.GetGlyph(' ');
glyphAdvance = options.IncrementalTabWidth > 0 ?
options.IncrementalTabWidth :
4 * typeface.GetGlyphAdvance(glyphIndex) * textScale;
} }
return shapedBuffer; shapedBuffer[i] = new Media.TextFormatting.GlyphInfo(glyphIndex, glyphCluster, glyphAdvance, glyphOffset);
} }
return shapedBuffer;
} }
private static void MergeBreakPair(Buffer buffer) private static void MergeBreakPair(Buffer buffer)

Loading…
Cancel
Save