Commit 76fbb491 authored by davescriven's avatar davescriven
Browse files

- Fixed #5650: Support SVG element "clipPath"

- Refactored SvgElementFactory to use the new SvgElementAttribute to get available elements.
parent c912ea85
......@@ -12,6 +12,7 @@ namespace Svg
/// <summary>
/// An SVG element to render circles to the document.
/// </summary>
[SvgElement("circle")]
public class SvgCircle : SvgVisualElement
{
private GraphicsPath _path;
......
......@@ -11,6 +11,7 @@ namespace Svg
/// <summary>
/// Represents and SVG ellipse element.
/// </summary>
[SvgElement("ellipse")]
public class SvgEllipse : SvgVisualElement
{
private SvgUnit _radiusX;
......
......@@ -10,6 +10,7 @@ namespace Svg
/// <summary>
/// Represents and SVG line element.
/// </summary>
[SvgElement("line")]
public class SvgLine : SvgVisualElement
{
private SvgUnit _startX;
......
......@@ -11,6 +11,7 @@ namespace Svg
/// <summary>
/// SvgPolygon defines a closed shape consisting of a set of connected straight line segments.
/// </summary>
[SvgElement("polygon")]
public class SvgPolygon : SvgVisualElement
{
protected GraphicsPath _path;
......
......@@ -10,6 +10,7 @@ namespace Svg
/// <summary>
/// SvgPolyline defines a set of connected straight line segments. Typically, <see cref="SvgPolyline"/> defines open shapes.
/// </summary>
[SvgElement("polyline")]
public class SvgPolyline : SvgPolygon
{
public override GraphicsPath Path
......
......@@ -7,6 +7,7 @@ namespace Svg
/// <summary>
/// Represents and SVG rectangle that could also have reounded edges.
/// </summary>
[SvgElement("rect")]
public class SvgRectangle : SvgVisualElement
{
private SvgUnit _cornerRadiusX;
......
......@@ -18,6 +18,7 @@ namespace Svg
private bool _dirty;
private bool _requiresSmoothRendering;
private Region _previousClip;
private SvgClipRule clipRule = SvgClipRule.NonZero;
/// <summary>
/// Gets the <see cref="GraphicsPath"/> for this element.
......@@ -51,6 +52,16 @@ namespace Svg
set { this.Attributes["clip-path"] = value; }
}
/// <summary>
///
/// </summary>
[SvgAttribute("clip-rule")]
public SvgClipRule ClipRule
{
get { return this.Attributes.GetAttribute<SvgClipRule>("clip-rule", SvgClipRule.NonZero); }
set { this.Attributes["clip-rule"] = value; }
}
/// <summary>
/// Gets or sets a value to determine if anti-aliasing should occur when the element is being rendered.
/// </summary>
......@@ -143,7 +154,7 @@ namespace Svg
if (clipPath != null)
{
renderer.SetClip(clipPath.GetClipRegion());
renderer.Clip = clipPath.GetClipRegion(this);
}
}
}
......@@ -154,9 +165,9 @@ namespace Svg
/// <param name="renderer">The <see cref="SvgRenderer"/> to have its clipping region reset.</param>
protected internal virtual void ResetClip(SvgRenderer renderer)
{
if (this.ClipPath != null)
if (this._previousClip != null)
{
renderer.SetClip(this._previousClip);
renderer.Clip = this._previousClip;
this._previousClip = null;
}
}
......
......@@ -16,6 +16,10 @@ namespace Svg
/// </summary>
Uri ClipPath { get; set; }
/// <summary>
/// Specifies the rule used to define the clipping region when the element is within a <see cref="SvgClipPath"/>.
/// </summary>
SvgClipRule ClipRule { get; set; }
/// <summary>
/// Sets the clipping region of the specified <see cref="SvgRenderer"/>.
/// </summary>
/// <param name="renderer">The <see cref="SvgRenderer"/> to have its clipping region set.</param>
......
......@@ -7,51 +7,46 @@ using System.Drawing.Drawing2D;
namespace Svg
{
/// <summary>
///
/// Defines a path that can be used by other <see cref="ISvgClipable"/> elements.
/// </summary>
[SvgElement("clipPath")]
public sealed class SvgClipPath : SvgElement
{
private SvgCoordinateUnits _clipPathUnits;
private bool _pathDirty;
private Region _region;
private bool _pathDirty = true;
/// <summary>
///
/// Specifies the coordinate system for the clipping path.
/// </summary>
[SvgAttribute("clipPathUnits")]
public SvgCoordinateUnits ClipPathUnits
{
get { return this._clipPathUnits; }
set { this._clipPathUnits = value; }
}
public SvgCoordinateUnits ClipPathUnits { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="SvgClipPath"/> class.
/// </summary>
public SvgClipPath()
{
this._clipPathUnits = SvgCoordinateUnits.ObjectBoundingBox;
this.ClipPathUnits = SvgCoordinateUnits.ObjectBoundingBox;
}
/// <summary>
/// Gets this <see cref="SvgClipPath"/>'s region to be clipped.
/// Gets this <see cref="SvgClipPath"/>'s region to be used as a clipping region.
/// </summary>
/// <returns>A new <see cref="Region"/> containing the area to be clipped.</returns>
protected internal Region GetClipRegion()
{
if (_region == null || _pathDirty)
/// <returns>A new <see cref="Region"/> containing the <see cref="Region"/> to be used for clipping.</returns>
public Region GetClipRegion(SvgVisualElement owner)
{
_region = new Region();
var path = new GraphicsPath();
if (this._pathDirty)
{
foreach (SvgElement element in this.Children)
{
ComplementRegion(_region, element);
this.CombinePaths(path, element);
}
_pathDirty = false;
this._pathDirty = false;
}
return _region;
return new Region(path);
}
/// <summary>
......@@ -59,27 +54,39 @@ namespace Svg
/// </summary>
/// <param name="region"></param>
/// <param name="element"></param>
private void ComplementRegion(Region region, SvgElement element)
private void CombinePaths(GraphicsPath path, SvgElement element)
{
SvgVisualElement graphicsElement = element as SvgVisualElement;
var graphicsElement = element as SvgVisualElement;
if (graphicsElement != null && graphicsElement.Path != null)
{
region.Complement(graphicsElement.Path);
path.FillMode = (graphicsElement.ClipRule == SvgClipRule.NonZero) ? FillMode.Winding : FillMode.Alternate;
path.AddPath(graphicsElement.Path, false);
}
foreach (SvgElement child in element.Children)
{
ComplementRegion(region, child);
this.CombinePaths(path, child);
}
}
/// <summary>
/// Called by the underlying <see cref="SvgElement"/> when an element has been added to the
/// <see cref="Children"/> collection.
/// </summary>
/// <param name="child">The <see cref="SvgElement"/> that has been added.</param>
/// <param name="index">An <see cref="int"/> representing the index where the element was added to the collection.</param>
protected override void AddElement(SvgElement child, int index)
{
base.AddElement(child, index);
this._pathDirty = true;
}
/// <summary>
/// Called by the underlying <see cref="SvgElement"/> when an element has been removed from the
/// <see cref="Children"/> collection.
/// </summary>
/// <param name="child">The <see cref="SvgElement"/> that has been removed.</param>
protected override void RemoveElement(SvgElement child)
{
base.RemoveElement(child);
......
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Svg
{
/// <summary>
/// Indicates the algorithm which is to be used to determine the clipping region.
/// </summary>
/// <remarks>
/// <para>This rule determines the "insideness" of a point on the canvas by drawing a ray from
/// that point to infinity in any direction and then examining the places where a segment of the
/// shape crosses the ray.</para>
/// </remarks>
public enum SvgClipRule
{
/// <summary>
/// This rule determines the "insideness" of a point on the canvas by drawing a ray from that point to infinity in any direction and then examining the places where a segment of the shape crosses the ray. Starting with a count of zero, add one each time a path segment crosses the ray from left to right and subtract one each time a path segment crosses the ray from right to left. After counting the crossings, if the result is zero then the point is outside the path. Otherwise, it is inside.
/// </summary>
NonZero,
/// <summary>
/// This rule determines the "insideness" of a point on the canvas by drawing a ray from that point to infinity in any direction and counting the number of path segments from the given shape that the ray crosses. If this number is odd, the point is inside; if even, the point is outside.
/// </summary>
EvenOdd
}
}
\ No newline at end of file
......@@ -120,6 +120,7 @@ namespace Svg
if (styleOwner == null)
{
_deviceValue = this.Value;
break;
}
// TODO : Support height percentages
......
......@@ -7,6 +7,7 @@ namespace Svg
/// <summary>
/// Represents a list of re-usable SVG components.
/// </summary>
[SvgElement("defs")]
public class SvgDefinitionList : SvgElement
{
/// <summary>
......
......@@ -6,6 +6,7 @@ using System.ComponentModel;
namespace Svg
{
[DefaultProperty("Text")]
[SvgElement("desc")]
public class SvgDescription : SvgElement
{
private string _text;
......
......@@ -11,6 +11,7 @@ namespace Svg
/// <summary>
/// An <see cref="SvgFragment"/> represents an SVG fragment that can be the root element or an embedded fragment of an SVG document.
/// </summary>
[SvgElement("svg")]
public class SvgFragment : SvgElement, ISvgViewPort
{
private SvgUnit _width;
......
......@@ -9,6 +9,7 @@ namespace Svg
/// <summary>
/// An element used to group SVG shapes.
/// </summary>
[SvgElement("g")]
public class SvgGroup : SvgVisualElement
{
public SvgGroup()
......@@ -51,7 +52,9 @@ namespace Svg
protected override void Render(SvgRenderer renderer)
{
this.PushTransforms(renderer);
this.SetClip(renderer);
base.RenderChildren(renderer);
this.ResetClip(renderer);
this.PopTransforms(renderer);
}
}
......
......@@ -8,6 +8,7 @@ using System.Drawing.Drawing2D;
namespace Svg
{
[SvgElement("use")]
public class SvgUse : SvgVisualElement
{
private Uri _referencedElement;
......@@ -33,15 +34,21 @@ namespace Svg
set { this.Attributes["y"] = value; }
}
/// <summary>
/// Applies the required transforms to <see cref="SvgRenderer"/>.
/// </summary>
/// <param name="renderer">The <see cref="SvgRenderer"/> to be transformed.</param>
protected internal override void PushTransforms(SvgRenderer renderer)
{
base.PushTransforms(renderer);
renderer.TranslateTransform(this.X.ToDeviceValue(this), this.Y.ToDeviceValue(this, true));
}
/// <summary>
/// Initializes a new instance of the <see cref="SvgUse"/> class.
/// </summary>
public SvgUse()
{
}
public override System.Drawing.Drawing2D.GraphicsPath Path
......
......@@ -26,6 +26,12 @@ namespace Svg
this._stops = new List<SvgGradientStop>();
}
/// <summary>
/// Called by the underlying <see cref="SvgElement"/> when an element has been added to the
/// <see cref="Children"/> collection.
/// </summary>
/// <param name="child">The <see cref="SvgElement"/> that has been added.</param>
/// <param name="index">An <see cref="int"/> representing the index where the element was added to the collection.</param>
protected override void AddElement(SvgElement child, int index)
{
if (child is SvgGradientStop)
......@@ -36,18 +42,32 @@ namespace Svg
base.AddElement(child, index);
}
/// <summary>
/// Called by the underlying <see cref="SvgElement"/> when an element has been removed from the
/// <see cref="Children"/> collection.
/// </summary>
/// <param name="child">The <see cref="SvgElement"/> that has been removed.</param>
protected override void RemoveElement(SvgElement child)
{
if (child is SvgGradientStop)
this.Stops.Add((SvgGradientStop)child);
{
this.Stops.Remove((SvgGradientStop)child);
}
base.RemoveElement(child);
}
/// <summary>
/// Gets the ramp of colors to use on a gradient.
/// </summary>
public List<SvgGradientStop> Stops
{
get { return this._stops; }
}
/// <summary>
/// Specifies what happens if the gradient starts or ends inside the bounds of the target rectangle.
/// </summary>
[SvgAttribute("spreadMethod")]
public SvgGradientSpreadMethod SpreadMethod
{
......@@ -55,6 +75,9 @@ namespace Svg
set { this._spreadMethod = value; }
}
/// <summary>
/// Gets or sets the coordinate system of the gradient.
/// </summary>
[SvgAttribute("gradientUnits")]
public SvgCoordinateUnits GradientUnits
{
......@@ -68,6 +91,11 @@ namespace Svg
set { this._inheritGradient = value; }
}
/// <summary>
/// Gets a <see cref="ColourBlend"/> representing the <see cref="SvgGradientServer"/>'s gradient stops.
/// </summary>
/// <param name="owner">The parent <see cref="SvgVisualElement"/>.</param>
/// <param name="opacity">The opacity of the colour blend.</param>
protected ColorBlend GetColourBlend(SvgVisualElement owner, float opacity)
{
ColorBlend blend = new ColorBlend();
......@@ -138,7 +166,9 @@ namespace Svg
List<SvgGradientStop> stops = new List<SvgGradientStop>();
if (this.Stops.Count > 0)
{
return stops;
}
if (this.InheritGradient != null)
{
......@@ -148,7 +178,5 @@ namespace Svg
return stops;
}
}
}
\ No newline at end of file
......@@ -9,6 +9,7 @@ namespace Svg
/// <summary>
/// Represents a colour stop in a gradient.
/// </summary>
[SvgElement("stop")]
public class SvgGradientStop : SvgElement
{
private SvgUnit _offset;
......
......@@ -7,6 +7,7 @@ using System.ComponentModel;
namespace Svg
{
[SvgElement("linearGradient")]
public sealed class SvgLinearGradientServer : SvgGradientServer
{
private SvgUnit _x1;
......
......@@ -7,23 +7,46 @@ using System.Drawing.Drawing2D;
namespace Svg
{
/// <summary>
/// Represents the base class for all paint servers that are intended to be used as a fill or stroke.
/// </summary>
[TypeConverter(typeof(SvgPaintServerFactory))]
public abstract class SvgPaintServer : SvgElement
{
/// <summary>
/// An unspecified <see cref="SvgPaintServer"/>.
/// </summary>
public static readonly SvgPaintServer None = new SvgColourServer();
/// <summary>
/// Initializes a new instance of the <see cref="SvgPaintServer"/> class.
/// </summary>
public SvgPaintServer()
{
}
/// <summary>
/// Renders the <see cref="SvgElement"/> and contents to the specified <see cref="SvgRenderer"/> object.
/// </summary>
/// <param name="renderer">The <see cref="SvgRenderer"/> object to render to.</param>
protected override void Render(SvgRenderer renderer)
{
// Never render paint servers or their children
}
/// <summary>
/// Gets a <see cref="Brush"/> representing the current paint server.
/// </summary>
/// <param name="styleOwner">The owner <see cref="SvgVisualElement"/>.</param>
/// <param name="opacity">The opacity of the brush.</param>
public abstract Brush GetBrush(SvgVisualElement styleOwner, float opacity);
/// <summary>
/// Returns a <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
/// </summary>
/// <returns>
/// A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
/// </returns>
public override string ToString()
{
return String.Format("url(#{0})", this.ID);
......
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