Skip to content

Use Spans and ReadOnlyCollection.Empty in TextBlock/Line, reduce allocs #9993

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,6 @@ internal override void Arrange(VisualCollection vc, Vector lineOffset)
Debug.Assert(runs != null, "Cannot retrieve runs collection.");

// Calculate offset shift due to trailing spaces
double adjustedXOffset = lineOffset.X + CalculateXOffsetShift();
foreach (TextSpan<TextRun> textSpan in runs)
{
TextRun run = textSpan.Value;
Expand All @@ -169,26 +168,24 @@ internal override void Arrange(VisualCollection vc, Vector lineOffset)
InlineObject inlineObject = run as InlineObject;

// Disconnect visual from its old parent, if necessary.
Visual currentParent = VisualTreeHelper.GetParent(inlineObject.Element) as Visual;
if (currentParent != null)
if (VisualTreeHelper.GetParent(inlineObject.Element) is Visual currentParent)
{
ContainerVisual parent = currentParent as ContainerVisual;
Invariant.Assert(parent != null, "parent should always derives from ContainerVisual");
parent.Children.Remove(inlineObject.Element);
}

// Get position of inline object withing the text line.
FlowDirection flowDirection;
Rect rect = GetBoundsFromPosition(runDcp, inlineObject.Length, out flowDirection);
Rect rect = GetBoundsFromPosition(runDcp, inlineObject.Length, out FlowDirection flowDirection);
Debug.Assert(DoubleUtil.GreaterThanOrClose(rect.Width, 0), "Negative inline object's width.");

ContainerVisual proxyVisual = new ContainerVisual();
if (inlineObject.Element is FrameworkElement)
if (inlineObject.Element is FrameworkElement frameworkElement)
{
FlowDirection parentFlowDirection = _owner.FlowDirection;
// Check parent's FlowDirection to determine if mirroring is needed

DependencyObject parent = ((FrameworkElement)inlineObject.Element).Parent;
DependencyObject parent = frameworkElement.Parent;
if(parent != null)
{
parentFlowDirection = (FlowDirection)parent.GetValue(FrameworkElement.FlowDirectionProperty);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,10 +160,8 @@ internal Rect GetBoundsFromTextPosition(int characterIndex, out FlowDirection fl
/// it uses those as the bounding rectangles. If not, it returns the rectangle for the first (and only) element
/// of the text bounds.
/// </remarks>
internal List<Rect> GetRangeBounds(int cp, int cch, double xOffset, double yOffset)
{
List<Rect> rectangles = new List<Rect>();

internal ReadOnlySpan<Rect> GetRangeBounds(int cp, int cch, double xOffset, double yOffset)
{
// Adjust x offset for trailing spaces
double delta = CalculateXOffsetShift();
double adjustedXOffset = xOffset + delta;
Expand All @@ -173,24 +171,27 @@ internal List<Rect> GetRangeBounds(int cp, int cch, double xOffset, double yOffs
{
// We should not shift offset in this case
Invariant.Assert(DoubleUtil.AreClose(delta, 0));
System.Windows.Media.TextFormatting.TextLine line = _line.Collapse(GetCollapsingProps(_wrappingWidth, _owner.ParagraphProperties));
TextLine line = _line.Collapse(GetCollapsingProps(_wrappingWidth, _owner.ParagraphProperties));
Invariant.Assert(line.HasCollapsed, "Line has not been collapsed");
textBounds = line.GetTextBounds(cp, cch);
}
else
{
textBounds = _line.GetTextBounds(cp, cch);
}

Invariant.Assert(textBounds.Count > 0);

Rect[] rectangles = new Rect[textBounds.Count];

for (int boundIndex = 0; boundIndex < textBounds.Count; boundIndex++)
for (int boundIndex = 0; boundIndex < rectangles.Length; boundIndex++)
{
Rect rect = textBounds[boundIndex].Rectangle;
rect.X += adjustedXOffset;
rect.Y += yOffset;
rectangles.Add(rect);
rectangles[boundIndex] = rect;
}

return rectangles;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,18 @@
// Enumerator class for returning descendants of TextBlock and FlowDocumentPage
//

using System.Diagnostics;
using System.Collections;
using MS.Internal.Documents;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System;
using System.Diagnostics;

#pragma warning disable 1634, 1691 // suppressing PreSharp warnings

namespace System.Windows.Documents
{
/// <summary>
/// Enumerator class for implementation of IContentHost on TextBlock and FlowDocumentPage.
/// Used to iterate through descendants of the content host
/// </summary>
internal class HostedElements : IEnumerator<IInputElement>
internal sealed class HostedElements : IEnumerator<IInputElement>
{
//-------------------------------------------------------------------
//
Expand Down Expand Up @@ -164,19 +160,13 @@ public IInputElement Current
{
get
{
// Disable PRESharp warning 6503: "Property get methods should not throw exceptions".
// HostedElements must throw exception if Current property is incorrectly accessed
if (_textSegments == null)
{
// Collection was modified
#pragma warning suppress 6503 // IEnumerator.Current is documented to throw this exception
throw new InvalidOperationException(SR.EnumeratorCollectionDisposed);
}

if (_currentPosition == null)
{
// Enumerator not started. Call MoveNext to see if we can move ahead
#pragma warning suppress 6503 // IEnumerator.Current is documented to throw this exception
throw new InvalidOperationException(SR.EnumeratorNotStarted);
}

Expand Down
Loading