Repository: AvaloniaUI/Avalonia
File: src/Avalonia.Base/Media/GlyphRun.cs (C#)

Code before:

                for (var i = 0; i < glyphIndex; i++)
                {
                    distance += _glyphInfos[i].GlyphAdvance;
                }

                return distance;
            }
            else
            {
                distance = Bounds.Width;

                //RightToLeft
                var glyphIndex = FindGlyphIndex(characterIndex);

                if (characterIndex > Metrics.LastCluster)
                {
                    return 0;
                }

                if (characterIndex <= Metrics.FirstCluster)
                {
                    return Bounds.Width;
                }

                for (var i = _glyphInfos.Count - 1; i >= 0; i--)
                {
                    var glyphInfo = _glyphInfos[i];

                    if(glyphInfo.GlyphCluster >= characterIndex)
                    {
                        break;
                    }

                    distance -= glyphInfo.GlyphAdvance;
                }

                return distance;
            }
        }

        /// <summary>
        /// Retrieves the <see cref="CharacterHit"/> value that represents the character hit of the caret of the <see cref="GlyphRun"/>.
        /// </summary>
        /// <param name="distance">Offset to use for computing the caret character hit.</param>
        /// <param name="isInside">Determines whether the character hit is inside the <see cref="GlyphRun"/>.</param>
        /// <returns>
        /// A <see cref="CharacterHit"/> value that represents the character hit that is closest to the distance value.
        /// The out parameter <c>isInside</c> returns <c>true</c> if the character hit is inside the <see cref="GlyphRun"/>; otherwise, <c>false</c>.
        /// </returns>
        public CharacterHit GetCharacterHitFromDistance(double distance, out bool isInside)

Review comment (performance):
Why the change? The complexity is now `O(N)`. Using the existing `glyphIndex` (the variable is now unused) which was retrieved using binary search in `FindGlyphIndex` was `O(log(N))`. (Also, the LTR and RTL algorithms were symmetric before and aren't anymore).

Reverting this completely still makes the two new tests pass, with a correct distance being returned - just double checked. Am I missing something here?

Code after:

                for (var i = 0; i < glyphIndex; i++)
                {
                    distance += _glyphInfos[i].GlyphAdvance;
                }

                return distance;
            }
            else
            {
                //RightToLeft
                var glyphIndex = FindGlyphIndex(characterIndex);

                if (characterIndex > Metrics.LastCluster)
                {
                    return 0;
                }

                if (characterIndex <= Metrics.FirstCluster)
                {
                    return Bounds.Width;
                }

                for (var i = glyphIndex + 1; i < _glyphInfos.Count; i++)
                {
                    distance += _glyphInfos[i].GlyphAdvance;
                }

                return Bounds.Width - distance;
            }
        }

        /// <summary>
        /// Retrieves the <see cref="CharacterHit"/> value that represents the character hit of the caret of the <see cref="GlyphRun"/>.
        /// </summary>
        /// <param name="distance">Offset to use for computing the caret character hit.</param>
        /// <param name="isInside">Determines whether the character hit is inside the <see cref="GlyphRun"/>.</param>
        /// <returns>
        /// A <see cref="CharacterHit"/> value that represents the character hit that is closest to the distance value.
        /// The out parameter <c>isInside</c> returns <c>true</c> if the character hit is inside the <see cref="GlyphRun"/>; otherwise, <c>false</c>.
        /// </returns>
        public CharacterHit GetCharacterHitFromDistance(double distance, out bool isInside)
        {
            // Before
            if (distance <= 0)
            {
                isInside = false;

                var firstCharacterHit = FindNearestCharacterHit(IsLeftToRight ? Metrics.FirstCluster : Metrics.LastCluster, out _);

                return IsLeftToRight ? new CharacterHit(firstCharacterHit.FirstCharacterIndex) : firstCharacterHit;