Browse Source

Rework GlyphRun BaselineOrigin

Benedikt Schroeder 5 years ago
parent
commit
c2fad766e5

+ 1 - 2
samples/RenderDemo/Pages/GlyphRunPage.xaml.cs

@@ -61,7 +61,6 @@ namespace RenderDemo.Pages
             {
                 Foreground = Brushes.Black,
                 GlyphRun = new GlyphRun(_glyphTypeface, _fontSize, _glyphIndices),
-                BaselineOrigin = new Point(0, -_glyphTypeface.Ascent * scale)
             };
 
             drawingGroup.Children.Add(glyphRunDrawing);
@@ -69,7 +68,7 @@ namespace RenderDemo.Pages
             var geometryDrawing = new GeometryDrawing
             {
                 Pen = new Pen(Brushes.Black),
-                Geometry = new RectangleGeometry { Rect = glyphRunDrawing.GlyphRun.Bounds }
+                Geometry = new RectangleGeometry { Rect = new Rect(glyphRunDrawing.GlyphRun.Size) }
             };
 
             drawingGroup.Children.Add(geometryDrawing);

+ 2 - 2
src/Avalonia.Controls/Primitives/AccessText.cs

@@ -126,7 +126,7 @@ namespace Avalonia.Controls.Primitives
 
                     if (shapedTextCharacters.GlyphRun.Characters.End < textPosition)
                     {
-                        currentX += shapedTextCharacters.GlyphRun.Bounds.Width;
+                        currentX += shapedTextCharacters.Size.Width;
 
                         continue;
                     }
@@ -143,7 +143,7 @@ namespace Avalonia.Controls.Primitives
                         width = 0.0;
                     }
 
-                    return new Rect(currentX, currentY, width, shapedTextCharacters.GlyphRun.Bounds.Height);
+                    return new Rect(currentX, currentY, width, shapedTextCharacters.Size.Height);
                 }
             }
 

+ 4 - 1
src/Avalonia.Controls/TextBlock.cs

@@ -434,7 +434,10 @@ namespace Avalonia.Controls
 
             var padding = Padding;
 
-            TextLayout.Draw(context, new Point(padding.Left + offsetX, padding.Top));
+            using (context.PushPostTransform(Matrix.CreateTranslation(padding.Left + offsetX, padding.Top)))
+            {
+                TextLayout.Draw(context);
+            }
         }
 
         /// <summary>

+ 1 - 1
src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs

@@ -385,7 +385,7 @@ namespace Avalonia.Headless
                 
             }
 
-            public void DrawGlyphRun(IBrush foreground, GlyphRun glyphRun, Point baselineOrigin)
+            public void DrawGlyphRun(IBrush foreground, GlyphRun glyphRun)
             {
                 
             }

+ 19 - 1
src/Avalonia.Visuals/ApiCompatBaseline.txt

@@ -1,15 +1,33 @@
 Compat issues with assembly Avalonia.Visuals:
+MembersMustExist : Member 'public void Avalonia.Media.DrawingContext.DrawGlyphRun(Avalonia.Media.IBrush, Avalonia.Media.GlyphRun, Avalonia.Point)' does not exist in the implementation but it does exist in the contract.
 MembersMustExist : Member 'public Avalonia.Media.Typeface Avalonia.Media.FontManager.GetOrAddTypeface(Avalonia.Media.FontFamily, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight)' does not exist in the implementation but it does exist in the contract.
 MembersMustExist : Member 'public Avalonia.Media.Typeface Avalonia.Media.FontManager.MatchCharacter(System.Int32, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight, Avalonia.Media.FontFamily, System.Globalization.CultureInfo)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public Avalonia.Rect Avalonia.Media.GlyphRun.Bounds.get()' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public Avalonia.StyledProperty<Avalonia.Point> Avalonia.StyledProperty<Avalonia.Point> Avalonia.Media.GlyphRunDrawing.BaselineOriginProperty' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public Avalonia.Point Avalonia.Media.GlyphRunDrawing.BaselineOrigin.get()' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public void Avalonia.Media.GlyphRunDrawing.BaselineOrigin.set(Avalonia.Point)' does not exist in the implementation but it does exist in the contract.
 CannotSealType : Type 'Avalonia.Media.Typeface' is actually (has the sealed modifier) sealed in the implementation but not sealed in the contract.
 TypeCannotChangeClassification : Type 'Avalonia.Media.Typeface' is a 'struct' in the implementation but is a 'class' in the contract.
 CannotMakeMemberNonVirtual : Member 'public System.Boolean Avalonia.Media.Typeface.Equals(System.Object)' is non-virtual in the implementation but is virtual in the contract.
 CannotMakeMemberNonVirtual : Member 'public System.Int32 Avalonia.Media.Typeface.GetHashCode()' is non-virtual in the implementation but is virtual in the contract.
 TypesMustExist : Type 'Avalonia.Media.Fonts.FontKey' does not exist in the implementation but it does exist in the contract.
+CannotAddAbstractMembers : Member 'public Avalonia.Size Avalonia.Media.TextFormatting.DrawableTextRun.Size' is abstract in the implementation but is missing in the contract.
+MembersMustExist : Member 'public Avalonia.Rect Avalonia.Media.TextFormatting.DrawableTextRun.Bounds.get()' does not exist in the implementation but it does exist in the contract.
+CannotAddAbstractMembers : Member 'public void Avalonia.Media.TextFormatting.DrawableTextRun.Draw(Avalonia.Media.DrawingContext)' is abstract in the implementation but is missing in the contract.
+MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.DrawableTextRun.Draw(Avalonia.Media.DrawingContext, Avalonia.Point)' does not exist in the implementation but it does exist in the contract.
+CannotAddAbstractMembers : Member 'public Avalonia.Size Avalonia.Media.TextFormatting.DrawableTextRun.Size.get()' is abstract in the implementation but is missing in the contract.
+MembersMustExist : Member 'public Avalonia.Rect Avalonia.Media.TextFormatting.ShapedTextCharacters.Bounds.get()' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.ShapedTextCharacters.Draw(Avalonia.Media.DrawingContext, Avalonia.Point)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.TextLayout.Draw(Avalonia.Media.DrawingContext, Avalonia.Point)' does not exist in the implementation but it does exist in the contract.
 CannotAddAbstractMembers : Member 'public Avalonia.Media.TextFormatting.TextLineBreak Avalonia.Media.TextFormatting.TextLine.TextLineBreak' is abstract in the implementation but is missing in the contract.
+CannotAddAbstractMembers : Member 'public void Avalonia.Media.TextFormatting.TextLine.Draw(Avalonia.Media.DrawingContext)' is abstract in the implementation but is missing in the contract.
+MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.TextLine.Draw(Avalonia.Media.DrawingContext, Avalonia.Point)' does not exist in the implementation but it does exist in the contract.
 MembersMustExist : Member 'public Avalonia.Media.TextFormatting.TextLineBreak Avalonia.Media.TextFormatting.TextLine.LineBreak.get()' does not exist in the implementation but it does exist in the contract.
 CannotAddAbstractMembers : Member 'public Avalonia.Media.TextFormatting.TextLineBreak Avalonia.Media.TextFormatting.TextLine.TextLineBreak.get()' is abstract in the implementation but is missing in the contract.
+InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.IDrawingContextImpl.DrawGlyphRun(Avalonia.Media.IBrush, Avalonia.Media.GlyphRun)' is present in the implementation but not in the contract.
+InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.IDrawingContextImpl.DrawGlyphRun(Avalonia.Media.IBrush, Avalonia.Media.GlyphRun, Avalonia.Point)' is present in the contract but not in the implementation.
+MembersMustExist : Member 'public void Avalonia.Platform.IDrawingContextImpl.DrawGlyphRun(Avalonia.Media.IBrush, Avalonia.Media.GlyphRun, Avalonia.Point)' does not exist in the implementation but it does exist in the contract.
 InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalonia.Platform.IFontManagerImpl.TryMatchCharacter(System.Int32, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight, Avalonia.Media.FontFamily, System.Globalization.CultureInfo, Avalonia.Media.Fonts.FontKey)' is present in the contract but not in the implementation.
 MembersMustExist : Member 'public System.Boolean Avalonia.Platform.IFontManagerImpl.TryMatchCharacter(System.Int32, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight, Avalonia.Media.FontFamily, System.Globalization.CultureInfo, Avalonia.Media.Fonts.FontKey)' does not exist in the implementation but it does exist in the contract.
 InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalonia.Platform.IFontManagerImpl.TryMatchCharacter(System.Int32, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight, Avalonia.Media.FontFamily, System.Globalization.CultureInfo, Avalonia.Media.Typeface)' is present in the implementation but not in the contract.
-Total Issues: 13
+Total Issues: 31

+ 2 - 3
src/Avalonia.Visuals/Media/DrawingContext.cs

@@ -206,14 +206,13 @@ namespace Avalonia.Media
         /// </summary>
         /// <param name="foreground">The foreground brush.</param>
         /// <param name="glyphRun">The glyph run.</param>
-        /// <param name="baselineOrigin">The baseline origin of the glyph run.</param>
-        public void DrawGlyphRun(IBrush foreground, GlyphRun glyphRun, Point baselineOrigin)
+        public void DrawGlyphRun(IBrush foreground, GlyphRun glyphRun)
         {
             Contract.Requires<ArgumentNullException>(glyphRun != null);
 
             if (foreground != null)
             {
-                PlatformImpl.DrawGlyphRun(foreground, glyphRun, baselineOrigin);
+                PlatformImpl.DrawGlyphRun(foreground, glyphRun);
             }
         }
 

+ 37 - 12
src/Avalonia.Visuals/Media/GlyphRun.cs

@@ -16,8 +16,9 @@ namespace Avalonia.Media
         private IGlyphRunImpl _glyphRunImpl;
         private GlyphTypeface _glyphTypeface;
         private double _fontRenderingEmSize;
-        private Rect? _bounds;
+        private Size? _size;
         private int _biDiLevel;
+        private Point? _baselineOrigin;
 
         private ReadOnlySlice<ushort> _glyphIndices;
         private ReadOnlySlice<double> _glyphAdvances;
@@ -89,6 +90,20 @@ namespace Avalonia.Media
             set => Set(ref _fontRenderingEmSize, value);
         }
 
+        /// <summary>
+        ///     Gets or sets the baseline origin of the<see cref="GlyphRun"/>.
+        /// </summary>
+        public Point BaselineOrigin
+        {
+            get
+            {
+                _baselineOrigin ??= CalculateBaselineOrigin();
+
+                return _baselineOrigin.Value;
+            }
+            set => Set(ref _baselineOrigin, value);
+        }
+
         /// <summary>
         ///     Gets or sets an array of <see cref="ushort"/> values that represent the glyph indices in the rendering physical font.
         /// </summary>
@@ -156,16 +171,13 @@ namespace Avalonia.Media
         /// <summary>
         ///     Gets or sets the conservative bounding box of the <see cref="GlyphRun"/>.
         /// </summary>
-        public Rect Bounds
+        public Size Size
         {
             get
             {
-                if (_bounds == null)
-                {
-                    _bounds = CalculateBounds();
-                }
+                _size ??= CalculateSize();
 
-                return _bounds.Value;
+                return _size.Value;
             }
         }
 
@@ -200,7 +212,7 @@ namespace Avalonia.Media
 
             if (characterHit.FirstCharacterIndex + characterHit.TrailingLength > Characters.End)
             {
-                return Bounds.Width;
+                return Size.Width;
             }
 
             var glyphIndex = FindGlyphIndex(characterHit.FirstCharacterIndex);
@@ -257,7 +269,7 @@ namespace Avalonia.Media
             }
 
             //After
-            if (distance > Bounds.Size.Width)
+            if (distance > Size.Width)
             {
                 isInside = false;
 
@@ -529,12 +541,21 @@ namespace Avalonia.Media
         }
 
         /// <summary>
-        /// Calculates the bounds of the <see cref="GlyphRun"/>.
+        /// Calculates the default baseline origin of the <see cref="GlyphRun"/>.
+        /// </summary>
+        /// <returns>The baseline origin.</returns>
+        private Point CalculateBaselineOrigin()
+        {
+            return new Point(0, -GlyphTypeface.Ascent * Scale);
+        }
+
+        /// <summary>
+        /// Calculates the size of the <see cref="GlyphRun"/>.
         /// </summary>
         /// <returns>
         /// The calculated bounds.
         /// </returns>
-        private Rect CalculateBounds()
+        private Size CalculateSize()
         {
             var height = (GlyphTypeface.Descent - GlyphTypeface.Ascent + GlyphTypeface.LineGap) * Scale;
 
@@ -555,7 +576,7 @@ namespace Avalonia.Media
                 }
             }
 
-            return new Rect(0, GlyphTypeface.Ascent * Scale, width, height);
+            return new Size(width, height);
         }
 
         private void Set<T>(ref T field, T value)
@@ -590,11 +611,15 @@ namespace Avalonia.Media
                 throw new InvalidOperationException();
             }
 
+            _baselineOrigin = new Point(0, -GlyphTypeface.Ascent * Scale);
+
             var platformRenderInterface = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>();
 
             _glyphRunImpl = platformRenderInterface.CreateGlyphRun(this, out var width);
 
             var height = (GlyphTypeface.Descent - GlyphTypeface.Ascent + GlyphTypeface.LineGap) * Scale;
+
+            _size = new Size(width, height);
         }
 
         void IDisposable.Dispose()

+ 2 - 11
src/Avalonia.Visuals/Media/GlyphRunDrawing.cs

@@ -8,9 +8,6 @@
         public static readonly StyledProperty<GlyphRun> GlyphRunProperty =
             AvaloniaProperty.Register<GlyphRunDrawing, GlyphRun>(nameof(GlyphRun));
 
-        public static readonly StyledProperty<Point> BaselineOriginProperty =
-            AvaloniaProperty.Register<GlyphRunDrawing, Point>(nameof(BaselineOrigin));
-
         public IBrush Foreground
         {
             get => GetValue(ForegroundProperty);
@@ -23,12 +20,6 @@
             set => SetValue(GlyphRunProperty, value);
         }
 
-        public Point BaselineOrigin
-        {
-            get => GetValue(BaselineOriginProperty);
-            set => SetValue(BaselineOriginProperty, value);
-        }
-
         public override void Draw(DrawingContext context)
         {
             if (GlyphRun == null)
@@ -36,12 +27,12 @@
                 return;
             }
 
-            context.DrawGlyphRun(Foreground, GlyphRun, BaselineOrigin);
+            context.DrawGlyphRun(Foreground, GlyphRun);
         }
 
         public override Rect GetBounds()
         {
-            return GlyphRun?.Bounds ?? default;
+            return GlyphRun != null ? new Rect(GlyphRun.Size) : Rect.Empty;
         }
     }
 }

+ 10 - 7
src/Avalonia.Visuals/Media/TextDecoration.cs

@@ -155,8 +155,7 @@ namespace Avalonia.Media
         /// </summary>
         /// <param name="drawingContext">The drawing context.</param>
         /// <param name="shapedTextCharacters">The shaped characters that are decorated.</param>
-        /// <param name="origin">The origin.</param>
-        internal void Draw(DrawingContext drawingContext, ShapedTextCharacters shapedTextCharacters, Point origin)
+        internal void Draw(DrawingContext drawingContext, ShapedTextCharacters shapedTextCharacters)
         {
             var fontRenderingEmSize = shapedTextCharacters.Properties.FontRenderingEmSize;
             var fontMetrics = shapedTextCharacters.FontMetrics;
@@ -181,16 +180,20 @@ namespace Avalonia.Media
                     break;
             }
 
+            var origin = new Point();
+
             switch (Location)
             {
-                case TextDecorationLocation.Overline:
-                    origin += new Point(0, fontMetrics.Ascent);
+                case TextDecorationLocation.Baseline:
+                    origin += shapedTextCharacters.GlyphRun.BaselineOrigin;
                     break;
                 case TextDecorationLocation.Strikethrough:
-                    origin += new Point(0, -fontMetrics.StrikethroughPosition);
+                    origin += new Point(shapedTextCharacters.GlyphRun.BaselineOrigin.X,
+                        shapedTextCharacters.GlyphRun.BaselineOrigin.Y - fontMetrics.StrikethroughPosition);
                     break;
                 case TextDecorationLocation.Underline:
-                    origin += new Point(0, -fontMetrics.UnderlinePosition);
+                    origin += new Point(shapedTextCharacters.GlyphRun.BaselineOrigin.X,
+                        shapedTextCharacters.GlyphRun.BaselineOrigin.Y - fontMetrics.UnderlinePosition);
                     break;
             }
 
@@ -207,7 +210,7 @@ namespace Avalonia.Media
             var pen = new Pen(Stroke ?? shapedTextCharacters.Properties.ForegroundBrush, thickness,
                 new DashStyle(StrokeDashArray, StrokeDashOffset), StrokeLineCap);
 
-            drawingContext.DrawLine(pen, origin, origin + new Point(shapedTextCharacters.Bounds.Width, 0));
+            drawingContext.DrawLine(pen, origin, origin + new Point(shapedTextCharacters.Size.Width, 0));
         }
     }
 }

+ 3 - 4
src/Avalonia.Visuals/Media/TextFormatting/DrawableTextRun.cs

@@ -6,15 +6,14 @@
     public abstract class DrawableTextRun : TextRun
     {
         /// <summary>
-        /// Gets the bounds.
+        /// Gets the size.
         /// </summary>
-        public abstract Rect Bounds { get; }
+        public abstract Size Size { get; }
 
         /// <summary>
         /// Draws the <see cref="DrawableTextRun"/> at the given origin.
         /// </summary>
         /// <param name="drawingContext">The drawing context.</param>
-        /// <param name="origin">The origin.</param>
-        public abstract void Draw(DrawingContext drawingContext, Point origin);
+        public abstract void Draw(DrawingContext drawingContext);
     }
 }

+ 5 - 6
src/Avalonia.Visuals/Media/TextFormatting/ShapedTextCharacters.cs

@@ -26,7 +26,7 @@ namespace Avalonia.Media.TextFormatting
         public override int TextSourceLength { get; }
 
         /// <inheritdoc/>
-        public override Rect Bounds => GlyphRun.Bounds;
+        public override Size Size => GlyphRun.Size;
 
         /// <summary>
         /// Gets the font metrics.
@@ -45,7 +45,7 @@ namespace Avalonia.Media.TextFormatting
         public GlyphRun GlyphRun { get; }
 
         /// <inheritdoc/>
-        public override void Draw(DrawingContext drawingContext, Point origin)
+        public override void Draw(DrawingContext drawingContext)
         {
             if (GlyphRun.GlyphIndices.Length == 0)
             {
@@ -64,11 +64,10 @@ namespace Avalonia.Media.TextFormatting
 
             if (Properties.BackgroundBrush != null)
             {
-                drawingContext.DrawRectangle(Properties.BackgroundBrush, null,
-                new Rect(origin.X, origin.Y + FontMetrics.Ascent, Bounds.Width, Bounds.Height));
+                drawingContext.DrawRectangle(Properties.BackgroundBrush, null, new Rect(Size));
             }
 
-            drawingContext.DrawGlyphRun(Properties.ForegroundBrush, GlyphRun, origin);
+            drawingContext.DrawGlyphRun(Properties.ForegroundBrush, GlyphRun);
 
             if (Properties.TextDecorations == null)
             {
@@ -77,7 +76,7 @@ namespace Avalonia.Media.TextFormatting
 
             foreach (var textDecoration in Properties.TextDecorations)
             {
-                textDecoration.Draw(drawingContext, this, origin);
+                textDecoration.Draw(drawingContext, this);
             }
         }
 

+ 3 - 3
src/Avalonia.Visuals/Media/TextFormatting/TextFormatterImpl.cs

@@ -52,7 +52,7 @@ namespace Avalonia.Media.TextFormatting
         {
             var glyphRun = textCharacters.GlyphRun;
 
-            if (glyphRun.Bounds.Width < availableWidth)
+            if (glyphRun.Size.Width < availableWidth)
             {
                 return glyphRun.Characters.Length;
             }
@@ -348,7 +348,7 @@ namespace Avalonia.Media.TextFormatting
             {
                 var currentRun = textRuns[runIndex];
 
-                if (currentWidth + currentRun.GlyphRun.Bounds.Width > availableWidth)
+                if (currentWidth + currentRun.Size.Width > availableWidth)
                 {
                     var measuredLength = MeasureCharacters(currentRun, paragraphWidth - currentWidth);
 
@@ -421,7 +421,7 @@ namespace Avalonia.Media.TextFormatting
                     return new TextLineImpl(splitResult.First, textLineMetrics, lineBreak);
                 }
 
-                currentWidth += currentRun.GlyphRun.Bounds.Width;
+                currentWidth += currentRun.Size.Width;
 
                 currentLength += currentRun.GlyphRun.Characters.Length;
 

+ 11 - 6
src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs

@@ -115,22 +115,27 @@ namespace Avalonia.Media.TextFormatting
         /// Draws the text layout.
         /// </summary>
         /// <param name="context">The drawing context.</param>
-        /// <param name="origin">The origin.</param>
-        public void Draw(DrawingContext context, Point origin)
+        public void Draw(DrawingContext context)
         {
             if (!TextLines.Any())
             {
                 return;
             }
 
-            var currentY = origin.Y;
+            var currentY = 0.0;
 
             foreach (var textLine in TextLines)
             {
-                var offsetX = TextLine.GetParagraphOffsetX(textLine.LineMetrics.Size.Width, Size.Width,
-                    _paragraphProperties.TextAlignment);
+                using (context.PushPostTransform(Matrix.CreateTranslation(0, currentY)))
+                {
+                    var offsetX = TextLine.GetParagraphOffsetX(textLine.LineMetrics.Size.Width, Size.Width,
+                        _paragraphProperties.TextAlignment);
 
-                textLine.Draw(context, new Point(origin.X + offsetX, currentY));
+                    using (context.PushPostTransform(Matrix.CreateTranslation(offsetX, 0)))
+                    {
+                        textLine.Draw(context);
+                    }
+                }
 
                 currentY += textLine.LineMetrics.Size.Height;
             }

+ 1 - 2
src/Avalonia.Visuals/Media/TextFormatting/TextLine.cs

@@ -51,8 +51,7 @@ namespace Avalonia.Media.TextFormatting
         /// Draws the <see cref="TextLine"/> at the given origin.
         /// </summary>
         /// <param name="drawingContext">The drawing context.</param>
-        /// <param name="origin">The origin.</param>
-        public abstract void Draw(DrawingContext drawingContext, Point origin);
+        public abstract void Draw(DrawingContext drawingContext);
 
         /// <summary>
         /// Create a collapsed line based on collapsed text properties.

+ 15 - 14
src/Avalonia.Visuals/Media/TextFormatting/TextLineImpl.cs

@@ -33,17 +33,18 @@ namespace Avalonia.Media.TextFormatting
         public override bool HasCollapsed { get; }
 
         /// <inheritdoc/>
-        public override void Draw(DrawingContext drawingContext, Point origin)
+        public override void Draw(DrawingContext drawingContext)
         {
-            var currentX = origin.X;
+            var currentX = 0.0;
 
             foreach (var textRun in _textRuns)
             {
-                var baselineOrigin = new Point(currentX, origin.Y + LineMetrics.TextBaseline);
-
-                textRun.Draw(drawingContext, baselineOrigin);
+                using (drawingContext.PushPostTransform(Matrix.CreateTranslation(currentX, 0)))
+                {
+                    textRun.Draw(drawingContext);
+                }
 
-                currentX += textRun.Bounds.Width;
+                currentX += textRun.Size.Width;
             }
         }
 
@@ -64,13 +65,13 @@ namespace Avalonia.Media.TextFormatting
 
             var shapedSymbol = CreateShapedSymbol(collapsingProperties.Symbol);
 
-            var availableWidth = collapsingProperties.Width - shapedSymbol.Bounds.Width;
+            var availableWidth = collapsingProperties.Width - shapedSymbol.Size.Width;
 
             while (runIndex < _textRuns.Count)
             {
                 var currentRun = _textRuns[runIndex];
 
-                currentWidth += currentRun.GlyphRun.Bounds.Width;
+                currentWidth += currentRun.Size.Width;
 
                 if (currentWidth > availableWidth)
                 {
@@ -125,7 +126,7 @@ namespace Avalonia.Media.TextFormatting
                     return new TextLineImpl(shapedTextCharacters, textLineMetrics, TextLineBreak, true);
                 }
 
-                availableWidth -= currentRun.GlyphRun.Bounds.Width;
+                availableWidth -= currentRun.Size.Width;
 
                 collapsedLength += currentRun.GlyphRun.Characters.Length;
 
@@ -133,7 +134,7 @@ namespace Avalonia.Media.TextFormatting
             }
 
             textLineMetrics =
-                new TextLineMetrics(LineMetrics.Size.WithWidth(LineMetrics.Size.Width + shapedSymbol.Bounds.Width),
+                new TextLineMetrics(LineMetrics.Size.WithWidth(LineMetrics.Size.Width + shapedSymbol.Size.Width),
                     LineMetrics.TextBaseline, TextRange, LineMetrics.HasOverflowed);
 
             return new TextLineImpl(new List<ShapedTextCharacters>(_textRuns) { shapedSymbol }, textLineMetrics, null,
@@ -156,12 +157,12 @@ namespace Avalonia.Media.TextFormatting
             {
                 characterHit = run.GlyphRun.GetCharacterHitFromDistance(distance, out _);
 
-                if (distance <= run.Bounds.Width)
+                if (distance <= run.Size.Width)
                 {
                     break;
                 }
 
-                distance -= run.Bounds.Width;
+                distance -= run.Size.Width;
             }
 
             return characterHit;
@@ -229,7 +230,7 @@ namespace Avalonia.Media.TextFormatting
             {
                 if (codepointIndex > textRun.Text.End)
                 {
-                    currentDistance += textRun.Bounds.Width;
+                    currentDistance += textRun.Size.Width;
 
                     continue;
                 }
@@ -405,7 +406,7 @@ namespace Avalonia.Media.TextFormatting
 
             for (var i = 0; i < shapedTextCharacters.Count; i++)
             {
-                shapedWidth += shapedTextCharacters[i].Bounds.Width;
+                shapedWidth += shapedTextCharacters[i].Size.Width;
             }
 
             return shapedWidth;

+ 1 - 1
src/Avalonia.Visuals/Media/TextFormatting/TextLineMetrics.cs

@@ -67,7 +67,7 @@ namespace Avalonia.Media.TextFormatting
                 var fontMetrics =
                     new FontMetrics(shapedRun.Properties.Typeface, shapedRun.Properties.FontRenderingEmSize);
 
-                lineWidth += shapedRun.Bounds.Width;
+                lineWidth += shapedRun.Size.Width;
 
                 if (ascent > fontMetrics.Ascent)
                 {

+ 1 - 2
src/Avalonia.Visuals/Platform/IDrawingContextImpl.cs

@@ -84,8 +84,7 @@ namespace Avalonia.Platform
         /// </summary>
         /// <param name="foreground">The foreground.</param>
         /// <param name="glyphRun">The glyph run.</param>
-        /// <param name="baselineOrigin">The baseline origin of the glyph run.</param>
-        void DrawGlyphRun(IBrush foreground, GlyphRun glyphRun, Point baselineOrigin);
+        void DrawGlyphRun(IBrush foreground, GlyphRun glyphRun);
 
         /// <summary>
         /// Creates a new <see cref="IRenderTargetBitmapImpl"/> that can be used as a render layer

+ 2 - 2
src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs

@@ -204,13 +204,13 @@ namespace Avalonia.Rendering.SceneGraph
         }
 
         /// <inheritdoc/>
-        public void DrawGlyphRun(IBrush foreground, GlyphRun glyphRun, Point baselineOrigin)
+        public void DrawGlyphRun(IBrush foreground, GlyphRun glyphRun)
         {
             var next = NextDrawAs<GlyphRunNode>();
 
             if (next == null || !next.Item.Equals(Transform, foreground, glyphRun))
             {
-                Add(new GlyphRunNode(Transform, foreground, glyphRun, baselineOrigin, CreateChildScene(foreground)));
+                Add(new GlyphRunNode(Transform, foreground, glyphRun, CreateChildScene(foreground)));
             }
 
             else

+ 3 - 10
src/Avalonia.Visuals/Rendering/SceneGraph/GlyphRunNode.cs

@@ -1,6 +1,7 @@
 using System.Collections.Generic;
 
 using Avalonia.Media;
+using Avalonia.Media.Immutable;
 using Avalonia.Platform;
 using Avalonia.VisualTree;
 
@@ -17,20 +18,17 @@ namespace Avalonia.Rendering.SceneGraph
         /// <param name="transform">The transform.</param>
         /// <param name="foreground">The foreground brush.</param>
         /// <param name="glyphRun">The glyph run to draw.</param>
-        /// <param name="baselineOrigin">The baseline origin of the glyph run.</param>
         /// <param name="childScenes">Child scenes for drawing visual brushes.</param>
         public GlyphRunNode(
             Matrix transform,
             IBrush foreground,
             GlyphRun glyphRun,
-            Point baselineOrigin,
             IDictionary<IVisual, Scene> childScenes = null)
-            : base(glyphRun.Bounds.Translate(baselineOrigin), transform)
+            : base(new Rect(glyphRun.Size), transform)
         {
             Transform = transform;
             Foreground = foreground?.ToImmutable();
             GlyphRun = glyphRun;
-            BaselineOrigin = baselineOrigin;
             ChildScenes = childScenes;
         }
 
@@ -49,11 +47,6 @@ namespace Avalonia.Rendering.SceneGraph
         /// </summary>
         public GlyphRun GlyphRun { get; }
 
-        /// <summary>
-        /// Gets the baseline origin.
-        /// </summary>
-        public Point BaselineOrigin { get; set; }
-
         /// <inheritdoc/>
         public override IDictionary<IVisual, Scene> ChildScenes { get; }
 
@@ -61,7 +54,7 @@ namespace Avalonia.Rendering.SceneGraph
         public override void Render(IDrawingContextImpl context)
         {
             context.Transform = Transform;
-            context.DrawGlyphRun(Foreground, GlyphRun, BaselineOrigin);
+            context.DrawGlyphRun(Foreground, GlyphRun);
         }
 
         /// <summary>

+ 4 - 4
src/Skia/Avalonia.Skia/DrawingContextImpl.cs

@@ -401,16 +401,16 @@ namespace Avalonia.Skia
         }
 
         /// <inheritdoc />
-        public void DrawGlyphRun(IBrush foreground, GlyphRun glyphRun, Point baselineOrigin)
+        public void DrawGlyphRun(IBrush foreground, GlyphRun glyphRun)
         {
-            using (var paintWrapper = CreatePaint(_fillPaint, foreground, glyphRun.Bounds.Size))
+            using (var paintWrapper = CreatePaint(_fillPaint, foreground, glyphRun.Size))
             {
                 var glyphRunImpl = (GlyphRunImpl)glyphRun.GlyphRunImpl;
 
                 ConfigureTextRendering(paintWrapper);
 
-                Canvas.DrawText(glyphRunImpl.TextBlob, (float)baselineOrigin.X,
-                    (float)baselineOrigin.Y, paintWrapper.Paint);
+                Canvas.DrawText(glyphRunImpl.TextBlob, (float)glyphRun.BaselineOrigin.X,
+                    (float)glyphRun.BaselineOrigin.Y, paintWrapper.Paint);
             }
         }
 

+ 4 - 3
src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs

@@ -324,13 +324,14 @@ namespace Avalonia.Direct2D1.Media
         /// <param name="foreground">The foreground.</param>
         /// <param name="glyphRun">The glyph run.</param>
         /// <param name="baselineOrigin"></param>
-        public void DrawGlyphRun(IBrush foreground, GlyphRun glyphRun, Point baselineOrigin)
+        public void DrawGlyphRun(IBrush foreground, GlyphRun glyphRun)
         {
-            using (var brush = CreateBrush(foreground, glyphRun.Bounds.Size))
+            using (var brush = CreateBrush(foreground, glyphRun.Size))
             {
                 var glyphRunImpl = (GlyphRunImpl)glyphRun.GlyphRunImpl;
 
-                _renderTarget.DrawGlyphRun(baselineOrigin.ToSharpDX(), glyphRunImpl.GlyphRun, brush.PlatformBrush, MeasuringMode.Natural);
+                _renderTarget.DrawGlyphRun(glyphRun.BaselineOrigin.ToSharpDX(), glyphRunImpl.GlyphRun,
+                    brush.PlatformBrush, MeasuringMode.Natural);
             }
         }
 

+ 1 - 1
tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLayoutTests.cs

@@ -369,7 +369,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
 
                 var glyphRun = shapedRun.GlyphRun;
 
-                var width = glyphRun.Bounds.Width;
+                var width = glyphRun.Size.Width;
 
                 var characterHit = glyphRun.GetCharacterHitFromDistance(width, out _);