|
@@ -2,6 +2,7 @@
|
|
|
using System.Buffers;
|
|
|
using System.Collections;
|
|
|
using System.Collections.Generic;
|
|
|
+using System.Runtime.CompilerServices;
|
|
|
using Avalonia.Utilities;
|
|
|
|
|
|
namespace Avalonia.Media.TextFormatting
|
|
@@ -9,12 +10,13 @@ namespace Avalonia.Media.TextFormatting
|
|
|
public sealed class ShapedBuffer : IReadOnlyList<GlyphInfo>, IDisposable
|
|
|
{
|
|
|
private GlyphInfo[]? _rentedBuffer;
|
|
|
+ private ArraySlice<GlyphInfo> _glyphInfos;
|
|
|
|
|
|
public ShapedBuffer(ReadOnlyMemory<char> text, int bufferLength, IGlyphTypeface glyphTypeface, double fontRenderingEmSize, sbyte bidiLevel)
|
|
|
{
|
|
|
- _rentedBuffer = ArrayPool<GlyphInfo>.Shared.Rent(bufferLength);
|
|
|
Text = text;
|
|
|
- GlyphInfos = new ArraySlice<GlyphInfo>(_rentedBuffer, 0, bufferLength);
|
|
|
+ _rentedBuffer = ArrayPool<GlyphInfo>.Shared.Rent(bufferLength);
|
|
|
+ _glyphInfos = new ArraySlice<GlyphInfo>(_rentedBuffer, 0, bufferLength);
|
|
|
GlyphTypeface = glyphTypeface;
|
|
|
FontRenderingEmSize = fontRenderingEmSize;
|
|
|
BidiLevel = bidiLevel;
|
|
@@ -23,27 +25,70 @@ namespace Avalonia.Media.TextFormatting
|
|
|
internal ShapedBuffer(ReadOnlyMemory<char> text, ArraySlice<GlyphInfo> glyphInfos, IGlyphTypeface glyphTypeface, double fontRenderingEmSize, sbyte bidiLevel)
|
|
|
{
|
|
|
Text = text;
|
|
|
- GlyphInfos = glyphInfos;
|
|
|
+ _glyphInfos = glyphInfos;
|
|
|
GlyphTypeface = glyphTypeface;
|
|
|
FontRenderingEmSize = fontRenderingEmSize;
|
|
|
BidiLevel = bidiLevel;
|
|
|
}
|
|
|
|
|
|
- internal ArraySlice<GlyphInfo> GlyphInfos { get; private set; }
|
|
|
-
|
|
|
- public int Length
|
|
|
- => GlyphInfos.Length;
|
|
|
+ /// <summary>
|
|
|
+ /// The buffer's length.
|
|
|
+ /// </summary>
|
|
|
+ public int Length => _glyphInfos.Length;
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// The buffer's glyph typeface.
|
|
|
+ /// </summary>
|
|
|
public IGlyphTypeface GlyphTypeface { get; }
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// The buffers font rendering em size.
|
|
|
+ /// </summary>
|
|
|
public double FontRenderingEmSize { get; }
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// The buffer's bidi level.
|
|
|
+ /// </summary>
|
|
|
public sbyte BidiLevel { get; }
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// The buffer's reading direction.
|
|
|
+ /// </summary>
|
|
|
public bool IsLeftToRight => (BidiLevel & 1) == 0;
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// The text that is represended by this buffer.
|
|
|
+ /// </summary>
|
|
|
public ReadOnlyMemory<char> Text { get; }
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Reverses the buffer.
|
|
|
+ /// </summary>
|
|
|
+ public void Reverse()
|
|
|
+ {
|
|
|
+ _glyphInfos.Span.Reverse();
|
|
|
+ }
|
|
|
+
|
|
|
+ public void Dispose()
|
|
|
+ {
|
|
|
+ if (_rentedBuffer is not null)
|
|
|
+ {
|
|
|
+ ArrayPool<GlyphInfo>.Shared.Return(_rentedBuffer);
|
|
|
+ _rentedBuffer = null;
|
|
|
+ _glyphInfos = ArraySlice<GlyphInfo>.Empty; // ensure we don't misuse the returned array
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public GlyphInfo this[int index]
|
|
|
+ {
|
|
|
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
|
+ get => _glyphInfos[index];
|
|
|
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
|
+ set => _glyphInfos[index] = value;
|
|
|
+ }
|
|
|
+
|
|
|
+ public IEnumerator<GlyphInfo> GetEnumerator() => _glyphInfos.GetEnumerator();
|
|
|
+
|
|
|
/// <summary>
|
|
|
/// Finds a glyph index for given character index.
|
|
|
/// </summary>
|
|
@@ -53,20 +98,19 @@ namespace Avalonia.Media.TextFormatting
|
|
|
/// </returns>
|
|
|
private int FindGlyphIndex(int characterIndex)
|
|
|
{
|
|
|
- if (characterIndex < GlyphInfos[0].GlyphCluster)
|
|
|
+ if (characterIndex < _glyphInfos[0].GlyphCluster)
|
|
|
{
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
- if (characterIndex > GlyphInfos[GlyphInfos.Length - 1].GlyphCluster)
|
|
|
+ if (characterIndex > _glyphInfos[_glyphInfos.Length - 1].GlyphCluster)
|
|
|
{
|
|
|
- return GlyphInfos.Length - 1;
|
|
|
+ return _glyphInfos.Length - 1;
|
|
|
}
|
|
|
|
|
|
-
|
|
|
var comparer = GlyphInfo.ClusterAscendingComparer;
|
|
|
|
|
|
- var glyphInfos = GlyphInfos.Span;
|
|
|
+ var glyphInfos = _glyphInfos.Span;
|
|
|
|
|
|
var searchValue = new GlyphInfo(default, characterIndex, default);
|
|
|
|
|
@@ -109,42 +153,24 @@ namespace Avalonia.Media.TextFormatting
|
|
|
return new SplitResult<ShapedBuffer>(this, null);
|
|
|
}
|
|
|
|
|
|
- var firstCluster = GlyphInfos[0].GlyphCluster;
|
|
|
- var lastCluster = GlyphInfos[GlyphInfos.Length - 1].GlyphCluster;
|
|
|
+ var firstCluster = _glyphInfos[0].GlyphCluster;
|
|
|
+ var lastCluster = _glyphInfos[_glyphInfos.Length - 1].GlyphCluster;
|
|
|
|
|
|
var start = firstCluster < lastCluster ? firstCluster : lastCluster;
|
|
|
|
|
|
var glyphCount = FindGlyphIndex(start + length);
|
|
|
|
|
|
var first = new ShapedBuffer(Text.Slice(0, length),
|
|
|
- GlyphInfos.Take(glyphCount), GlyphTypeface, FontRenderingEmSize, BidiLevel);
|
|
|
+ _glyphInfos.Take(glyphCount), GlyphTypeface, FontRenderingEmSize, BidiLevel);
|
|
|
|
|
|
var second = new ShapedBuffer(Text.Slice(length),
|
|
|
- GlyphInfos.Skip(glyphCount), GlyphTypeface, FontRenderingEmSize, BidiLevel);
|
|
|
+ _glyphInfos.Skip(glyphCount), GlyphTypeface, FontRenderingEmSize, BidiLevel);
|
|
|
|
|
|
return new SplitResult<ShapedBuffer>(first, second);
|
|
|
}
|
|
|
|
|
|
- int IReadOnlyCollection<GlyphInfo>.Count => GlyphInfos.Length;
|
|
|
-
|
|
|
- public GlyphInfo this[int index]
|
|
|
- {
|
|
|
- get => GlyphInfos[index];
|
|
|
- set => GlyphInfos[index] = value;
|
|
|
- }
|
|
|
-
|
|
|
- public IEnumerator<GlyphInfo> GetEnumerator() => GlyphInfos.GetEnumerator();
|
|
|
+ int IReadOnlyCollection<GlyphInfo>.Count => _glyphInfos.Length;
|
|
|
|
|
|
- IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
|
|
-
|
|
|
- public void Dispose()
|
|
|
- {
|
|
|
- if (_rentedBuffer is not null)
|
|
|
- {
|
|
|
- ArrayPool<GlyphInfo>.Shared.Return(_rentedBuffer);
|
|
|
- _rentedBuffer = null;
|
|
|
- GlyphInfos = ArraySlice<GlyphInfo>.Empty; // ensure we don't misuse the returned array
|
|
|
- }
|
|
|
- }
|
|
|
+ IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
|
|
}
|
|
|
}
|