GlyphRunTests.cs 8.7 KB

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