Browse Source

Fix BaselineAlignment (#16276)

* Implement remaining BaselineAlignment variants

* Adjust BaselineOffset calculation

* Draw all shaped runs at the baseline

* Random change please revert

* Revert change
pull/16321/head
Benedikt Stebner 2 years ago
committed by GitHub
parent
commit
376a84182e
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 15
      src/Avalonia.Base/Media/TextDecoration.cs
  2. 5
      src/Avalonia.Base/Media/TextFormatting/ShapedTextRun.cs
  3. 96
      src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs
  4. 2
      tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLayoutTests.cs

15
src/Avalonia.Base/Media/TextDecoration.cs

@ -182,18 +182,18 @@ namespace Avalonia.Media
break; break;
} }
var origin = new Point(); var origin = baselineOrigin;
switch (Location) switch (Location)
{ {
case TextDecorationLocation.Baseline: case TextDecorationLocation.Overline:
origin += glyphRun.BaselineOrigin; origin += new Point(0, textMetrics.Ascent);
break; break;
case TextDecorationLocation.Strikethrough: case TextDecorationLocation.Strikethrough:
origin += new Point(baselineOrigin.X, baselineOrigin.Y + textMetrics.StrikethroughPosition); origin += new Point(0, textMetrics.StrikethroughPosition);
break; break;
case TextDecorationLocation.Underline: case TextDecorationLocation.Underline:
origin += new Point(baselineOrigin.X, baselineOrigin.Y + textMetrics.UnderlinePosition); origin += new Point(0, textMetrics.UnderlinePosition);
break; break;
} }
@ -255,7 +255,10 @@ namespace Avalonia.Media
} }
} }
drawingContext.DrawLine(pen, origin, origin + new Point(glyphRun.Metrics.Width, 0)); var p1 = origin;
var p2 = p1 + new Point(glyphRun.Metrics.Width, 0);
drawingContext.DrawLine(pen, p1, p2);
} }
} }
} }

5
src/Avalonia.Base/Media/TextFormatting/ShapedTextRun.cs

@ -64,7 +64,7 @@ namespace Avalonia.Media.TextFormatting
if (Properties.BackgroundBrush != null) 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); drawingContext.DrawGlyphRun(Properties.ForegroundBrush, GlyphRun);
@ -204,7 +204,8 @@ namespace Avalonia.Media.TextFormatting
ShapedBuffer.FontRenderingEmSize, ShapedBuffer.FontRenderingEmSize,
Text, Text,
ShapedBuffer, ShapedBuffer,
biDiLevel: BidiLevel); biDiLevel: BidiLevel,
baselineOrigin: new Point());
} }
public void Dispose() public void Dispose()

96
src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs

@ -115,18 +115,19 @@ namespace Avalonia.Media.TextFormatting
switch (baselineAlignment) switch (baselineAlignment)
{ {
case BaselineAlignment.Baseline:
return textLine.Baseline;
case BaselineAlignment.Top: case BaselineAlignment.Top:
return 0; case BaselineAlignment.TextTop:
return textLine.Baseline - textLine.Extent + textRun.Size.Height / 2;
case BaselineAlignment.Center: case BaselineAlignment.Center:
return textLine.Height / 2 - textRun.Size.Height / 2; return textLine.Height / 2 + baseline - textRun.Size.Height / 2;
case BaselineAlignment.Subscript:
case BaselineAlignment.Bottom: case BaselineAlignment.Bottom:
return textLine.Height - textRun.Size.Height;
case BaselineAlignment.Baseline:
case BaselineAlignment.TextTop:
case BaselineAlignment.TextBottom: case BaselineAlignment.TextBottom:
case BaselineAlignment.Subscript: return textLine.Height - textRun.Size.Height + baseline;
case BaselineAlignment.Superscript: case BaselineAlignment.Superscript:
return textLine.Baseline - baseline; return baseline;
default: default:
throw new ArgumentOutOfRangeException(nameof(baselineAlignment), baselineAlignment, null); throw new ArgumentOutOfRangeException(nameof(baselineAlignment), baselineAlignment, null);
} }
@ -1143,7 +1144,6 @@ namespace Avalonia.Media.TextFormatting
} }
TextRun? currentRun = null; TextRun? currentRun = null;
TextRun? previousRun = null;
while (runIndex < _indexedTextRuns.Count) while (runIndex < _indexedTextRuns.Count)
{ {
@ -1182,7 +1182,7 @@ namespace Avalonia.Media.TextFormatting
break; break;
} }
case TextRun: case not null:
{ {
if(direction == LogicalDirection.Forward) if(direction == LogicalDirection.Forward)
{ {
@ -1212,8 +1212,6 @@ namespace Avalonia.Media.TextFormatting
} }
runIndex++; runIndex++;
previousRun = currentRun;
} }
return currentRun; return currentRun;
@ -1242,61 +1240,57 @@ namespace Avalonia.Media.TextFormatting
switch (_textRuns[index]) switch (_textRuns[index])
{ {
case ShapedTextRun textRun: case ShapedTextRun textRun:
{ {
var textMetrics = textRun.TextMetrics; var textMetrics = textRun.TextMetrics;
var glyphRun = textRun.GlyphRun; var glyphRun = textRun.GlyphRun;
var runBounds = glyphRun.InkBounds.WithX(widthIncludingWhitespace + glyphRun.InkBounds.X); var runBounds = glyphRun.InkBounds.WithX(widthIncludingWhitespace + glyphRun.InkBounds.X);
bounds = bounds.Union(runBounds); bounds = bounds.Union(runBounds);
if (fontRenderingEmSize < textMetrics.FontRenderingEmSize) if (ascent > textMetrics.Ascent)
{ {
fontRenderingEmSize = textMetrics.FontRenderingEmSize; ascent = textMetrics.Ascent;
}
if (ascent > textMetrics.Ascent) if (descent < textMetrics.Descent)
{ {
ascent = textMetrics.Ascent; descent = textMetrics.Descent;
} }
if (descent < textMetrics.Descent) if (lineGap < textMetrics.LineGap)
{ {
descent = textMetrics.Descent; lineGap = textMetrics.LineGap;
} }
if (lineGap < textMetrics.LineGap) if (descent - ascent + lineGap > height)
{ {
lineGap = textMetrics.LineGap; 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: 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) //Adjust current ascent so drawables and text align at the bottom edge of the line.
{ var offset = Math.Max(0, drawableTextRun.Baseline + ascent - descent);
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);
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;
} }
} }
} }

2
tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLayoutTests.cs

@ -1123,7 +1123,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
var rect = textLayout.HitTestTextPosition(text.Length); var rect = textLayout.HitTestTextPosition(text.Length);
Assert.Equal(14.0625, rect.Top); Assert.Equal(16.32, rect.Top);
} }
} }

Loading…
Cancel
Save