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 ...@@ -12,6 +12,7 @@ namespace Svg
/// <summary> /// <summary>
/// An SVG element to render circles to the document. /// An SVG element to render circles to the document.
/// </summary> /// </summary>
[SvgElement("circle")]
public class SvgCircle : SvgVisualElement public class SvgCircle : SvgVisualElement
{ {
private GraphicsPath _path; private GraphicsPath _path;
......
...@@ -11,6 +11,7 @@ namespace Svg ...@@ -11,6 +11,7 @@ namespace Svg
/// <summary> /// <summary>
/// Represents and SVG ellipse element. /// Represents and SVG ellipse element.
/// </summary> /// </summary>
[SvgElement("ellipse")]
public class SvgEllipse : SvgVisualElement public class SvgEllipse : SvgVisualElement
{ {
private SvgUnit _radiusX; private SvgUnit _radiusX;
......
...@@ -10,6 +10,7 @@ namespace Svg ...@@ -10,6 +10,7 @@ namespace Svg
/// <summary> /// <summary>
/// Represents and SVG line element. /// Represents and SVG line element.
/// </summary> /// </summary>
[SvgElement("line")]
public class SvgLine : SvgVisualElement public class SvgLine : SvgVisualElement
{ {
private SvgUnit _startX; private SvgUnit _startX;
......
...@@ -11,6 +11,7 @@ namespace Svg ...@@ -11,6 +11,7 @@ namespace Svg
/// <summary> /// <summary>
/// SvgPolygon defines a closed shape consisting of a set of connected straight line segments. /// SvgPolygon defines a closed shape consisting of a set of connected straight line segments.
/// </summary> /// </summary>
[SvgElement("polygon")]
public class SvgPolygon : SvgVisualElement public class SvgPolygon : SvgVisualElement
{ {
protected GraphicsPath _path; protected GraphicsPath _path;
......
...@@ -10,6 +10,7 @@ namespace Svg ...@@ -10,6 +10,7 @@ namespace Svg
/// <summary> /// <summary>
/// SvgPolyline defines a set of connected straight line segments. Typically, <see cref="SvgPolyline"/> defines open shapes. /// SvgPolyline defines a set of connected straight line segments. Typically, <see cref="SvgPolyline"/> defines open shapes.
/// </summary> /// </summary>
[SvgElement("polyline")]
public class SvgPolyline : SvgPolygon public class SvgPolyline : SvgPolygon
{ {
public override GraphicsPath Path public override GraphicsPath Path
......
...@@ -7,6 +7,7 @@ namespace Svg ...@@ -7,6 +7,7 @@ namespace Svg
/// <summary> /// <summary>
/// Represents and SVG rectangle that could also have reounded edges. /// Represents and SVG rectangle that could also have reounded edges.
/// </summary> /// </summary>
[SvgElement("rect")]
public class SvgRectangle : SvgVisualElement public class SvgRectangle : SvgVisualElement
{ {
private SvgUnit _cornerRadiusX; private SvgUnit _cornerRadiusX;
......
...@@ -18,6 +18,7 @@ namespace Svg ...@@ -18,6 +18,7 @@ namespace Svg
private bool _dirty; private bool _dirty;
private bool _requiresSmoothRendering; private bool _requiresSmoothRendering;
private Region _previousClip; private Region _previousClip;
private SvgClipRule clipRule = SvgClipRule.NonZero;
/// <summary> /// <summary>
/// Gets the <see cref="GraphicsPath"/> for this element. /// Gets the <see cref="GraphicsPath"/> for this element.
...@@ -51,6 +52,16 @@ namespace Svg ...@@ -51,6 +52,16 @@ namespace Svg
set { this.Attributes["clip-path"] = value; } 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> /// <summary>
/// Gets or sets a value to determine if anti-aliasing should occur when the element is being rendered. /// Gets or sets a value to determine if anti-aliasing should occur when the element is being rendered.
/// </summary> /// </summary>
...@@ -143,7 +154,7 @@ namespace Svg ...@@ -143,7 +154,7 @@ namespace Svg
if (clipPath != null) if (clipPath != null)
{ {
renderer.SetClip(clipPath.GetClipRegion()); renderer.Clip = clipPath.GetClipRegion(this);
} }
} }
} }
...@@ -154,9 +165,9 @@ namespace Svg ...@@ -154,9 +165,9 @@ namespace Svg
/// <param name="renderer">The <see cref="SvgRenderer"/> to have its clipping region reset.</param> /// <param name="renderer">The <see cref="SvgRenderer"/> to have its clipping region reset.</param>
protected internal virtual void ResetClip(SvgRenderer renderer) 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; this._previousClip = null;
} }
} }
......
...@@ -16,6 +16,10 @@ namespace Svg ...@@ -16,6 +16,10 @@ namespace Svg
/// </summary> /// </summary>
Uri ClipPath { get; set; } Uri ClipPath { get; set; }
/// <summary> /// <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"/>. /// Sets the clipping region of the specified <see cref="SvgRenderer"/>.
/// </summary> /// </summary>
/// <param name="renderer">The <see cref="SvgRenderer"/> to have its clipping region set.</param> /// <param name="renderer">The <see cref="SvgRenderer"/> to have its clipping region set.</param>
......
...@@ -7,51 +7,46 @@ using System.Drawing.Drawing2D; ...@@ -7,51 +7,46 @@ using System.Drawing.Drawing2D;
namespace Svg namespace Svg
{ {
/// <summary> /// <summary>
/// /// Defines a path that can be used by other <see cref="ISvgClipable"/> elements.
/// </summary> /// </summary>
[SvgElement("clipPath")]
public sealed class SvgClipPath : SvgElement public sealed class SvgClipPath : SvgElement
{ {
private SvgCoordinateUnits _clipPathUnits; private bool _pathDirty = true;
private bool _pathDirty;
private Region _region;
/// <summary> /// <summary>
/// /// Specifies the coordinate system for the clipping path.
/// </summary> /// </summary>
[SvgAttribute("clipPathUnits")] [SvgAttribute("clipPathUnits")]
public SvgCoordinateUnits ClipPathUnits public SvgCoordinateUnits ClipPathUnits { get; set; }
{
get { return this._clipPathUnits; }
set { this._clipPathUnits = value; }
}
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="SvgClipPath"/> class. /// Initializes a new instance of the <see cref="SvgClipPath"/> class.
/// </summary> /// </summary>
public SvgClipPath() public SvgClipPath()
{ {
this._clipPathUnits = SvgCoordinateUnits.ObjectBoundingBox; this.ClipPathUnits = SvgCoordinateUnits.ObjectBoundingBox;
} }
/// <summary> /// <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> /// </summary>
/// <returns>A new <see cref="Region"/> containing the area to be clipped.</returns> /// <returns>A new <see cref="Region"/> containing the <see cref="Region"/> to be used for clipping.</returns>
protected internal Region GetClipRegion() public Region GetClipRegion(SvgVisualElement owner)
{ {
if (_region == null || _pathDirty) var path = new GraphicsPath();
{
_region = new Region();
if (this._pathDirty)
{
foreach (SvgElement element in this.Children) 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> /// <summary>
...@@ -59,27 +54,39 @@ namespace Svg ...@@ -59,27 +54,39 @@ namespace Svg
/// </summary> /// </summary>
/// <param name="region"></param> /// <param name="region"></param>
/// <param name="element"></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) 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) 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) protected override void AddElement(SvgElement child, int index)
{ {
base.AddElement(child, index); base.AddElement(child, index);
this._pathDirty = true; 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) protected override void RemoveElement(SvgElement child)
{ {
base.RemoveElement(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 ...@@ -120,6 +120,7 @@ namespace Svg
if (styleOwner == null) if (styleOwner == null)
{ {
_deviceValue = this.Value; _deviceValue = this.Value;
break;
} }
// TODO : Support height percentages // TODO : Support height percentages
......
...@@ -7,6 +7,7 @@ namespace Svg ...@@ -7,6 +7,7 @@ namespace Svg
/// <summary> /// <summary>
/// Represents a list of re-usable SVG components. /// Represents a list of re-usable SVG components.
/// </summary> /// </summary>
[SvgElement("defs")]
public class SvgDefinitionList : SvgElement public class SvgDefinitionList : SvgElement
{ {
/// <summary> /// <summary>
......
...@@ -6,6 +6,7 @@ using System.ComponentModel; ...@@ -6,6 +6,7 @@ using System.ComponentModel;
namespace Svg namespace Svg
{ {
[DefaultProperty("Text")] [DefaultProperty("Text")]
[SvgElement("desc")]
public class SvgDescription : SvgElement public class SvgDescription : SvgElement
{ {
private string _text; private string _text;
......
...@@ -11,6 +11,7 @@ namespace Svg ...@@ -11,6 +11,7 @@ namespace Svg
/// <summary> /// <summary>
/// An <see cref="SvgFragment"/> represents an SVG fragment that can be the root element or an embedded fragment of an SVG document. /// An <see cref="SvgFragment"/> represents an SVG fragment that can be the root element or an embedded fragment of an SVG document.
/// </summary> /// </summary>
[SvgElement("svg")]
public class SvgFragment : SvgElement, ISvgViewPort public class SvgFragment : SvgElement, ISvgViewPort
{ {
private SvgUnit _width; private SvgUnit _width;
......
...@@ -9,6 +9,7 @@ namespace Svg ...@@ -9,6 +9,7 @@ namespace Svg
/// <summary> /// <summary>
/// An element used to group SVG shapes. /// An element used to group SVG shapes.
/// </summary> /// </summary>
[SvgElement("g")]
public class SvgGroup : SvgVisualElement public class SvgGroup : SvgVisualElement
{ {
public SvgGroup() public SvgGroup()
...@@ -51,7 +52,9 @@ namespace Svg ...@@ -51,7 +52,9 @@ namespace Svg
protected override void Render(SvgRenderer renderer) protected override void Render(SvgRenderer renderer)
{ {
this.PushTransforms(renderer); this.PushTransforms(renderer);
this.SetClip(renderer);
base.RenderChildren(renderer); base.RenderChildren(renderer);
this.ResetClip(renderer);
this.PopTransforms(renderer); this.PopTransforms(renderer);
} }
} }
......
...@@ -8,6 +8,7 @@ using System.Drawing.Drawing2D; ...@@ -8,6 +8,7 @@ using System.Drawing.Drawing2D;
namespace Svg namespace Svg
{ {
[SvgElement("use")]
public class SvgUse : SvgVisualElement public class SvgUse : SvgVisualElement
{ {
private Uri _referencedElement; private Uri _referencedElement;
...@@ -33,15 +34,21 @@ namespace Svg ...@@ -33,15 +34,21 @@ namespace Svg
set { this.Attributes["y"] = value; } 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) protected internal override void PushTransforms(SvgRenderer renderer)
{ {
base.PushTransforms(renderer); base.PushTransforms(renderer);
renderer.TranslateTransform(this.X.ToDeviceValue(this), this.Y.ToDeviceValue(this, true)); 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 SvgUse()
{ {
} }
public override System.Drawing.Drawing2D.GraphicsPath Path public override System.Drawing.Drawing2D.GraphicsPath Path
......
...@@ -26,6 +26,12 @@ namespace Svg ...@@ -26,6 +26,12 @@ namespace Svg
this._stops = new List<SvgGradientStop>(); 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) protected override void AddElement(SvgElement child, int index)
{ {
if (child is SvgGradientStop) if (child is SvgGradientStop)
...@@ -36,18 +42,32 @@ namespace Svg ...@@ -36,18 +42,32 @@ namespace Svg
base.AddElement(child, index); 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) protected override void RemoveElement(SvgElement child)
{ {
if (child is SvgGradientStop) if (child is SvgGradientStop)
this.Stops.Add((SvgGradientStop)child); {
this.Stops.Remove((SvgGradientStop)child);
}
base.RemoveElement(child); base.RemoveElement(child);
} }
/// <summary>
/// Gets the ramp of colors to use on a gradient.
/// </summary>
public List<SvgGradientStop> Stops public List<SvgGradientStop> Stops
{ {
get { return this._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")] [SvgAttribute("spreadMethod")]
public SvgGradientSpreadMethod SpreadMethod public SvgGradientSpreadMethod SpreadMethod
{ {
...@@ -55,6 +75,9 @@ namespace Svg ...@@ -55,6 +75,9 @@ namespace Svg
set { this._spreadMethod = value; } set { this._spreadMethod = value; }
} }
/// <summary>
/// Gets or sets the coordinate system of the gradient.
/// </summary>
[SvgAttribute("gradientUnits")] [SvgAttribute("gradientUnits")]
public SvgCoordinateUnits GradientUnits public SvgCoordinateUnits GradientUnits
{ {
...@@ -68,6 +91,11 @@ namespace Svg ...@@ -68,6 +91,11 @@ namespace Svg
set { this._inheritGradient = value; } 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) protected ColorBlend GetColourBlend(SvgVisualElement owner, float opacity)
{ {
ColorBlend blend = new ColorBlend(); ColorBlend blend = new ColorBlend();
...@@ -138,7 +166,9 @@ namespace Svg ...@@ -138,7 +166,9 @@ namespace Svg
List<SvgGradientStop> stops = new List<SvgGradientStop>(); List<SvgGradientStop> stops = new List<SvgGradientStop>();
if (this.Stops.Count > 0) if (this.Stops.Count > 0)
{
return stops; return stops;
}
if (this.InheritGradient != null) if (this.InheritGradient != null)
{ {
...@@ -148,7 +178,5 @@ namespace Svg ...@@ -148,7 +178,5 @@ namespace Svg
return stops; return stops;
} }
} }
} }
\ No newline at end of file
...@@ -9,6 +9,7 @@ namespace Svg ...@@ -9,6 +9,7 @@ namespace Svg
/// <summary> /// <summary>
/// Represents a colour stop in a gradient. /// Represents a colour stop in a gradient.
/// </summary> /// </summary>
[SvgElement("stop")]
public class SvgGradientStop : SvgElement public class SvgGradientStop : SvgElement
{ {
private SvgUnit _offset; private SvgUnit _offset;
......
...@@ -7,6 +7,7 @@ using System.ComponentModel; ...@@ -7,6 +7,7 @@ using System.ComponentModel;
namespace Svg namespace Svg
{ {
[SvgElement("linearGradient")]
public sealed class SvgLinearGradientServer : SvgGradientServer public sealed class SvgLinearGradientServer : SvgGradientServer
{ {
private SvgUnit _x1; private SvgUnit _x1;
......
...@@ -7,23 +7,46 @@ using System.Drawing.Drawing2D; ...@@ -7,23 +7,46 @@ using System.Drawing.Drawing2D;
namespace Svg 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))] [TypeConverter(typeof(SvgPaintServerFactory))]
public abstract class SvgPaintServer : SvgElement public abstract class SvgPaintServer : SvgElement
{ {
/// <summary>
/// An unspecified <see cref="SvgPaintServer"/>.
/// </summary>
public static readonly SvgPaintServer None = new SvgColourServer(); public static readonly SvgPaintServer None = new SvgColourServer();
/// <summary>
/// Initializes a new instance of the <see cref="SvgPaintServer"/> class.
/// </summary>
public SvgPaintServer() 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) protected override void Render(SvgRenderer renderer)
{ {
// Never render paint servers or their children // 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); 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() public override string ToString()
{ {
return String.Format("url(#{0})", this.ID); 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