diff --git a/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs b/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs index ae927d44a5..9c2ca053e6 100644 --- a/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs +++ b/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs @@ -238,22 +238,44 @@ namespace Avalonia.Direct2D1 width = 0; - for (var i = 0; i < glyphCount; i++) + if (glyphRun.GlyphAdvances.IsEmpty) + { + for (var i = 0; i < glyphCount; i++) + { + var advance = glyphTypeface.GetGlyphAdvance(glyphRun.GlyphIndices[i]); + + run.Advances[i] = advance; + + width += advance; + } + } + else + { + for (var i = 0; i < glyphCount; i++) + { + var advance = (float)glyphRun.GlyphAdvances[i]; + + run.Advances[i] = advance; + + width += advance; + } + } + + if (glyphRun.GlyphOffsets.IsEmpty) { - run.Advances[i] = (float)glyphRun.GlyphAdvances[i]; - width += run.Advances[i]; + return new GlyphRunImpl(run); } run.Offsets = new GlyphOffset[glyphCount]; for (var i = 0; i < glyphCount; i++) { - var offset = glyphRun.GlyphOffsets[i]; + var (x, y) = glyphRun.GlyphOffsets[i]; run.Offsets[i] = new GlyphOffset { - AdvanceOffset = (float)offset.X, - AscenderOffset = (float)offset.Y + AdvanceOffset = (float)x, + AscenderOffset = (float)y }; } diff --git a/src/Windows/Avalonia.Direct2D1/Media/TextShaperImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/TextShaperImpl.cs index 254b5684a4..20b09a9aac 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/TextShaperImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/TextShaperImpl.cs @@ -1,6 +1,6 @@ -using System.Globalization; +using System; +using System.Globalization; using Avalonia.Media; -using Avalonia.Media.TextFormatting; using Avalonia.Media.TextFormatting.Unicode; using Avalonia.Platform; using Avalonia.Utilities; @@ -15,51 +15,9 @@ namespace Avalonia.Direct2D1.Media { using (var buffer = new Buffer()) { - buffer.ContentType = ContentType.Unicode; + FillBuffer(buffer, text); - var breakCharPosition = text.Length - 1; - - var codepoint = Codepoint.ReadAt(text, breakCharPosition, out var count); - - if (codepoint.IsBreakChar) - { - var breakCharCount = 1; - - if (text.Length > 1) - { - var previousCodepoint = Codepoint.ReadAt(text, breakCharPosition - count, out _); - - if (codepoint == '\r' && previousCodepoint == '\n' - || codepoint == '\n' && previousCodepoint == '\r') - { - breakCharCount = 2; - } - } - - if (breakCharPosition != text.Start) - { - buffer.AddUtf16(text.Buffer.Span.Slice(0, text.Length - breakCharCount)); - } - - var cluster = buffer.GlyphInfos.Length > 0 ? - buffer.GlyphInfos[buffer.Length - 1].Cluster + 1 : - (uint)text.Start; - - switch (breakCharCount) - { - case 1: - buffer.Add('\u200C', cluster); - break; - case 2: - buffer.Add('\u200C', cluster); - buffer.Add('\u200D', cluster); - break; - } - } - else - { - buffer.AddUtf16(text.Buffer.Span); - } + buffer.Language = new Language(culture ?? CultureInfo.CurrentCulture); buffer.GuessSegmentProperties(); @@ -67,44 +25,38 @@ namespace Avalonia.Direct2D1.Media var font = ((GlyphTypefaceImpl)glyphTypeface.PlatformImpl).Font; - buffer.Language = new Language(culture ?? CultureInfo.CurrentCulture); - font.Shape(buffer); font.GetScale(out var scaleX, out _); var textScale = fontRenderingEmSize / scaleX; - var len = buffer.Length; + var bufferLength = buffer.Length; - var info = buffer.GetGlyphInfoSpan(); + var glyphInfos = buffer.GetGlyphInfoSpan(); - var pos = buffer.GetGlyphPositionSpan(); + var glyphPositions = buffer.GetGlyphPositionSpan(); - var glyphIndices = new ushort[len]; + var glyphIndices = new ushort[bufferLength]; - var clusters = new ushort[len]; + var clusters = new ushort[bufferLength]; - var glyphAdvances = new double[len]; + double[] glyphAdvances = null; - var glyphOffsets = new Vector[len]; + Vector[] glyphOffsets = null; - for (var i = 0; i < len; i++) + for (var i = 0; i < bufferLength; i++) { - glyphIndices[i] = (ushort)info[i].Codepoint; - - clusters[i] = (ushort)(text.Start + info[i].Cluster); - - var advanceX = pos[i].XAdvance * textScale; - // Depends on direction of layout - //var advanceY = pos[i].YAdvance * textScale; + glyphIndices[i] = (ushort)glyphInfos[i].Codepoint; - glyphAdvances[i] = advanceX; + clusters[i] = (ushort)glyphInfos[i].Cluster; - var offsetX = pos[i].XOffset * textScale; - var offsetY = pos[i].YOffset * textScale; + if (!glyphTypeface.IsFixedPitch) + { + SetAdvance(glyphPositions, i, textScale, ref glyphAdvances); + } - glyphOffsets[i] = new Vector(offsetX, offsetY); + SetOffset(glyphPositions, i, textScale, ref glyphOffsets); } return new GlyphRun(glyphTypeface, fontRenderingEmSize, @@ -115,5 +67,79 @@ namespace Avalonia.Direct2D1.Media new ReadOnlySlice(clusters)); } } + + private static void FillBuffer(Buffer buffer, ReadOnlySlice text) + { + buffer.ContentType = ContentType.Unicode; + + var i = 0; + + while (i < text.Length) + { + var codepoint = Codepoint.ReadAt(text, i, out var count); + + var cluster = (uint)(text.Start + i); + + if (codepoint.IsBreakChar) + { + if (i + 1 < text.Length) + { + var nextCodepoint = Codepoint.ReadAt(text, i + 1, out _); + + if (nextCodepoint == '\r' && codepoint == '\n' || nextCodepoint == '\n' && codepoint == '\r') + { + count++; + + buffer.Add('\u200C', cluster); + + buffer.Add('\u200D', cluster); + } + else + { + buffer.Add('\u200C', cluster); + } + } + else + { + buffer.Add('\u200C', cluster); + } + } + else + { + buffer.Add(codepoint, cluster); + } + + i += count; + } + } + + private static void SetOffset(ReadOnlySpan glyphPositions, int index, double textScale, + ref Vector[] offsetBuffer) + { + var position = glyphPositions[index]; + + if (position.XOffset == 0 && position.YOffset == 0) + { + return; + } + + offsetBuffer ??= new Vector[glyphPositions.Length]; + + var offsetX = position.XOffset * textScale; + + var offsetY = position.YOffset * textScale; + + offsetBuffer[index] = new Vector(offsetX, offsetY); + } + + private static void SetAdvance(ReadOnlySpan glyphPositions, int index, double textScale, + ref double[] advanceBuffer) + { + advanceBuffer ??= new double[glyphPositions.Length]; + + // Depends on direction of layout + // advanceBuffer[index] = buffer.GlyphPositions[index].YAdvance * textScale; + advanceBuffer[index] = glyphPositions[index].XAdvance * textScale; + } } }