Commit 1b8cb43f authored by Dan Backes's avatar Dan Backes Committed by James Welle
Browse files

Performance Improvement

- Because querying the Bounds property of an ISvgBoundable or
  SvgVisualElement is expensive, we introduced an ImmutableBoundable that
  is used by the SvgRenderer. This class stores the bounds of the supplied
  ISvgBoundable so that multiple queries for the bounds do not hamper
  performance when rendering.

- Converted the Bounds property on ISvgBoundable and SvgVisualElement to a
  CalculateBounds method to indicate that it is an expensive operation
  that returns a new value each time it is called.

- Removed redundant ISvgBoundable Location and Size properties.

- Fixed a bug in SvgFragment.Path property by converting it to a method that
  indicates it returns a new GraphicsPath instance when called and by
  disposing of that instance in CalculateBounds. There are many more
  instances of GraphicsPath not being disposed in the code base but we did
  not address that here.
parent a653b4a4
...@@ -78,10 +78,10 @@ namespace Svg ...@@ -78,10 +78,10 @@ namespace Svg
/// <summary> /// <summary>
/// Gets the bounds of the circle. /// Gets the bounds of the circle.
/// </summary> /// </summary>
/// <value>The rectangular bounds of the circle.</value> /// <returns>The rectangular bounds of the circle.</returns>
public override RectangleF Bounds public override RectangleF CalculateBounds()
{ {
get { return this.Path(null).GetBounds(); } return this.Path(null).GetBounds();
} }
/// <summary> /// <summary>
......
...@@ -92,10 +92,10 @@ namespace Svg ...@@ -92,10 +92,10 @@ namespace Svg
/// <summary> /// <summary>
/// Gets the bounds of the element. /// Gets the bounds of the element.
/// </summary> /// </summary>
/// <value>The bounds.</value> /// <returns>The bounds.</returns>
public override RectangleF Bounds public override RectangleF CalculateBounds()
{ {
get { return this.Path(null).GetBounds(); } return this.Path(null).GetBounds();
} }
/// <summary> /// <summary>
......
...@@ -76,21 +76,21 @@ namespace Svg ...@@ -76,21 +76,21 @@ namespace Svg
{ {
get { return this.Attributes.GetAttribute<Uri>("href"); } get { return this.Attributes.GetAttribute<Uri>("href"); }
set { this.Attributes["href"] = value; } set { this.Attributes["href"] = value; }
} }
/// <summary> /// <summary>
/// Gets the bounds of the element. /// Gets the bounds of the element.
/// </summary> /// </summary>
/// <value>The bounds.</value> /// <returns>The bounds.</returns>
public override RectangleF Bounds public override RectangleF CalculateBounds()
{ {
get { return new RectangleF(this.Location.ToDeviceValue(null, this), return new RectangleF(this.Location.ToDeviceValue(null, this),
new SizeF(this.Width.ToDeviceValue(null, UnitRenderingType.Horizontal, this), new SizeF(this.Width.ToDeviceValue(null, UnitRenderingType.Horizontal, this),
this.Height.ToDeviceValue(null, UnitRenderingType.Vertical, this))); } this.Height.ToDeviceValue(null, UnitRenderingType.Vertical, this)));
} }
/// <summary> /// <summary>
/// Gets the <see cref="GraphicsPath"/> for this element. /// Gets the <see cref="GraphicsPath"/> for this element.
/// </summary> /// </summary>
......
...@@ -171,9 +171,9 @@ namespace Svg ...@@ -171,9 +171,9 @@ namespace Svg
return result; return result;
} }
public override System.Drawing.RectangleF Bounds public override RectangleF CalculateBounds()
{ {
get { return this.Path(null).GetBounds(); } return this.Path(null).GetBounds();
} }
public override SvgElement DeepCopy() public override SvgElement DeepCopy()
......
...@@ -130,9 +130,9 @@ namespace Svg ...@@ -130,9 +130,9 @@ namespace Svg
return result; return result;
} }
public override RectangleF Bounds public override RectangleF CalculateBounds()
{ {
get { return this.Path(null).GetBounds(); } return this.Path(null).GetBounds();
} }
......
...@@ -165,10 +165,10 @@ namespace Svg ...@@ -165,10 +165,10 @@ namespace Svg
/// <summary> /// <summary>
/// Gets the bounds of the element. /// Gets the bounds of the element.
/// </summary> /// </summary>
/// <value>The bounds.</value> /// <returns>The bounds.</returns>
public override RectangleF Bounds public override RectangleF CalculateBounds()
{ {
get { return Path(null).GetBounds(); } return Path(null).GetBounds();
} }
/// <summary> /// <summary>
......
...@@ -19,27 +19,11 @@ namespace Svg ...@@ -19,27 +19,11 @@ namespace Svg
/// </summary> /// </summary>
public abstract GraphicsPath Path(ISvgRenderer renderer); public abstract GraphicsPath Path(ISvgRenderer renderer);
PointF ISvgBoundable.Location
{
get
{
return Bounds.Location;
}
}
SizeF ISvgBoundable.Size
{
get
{
return Bounds.Size;
}
}
/// <summary> /// <summary>
/// Gets the bounds of the element. /// Gets the bounds of the element.
/// </summary> /// </summary>
/// <value>The bounds.</value> /// <returns>The bounds.</returns>
public abstract RectangleF Bounds { get; } public abstract RectangleF CalculateBounds();
/// <summary> /// <summary>
/// Gets the associated <see cref="SvgClipPath"/> if one has been specified. /// Gets the associated <see cref="SvgClipPath"/> if one has been specified.
......
...@@ -149,7 +149,7 @@ namespace Svg ...@@ -149,7 +149,7 @@ namespace Svg
break; break;
} }
System.Drawing.SizeF size = boundable.Bounds.Size; System.Drawing.SizeF size = boundable.CalculateBounds().Size;
switch (renderType) switch (renderType)
{ {
...@@ -157,13 +157,13 @@ namespace Svg ...@@ -157,13 +157,13 @@ namespace Svg
_deviceValue = (size.Width / 100) * value; _deviceValue = (size.Width / 100) * value;
break; break;
case UnitRenderingType.HorizontalOffset: case UnitRenderingType.HorizontalOffset:
_deviceValue = (size.Width / 100) * value + boundable.Location.X; _deviceValue = (size.Width / 100) * value + boundable.CalculateBounds().Location.X;
break; break;
case UnitRenderingType.Vertical: case UnitRenderingType.Vertical:
_deviceValue = (size.Height / 100) * value; _deviceValue = (size.Height / 100) * value;
break; break;
case UnitRenderingType.VerticalOffset: case UnitRenderingType.VerticalOffset:
_deviceValue = (size.Height / 100) * value + boundable.Location.Y; _deviceValue = (size.Height / 100) * value + boundable.CalculateBounds().Location.Y;
break; break;
default: default:
_deviceValue = (float)(Math.Sqrt(Math.Pow(size.Width, 2) + Math.Pow(size.Height, 2)) / Math.Sqrt(2) * value / 100.0); _deviceValue = (float)(Math.Sqrt(Math.Pow(size.Width, 2) + Math.Pow(size.Height, 2)) / Math.Sqrt(2) * value / 100.0);
......
...@@ -15,28 +15,9 @@ namespace Svg ...@@ -15,28 +15,9 @@ namespace Svg
/// </summary> /// </summary>
public static readonly Uri Namespace = new Uri("http://www.w3.org/2000/svg"); public static readonly Uri Namespace = new Uri("http://www.w3.org/2000/svg");
PointF ISvgBoundable.Location RectangleF ISvgBoundable.CalculateBounds()
{ {
get return new RectangleF(PointF.Empty, GetDimensions());
{
return PointF.Empty;
}
}
SizeF ISvgBoundable.Size
{
get
{
return GetDimensions();
}
}
RectangleF ISvgBoundable.Bounds
{
get
{
return new RectangleF(((ISvgBoundable)this).Location, ((ISvgBoundable)this).Size);
}
} }
private SvgUnit _x; private SvgUnit _x;
...@@ -185,32 +166,29 @@ namespace Svg ...@@ -185,32 +166,29 @@ namespace Svg
break; break;
} }
} }
/// <summary> /// <summary>
/// Gets the <see cref="GraphicsPath"/> for this element. /// Gets the <see cref="GraphicsPath"/> for this element.
/// </summary> /// </summary>
/// <value></value> /// <value></value>
public GraphicsPath Path public GraphicsPath CreatePath()
{ {
get var path = new GraphicsPath();
{
var path = new GraphicsPath();
AddPaths(this, path); AddPaths(this, path);
return path; return path;
}
} }
/// <summary> /// <summary>
/// Gets the bounds of the svg element. /// Gets the bounds of the svg element.
/// </summary> /// </summary>
/// <value>The bounds.</value> /// <returns>The bounds.</returns>
public RectangleF Bounds public RectangleF CalculateBounds()
{ {
get using (var path = CreatePath())
{ {
return this.Path.GetBounds(); return path.GetBounds();
} }
} }
...@@ -242,7 +220,7 @@ namespace Svg ...@@ -242,7 +220,7 @@ namespace Svg
} }
else else
{ {
bounds = this.Bounds; //do just one call to the recursive bounds property bounds = this.CalculateBounds(); //do just one call to the expensive bounds calculation method
} }
} }
......
...@@ -21,35 +21,32 @@ namespace Svg ...@@ -21,35 +21,32 @@ namespace Svg
/// <summary> /// <summary>
/// Gets the bounds of the element. /// Gets the bounds of the element.
/// </summary> /// </summary>
/// <value>The bounds.</value> /// <returns>The bounds.</returns>
public override System.Drawing.RectangleF Bounds public override RectangleF CalculateBounds()
{ {
get var r = new RectangleF();
{ foreach (var c in this.Children)
var r = new RectangleF(); {
foreach(var c in this.Children) if (c is SvgVisualElement)
{ {
if (c is SvgVisualElement) // First it should check if rectangle is empty or it will return the wrong Bounds.
// This is because when the Rectangle is Empty, the Union method adds as if the first values where X=0, Y=0
if (r.IsEmpty)
{ {
// First it should check if rectangle is empty or it will return the wrong Bounds. r = ((SvgVisualElement) c).CalculateBounds();
// This is because when the Rectangle is Empty, the Union method adds as if the first values where X=0, Y=0 }
if (r.IsEmpty) else
{ {
r = ((SvgVisualElement)c).Bounds; var childBounds = ((SvgVisualElement) c).CalculateBounds();
} if (!childBounds.IsEmpty)
else
{ {
var childBounds = ((SvgVisualElement)c).Bounds; r = RectangleF.Union(r, childBounds);
if (!childBounds.IsEmpty)
{
r = RectangleF.Union(r, childBounds);
}
} }
} }
} }
return r;
} }
return r;
} }
protected override bool Renderable { get { return false; } } protected override bool Renderable { get { return false; } }
......
...@@ -25,35 +25,32 @@ namespace Svg ...@@ -25,35 +25,32 @@ namespace Svg
/// <summary> /// <summary>
/// Gets the bounds of the element. /// Gets the bounds of the element.
/// </summary> /// </summary>
/// <value>The bounds.</value> /// <returns>The bounds.</returns>
public override System.Drawing.RectangleF Bounds public override RectangleF CalculateBounds()
{ {
get var r = new RectangleF();
foreach (var c in this.Children)
{ {
var r = new RectangleF(); if (c is SvgVisualElement)
foreach (var c in this.Children)
{ {
if (c is SvgVisualElement) // First it should check if rectangle is empty or it will return the wrong Bounds.
// This is because when the Rectangle is Empty, the Union method adds as if the first values where X=0, Y=0
if (r.IsEmpty)
{ {
// First it should check if rectangle is empty or it will return the wrong Bounds. r = ((SvgVisualElement) c).CalculateBounds();
// This is because when the Rectangle is Empty, the Union method adds as if the first values where X=0, Y=0 }
if (r.IsEmpty) else
{ {
r = ((SvgVisualElement)c).Bounds; var childBounds = ((SvgVisualElement) c).CalculateBounds();
} if (!childBounds.IsEmpty)
else
{ {
var childBounds = ((SvgVisualElement)c).Bounds; r = RectangleF.Union(r, childBounds);
if (!childBounds.IsEmpty)
{
r = RectangleF.Union(r, childBounds);
}
} }
} }
} }
return r;
} }
return r;
} }
/// <summary> /// <summary>
......
...@@ -48,34 +48,31 @@ namespace Svg.Document_Structure ...@@ -48,34 +48,31 @@ namespace Svg.Document_Structure
/// Gets the bounds of the element. /// Gets the bounds of the element.
/// </summary> /// </summary>
/// <value>The bounds.</value> /// <value>The bounds.</value>
public override System.Drawing.RectangleF Bounds public override System.Drawing.RectangleF CalculateBounds()
{ {
get var r = new RectangleF();
foreach (var c in this.Children)
{ {
var r = new RectangleF(); if (c is SvgVisualElement)
foreach (var c in this.Children)
{ {
if (c is SvgVisualElement) // First it should check if rectangle is empty or it will return the wrong Bounds.
// This is because when the Rectangle is Empty, the Union method adds as if the first values where X=0, Y=0
if (r.IsEmpty)
{ {
// First it should check if rectangle is empty or it will return the wrong Bounds. r = ((SvgVisualElement)c).CalculateBounds();
// This is because when the Rectangle is Empty, the Union method adds as if the first values where X=0, Y=0 }
if (r.IsEmpty) else
{ {
r = ((SvgVisualElement)c).Bounds; var childBounds = ((SvgVisualElement)c).CalculateBounds();
} if (!childBounds.IsEmpty)
else
{ {
var childBounds = ((SvgVisualElement)c).Bounds; r = RectangleF.Union(r, childBounds);
if (!childBounds.IsEmpty)
{
r = RectangleF.Union(r, childBounds);
}
} }
} }
} }
return r;
} }
return r;
} }
protected override bool Renderable { get { return false; } } protected override bool Renderable { get { return false; } }
......
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Drawing;
using System.Text; using System.Text;
using System.Web; using System.Web;
using System.Xml; using System.Xml;
...@@ -61,9 +62,9 @@ namespace Svg ...@@ -61,9 +62,9 @@ namespace Svg
return (element != null) ? element.Path(renderer) : null; return (element != null) ? element.Path(renderer) : null;
} }
public override System.Drawing.RectangleF Bounds public override RectangleF CalculateBounds()
{ {
get { return new System.Drawing.RectangleF(); } return new System.Drawing.RectangleF();
} }
protected override bool Renderable { get { return false; } } protected override bool Renderable { get { return false; } }
......
...@@ -25,35 +25,32 @@ namespace Svg ...@@ -25,35 +25,32 @@ namespace Svg
/// <summary> /// <summary>
/// Gets the bounds of the element. /// Gets the bounds of the element.
/// </summary> /// </summary>
/// <value>The bounds.</value> /// <returns>The bounds.</returns>
public override System.Drawing.RectangleF Bounds public override RectangleF CalculateBounds()
{ {
get var r = new RectangleF();
foreach (var c in this.Children)
{ {
var r = new RectangleF(); if (c is SvgVisualElement)
foreach (var c in this.Children)
{ {
if (c is SvgVisualElement) // First it should check if rectangle is empty or it will return the wrong Bounds.
// This is because when the Rectangle is Empty, the Union method adds as if the first values where X=0, Y=0
if (r.IsEmpty)
{ {
// First it should check if rectangle is empty or it will return the wrong Bounds. r = ((SvgVisualElement) c).CalculateBounds();
// This is because when the Rectangle is Empty, the Union method adds as if the first values where X=0, Y=0 }
if (r.IsEmpty) else
{ {
r = ((SvgVisualElement)c).Bounds; var childBounds = ((SvgVisualElement) c).CalculateBounds();
} if (!childBounds.IsEmpty)
else
{ {
var childBounds = ((SvgVisualElement)c).Bounds; r = RectangleF.Union(r, childBounds);
if (!childBounds.IsEmpty)
{
r = RectangleF.Union(r, childBounds);
}
} }
} }
} }
return r;
} }
return r;
} }
protected override bool Renderable { get { return false; } } protected override bool Renderable { get { return false; } }
......
...@@ -19,19 +19,9 @@ namespace Svg ...@@ -19,19 +19,9 @@ namespace Svg
_rect = new RectangleF(x, y, width, height); _rect = new RectangleF(x, y, width, height);
} }
public System.Drawing.PointF Location public RectangleF CalculateBounds()
{ {
get { return _rect.Location; } return _rect;
}
public System.Drawing.SizeF Size
{
get { return _rect.Size; }
}
public System.Drawing.RectangleF Bounds
{
get { return _rect; }
} }
} }
} }
...@@ -4,19 +4,6 @@ namespace Svg ...@@ -4,19 +4,6 @@ namespace Svg
{ {
public interface ISvgBoundable public interface ISvgBoundable
{ {
PointF Location RectangleF CalculateBounds();
{
get;
}
SizeF Size
{
get;
}
RectangleF Bounds
{
get;
}
} }
} }
\ No newline at end of file
using System.Drawing;
namespace Svg
{
internal sealed class ImmutableBoundable : ISvgBoundable
{
private readonly RectangleF bounds;
public ImmutableBoundable(ISvgBoundable boundable)
{
bounds = boundable.CalculateBounds();
}
public RectangleF CalculateBounds()
{
return bounds;
}
}
}
\ No newline at end of file
...@@ -178,7 +178,7 @@ namespace Svg ...@@ -178,7 +178,7 @@ namespace Svg
for (int i = 0; i < colourBlends; i++) for (int i = 0; i < colourBlends; i++)
{ {
var currentStop = this.Stops[radial ? this.Stops.Count - 1 - actualStops : actualStops]; var currentStop = this.Stops[radial ? this.Stops.Count - 1 - actualStops : actualStops];
var boundWidth = renderer.GetBoundable().Bounds.Width; var boundWidth = renderer.GetBoundable().CalculateBounds().Width;
mergedOpacity = opacity * currentStop.GetOpacity(); mergedOpacity = opacity * currentStop.GetOpacity();
position = position =
......
...@@ -191,7 +191,7 @@ namespace Svg ...@@ -191,7 +191,7 @@ namespace Svg
private LinePoints PointsToMove(ISvgBoundable boundable, PointF specifiedStart, PointF specifiedEnd) private LinePoints PointsToMove(ISvgBoundable boundable, PointF specifiedStart, PointF specifiedEnd)
{ {
var bounds = boundable.Bounds; var bounds = boundable.CalculateBounds();
if (specifiedStart.X == specifiedEnd.X) if (specifiedStart.X == specifiedEnd.X)
{ {
return (bounds.Top < specifiedStart.Y && specifiedStart.Y < bounds.Bottom ? LinePoints.Start : LinePoints.None) | return (bounds.Top < specifiedStart.Y && specifiedStart.Y < bounds.Bottom ? LinePoints.Start : LinePoints.None) |
...@@ -227,7 +227,7 @@ namespace Svg ...@@ -227,7 +227,7 @@ namespace Svg
return new GradientPoints(specifiedStart, specifiedEnd); return new GradientPoints(specifiedStart, specifiedEnd);
} }
var bounds = boundable.Bounds; var bounds = boundable.CalculateBounds();
var effectiveStart = specifiedStart; var effectiveStart = specifiedStart;
var effectiveEnd = specifiedEnd; var effectiveEnd = specifiedEnd;
var intersectionPoints = CandidateIntersections(bounds, specifiedStart, specifiedEnd); var intersectionPoints = CandidateIntersections(bounds, specifiedStart, specifiedEnd);
......
...@@ -95,15 +95,12 @@ namespace Svg ...@@ -95,15 +95,12 @@ namespace Svg
return null; return null;
} }
public override System.Drawing.RectangleF Bounds public override RectangleF CalculateBounds()
{ {
get var path = this.Path(null);
{ if (path != null)
var path = this.Path(null); return path.GetBounds();
if (path != null) return new System.Drawing.RectangleF();
return path.GetBounds();
return new System.Drawing.RectangleF();
}
} }
public override SvgElement DeepCopy() public override SvgElement DeepCopy()
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment