123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389 |
- using System;
- using System.Drawing;
- using System.Drawing.Drawing2D;
- using System.Diagnostics;
- using System.Linq;
- #pragma warning disable
- namespace Svg
- {
- /// <summary>
- /// The class that all SVG elements should derive from when they are to be rendered.
- /// </summary>
- public abstract partial class SvgVisualElement : SvgElement, ISvgBoundable, ISvgStylable, ISvgClipable
- {
- private bool? _requiresSmoothRendering;
- private Region _previousClip;
- /// <summary>
- /// Gets the <see cref="GraphicsPath"/> for this element.
- /// </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; }
- /// <summary>
- /// Gets the associated <see cref="SvgClipPath"/> if one has been specified.
- /// </summary>
- [SvgAttribute("clip")]
- public virtual string Clip
- {
- get { return this.Attributes.GetInheritedAttribute<string>("clip"); }
- set { this.Attributes["clip"] = value; }
- }
- /// <summary>
- /// Gets the associated <see cref="SvgClipPath"/> if one has been specified.
- /// </summary>
- [SvgAttribute("clip-path")]
- public virtual Uri ClipPath
- {
- get { return this.Attributes.GetAttribute<Uri>("clip-path"); }
- set { this.Attributes["clip-path"] = value; }
- }
- /// <summary>
- /// Gets or sets the algorithm which is to be used to determine the clipping region.
- /// </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 the associated <see cref="SvgClipPath"/> if one has been specified.
- /// </summary>
- [SvgAttribute("filter")]
- public virtual Uri Filter
- {
- get { return this.Attributes.GetInheritedAttribute<Uri>("filter"); }
- set { this.Attributes["filter"] = value; }
- }
- /// <summary>
- /// Gets or sets a value to determine if anti-aliasing should occur when the element is being rendered.
- /// </summary>
- protected virtual bool RequiresSmoothRendering
- {
- get
- {
- if (_requiresSmoothRendering == null)
- _requiresSmoothRendering = ConvertShapeRendering2AntiAlias(ShapeRendering);
- return _requiresSmoothRendering.Value;
- }
- }
- private bool ConvertShapeRendering2AntiAlias(SvgShapeRendering shapeRendering)
- {
- switch (shapeRendering)
- {
- case SvgShapeRendering.OptimizeSpeed:
- case SvgShapeRendering.CrispEdges:
- case SvgShapeRendering.GeometricPrecision:
- return false;
- default:
- // SvgShapeRendering.Auto
- // SvgShapeRendering.Inherit
- return true;
- }
- }
- /// <summary>
- /// Initializes a new instance of the <see cref="SvgGraphicsElement"/> class.
- /// </summary>
- public SvgVisualElement()
- {
- this.IsPathDirty = true;
- }
- protected virtual bool Renderable { get { return true; } }
- /// <summary>
- /// Renders the <see cref="SvgElement"/> and contents to the specified <see cref="Graphics"/> object.
- /// </summary>
- /// <param name="renderer">The <see cref="ISvgRenderer"/> object to render to.</param>
- protected override void Render(ISvgRenderer renderer)
- {
- this.Render(renderer, true);
- }
- private void Render(ISvgRenderer renderer, bool renderFilter)
- {
- if (this.Visible && this.Displayable && this.PushTransforms(renderer) &&
- (!Renderable || this.Path(renderer) != null))
- {
- bool renderNormal = true;
- if (renderFilter && this.Filter != null)
- {
- var filterPath = this.Filter;
- if (filterPath.ToString().StartsWith("url("))
- {
- filterPath = new Uri(filterPath.ToString().Substring(4, filterPath.ToString().Length - 5), UriKind.RelativeOrAbsolute);
- }
- var filter = this.OwnerDocument.IdManager.GetElementById(filterPath) as FilterEffects.SvgFilter;
- if (filter != null)
- {
- this.PopTransforms(renderer);
- try
- {
- filter.ApplyFilter(this, renderer, (r) => this.Render(r, false));
- }
- catch (Exception ex) { Debug.Print(ex.ToString()); }
- renderNormal = false;
- }
- }
- if (renderNormal)
- {
- this.SetClip(renderer);
- if (Renderable)
- {
- // If this element needs smoothing enabled turn anti-aliasing on
- if (this.RequiresSmoothRendering)
- {
- renderer.SmoothingMode = SmoothingMode.AntiAlias;
- }
- this.RenderFill(renderer);
- this.RenderStroke(renderer);
- // Reset the smoothing mode
- if (this.RequiresSmoothRendering && renderer.SmoothingMode == SmoothingMode.AntiAlias)
- {
- renderer.SmoothingMode = SmoothingMode.Default;
- }
- }
- else
- {
- base.RenderChildren(renderer);
- }
- this.ResetClip(renderer);
- this.PopTransforms(renderer);
- }
- }
- }
- /// <summary>
- /// Renders the fill of the <see cref="SvgVisualElement"/> to the specified <see cref="ISvgRenderer"/>
- /// </summary>
- /// <param name="renderer">The <see cref="ISvgRenderer"/> object to render to.</param>
- protected internal virtual void RenderFill(ISvgRenderer renderer)
- {
- if (this.Fill != null)
- {
- using (var brush = this.Fill.GetBrush(this, renderer, Math.Min(Math.Max(this.FillOpacity * this.Opacity, 0), 1)))
- {
- if (brush != null)
- {
- this.Path(renderer).FillMode = this.FillRule == SvgFillRule.NonZero ? FillMode.Winding : FillMode.Alternate;
- renderer.FillPath(brush, this.Path(renderer));
- }
- }
- }
- }
- /// <summary>
- /// Renders the stroke of the <see cref="SvgVisualElement"/> to the specified <see cref="ISvgRenderer"/>
- /// </summary>
- /// <param name="renderer">The <see cref="ISvgRenderer"/> object to render to.</param>
- protected internal virtual bool RenderStroke(ISvgRenderer renderer)
- {
- if (this.Stroke != null && this.Stroke != SvgColourServer.None && this.StrokeWidth > 0)
- {
- float strokeWidth = this.StrokeWidth.ToDeviceValue(renderer, UnitRenderingType.Other, this);
- using (var brush = this.Stroke.GetBrush(this, renderer, Math.Min(Math.Max(this.StrokeOpacity * this.Opacity, 0), 1), true))
- {
- if (brush != null)
- {
- var path = this.Path(renderer);
- var bounds = path.GetBounds();
- if (path.PointCount < 1) return false;
- if (bounds.Width <= 0 && bounds.Height <= 0)
- {
- switch (this.StrokeLineCap)
- {
- case SvgStrokeLineCap.Round:
- using (var capPath = new GraphicsPath())
- {
- capPath.AddEllipse(path.PathPoints[0].X - strokeWidth / 2, path.PathPoints[0].Y - strokeWidth / 2, strokeWidth, strokeWidth);
- renderer.FillPath(brush, capPath);
- }
- break;
- case SvgStrokeLineCap.Square:
- using (var capPath = new GraphicsPath())
- {
- capPath.AddRectangle(new RectangleF(path.PathPoints[0].X - strokeWidth / 2, path.PathPoints[0].Y - strokeWidth / 2, strokeWidth, strokeWidth));
- renderer.FillPath(brush, capPath);
- }
- break;
- }
- }
- else
- {
- using (var pen = new Pen(brush, strokeWidth))
- {
- if (this.StrokeDashArray != null && this.StrokeDashArray.Count > 0)
- {
- /* divide by stroke width - GDI behaviour that I don't quite understand yet.*/
- pen.DashPattern = this.StrokeDashArray.ConvertAll(u => ((u.ToDeviceValue(renderer, UnitRenderingType.Other, this) <= 0) ? 1 : u.ToDeviceValue(renderer, UnitRenderingType.Other, this)) /
- ((strokeWidth <= 0) ? 1 : strokeWidth)).ToArray();
- }
- switch (this.StrokeLineJoin)
- {
- case SvgStrokeLineJoin.Bevel:
- pen.LineJoin = LineJoin.Bevel;
- break;
- case SvgStrokeLineJoin.Round:
- pen.LineJoin = LineJoin.Round;
- break;
- default:
- pen.LineJoin = LineJoin.Miter;
- break;
- }
- pen.MiterLimit = this.StrokeMiterLimit;
- switch (this.StrokeLineCap)
- {
- case SvgStrokeLineCap.Round:
- pen.StartCap = LineCap.Round;
- pen.EndCap = LineCap.Round;
- break;
- case SvgStrokeLineCap.Square:
- pen.StartCap = LineCap.Square;
- pen.EndCap = LineCap.Square;
- break;
- }
- renderer.DrawPath(pen, path);
- return true;
- }
- }
- }
- }
- }
- return false;
- }
- /// <summary>
- /// Sets the clipping region of the specified <see cref="ISvgRenderer"/>.
- /// </summary>
- /// <param name="renderer">The <see cref="ISvgRenderer"/> to have its clipping region set.</param>
- protected internal virtual void SetClip(ISvgRenderer renderer)
- {
- if (this.ClipPath != null || !string.IsNullOrEmpty(this.Clip))
- {
- this._previousClip = renderer.GetClip();
- if (this.ClipPath != null)
- {
- SvgClipPath clipPath = this.OwnerDocument.GetElementById<SvgClipPath>(this.ClipPath.ToString());
- if (clipPath != null) renderer.SetClip(clipPath.GetClipRegion(this), CombineMode.Intersect);
- }
- var clip = this.Clip;
- if (!string.IsNullOrEmpty(clip) && clip.StartsWith("rect("))
- {
- clip = clip.Trim();
- var offsets = (from o in clip.Substring(5, clip.Length - 6).Split(',')
- select float.Parse(o.Trim())).ToList();
- var bounds = this.Bounds;
- var clipRect = new RectangleF(bounds.Left + offsets[3], bounds.Top + offsets[0],
- bounds.Width - (offsets[3] + offsets[1]),
- bounds.Height - (offsets[2] + offsets[0]));
- renderer.SetClip(new Region(clipRect), CombineMode.Intersect);
- }
- }
- }
- /// <summary>
- /// Resets the clipping region of the specified <see cref="ISvgRenderer"/> back to where it was before the <see cref="SetClip"/> method was called.
- /// </summary>
- /// <param name="renderer">The <see cref="ISvgRenderer"/> to have its clipping region reset.</param>
- protected internal virtual void ResetClip(ISvgRenderer renderer)
- {
- if (this._previousClip != null)
- {
- renderer.SetClip(this._previousClip);
- this._previousClip = null;
- }
- }
- /// <summary>
- /// Sets the clipping region of the specified <see cref="ISvgRenderer"/>.
- /// </summary>
- /// <param name="renderer">The <see cref="ISvgRenderer"/> to have its clipping region set.</param>
- void ISvgClipable.SetClip(ISvgRenderer renderer)
- {
- this.SetClip(renderer);
- }
- /// <summary>
- /// Resets the clipping region of the specified <see cref="ISvgRenderer"/> back to where it was before the <see cref="SetClip"/> method was called.
- /// </summary>
- /// <param name="renderer">The <see cref="ISvgRenderer"/> to have its clipping region reset.</param>
- void ISvgClipable.ResetClip(ISvgRenderer renderer)
- {
- this.ResetClip(renderer);
- }
- public override SvgElement DeepCopy<T>()
- {
- var newObj = base.DeepCopy<T>() as SvgVisualElement;
- newObj.ClipPath = this.ClipPath;
- newObj.ClipRule = this.ClipRule;
- newObj.Filter = this.Filter;
- newObj.Visible = this.Visible;
- if (this.Fill != null)
- newObj.Fill = this.Fill;
- if (this.Stroke != null)
- newObj.Stroke = this.Stroke;
- newObj.FillRule = this.FillRule;
- newObj.FillOpacity = this.FillOpacity;
- newObj.StrokeWidth = this.StrokeWidth;
- newObj.StrokeLineCap = this.StrokeLineCap;
- newObj.StrokeLineJoin = this.StrokeLineJoin;
- newObj.StrokeMiterLimit = this.StrokeMiterLimit;
- newObj.StrokeDashArray = this.StrokeDashArray;
- newObj.StrokeDashOffset = this.StrokeDashOffset;
- newObj.StrokeOpacity = this.StrokeOpacity;
- newObj.Opacity = this.Opacity;
- return newObj;
- }
- }
- }
- #pragma warning restore
|