GlyphRunTests.cs 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. using System;
  2. using System.Linq;
  3. using Avalonia.Media;
  4. using Avalonia.Media.TextFormatting;
  5. using Avalonia.Platform;
  6. using Avalonia.UnitTests;
  7. using Avalonia.Utilities;
  8. using Xunit;
  9. namespace Avalonia.Base.UnitTests.Media
  10. {
  11. public class GlyphRunTests : TestWithServicesBase
  12. {
  13. public GlyphRunTests()
  14. {
  15. AvaloniaLocator.CurrentMutable
  16. .Bind<IPlatformRenderInterface>().ToSingleton<MockPlatformRenderInterface>();
  17. }
  18. [InlineData(new double[] { 30, 0, 0 }, new int[] { 0, 0, 0 }, 0, 0, 0)]
  19. [InlineData(new double[] { 30, 0, 0 }, new int[] { 0, 0, 0 }, 0, 3, 30)]
  20. [InlineData(new double[] { 10, 10, 10 }, new int[] { 0, 1, 2 }, 1, 0, 10)]
  21. [InlineData(new double[] { 10, 10, 10 }, new int[] { 0, 1, 2 }, 2, 0, 20)]
  22. [InlineData(new double[] { 10, 10, 10 }, new int[] { 0, 1, 2 }, 2, 1, 30)]
  23. [Theory]
  24. public void Should_Get_Distance_From_CharacterHit(double[] advances, int[] clusters, int start, int trailingLength, double expectedDistance)
  25. {
  26. using(UnitTestApplication.Start(TestServices.StyledWindow))
  27. using (var glyphRun = CreateGlyphRun(advances, clusters))
  28. {
  29. var characterHit = new CharacterHit(start, trailingLength);
  30. var distance = glyphRun.GetDistanceFromCharacterHit(characterHit);
  31. Assert.Equal(expectedDistance, distance);
  32. }
  33. }
  34. [InlineData(new double[] { 30, 0, 0 }, new int[] { 0, 0, 0 }, 26.0, 0, 3, true)]
  35. [InlineData(new double[] { 10, 10, 10 }, new int[] { 0, 1, 2 }, 20.0, 1, 1, true)]
  36. [InlineData(new double[] { 10, 10, 10 }, new int[] { 0, 1, 2 }, 26.0, 2, 1, true)]
  37. [InlineData(new double[] { 10, 10, 10 }, new int[] { 0, 1, 2 }, 35.0, 2, 1, false)]
  38. [Theory]
  39. public void Should_Get_CharacterHit_FromDistance(double[] advances, int[] clusters, double distance, int start,
  40. int trailingLengthExpected, bool isInsideExpected)
  41. {
  42. using(UnitTestApplication.Start(TestServices.StyledWindow))
  43. using (var glyphRun = CreateGlyphRun(advances, clusters))
  44. {
  45. var textBounds = glyphRun.GetCharacterHitFromDistance(distance, out var isInside);
  46. Assert.Equal(start, textBounds.FirstCharacterIndex);
  47. Assert.Equal(trailingLengthExpected, textBounds.TrailingLength);
  48. Assert.Equal(isInsideExpected, isInside);
  49. }
  50. }
  51. [InlineData(new double[] { 10, 10, 10 }, new int[] { 10, 11, 12 }, 0, -1, 10, 1, 10)]
  52. [InlineData(new double[] { 10, 10, 10 }, new int[] { 10, 11, 12 }, 0, 15, 12, 1, 10)]
  53. [InlineData(new double[] { 30, 0, 0 }, new int[] { 0, 0, 0 }, 0, 0, 0, 3, 30.0)]
  54. [InlineData(new double[] { 10, 10, 10 }, new int[] { 0, 1, 2 }, 0, 1, 1, 1, 10.0)]
  55. [InlineData(new double[] { 10, 20, 0, 10 }, new int[] { 0, 1, 1, 3 }, 0, 2, 1, 2, 20.0)]
  56. [InlineData(new double[] { 10, 20, 0, 10 }, new int[] { 0, 1, 1, 3 }, 0, 1, 1, 2, 20.0)]
  57. [InlineData(new double[] { 10, 0, 20, 10 }, new int[] { 3, 1, 1, 0 }, 1, 1, 1, 2, 20.0)]
  58. [Theory]
  59. public void Should_Find_Nearest_CharacterHit(double[] advances, int[] clusters, int bidiLevel,
  60. int index, int expectedIndex, int expectedLength, double expectedWidth)
  61. {
  62. using(UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
  63. using (var glyphRun = CreateGlyphRun(advances, clusters, bidiLevel))
  64. {
  65. var textBounds = glyphRun.FindNearestCharacterHit(index, out var width);
  66. Assert.Equal(expectedIndex, textBounds.FirstCharacterIndex);
  67. Assert.Equal(expectedLength, textBounds.TrailingLength);
  68. Assert.Equal(expectedWidth, width, 2);
  69. }
  70. }
  71. [InlineData(new double[] { 30, 0, 0 }, new int[] { 0, 0, 0 }, 0, 0, 0, 3, 0)]
  72. [InlineData(new double[] { 0, 0, 30 }, new int[] { 0, 0, 0 }, 0, 0, 0, 3, 1)]
  73. [InlineData(new double[] { 30, 0, 0, 10 }, new int[] { 0, 0, 0, 3 }, 3, 0, 3, 1, 0)]
  74. [InlineData(new double[] { 10, 0, 0, 30 }, new int[] { 3, 0, 0, 0 }, 3, 0, 3, 1, 1)]
  75. [InlineData(new double[] { 10, 30, 0, 0, 10 }, new int[] { 0, 1, 1, 1, 4 }, 1, 0, 4, 0, 0)]
  76. [InlineData(new double[] { 10, 0, 0, 30, 10 }, new int[] { 4, 1, 1, 1, 0 }, 1, 0, 4, 0, 1)]
  77. [Theory]
  78. public void Should_Get_Next_CharacterHit(double[] advances,int[] clusters,
  79. int firstCharacterIndex, int trailingLength,
  80. int nextIndex, int nextLength,
  81. int bidiLevel)
  82. {
  83. using(UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
  84. using (var glyphRun = CreateGlyphRun(advances, clusters, bidiLevel))
  85. {
  86. var characterHit = glyphRun.GetNextCaretCharacterHit(new CharacterHit(firstCharacterIndex, trailingLength));
  87. Assert.Equal(nextIndex, characterHit.FirstCharacterIndex);
  88. Assert.Equal(nextLength, characterHit.TrailingLength);
  89. }
  90. }
  91. [InlineData(new double[] { 30, 0, 0 }, new int[] { 0, 0, 0 }, 0, 0, 0, 0, 0)]
  92. [InlineData(new double[] { 0, 0, 30 }, new int[] { 0, 0, 0 }, 0, 0, 0, 0, 1)]
  93. [InlineData(new double[] { 30, 0, 0, 10 }, new int[] { 0, 0, 0, 3 }, 3, 1, 3, 0, 0)]
  94. [InlineData(new double[] { 0, 0, 30, 10 }, new int[] { 3, 0, 0, 0 }, 3, 1, 3, 0, 1)]
  95. [InlineData(new double[] { 10, 30, 0, 0, 10 }, new int[] { 0, 1, 1, 1, 4 }, 4, 1, 4, 0, 0)]
  96. [InlineData(new double[] { 10, 0, 0, 30, 10 }, new int[] { 4, 1, 1, 1, 0 }, 4, 1, 4, 0, 1)]
  97. [Theory]
  98. public void Should_Get_Previous_CharacterHit(double[] advances, int[] clusters,
  99. int currentIndex, int currentLength,
  100. int previousIndex, int previousLength,
  101. int bidiLevel)
  102. {
  103. using(UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
  104. using (var glyphRun = CreateGlyphRun(advances, clusters, bidiLevel))
  105. {
  106. var characterHit = glyphRun.GetPreviousCaretCharacterHit(new CharacterHit(currentIndex, currentLength));
  107. Assert.Equal(previousIndex, characterHit.FirstCharacterIndex);
  108. Assert.Equal(previousLength, characterHit.TrailingLength);
  109. }
  110. }
  111. [InlineData(new double[] { 30, 0, 0 }, new int[] { 0, 0, 0 }, 0)]
  112. [InlineData(new double[] { 0, 0, 30 }, new int[] { 0, 0, 0 }, 1)]
  113. [InlineData(new double[] { 10, 10, 10, 10 }, new int[] { 0, 0, 0, 3 }, 0)]
  114. [InlineData(new double[] { 10, 10, 10, 10 }, new int[] { 3, 0, 0, 0 }, 1)]
  115. [InlineData(new double[] { 10, 10, 10, 10, 10 }, new int[] { 0, 1, 1, 1, 4 }, 0)]
  116. [InlineData(new double[] { 10, 10, 10, 10, 10 }, new int[] { 4, 1, 1, 1, 0 }, 1)]
  117. [Theory]
  118. public void Should_Find_Glyph_Index(double[] advances, int[] clusters, int bidiLevel)
  119. {
  120. using(UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
  121. using (var glyphRun = CreateGlyphRun(advances, clusters, bidiLevel))
  122. {
  123. if (glyphRun.IsLeftToRight)
  124. {
  125. for (var i = 0; i < clusters.Length; i++)
  126. {
  127. var cluster = clusters[i];
  128. var found = glyphRun.FindGlyphIndex(cluster);
  129. var expected = i;
  130. while (expected - 1 >= 0 && clusters[expected - 1] == cluster)
  131. {
  132. expected--;
  133. }
  134. Assert.Equal(expected, found);
  135. }
  136. }
  137. else
  138. {
  139. for (var i = clusters.Length - 1; i > 0; i--)
  140. {
  141. var cluster = clusters[i];
  142. var found = glyphRun.FindGlyphIndex(cluster);
  143. var expected = i;
  144. while (expected + 1 < clusters.Length && clusters[expected + 1] == cluster)
  145. {
  146. expected++;
  147. }
  148. Assert.Equal(expected, found);
  149. }
  150. }
  151. }
  152. }
  153. private static GlyphRun CreateGlyphRun(double[] glyphAdvances, int[] glyphClusters, int bidiLevel = 0)
  154. {
  155. var count = glyphAdvances.Length;
  156. var glyphInfos = new GlyphInfo[count];
  157. for (var i = 0; i < count; ++i)
  158. {
  159. glyphInfos[i] = new GlyphInfo(0, glyphClusters[i], glyphAdvances[i]);
  160. }
  161. return new GlyphRun(new MockGlyphTypeface(), 10, new string('a', count).AsMemory(), glyphInfos, biDiLevel: bidiLevel);
  162. }
  163. }
  164. }