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
/// <summary>
/// Gets the bounds of the circle.
/// </summary>
/// <value>The rectangular bounds of the circle.</value>
public override RectangleF Bounds
/// <returns>The rectangular bounds of the circle.</returns>
public override RectangleF CalculateBounds()
{
get { return this.Path(null).GetBounds(); }
return this.Path(null).GetBounds();
}
/// <summary>
......
......@@ -92,10 +92,10 @@ namespace Svg
/// <summary>
/// Gets the bounds of the element.
/// </summary>
/// <value>The bounds.</value>
public override RectangleF Bounds
/// <returns>The bounds.</returns>
public override RectangleF CalculateBounds()
{
get { return this.Path(null).GetBounds(); }
return this.Path(null).GetBounds();
}
/// <summary>
......
......@@ -76,21 +76,21 @@ namespace Svg
{
get { return this.Attributes.GetAttribute<Uri>("href"); }
set { this.Attributes["href"] = value; }
}
/// <summary>
/// Gets the bounds of the element.
/// </summary>
/// <value>The bounds.</value>
public override RectangleF Bounds
{
get { return new RectangleF(this.Location.ToDeviceValue(null, this),
new SizeF(this.Width.ToDeviceValue(null, UnitRenderingType.Horizontal, this),
this.Height.ToDeviceValue(null, UnitRenderingType.Vertical, this))); }
}
}
/// <summary>
/// Gets the bounds of the element.
/// </summary>
/// <returns>The bounds.</returns>
public override RectangleF CalculateBounds()
{
return new RectangleF(this.Location.ToDeviceValue(null, this),
new SizeF(this.Width.ToDeviceValue(null, UnitRenderingType.Horizontal, this),
this.Height.ToDeviceValue(null, UnitRenderingType.Vertical, this)));
}
/// <summary>
/// Gets the <see cref="GraphicsPath"/> for this element.
/// </summary>
......
......@@ -171,9 +171,9 @@ namespace Svg
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()
......
......@@ -130,9 +130,9 @@ namespace Svg
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
/// <summary>
/// Gets the bounds of the element.
/// </summary>
/// <value>The bounds.</value>
public override RectangleF Bounds
/// <returns>The bounds.</returns>
public override RectangleF CalculateBounds()
{
get { return Path(null).GetBounds(); }
return Path(null).GetBounds();
}
/// <summary>
......
......@@ -19,27 +19,11 @@ namespace Svg
/// </summary>
public abstract GraphicsPath Path(ISvgRenderer renderer);
PointF ISvgBoundable.Location
{
get
{
return Bounds.Location;
}
}
SizeF ISvgBoundable.Size
{
get
{
return Bounds.Size;
}
}
/// <summary>
/// Gets the bounds of the element.
/// </summary>
/// <value>The bounds.</value>
public abstract RectangleF Bounds { get; }
/// <returns>The bounds.</returns>
public abstract RectangleF CalculateBounds();
/// <summary>
/// Gets the associated <see cref="SvgClipPath"/> if one has been specified.
......
......@@ -149,7 +149,7 @@ namespace Svg
break;
}
System.Drawing.SizeF size = boundable.Bounds.Size;
System.Drawing.SizeF size = boundable.CalculateBounds().Size;
switch (renderType)
{
......@@ -157,13 +157,13 @@ namespace Svg
_deviceValue = (size.Width / 100) * value;
break;
case UnitRenderingType.HorizontalOffset:
_deviceValue = (size.Width / 100) * value + boundable.Location.X;
_deviceValue = (size.Width / 100) * value + boundable.CalculateBounds().Location.X;
break;
case UnitRenderingType.Vertical:
_deviceValue = (size.Height / 100) * value;
break;
case UnitRenderingType.VerticalOffset:
_deviceValue = (size.Height / 100) * value + boundable.Location.Y;
_deviceValue = (size.Height / 100) * value + boundable.CalculateBounds().Location.Y;
break;
default:
_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
/// </summary>
public static readonly Uri Namespace = new Uri("http://www.w3.org/2000/svg");
PointF ISvgBoundable.Location
RectangleF ISvgBoundable.CalculateBounds()
{
get
{
return PointF.Empty;
}
}
SizeF ISvgBoundable.Size
{
get
{
return GetDimensions();
}
}
RectangleF ISvgBoundable.Bounds
{
get
{
return new RectangleF(((ISvgBoundable)this).Location, ((ISvgBoundable)this).Size);
}
return new RectangleF(PointF.Empty, GetDimensions());
}
private SvgUnit _x;
......@@ -185,32 +166,29 @@ namespace Svg
break;
}
}
/// <summary>
/// Gets the <see cref="GraphicsPath"/> for this element.
/// </summary>
/// <value></value>
public GraphicsPath Path
public GraphicsPath CreatePath()
{
get
{
var path = new GraphicsPath();
var path = new GraphicsPath();
AddPaths(this, path);
return path;
}
AddPaths(this, path);
return path;
}
/// <summary>
/// Gets the bounds of the svg element.
/// </summary>
/// <value>The bounds.</value>
public RectangleF Bounds
{
get
/// <returns>The bounds.</returns>
public RectangleF CalculateBounds()
{
using (var path = CreatePath())
{
return this.Path.GetBounds();
return path.GetBounds();
}
}
......@@ -242,7 +220,7 @@ namespace Svg
}
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
/// <summary>
/// Gets the bounds of the element.
/// </summary>
/// <value>The bounds.</value>
public override System.Drawing.RectangleF Bounds
/// <returns>The bounds.</returns>
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.
// 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)
{
r = ((SvgVisualElement)c).Bounds;
}
else
r = ((SvgVisualElement) c).CalculateBounds();
}
else
{
var childBounds = ((SvgVisualElement) c).CalculateBounds();
if (!childBounds.IsEmpty)
{
var childBounds = ((SvgVisualElement)c).Bounds;
if (!childBounds.IsEmpty)
{
r = RectangleF.Union(r, childBounds);
}
r = RectangleF.Union(r, childBounds);
}
}
}
return r;
}
return r;
}
protected override bool Renderable { get { return false; } }
......
......@@ -25,35 +25,32 @@ namespace Svg
/// <summary>
/// Gets the bounds of the element.
/// </summary>
/// <value>The bounds.</value>
public override System.Drawing.RectangleF Bounds
/// <returns>The bounds.</returns>
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.
// 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)
{
r = ((SvgVisualElement)c).Bounds;
}
else
r = ((SvgVisualElement) c).CalculateBounds();
}
else
{
var childBounds = ((SvgVisualElement) c).CalculateBounds();
if (!childBounds.IsEmpty)
{
var childBounds = ((SvgVisualElement)c).Bounds;
if (!childBounds.IsEmpty)
{
r = RectangleF.Union(r, childBounds);
}
r = RectangleF.Union(r, childBounds);
}
}
}
return r;
}
return r;
}
/// <summary>
......
......@@ -48,34 +48,31 @@ namespace Svg.Document_Structure
/// Gets the bounds of the element.
/// </summary>
/// <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();
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.
// 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)
{
r = ((SvgVisualElement)c).Bounds;
}
else
r = ((SvgVisualElement)c).CalculateBounds();
}
else
{
var childBounds = ((SvgVisualElement)c).CalculateBounds();
if (!childBounds.IsEmpty)
{
var childBounds = ((SvgVisualElement)c).Bounds;
if (!childBounds.IsEmpty)
{
r = RectangleF.Union(r, childBounds);
}
r = RectangleF.Union(r, childBounds);
}
}
}
return r;
}
return r;
}
protected override bool Renderable { get { return false; } }
......
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Text;
using System.Web;
using System.Xml;
......@@ -61,9 +62,9 @@ namespace Svg
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; } }
......
......@@ -25,35 +25,32 @@ namespace Svg
/// <summary>
/// Gets the bounds of the element.
/// </summary>
/// <value>The bounds.</value>
public override System.Drawing.RectangleF Bounds
/// <returns>The bounds.</returns>
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.
// 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)
{
r = ((SvgVisualElement)c).Bounds;
}
else
r = ((SvgVisualElement) c).CalculateBounds();
}
else
{
var childBounds = ((SvgVisualElement) c).CalculateBounds();
if (!childBounds.IsEmpty)
{
var childBounds = ((SvgVisualElement)c).Bounds;
if (!childBounds.IsEmpty)
{
r = RectangleF.Union(r, childBounds);
}
r = RectangleF.Union(r, childBounds);
}
}
}
return r;
}
return r;
}
protected override bool Renderable { get { return false; } }
......
......@@ -19,19 +19,9 @@ namespace Svg
_rect = new RectangleF(x, y, width, height);
}
public System.Drawing.PointF Location
public RectangleF CalculateBounds()
{
get { return _rect.Location; }
}
public System.Drawing.SizeF Size
{
get { return _rect.Size; }
}
public System.Drawing.RectangleF Bounds
{
get { return _rect; }
return _rect;
}
}
}
......@@ -4,19 +4,6 @@ namespace Svg
{
public interface ISvgBoundable
{
PointF Location
{
get;
}
SizeF Size
{
get;
}
RectangleF Bounds
{
get;
}
RectangleF CalculateBounds();
}
}
\ 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
for (int i = 0; i < colourBlends; i++)
{
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();
position =
......
......@@ -191,7 +191,7 @@ namespace Svg
private LinePoints PointsToMove(ISvgBoundable boundable, PointF specifiedStart, PointF specifiedEnd)
{
var bounds = boundable.Bounds;
var bounds = boundable.CalculateBounds();
if (specifiedStart.X == specifiedEnd.X)
{
return (bounds.Top < specifiedStart.Y && specifiedStart.Y < bounds.Bottom ? LinePoints.Start : LinePoints.None) |
......@@ -227,7 +227,7 @@ namespace Svg
return new GradientPoints(specifiedStart, specifiedEnd);
}
var bounds = boundable.Bounds;
var bounds = boundable.CalculateBounds();
var effectiveStart = specifiedStart;
var effectiveEnd = specifiedEnd;
var intersectionPoints = CandidateIntersections(bounds, specifiedStart, specifiedEnd);
......
......@@ -95,15 +95,12 @@ namespace Svg
return null;
}
public override System.Drawing.RectangleF Bounds
public override RectangleF CalculateBounds()
{
get
{
var path = this.Path(null);
if (path != null)
return path.GetBounds();
return new System.Drawing.RectangleF();
}
var path = this.Path(null);
if (path != null)
return path.GetBounds();
return new System.Drawing.RectangleF();
}
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