Browse Source

Merge pull request #9928 from Mikolaytis/GraphemeOptimize

[Text] GraphemeIterator perf improve
pull/9949/head
Benedikt Stebner 3 years ago
committed by GitHub
parent
commit
a56417a569
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 27
      src/Avalonia.Base/Media/TextFormatting/CharacterBufferRange.cs
  2. 2
      src/Avalonia.Base/Media/TextFormatting/FormattedTextSource.cs
  3. 4
      src/Avalonia.Base/Media/TextFormatting/TextCharacters.cs
  4. 68
      src/Avalonia.Base/Media/TextFormatting/Unicode/Codepoint.cs
  5. 14
      src/Avalonia.Base/Media/TextFormatting/Unicode/Grapheme.cs
  6. 4
      src/Avalonia.Base/Media/TextFormatting/Unicode/GraphemeEnumerator.cs
  7. 2
      src/Avalonia.Controls/TextBox.cs
  8. 4
      tests/Avalonia.Base.UnitTests/Media/TextFormatting/GraphemeBreakClassTrieGeneratorTests.cs
  9. 62
      tests/Avalonia.Benchmarks/Text/HugeTextLayout.cs
  10. 8
      tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLayoutTests.cs

27
src/Avalonia.Base/Media/TextFormatting/CharacterBufferRange.cs

@ -140,7 +140,7 @@ namespace Avalonia.Media.TextFormatting
throw new ArgumentOutOfRangeException(nameof(index));
}
#endif
return Span[index];
return CharacterBufferReference.CharacterBuffer.Span[CharacterBufferReference.OffsetToFirstChar + index];
}
}
@ -163,27 +163,18 @@ namespace Avalonia.Media.TextFormatting
/// <summary>
/// Gets the character memory buffer
/// </summary>
internal ReadOnlyMemory<char> CharacterBuffer
{
get { return CharacterBufferReference.CharacterBuffer; }
}
internal ReadOnlyMemory<char> CharacterBuffer => CharacterBufferReference.CharacterBuffer;
/// <summary>
/// Gets the character offset relative to the beginning of buffer to
/// the first character of the run
/// </summary>
internal int OffsetToFirstChar
{
get { return CharacterBufferReference.OffsetToFirstChar; }
}
internal int OffsetToFirstChar => CharacterBufferReference.OffsetToFirstChar;
/// <summary>
/// Indicate whether the character buffer range is empty
/// </summary>
internal bool IsEmpty
{
get { return CharacterBufferReference.CharacterBuffer.Length == 0 || Length <= 0; }
}
internal bool IsEmpty => CharacterBufferReference.CharacterBuffer.Length == 0 || Length <= 0;
internal CharacterBufferRange Take(int length)
{
@ -280,14 +271,8 @@ namespace Avalonia.Media.TextFormatting
int IReadOnlyCollection<char>.Count => Length;
public IEnumerator<char> GetEnumerator()
{
return new ImmutableReadOnlyListStructEnumerator<char>(this);
}
public IEnumerator<char> GetEnumerator() => new ImmutableReadOnlyListStructEnumerator<char>(this);
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
}

2
src/Avalonia.Base/Media/TextFormatting/FormattedTextSource.cs

@ -132,7 +132,7 @@ namespace Avalonia.Media.TextFormatting
{
var grapheme = graphemeEnumerator.Current;
finalLength += grapheme.Text.Length;
finalLength += grapheme.Length;
if (finalLength >= length)
{

4
src/Avalonia.Base/Media/TextFormatting/TextCharacters.cs

@ -196,7 +196,7 @@ namespace Avalonia.Media.TextFormatting
break;
}
count += grapheme.Text.Length;
count += grapheme.Length;
}
return new UnshapedTextRun(characterBufferRange.CharacterBufferReference, count, defaultProperties, biDiLevel);
@ -264,7 +264,7 @@ namespace Avalonia.Media.TextFormatting
}
}
length += currentGrapheme.Text.Length;
length += currentGrapheme.Length;
}
return length > 0;

68
src/Avalonia.Base/Media/TextFormatting/Unicode/Codepoint.cs

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
namespace Avalonia.Media.TextFormatting.Unicode
@ -222,6 +223,71 @@ namespace Avalonia.Media.TextFormatting.Unicode
return new Codepoint(code);
}
/// <summary>
/// Reads the <see cref="Codepoint"/> at specified position.
/// </summary>
/// <param name="text">The buffer to read from.</param>
/// <param name="index">The index to read at.</param>
/// <param name="count">The count of character that were read.</param>
/// <returns></returns>
public static Codepoint ReadAt(CharacterBufferRange text, int index, out int count)
{
count = 1;
if (index >= text.Length)
{
return ReplacementCodepoint;
}
var code = text[index];
ushort hi, low;
//# High surrogate
if (0xD800 <= code && code <= 0xDBFF)
{
hi = code;
if (index + 1 == text.Length)
{
return ReplacementCodepoint;
}
low = text[index + 1];
if (0xDC00 <= low && low <= 0xDFFF)
{
count = 2;
return new Codepoint((uint)((hi - 0xD800) * 0x400 + (low - 0xDC00) + 0x10000));
}
return ReplacementCodepoint;
}
//# Low surrogate
if (0xDC00 <= code && code <= 0xDFFF)
{
if (index == 0)
{
return ReplacementCodepoint;
}
hi = text[index - 1];
low = code;
if (0xD800 <= hi && hi <= 0xDBFF)
{
count = 2;
return new Codepoint((uint)((hi - 0xD800) * 0x400 + (low - 0xDC00) + 0x10000));
}
return ReplacementCodepoint;
}
return new Codepoint(code);
}
/// <summary>
/// Returns <see langword="true"/> if <paramref name="cp"/> is between

14
src/Avalonia.Base/Media/TextFormatting/Unicode/Grapheme.cs

@ -7,10 +7,11 @@ namespace Avalonia.Media.TextFormatting.Unicode
/// </summary>
public readonly ref struct Grapheme
{
public Grapheme(Codepoint firstCodepoint, ReadOnlySpan<char> text)
public Grapheme(Codepoint firstCodepoint, int offset, int length)
{
FirstCodepoint = firstCodepoint;
Text = text;
Offset = offset;
Length = length;
}
/// <summary>
@ -19,8 +20,13 @@ namespace Avalonia.Media.TextFormatting.Unicode
public Codepoint FirstCodepoint { get; }
/// <summary>
/// The text that is representing the <see cref="Grapheme"/>.
/// The Offset to the FirstCodepoint
/// </summary>
public ReadOnlySpan<char> Text { get; }
public int Offset { get; }
/// <summary>
/// The length of the grapheme cluster
/// </summary>
public int Length { get; }
}
}

4
src/Avalonia.Base/Media/TextFormatting/Unicode/GraphemeEnumerator.cs

@ -185,9 +185,7 @@ namespace Avalonia.Media.TextFormatting.Unicode
Return:
var text = _text.Take(processor.CurrentCodeUnitOffset);
Current = new Grapheme(firstCodepoint, text.Span);
Current = new Grapheme(firstCodepoint, _text.OffsetToFirstChar, processor.CurrentCodeUnitOffset);
_text = _text.Skip(processor.CurrentCodeUnitOffset);

2
src/Avalonia.Controls/TextBox.cs

@ -981,7 +981,7 @@ namespace Avalonia.Controls
}
}
length += grapheme.Text.Length;
length += grapheme.Length;
}
if (length < input.Length)

4
tests/Avalonia.Base.UnitTests/Media/TextFormatting/GraphemeBreakClassTrieGeneratorTests.cs

@ -42,7 +42,7 @@ namespace Avalonia.Base.UnitTests.Media.TextFormatting
enumerator.MoveNext();
var actual = enumerator.Current.Text;
var actual = text.AsSpan(enumerator.Current.Offset, enumerator.Current.Length);
var pass = true;
@ -93,7 +93,7 @@ namespace Avalonia.Base.UnitTests.Media.TextFormatting
while (enumerator.MoveNext())
{
Assert.Equal(1, enumerator.Current.Text.Length);
Assert.Equal(1, enumerator.Current.Length);
count++;
}

62
tests/Avalonia.Benchmarks/Text/HugeTextLayout.cs

@ -0,0 +1,62 @@
using System;
using Avalonia.Media;
using Avalonia.Media.TextFormatting;
using Avalonia.UnitTests;
using BenchmarkDotNet.Attributes;
namespace Avalonia.Benchmarks.Text;
[MemoryDiagnoser]
public class HugeTextLayout : IDisposable
{
private readonly IDisposable _app;
public HugeTextLayout()
{
_app = UnitTestApplication.Start(
TestServices.StyledWindow.With(
renderInterface: new NullRenderingPlatform(),
threadingInterface: new NullThreadingPlatform(),
standardCursorFactory: new NullCursorFactory()));
}
private const string Text = @"Though, the objectives of the development of the prominent landmarks can be neglected in most cases, it should be realized that after the completion of the strategic decision gives rise to The Expertise of Regular Program (Carlton Cartwright in The Book of the Key Factor)
A number of key issues arise from the belief that the explicit examination of strategic management should correlate with the conceptual design.
By all means, the unification of the reliably developed techniques indicates the importance of the ultimate advantage of episodic skill over alternate practices.
Let's consider, that the portion of the continuing support can be regarded as relentlessly insignificant. The hardware maintenance focuses our attention on the structure absorption. The real reason of the permanent growth drastically the preliminary action plan the ultimate advantage of useful probability over alternate practices.
Let it not be said that a section of the essential component discards the principle of the more interconnection of critical thinking with productivity boosting of the referential arguments.
One should, however, not forget that concentration of violations of the strategic management requires urgent actions to be taken towards the comprehensive set of policy statements. Therefore, the concept of the design aspects can be treated as the only solution.
In a loose sense concentration of the center of the critical thinking provides a deep insight into the emergency planning. The comparison is quite a powerful matter.
Resulting from review or analysis of threats and opportunities, we can presume that either significant improvement or basics of planning and scheduling reveals the patterns of the final draft. Therefore, the concept of the crucial component can be treated as the only solution.
One should, nevertheless, consider that the exceptional results of the diverse sources of information gives an overview of the production cycle. It should rather be regarded as an integral part of the direct access to key resources.
Admitting that the possibility of achieving the results of the constructive criticism, as far as the strategic management is questionable, cannot rely only on the critical thinking. It may reveal how the systems approach partially the comprehensive project management. We must be ready for outline design stage and network development investigation of every contradiction between the effective time management and the efficient decision the network development. Everyone understands what it takes to the draft analysis and prior decisions and early design solutions. In any case, we can systematically change the mechanism of the sources and influences of the continuing financing doctrine. This could exceedingly be a result of a task analysis the hardware maintenance. The real reason of the strategic planning seemingly the influence on eventual productivity. Everyone understands what it takes to the well-known practice. Therefore, the concept of the productivity boost can be treated as the only solution the driving factor. It may reveal how the matters of peculiar interest slowly the goals and objectives or the diverse sources of information the positive influence of any major outcomes complete failure of the supposed theory.
In respect that the structure of the sufficient amount poses problems and challenges for both the set of related commands and controls and the ability bias.";
[Benchmark]
public TextLayout BuildTextLayout() => new TextLayout(Text, Typeface.Default, 12d, Brushes.Black);
private const string Emojis = @"😀 😁 😂 🤣 😃 😄 😅 😆 😉 😊 😋 😎 😍 😘 🥰 😗 😙 😚 ☺️ 🙂 🤗 🤩 🤔 🤨 😐 😑 😶 🙄 😏 😣 😥 😮 🤐 😯 😪 😫 😴 😌 😛 😜 😝 🤤 😒 😓 😔 😕 🙃 🤑 😲 ☹️ 🙁 😖 😞 😟 😤 😢 😭 😦 😧 😨 😩 🤯 😬 😰 😱 🥵 🥶 😳 🤪 😵 😡 😠 🤬 😷 🤒 🤕 🤢 🤮 🤧 😇 🤠 🤡 🥳 🥴 🥺 🤥 🤫 🤭 🧐 🤓 😈 👿 👹 👺 💀 👻 👽 🤖 💩 😺 😸 😹 😻 😼 😽 🙀 😿 😾
👶 👧 🧒 👦 👩 🧑 👨 👵 🧓 👴 👲 👳 👳 🧕 🧔 👱 👱 👨🦰 👩🦰 👨🦱 👩🦱 👨🦲 👩🦲 👨🦳 👩🦳 🦸 🦸 🦹 🦹 👮 👮 👷 👷 💂 💂 🕵 🕵 👩 👨 👩🌾 👨🌾 👩🍳 👨🍳 👩🎓 👨🎓 👩🎤 👨🎤 👩🏫 👨🏫 👩🏭 👨🏭 👩💻 👨💻 👩💼 👨💼 👩🔧 👨🔧 👩🔬 👨🔬 👩🎨 👨🎨 👩🚒 👨🚒 👩 👨 👩🚀 👨🚀 👩 👨 👰 🤵 👸 🤴 🤶 🎅 🧙 🧙 🧝 🧝 🧛 🧛 🧟 🧟 🧞 🧞 🧜 🧜 🧚 🧚 👼 🤰 🤱 🙇 🙇 💁 💁 🙅 🙅 🙆 🙆 🙋 🙋 🤦 🤦 🤷 🤷 🙎 🙎 🙍 🙍 💇 💇 💆 💆 🧖 🧖 💅 🤳 💃 🕺 👯 👯 🕴 🚶 🚶 🏃 🏃 👫 👭 👬 💑 👩👩 👨👨 💏 👩💋👩 👨💋👨 👪 👨👩👧 👨👩👧👦 👨👩👦👦 👨👩👧👧 👩👩👦 👩👩👧 👩👩👧👦 👩👩👦👦 👩👩👧👧 👨👨👦 👨👨👧 👨👨👧👦 👨👨👦👦 👨👨👧👧 👩👦 👩👧 👩👧👦 👩👦👦 👩👧👧 👨👦 👨👧 👨👧👦 👨👦👦 👨👧👧 🤲 👐 🙌 👏 🤝 👍 👎 👊 🤛 🤜 🤞 🤟 🤘 👌 👈 👉 👆 👇 🤚 🖐 🖖 👋 🤙 💪 🦵 🦶 🖕 🙏 💍 💄 💋 👄 👅 👂 👃 👣 👁 👀 🧠 🦴 🦷 🗣 👤 👥
🧥 👚 👕 👖 👔 👗 👙 👘 👠 👡 👢 👞 👟 🥾 🥿 🧦 🧤 🧣 🎩 🧢 👒 🎓 👑 👝 👛 👜 💼 🎒 👓 🕶 🥽 🥼 🌂 🧵 🧶
👶🏻 👦🏻 👧🏻 👨🏻 👩🏻 👱🏻 👱🏻 👴🏻 👵🏻 👲🏻 👳🏻 👳🏻 👮🏻 👮🏻 👷🏻 👷🏻 💂🏻 💂🏻 🕵🏻 🕵🏻 👩🏻 👨🏻 👩🏻🌾 👨🏻🌾 👩🏻🍳 👨🏻🍳 👩🏻🎓 👨🏻🎓 👩🏻🎤 👨🏻🎤 👩🏻🏫 👨🏻🏫 👩🏻🏭 👨🏻🏭 👩🏻💻 👨🏻💻 👩🏻💼 👨🏻💼 👩🏻🔧 👨🏻🔧 👩🏻🔬 👨🏻🔬 👩🏻🎨 👨🏻🎨 👩🏻🚒 👨🏻🚒 👩🏻 👨🏻 👩🏻🚀 👨🏻🚀 👩🏻 👨🏻 🤶🏻 🎅🏻 👸🏻 🤴🏻 👰🏻 🤵🏻 👼🏻 🤰🏻 🙇🏻 🙇🏻 💁🏻 💁🏻 🙅🏻 🙅🏻 🙆🏻 🙆🏻 🙋🏻 🙋🏻 🤦🏻 🤦🏻 🤷🏻 🤷🏻 🙎🏻 🙎🏻 🙍🏻 🙍🏻 💇🏻 💇🏻 💆🏻 💆🏻 🕴🏻 💃🏻 🕺🏻 🚶🏻 🚶🏻 🏃🏻 🏃🏻 🤲🏻 👐🏻 🙌🏻 👏🏻 🙏🏻 👍🏻 👎🏻 👊🏻 🏻 🤛🏻 🤜🏻 🤞🏻 🏻 🤟🏻 🤘🏻 👌🏻 👈🏻 👉🏻 👆🏻 👇🏻 🏻 🏻 🤚🏻 🖐🏻 🖖🏻 👋🏻 🤙🏻 💪🏻 🖕🏻 🏻 🤳🏻 💅🏻 👂🏻 👃🏻
👶🏼 👦🏼 👧🏼 👨🏼 👩🏼 👱🏼 👱🏼 👴🏼 👵🏼 👲🏼 👳🏼 👳🏼 👮🏼 👮🏼 👷🏼 👷🏼 💂🏼 💂🏼 🕵🏼 🕵🏼 👩🏼 👨🏼 👩🏼🌾 👨🏼🌾 👩🏼🍳 👨🏼🍳 👩🏼🎓 👨🏼🎓 👩🏼🎤 👨🏼🎤 👩🏼🏫 👨🏼🏫 👩🏼🏭 👨🏼🏭 👩🏼💻 👨🏼💻 👩🏼💼 👨🏼💼 👩🏼🔧 👨🏼🔧 👩🏼🔬 👨🏼🔬 👩🏼🎨 👨🏼🎨 👩🏼🚒 👨🏼🚒 👩🏼 👨🏼 👩🏼🚀 👨🏼🚀 👩🏼 👨🏼 🤶🏼 🎅🏼 👸🏼 🤴🏼 👰🏼 🤵🏼 👼🏼 🤰🏼 🙇🏼 🙇🏼 💁🏼 💁🏼 🙅🏼 🙅🏼 🙆🏼 🙆🏼 🙋🏼 🙋🏼 🤦🏼 🤦🏼 🤷🏼 🤷🏼 🙎🏼 🙎🏼 🙍🏼 🙍🏼 💇🏼 💇🏼 💆🏼 💆🏼 🕴🏼 💃🏼 🕺🏼 🚶🏼 🚶🏼 🏃🏼 🏃🏼 🤲🏼 👐🏼 🙌🏼 👏🏼 🙏🏼 👍🏼 👎🏼 👊🏼 🏼 🤛🏼 🤜🏼 🤞🏼 🏼 🤟🏼 🤘🏼 👌🏼 👈🏼 👉🏼 👆🏼 👇🏼 🏼 🏼 🤚🏼 🖐🏼 🖖🏼 👋🏼 🤙🏼 💪🏼 🖕🏼 🏼 🤳🏼 💅🏼 👂🏼 👃🏼
👶🏽 👦🏽 👧🏽 👨🏽 👩🏽 👱🏽 👱🏽 👴🏽 👵🏽 👲🏽 👳🏽 👳🏽 👮🏽 👮🏽 👷🏽 👷🏽 💂🏽 💂🏽 🕵🏽 🕵🏽 👩🏽 👨🏽 👩🏽🌾 👨🏽🌾 👩🏽🍳 👨🏽🍳 👩🏽🎓 👨🏽🎓 👩🏽🎤 👨🏽🎤 👩🏽🏫 👨🏽🏫 👩🏽🏭 👨🏽🏭 👩🏽💻 👨🏽💻 👩🏽💼 👨🏽💼 👩🏽🔧 👨🏽🔧 👩🏽🔬 👨🏽🔬 👩🏽🎨 👨🏽🎨 👩🏽🚒 👨🏽🚒 👩🏽 👨🏽 👩🏽🚀 👨🏽🚀 👩🏽 👨🏽 🤶🏽 🎅🏽 👸🏽 🤴🏽 👰🏽 🤵🏽 👼🏽 🤰🏽 🙇🏽 🙇🏽 💁🏽 💁🏽 🙅🏽 🙅🏽 🙆🏽 🙆🏽 🙋🏽 🙋🏽 🤦🏽 🤦🏽 🤷🏽 🤷🏽 🙎🏽 🙎🏽 🙍🏽 🙍🏽 💇🏽 💇🏽 💆🏽 💆🏽 🕴🏼 💃🏽 🕺🏽 🚶🏽 🚶🏽 🏃🏽 🏃🏽 🤲🏽 👐🏽 🙌🏽 👏🏽 🙏🏽 👍🏽 👎🏽 👊🏽 🏽 🤛🏽 🤜🏽 🤞🏽 🏽 🤟🏽 🤘🏽 👌🏽 👈🏽 👉🏽 👆🏽 👇🏽 🏽 🏽 🤚🏽 🖐🏽 🖖🏽 👋🏽 🤙🏽 💪🏽 🖕🏽 🏽 🤳🏽 💅🏽 👂🏽 👃🏽
👶🏾 👦🏾 👧🏾 👨🏾 👩🏾 👱🏾 👱🏾 👴🏾 👵🏾 👲🏾 👳🏾 👳🏾 👮🏾 👮🏾 👷🏾 👷🏾 💂🏾 💂🏾 🕵🏾 🕵🏾 👩🏾 👨🏾 👩🏾🌾 👨🏾🌾 👩🏾🍳 👨🏾🍳 👩🏾🎓 👨🏾🎓 👩🏾🎤 👨🏾🎤 👩🏾🏫 👨🏾🏫 👩🏾🏭 👨🏾🏭 👩🏾💻 👨🏾💻 👩🏾💼 👨🏾💼 👩🏾🔧 👨🏾🔧 👩🏾🔬 👨🏾🔬 👩🏾🎨 👨🏾🎨 👩🏾🚒 👨🏾🚒 👩🏾 👨🏾 👩🏾🚀 👨🏾🚀 👩🏾 👨🏾 🤶🏾 🎅🏾 👸🏾 🤴🏾 👰🏾 🤵🏾 👼🏾 🤰🏾 🙇🏾 🙇🏾 💁🏾 💁🏾 🙅🏾 🙅🏾 🙆🏾 🙆🏾 🙋🏾 🙋🏾 🤦🏾 🤦🏾 🤷🏾 🤷🏾 🙎🏾 🙎🏾 🙍🏾 🙍🏾 💇🏾 💇🏾 💆🏾 💆🏾 🕴🏾 💃🏾 🕺🏾 🚶🏾 🚶🏾 🏃🏾 🏃🏾 🤲🏾 👐🏾 🙌🏾 👏🏾 🙏🏾 👍🏾 👎🏾 👊🏾 🏾 🤛🏾 🤜🏾 🤞🏾 🏾 🤟🏾 🤘🏾 👌🏾 👈🏾 👉🏾 👆🏾 👇🏾 🏾 🏾 🤚🏾 🖐🏾 🖖🏾 👋🏾 🤙🏾 💪🏾 🖕🏾 🏾 🤳🏾 💅🏾 👂🏾 👃🏾
👶🏿 👦🏿 👧🏿 👨🏿 👩🏿 👱🏿 👱🏿 👴🏿 👵🏿 👲🏿 👳🏿 👳🏿 👮🏿 👮🏿 👷🏿 👷🏿 💂🏿 💂🏿 🕵🏿 🕵🏿 👩🏿 👨🏿 👩🏿🌾 👨🏿🌾 👩🏿🍳 👨🏿🍳 👩🏿🎓 👨🏿🎓 👩🏿🎤 👨🏿🎤 👩🏿🏫 👨🏿🏫 👩🏿🏭 👨🏿🏭 👩🏿💻 👨🏿💻 👩🏿💼 👨🏿💼 👩🏿🔧 👨🏿🔧 👩🏿🔬 👨🏿🔬 👩🏿🎨 👨🏿🎨 👩🏿🚒 👨🏿🚒 👩🏿 👨🏿 👩🏿🚀 👨🏿🚀 👩🏿 👨🏿 🤶🏿 🎅🏿 👸🏿 🤴🏿 👰🏿 🤵🏿 👼🏿 🤰🏿 🙇🏿 🙇🏿 💁🏿 💁🏿 🙅🏿 🙅🏿 🙆🏿 🙆🏿 🙋🏿 🙋🏿 🤦🏿 🤦🏿 🤷🏿 🤷🏿 🙎🏿 🙎🏿 🙍🏿 🙍🏿 💇🏿 💇🏿 💆🏿 💆🏿 🕴🏿 💃🏿 🕺🏿 🚶🏿 🚶🏿 🏃🏿 🏃🏿 🤲🏿 👐🏿 🙌🏿 👏🏿 🙏🏿 👍🏿 👎🏿 👊🏿 🏿 🤛🏿 🤜🏿 🤞🏿 🏿 🤟🏿 🤘🏿 👌🏿 👈🏿 👉🏿 👆🏿 👇🏿 🏿 🏿 🤚🏿 🖐🏿 🖖🏿 👋🏿 🤙🏿 💪🏿 🖕🏿 🏿 🤳🏿 💅🏿 👂🏿 👃🏿
🐶 🐱 🐭 🐹 🐰 🦊 🦝 🐻 🐼 🦘 🦡 🐨 🐯 🦁 🐮 🐷 🐽 🐸 🐵 🙈 🙉 🙊 🐒 🐔 🐧 🐦 🐤 🐣 🐥 🦆 🦢 🦅 🦉 🦚 🦜 🦇 🐺 🐗 🐴 🦄 🐝 🐛 🦋 🐌 🐚 🐞 🐜 🦗 🕷 🕸 🦂 🦟 🦠 🐢 🐍 🦎 🦖 🦕 🐙 🦑 🦐 🦀 🐡 🐠 🐟 🐬 🐳 🐋 🦈 🐊 🐅 🐆 🦓 🦍 🐘 🦏 🦛 🐪 🐫 🦙 🦒 🐃 🐂 🐄 🐎 🐖 🐏 🐑 🐐 🦌 🐕 🐩 🐈 🐓 🦃 🕊 🐇 🐁 🐀 🐿 🦔 🐾 🐉 🐲 🌵 🎄 🌲 🌳 🌴 🌱 🌿 🍀 🎍 🎋 🍃 🍂 🍁 🍄 🌾 💐 🌷 🌹 🥀 🌺 🌸 🌼 🌻 🌞 🌝 🌛 🌜 🌚 🌕 🌖 🌗 🌘 🌑 🌒 🌓 🌔 🌙 🌎 🌍 🌏 💫 🌟 💥 🔥 🌪 🌈 🌤 🌥 🌦 🌧 🌩 🌨 🌬 💨 💧 💦 🌊 🌫
🍏 🍎 🍐 🍊 🍋 🍌 🍉 🍇 🍓 🍈 🍒 🍑 🍍 🥭 🥥 🥝 🍅 🍆 🥑 🥦 🥒 🥬 🌶 🌽 🥕 🥔 🍠 🥐 🍞 🥖 🥨 🥯 🧀 🥚 🍳 🥞 🥓 🥩 🍗 🍖 🌭 🍔 🍟 🍕 🥪 🥙 🌮 🌯 🥗 🥘 🥫 🍝 🍜 🍲 🍛 🍣 🍱 🥟 🍤 🍙 🍚 🍘 🍥 🥮 🥠 🍢 🍡 🍧 🍨 🍦 🥧 🍰 🎂 🍮 🍭 🍬 🍫 🍿 🧂 🍩 🍪 🌰 🥜 🍯 🥛 🍼 🍵 🥤 🍶 🍺 🍻 🥂 🍷 🥃 🍸 🍹 🍾 🥄 🍴 🍽 🥣 🥡 🥢
🏀 🏈 🥎 🏐 🏉 🎾 🥏 🎱 🏓 🏸 🥅 🏒 🏑 🥍 🏏 🏹 🎣 🥊 🥋 🎽 🥌 🛷 🛹 🎿 🏂 🏋 🏋🏻 🏋🏼 🏋🏽 🏋🏾 🏋🏿 🏋 🏋🏻 🏋🏼 🏋🏽 🏋🏾 🏋🏿 🤼 🤼 🤸 🤸🏻 🤸🏼 🤸🏽 🤸🏾 🤸🏿 🤸 🤸🏻 🤸🏼 🤸🏽 🤸🏾 🤸🏿 🏻 🏼 🏽 🏾 🏿 🏻 🏼 🏽 🏾 🏿 🤺 🤾 🤾🏻 🤾🏼 🤾🏾 🤾🏾 🤾🏿 🤾 🤾🏻 🤾🏼 🤾🏽 🤾🏾 🤾🏿 🏌 🏌🏻 🏌🏼 🏌🏽 🏌🏾 🏌🏿 🏌 🏌🏻 🏌🏼 🏌🏽 🏌🏾 🏌🏿 🏇 🏇🏻 🏇🏼 🏇🏽 🏇🏾 🏇🏿 🧘 🧘🏻 🧘🏼 🧘🏽 🧘🏾 🧘🏿 🧘 🧘🏻 🧘🏼 🧘🏽 🧘🏾 🧘🏿 🏄 🏄🏻 🏄🏼 🏄🏽 🏄🏾 🏄🏿 🏄 🏄🏻 🏄🏼 🏄🏽 🏄🏾 🏄🏿 🏊 🏊🏻 🏊🏼 🏊🏽 🏊🏾 🏊🏿 🏊 🏊🏻 🏊🏼 🏊🏽 🏊🏾 🏊🏿 🤽 🤽🏻 🤽🏼 🤽🏽 🤽🏾 🤽🏿 🤽 🤽🏻 🤽🏼 🤽🏽 🤽🏾 🤽🏿 🚣 🚣🏻 🚣🏼 🚣🏽 🚣🏾 🚣🏿 🚣 🚣🏻 🚣🏼 🚣🏽 🚣🏾 🚣🏿 🧗 🧗🏻 🧗🏼 🧗🏽 🧗🏾 🧗🏿 🧗 🧗🏻 🧗🏼 🧗🏽 🧗🏾 🧗🏿 🚵 🚵🏻 🚵🏼 🚵🏽 🚵🏾 🚵🏿 🚵 🚵🏻 🚵🏼 🚵🏽 🚵🏾 🚵🏿 🚴 🚴🏻 🚴🏼 🚴🏽 🚴🏾 🚴🏿 🚴 🚴🏻 🚴🏼 🚴🏽 🚴🏾 🚴🏿 🏆 🥇 🥈 🥉 🏅 🎖 🏵 🎗 🎫 🎟 🎪 🤹 🤹🏻 🤹🏼 🤹🏽 🤹🏾 🤹🏿 🤹 🤹🏻 🤹🏼 🤹🏽 🤹🏾 🤹🏿 🎭 🎨 🎬 🎤 🎧 🎼 🎹 🥁 🎷 🎺 🎸 🎻 🎲 🧩 🎯 🎳 🎮 🎰
🚗 🚕 🚙 🚌 🚎 🏎 🚓 🚑 🚒 🚐 🚚 🚛 🚜 🛴 🚲 🛵 🏍 🚨 🚔 🚍 🚘 🚖 🚡 🚠 🚟 🚃 🚋 🚞 🚝 🚄 🚅 🚈 🚂 🚆 🚇 🚊 🚉 🛫 🛬 🛩 💺 🛰 🚀 🛸 🚁 🛶 🚤 🛥 🛳 🚢 🚧 🚦 🚥 🚏 🗺 🗿 🗽 🗼 🏰 🏯 🏟 🎡 🎢 🎠 🏖 🏝 🏜 🌋 🏔 🗻 🏕 🏠 🏡 🏘 🏚 🏗 🏭 🏢 🏬 🏣 🏤 🏥 🏦 🏨 🏪 🏫 🏩 💒 🏛 🕌 🕍 🕋 🛤 🛣 🗾 🎑 🏞 🌅 🌄 🌠 🎇 🎆 🌇 🌆 🏙 🌃 🌌 🌉 🌁
📱 📲 💻 🖥 🖨 🖱 🖲 🕹 🗜 💽 💾 💿 📀 📼 📷 📸 📹 🎥 📽 🎞 📞 📟 📠 📺 📻 🎙 🎚 🎛 🕰 📡 🔋 🔌 💡 🔦 🕯 🗑 🛢 💸 💵 💴 💶 💷 💰 💳 🧾 💎 🔧 🔨 🛠 🔩 🔫 💣 🔪 🗡 🛡 🚬 🏺 🧭 🧱 🔮 🧿 🧸 📿 💈 🔭 🧰 🧲 🧪 🧫 🧬 🧯 🔬 🕳 💊 💉 🌡 🚽 🚰 🚿 🛁 🛀 🛀🏻 🛀🏼 🛀🏽 🛀🏾 🛀🏿 🧴 🧵 🧶 🧷 🧹 🧺 🧻 🧼 🧽 🛎 🔑 🗝 🚪 🛋 🛏 🛌 🖼 🛍 🧳 🛒 🎁 🎈 🎏 🎀 🎊 🎉 🧨 🎎 🏮 🎐 🧧 📩 📨 📧 💌 📥 📤 📦 🏷 📪 📫 📬 📭 📮 📯 📜 📃 📄 📑 📊 📈 📉 🗒 🗓 📆 📅 📇 🗃 🗳 🗄 📋 📁 📂 🗂 🗞 📰 📓 📔 📒 📕 📗 📘 📙 📚 📖 🔖 🔗 📎 🖇 📐 📏 📌 📍 🖊 🖋 🖌 🖍 📝 🔍 🔎 🔏 🔐 🔒 🔓
🧡 💛 💚 💙 💜 🖤 💔 💕 💞 💓 💗 💖 💘 💝 💟 🕉 🔯 🕎 🛐 🆔 🉑 📴 📳 🈶 🈚 🈸 🈺 🈷 🆚 💮 🉐 🈴 🈵 🈹 🈲 🅰 🅱 🆎 🆑 🅾 🆘 🛑 📛 🚫 💯 💢 🚷 🚯 🚳 🚱 🔞 📵 🚭 🔅 🔆 🚸 🔱 🔰 🈯 💹 🌐 💠 🌀 💤 🏧 🚾 🅿 🈳 🈂 🛂 🛃 🛄 🛅 🚹 🚺 🚼 🚻 🚮 🎦 📶 🈁 🔣 🔤 🔡 🔠 🆖 🆗 🆙 🆒 🆕 🆓 0 1 2 3 4 5 6 7 8 9 🔟 🔢 # * 🔼 🔽 🔀 🔁 🔂 🔄 🔃 🎵 🎶 💲 💱 © ® 🔚 🔙 🔛 🔝 🔜 🔘 🔴 🔵 🔺 🔻 🔸 🔹 🔶 🔷 🔳 🔲 🔈 🔇 🔉 🔊 🔔 🔕 📣 📢 👁🗨 💬 💭 🗯 🃏 🎴 🀄 🕐 🕑 🕒 🕓 🕔 🕕 🕖 🕗 🕘 🕙 🕚 🕛 🕜 🕝 🕞 🕟 🕠 🕡 🕢 🕣 🕤 🕥 🕦 🕧
🏳 🏴 🏁 🚩 🏳🌈 🏴 🇦🇫 🇦🇽 🇦🇱 🇩🇿 🇦🇸 🇦🇩 🇦🇴 🇦🇮 🇦🇶 🇦🇬 🇦🇷 🇦🇲 🇦🇼 🇦🇺 🇦🇹 🇦🇿 🇧🇸 🇧🇭 🇧🇩 🇧🇧 🇧🇾 🇧🇪 🇧🇿 🇧🇯 🇧🇲 🇧🇹 🇧🇴 🇧🇦 🇧🇼 🇧🇷 🇮🇴 🇻🇬 🇧🇳 🇧🇬 🇧🇫 🇧🇮 🇰🇭 🇨🇲 🇨🇦 🇮🇨 🇨🇻 🇧🇶 🇰🇾 🇨🇫 🇹🇩 🇨🇱 🇨🇳 🇨🇽 🇨🇨 🇨🇴 🇰🇲 🇨🇬 🇨🇩 🇨🇰 🇨🇷 🇨🇮 🇭🇷 🇨🇺 🇨🇼 🇨🇾 🇨🇿 🇩🇰 🇩🇯 🇩🇲 🇩🇴 🇪🇨 🇪🇬 🇸🇻 🇬🇶 🇪🇷 🇪🇪 🇪🇹 🇪🇺 🇫🇰 🇫🇴 🇫🇯 🇫🇮 🇫🇷 🇬🇫 🇵🇫 🇹🇫 🇬🇦 🇬🇲 🇬🇪 🇩🇪 🇬🇭 🇬🇮 🇬🇷 🇬🇱 🇬🇩 🇬🇵 🇬🇺 🇬🇹 🇬🇬 🇬🇳 🇬🇼 🇬🇾 🇭🇹 🇭🇳 🇭🇰 🇭🇺 🇮🇸 🇮🇳 🇮🇩 🇮🇷 🇮🇶 🇮🇪 🇮🇲 🇮🇱 🇮🇹 🇯🇲 🇯🇵 🎌 🇯🇪 🇯🇴 🇰🇿 🇰🇪 🇰🇮 🇽🇰 🇰🇼 🇰🇬 🇱🇦 🇱🇻 🇱🇧 🇱🇸 🇱🇷 🇱🇾 🇱🇮 🇱🇹 🇱🇺 🇲🇴 🇲🇰 🇲🇬 🇲🇼 🇲🇾 🇲🇻 🇲🇱 🇲🇹 🇲🇭 🇲🇶 🇲🇷 🇲🇺 🇾🇹 🇲🇽 🇫🇲 🇲🇩 🇲🇨 🇲🇳 🇲🇪 🇲🇸 🇲🇦 🇲🇿 🇲🇲 🇳🇦 🇳🇷 🇳🇵 🇳🇱 🇳🇨 🇳🇿 🇳🇮 🇳🇪 🇳🇬 🇳🇺 🇳🇫 🇰🇵 🇲🇵 🇳🇴 🇴🇲 🇵🇰 🇵🇼 🇵🇸 🇵🇦 🇵🇬 🇵🇾 🇵🇪 🇵🇭 🇵🇳 🇵🇱 🇵🇹 🇵🇷 🇶🇦 🇷🇪 🇷🇴 🇷🇺 🇷🇼 🇼🇸 🇸🇲 🇸🇦 🇸🇳 🇷🇸 🇸🇨 🇸🇱 🇸🇬 🇸🇽 🇸🇰 🇸🇮 🇬🇸 🇸🇧 🇸🇴 🇿🇦 🇰🇷 🇸🇸 🇪🇸 🇱🇰 🇧🇱 🇸🇭 🇰🇳 🇱🇨 🇵🇲 🇻🇨 🇸🇩 🇸🇷 🇸🇿 🇸🇪 🇨🇭 🇸🇾 🇹🇼 🇹🇯 🇹🇿 🇹🇭 🇹🇱 🇹🇬 🇹🇰 🇹🇴 🇹🇹 🇹🇳 🇹🇷 🇹🇲 🇹🇨 🇹🇻 🇻🇮 🇺🇬 🇺🇦 🇦🇪 🇬🇧 🏴󠁧󠁢󠁥󠁮󠁧󠁿 🏴󠁧󠁢󠁳󠁣󠁴󠁿 🏴󠁧󠁢󠁷󠁬󠁳󠁿 🇺🇳 🇺🇸 🇺🇾 🇺🇿 🇻🇺 🇻🇦 🇻🇪 🇻🇳 🇼🇫 🇪🇭 🇾🇪 🇿🇲 🇿🇼
🥱 🤏 🦾 🦿 🦻 🧏 🧏 🧏 🧍 🧍 🧍 🧎 🧎 🧎 👨🦯 👩🦯 👨🦼 👩🦼 👨🦽 👩🦽 🦧 🦮 🐕🦺 🦥 🦦 🦨 🦩 🧄 🧅 🧇 🧆 🧈 🦪 🧃 🧉 🧊 🛕 🦽 🦼 🛺 🪂 🪐 🤿 🪀 🪁 🦺 🥻 🩱 🩲 🩳 🩰 🪕 🪔 🪓 🦯 🩸 🩹 🩺 🪑 🪒 🤎 🤍 🟠 🟡 🟢 🟣 🟤 🟥 🟧 🟨 🟩 🟦 🟪 🟫";
[Benchmark]
public TextLayout BuildEmojisTextLayout() => new TextLayout(Emojis, Typeface.Default, 12d, Brushes.Black);
public void Dispose()
{
_app?.Dispose();
}
}

8
tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLayoutTests.cs

@ -153,7 +153,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
{
while (inner.MoveNext())
{
j += inner.Current.Text.Length;
j += inner.Current.Length;
if (j + i > text.Length)
{
@ -192,7 +192,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
inner = new GraphemeEnumerator(new CharacterBufferRange(text));
i += outer.Current.Text.Length;
i += outer.Current.Length;
}
}
}
@ -974,9 +974,9 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
{
var grapheme = graphemeEnumerator.Current;
var textStyleOverrides = new[] { new ValueSpan<TextRunProperties>(i, grapheme.Text.Length, new GenericTextRunProperties(Typeface.Default, 12, foregroundBrush: Brushes.Red)) };
var textStyleOverrides = new[] { new ValueSpan<TextRunProperties>(i, grapheme.Length, new GenericTextRunProperties(Typeface.Default, 12, foregroundBrush: Brushes.Red)) };
i += grapheme.Text.Length;
i += grapheme.Length;
var layout = new TextLayout(
text,

Loading…
Cancel
Save