From b33152d7125594ebfeb1cd7bca724ac063e9bb44 Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Wed, 10 Jul 2024 10:37:14 +0200 Subject: [PATCH 1/5] Implement remaining BaselineAlignment variants --- src/Avalonia.Base/Media/TextDecoration.cs | 15 +++++++++------ .../Media/TextFormatting/ShapedTextRun.cs | 3 ++- .../Media/TextFormatting/TextLineImpl.cs | 16 +++++++++------- 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/src/Avalonia.Base/Media/TextDecoration.cs b/src/Avalonia.Base/Media/TextDecoration.cs index 8661959aa6a..7ad5e21c708 100644 --- a/src/Avalonia.Base/Media/TextDecoration.cs +++ b/src/Avalonia.Base/Media/TextDecoration.cs @@ -182,18 +182,18 @@ internal void Draw(DrawingContext drawingContext, GlyphRun glyphRun, TextMetrics break; } - var origin = new Point(); + var origin = baselineOrigin; switch (Location) { - case TextDecorationLocation.Baseline: - origin += glyphRun.BaselineOrigin; + case TextDecorationLocation.Overline: + origin += new Point(0, textMetrics.Ascent); break; case TextDecorationLocation.Strikethrough: - origin += new Point(baselineOrigin.X, baselineOrigin.Y + textMetrics.StrikethroughPosition); + origin += new Point(0, textMetrics.StrikethroughPosition); break; case TextDecorationLocation.Underline: - origin += new Point(baselineOrigin.X, baselineOrigin.Y + textMetrics.UnderlinePosition); + origin += new Point(0, textMetrics.UnderlinePosition); break; } @@ -255,7 +255,10 @@ internal void Draw(DrawingContext drawingContext, GlyphRun glyphRun, TextMetrics } } - drawingContext.DrawLine(pen, origin, origin + new Point(glyphRun.Metrics.Width, 0)); + var p1 = origin + new Point(0, thickness / 2); + var p2 = p1 + new Point(glyphRun.Metrics.Width, 0); + + drawingContext.DrawLine(pen, p1, p2); } } } diff --git a/src/Avalonia.Base/Media/TextFormatting/ShapedTextRun.cs b/src/Avalonia.Base/Media/TextFormatting/ShapedTextRun.cs index a1dc0827e87..c5aa042812e 100644 --- a/src/Avalonia.Base/Media/TextFormatting/ShapedTextRun.cs +++ b/src/Avalonia.Base/Media/TextFormatting/ShapedTextRun.cs @@ -204,7 +204,8 @@ internal GlyphRun CreateGlyphRun() ShapedBuffer.FontRenderingEmSize, Text, ShapedBuffer, - biDiLevel: BidiLevel); + biDiLevel: BidiLevel, + baselineOrigin: new Point()); } public void Dispose() diff --git a/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs b/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs index 954a9b2debf..dc3ce4585bf 100644 --- a/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs +++ b/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs @@ -116,17 +116,19 @@ private static double GetBaselineOffset(TextLine textLine, DrawableTextRun textR switch (baselineAlignment) { case BaselineAlignment.Top: - return 0; + case BaselineAlignment.TextTop: + return baseline; case BaselineAlignment.Center: - return textLine.Height / 2 - textRun.Size.Height / 2; + return textLine.Height / 2 + baseline - textRun.Size.Height / 2; case BaselineAlignment.Bottom: - return textLine.Height - textRun.Size.Height; - case BaselineAlignment.Baseline: - case BaselineAlignment.TextTop: case BaselineAlignment.TextBottom: - case BaselineAlignment.Subscript: + return textLine.Height - (textRun.Size.Height - baseline); + case BaselineAlignment.Baseline: + return textLine.Baseline; case BaselineAlignment.Superscript: - return textLine.Baseline - baseline; + return textLine.Height / 2 - textRun.Size.Height / 2; + case BaselineAlignment.Subscript: + return textLine.Baseline + textRun.Size.Height / 2; default: throw new ArgumentOutOfRangeException(nameof(baselineAlignment), baselineAlignment, null); } From f9d904645b7465ea7253b9d1c6e550b7ef63bd16 Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Thu, 11 Jul 2024 07:31:07 +0200 Subject: [PATCH 2/5] Adjust BaselineOffset calculation --- src/Avalonia.Base/Media/TextDecoration.cs | 2 +- .../Media/TextFormatting/ShapedTextRun.cs | 5 +- .../Media/TextFormatting/TextLineImpl.cs | 94 +++++++++---------- .../Media/TextFormatting/TextLayoutTests.cs | 2 +- 4 files changed, 47 insertions(+), 56 deletions(-) diff --git a/src/Avalonia.Base/Media/TextDecoration.cs b/src/Avalonia.Base/Media/TextDecoration.cs index 7ad5e21c708..beac28da4ac 100644 --- a/src/Avalonia.Base/Media/TextDecoration.cs +++ b/src/Avalonia.Base/Media/TextDecoration.cs @@ -255,7 +255,7 @@ internal void Draw(DrawingContext drawingContext, GlyphRun glyphRun, TextMetrics } } - var p1 = origin + new Point(0, thickness / 2); + var p1 = origin; var p2 = p1 + new Point(glyphRun.Metrics.Width, 0); drawingContext.DrawLine(pen, p1, p2); diff --git a/src/Avalonia.Base/Media/TextFormatting/ShapedTextRun.cs b/src/Avalonia.Base/Media/TextFormatting/ShapedTextRun.cs index c5aa042812e..82e59735197 100644 --- a/src/Avalonia.Base/Media/TextFormatting/ShapedTextRun.cs +++ b/src/Avalonia.Base/Media/TextFormatting/ShapedTextRun.cs @@ -45,7 +45,7 @@ public override int Length /// public override void Draw(DrawingContext drawingContext, Point origin) { - using (drawingContext.PushTransform(Matrix.CreateTranslation(origin))) + using (drawingContext.PushTransform(Matrix.CreateTranslation(origin - new Point(0, Baseline)))) { if (GlyphRun.GlyphInfos.Count == 0) { @@ -204,8 +204,7 @@ internal GlyphRun CreateGlyphRun() ShapedBuffer.FontRenderingEmSize, Text, ShapedBuffer, - biDiLevel: BidiLevel, - baselineOrigin: new Point()); + biDiLevel: BidiLevel); } public void Dispose() diff --git a/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs b/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs index dc3ce4585bf..cbed2cea276 100644 --- a/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs +++ b/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs @@ -115,20 +115,19 @@ private static double GetBaselineOffset(TextLine textLine, DrawableTextRun textR switch (baselineAlignment) { + case BaselineAlignment.Baseline: + return textLine.Baseline; case BaselineAlignment.Top: case BaselineAlignment.TextTop: - return baseline; + return textLine.Baseline - textLine.Extent + textRun.Size.Height / 2; case BaselineAlignment.Center: return textLine.Height / 2 + baseline - textRun.Size.Height / 2; + case BaselineAlignment.Subscript: case BaselineAlignment.Bottom: case BaselineAlignment.TextBottom: - return textLine.Height - (textRun.Size.Height - baseline); - case BaselineAlignment.Baseline: - return textLine.Baseline; + return textLine.Height - textRun.Size.Height + baseline; case BaselineAlignment.Superscript: - return textLine.Height / 2 - textRun.Size.Height / 2; - case BaselineAlignment.Subscript: - return textLine.Baseline + textRun.Size.Height / 2; + return baseline; default: throw new ArgumentOutOfRangeException(nameof(baselineAlignment), baselineAlignment, null); } @@ -1145,7 +1144,6 @@ public void FinalizeLine() } TextRun? currentRun = null; - TextRun? previousRun = null; while (runIndex < _indexedTextRuns.Count) { @@ -1184,7 +1182,7 @@ public void FinalizeLine() break; } - case TextRun: + case not null: { if(direction == LogicalDirection.Forward) { @@ -1214,8 +1212,6 @@ public void FinalizeLine() } runIndex++; - - previousRun = currentRun; } return currentRun; @@ -1244,61 +1240,57 @@ private TextLineMetrics CreateLineMetrics() switch (_textRuns[index]) { case ShapedTextRun textRun: - { - var textMetrics = textRun.TextMetrics; - var glyphRun = textRun.GlyphRun; - var runBounds = glyphRun.InkBounds.WithX(widthIncludingWhitespace + glyphRun.InkBounds.X); + { + var textMetrics = textRun.TextMetrics; + var glyphRun = textRun.GlyphRun; + var runBounds = glyphRun.InkBounds.WithX(widthIncludingWhitespace + glyphRun.InkBounds.X); - bounds = bounds.Union(runBounds); + bounds = bounds.Union(runBounds); - if (fontRenderingEmSize < textMetrics.FontRenderingEmSize) - { - fontRenderingEmSize = textMetrics.FontRenderingEmSize; + if (ascent > textMetrics.Ascent) + { + ascent = textMetrics.Ascent; + } - if (ascent > textMetrics.Ascent) - { - ascent = textMetrics.Ascent; - } + if (descent < textMetrics.Descent) + { + descent = textMetrics.Descent; + } - if (descent < textMetrics.Descent) - { - descent = textMetrics.Descent; - } + if (lineGap < textMetrics.LineGap) + { + lineGap = textMetrics.LineGap; + } - if (lineGap < textMetrics.LineGap) - { - lineGap = textMetrics.LineGap; - } + if (descent - ascent + lineGap > height) + { + height = descent - ascent + lineGap; + } - if (descent - ascent + lineGap > height) - { - height = descent - ascent + lineGap; - } - } - widthIncludingWhitespace += textRun.Size.Width; + widthIncludingWhitespace += textRun.Size.Width; - break; - } + break; + } case DrawableTextRun drawableTextRun: + { + widthIncludingWhitespace += drawableTextRun.Size.Width; + + if (drawableTextRun.Size.Height > height) { - widthIncludingWhitespace += drawableTextRun.Size.Width; + height = drawableTextRun.Size.Height; + } - if (drawableTextRun.Size.Height > height) - { - height = drawableTextRun.Size.Height; - } - - //Adjust current ascent so drawables and text align at the bottom edge of the line. - var offset = Math.Max(0, drawableTextRun.Baseline + ascent - descent); + //Adjust current ascent so drawables and text align at the bottom edge of the line. + var offset = Math.Max(0, drawableTextRun.Baseline + ascent - descent); - ascent -= offset; + ascent -= offset; - bounds = bounds.Union(new Rect(new Point(bounds.Right, 0), drawableTextRun.Size)); + bounds = bounds.Union(new Rect(new Point(bounds.Right, 0), drawableTextRun.Size)); - break; - } + break; + } } } diff --git a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLayoutTests.cs b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLayoutTests.cs index cb9b3789863..3d19efeae95 100644 --- a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLayoutTests.cs +++ b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLayoutTests.cs @@ -1123,7 +1123,7 @@ public void Should_HitTestTextPosition_EndOfLine_RTL() var rect = textLayout.HitTestTextPosition(text.Length); - Assert.Equal(14.0625, rect.Top); + Assert.Equal(16.32, rect.Top); } } From 59f21e529c275d4b7c1683879abd3e447ec7ca6d Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Thu, 11 Jul 2024 08:23:33 +0200 Subject: [PATCH 3/5] Draw all shaped runs at the baseline --- src/Avalonia.Base/Media/TextFormatting/ShapedTextRun.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Base/Media/TextFormatting/ShapedTextRun.cs b/src/Avalonia.Base/Media/TextFormatting/ShapedTextRun.cs index 82e59735197..5bb8ad5b958 100644 --- a/src/Avalonia.Base/Media/TextFormatting/ShapedTextRun.cs +++ b/src/Avalonia.Base/Media/TextFormatting/ShapedTextRun.cs @@ -45,7 +45,7 @@ public override int Length /// public override void Draw(DrawingContext drawingContext, Point origin) { - using (drawingContext.PushTransform(Matrix.CreateTranslation(origin - new Point(0, Baseline)))) + using (drawingContext.PushTransform(Matrix.CreateTranslation(origin))) { if (GlyphRun.GlyphInfos.Count == 0) { @@ -64,7 +64,7 @@ public override void Draw(DrawingContext drawingContext, Point origin) if (Properties.BackgroundBrush != null) { - drawingContext.DrawRectangle(Properties.BackgroundBrush, null, GlyphRun.Bounds); + drawingContext.DrawRectangle(Properties.BackgroundBrush, null, GlyphRun.Bounds.Translate(new Vector(0, -Baseline))); } drawingContext.DrawGlyphRun(Properties.ForegroundBrush, GlyphRun); @@ -204,7 +204,8 @@ internal GlyphRun CreateGlyphRun() ShapedBuffer.FontRenderingEmSize, Text, ShapedBuffer, - biDiLevel: BidiLevel); + biDiLevel: BidiLevel, + baselineOrigin: new Point()); } public void Dispose() From 55a88113840b0b3b5528b5619c124d25789d4a0f Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Thu, 11 Jul 2024 09:08:38 +0200 Subject: [PATCH 4/5] Random change please revert --- samples/Sandbox/MainWindow.axaml | 1 + 1 file changed, 1 insertion(+) diff --git a/samples/Sandbox/MainWindow.axaml b/samples/Sandbox/MainWindow.axaml index 6929f192c75..6e5a412ab7c 100644 --- a/samples/Sandbox/MainWindow.axaml +++ b/samples/Sandbox/MainWindow.axaml @@ -1,4 +1,5 @@ + From 35f6f6508046100c04d6a6134b3c484d13914640 Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Fri, 12 Jul 2024 09:55:10 +0200 Subject: [PATCH 5/5] Revert change --- samples/Sandbox/MainWindow.axaml | 1 - 1 file changed, 1 deletion(-) diff --git a/samples/Sandbox/MainWindow.axaml b/samples/Sandbox/MainWindow.axaml index 6e5a412ab7c..6929f192c75 100644 --- a/samples/Sandbox/MainWindow.axaml +++ b/samples/Sandbox/MainWindow.axaml @@ -1,5 +1,4 @@ -