diff --git a/Source/Basic Shapes/SvgVisualElement.cs b/Source/Basic Shapes/SvgVisualElement.cs
index 96d19d4881a39c26a0ea83258c5b02547bbd3c75..52169422564527a7f6a6abb18575e5fdc2efe4a9 100644
--- a/Source/Basic Shapes/SvgVisualElement.cs
+++ b/Source/Basic Shapes/SvgVisualElement.cs
@@ -99,34 +99,66 @@ namespace Svg
this._requiresSmoothRendering = false;
}
+ protected virtual bool Renderable { get { return true; } }
+
///
/// Renders the and contents to the specified object.
///
/// The object to render to.
protected override void Render(SvgRenderer renderer)
{
- if ((this.Path(renderer) != null) && this.Visible && this.Displayable)
+ this.Render(renderer, true);
+ }
+
+ private void Render(SvgRenderer renderer, bool renderFilter)
+ {
+ if (this.Visible && this.Displayable && this.PushTransforms(renderer) &&
+ (!Renderable || this.Path(renderer) != null))
{
- this.PushTransforms(renderer);
- this.SetClip(renderer);
+ bool renderNormal = true;
- // If this element needs smoothing enabled turn anti-aliasing on
- if (this.RequiresSmoothRendering)
+ if (renderFilter && this.Filter != null)
{
- renderer.SmoothingMode = SmoothingMode.AntiAlias;
+ var filter = this.OwnerDocument.IdManager.GetElementById(this.Filter) as FilterEffects.SvgFilter;
+ if (filter != null)
+ {
+ this.PopTransforms(renderer);
+ filter.ApplyFilter(this, renderer, (r) => this.Render(r, false));
+ renderNormal = false;
+ }
}
- this.RenderFill(renderer);
- this.RenderStroke(renderer);
- // Reset the smoothing mode
- if (this.RequiresSmoothRendering && renderer.SmoothingMode == SmoothingMode.AntiAlias)
+ if (renderNormal)
{
- renderer.SmoothingMode = SmoothingMode.Default;
+ 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);
}
- this.ResetClip(renderer);
- this.PopTransforms(renderer);
}
}
diff --git a/Source/Basic Shapes/SvgVisualElementStyle.cs b/Source/Basic Shapes/SvgVisualElementStyle.cs
index 17884638af7e74f7c0c95d986bea4c140bd88e47..6433cbb683c7375533668ca721f86a4dd4376339 100644
--- a/Source/Basic Shapes/SvgVisualElementStyle.cs
+++ b/Source/Basic Shapes/SvgVisualElementStyle.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Drawing;
using System.Text;
using System.Reflection;
using System.ComponentModel;
@@ -53,6 +54,16 @@ namespace Svg
}
}
+ ///
+ /// Gets or sets the fill of this element.
+ ///
+ [SvgAttribute("enable-background")]
+ public virtual string EnableBackground
+ {
+ get { return this.Attributes["enable-background"] as string; }
+ set { this.Attributes["enable-background"] = value; }
+ }
+
///
/// Gets or sets the fill of this element.
///
@@ -361,26 +372,43 @@ namespace Svg
break;
}
+ var family = ValidateFontFamily(this.FontFamily);
+ if (!family.IsStyleAvailable(fontStyle))
+ {
+ // Do Something
+ }
+
// Get the font-family
- string family = ValidateFontFamily(this.FontFamily) ?? DefaultFontFamily;
return new System.Drawing.Font(family, fontSize, fontStyle, System.Drawing.GraphicsUnit.Pixel);
}
- private static string ValidateFontFamily(string fontFamilyList)
+ private static FontFamily ValidateFontFamily(string fontFamilyList)
{
// Split font family list on "," and then trim start and end spaces and quotes.
var fontParts = (fontFamilyList ?? "").Split(new[] { ',' }).Select(fontName => fontName.Trim(new[] { '"', ' ', '\'' }));
-
var families = System.Drawing.FontFamily.Families;
+ FontFamily family;
// Find a the first font that exists in the list of installed font families.
//styles from IE get sent through as lowercase.
- foreach (var f in fontParts.Where(f => families.Any(family => family.Name.ToLower() == f.ToLower())))
+ foreach (var f in fontParts)
{
- return f;
+ family = families.FirstOrDefault(ff => ff.Name.ToLower() == f.ToLower());
+ if (family != null) return family;
+
+ switch (f)
+ {
+ case "serif":
+ return System.Drawing.FontFamily.GenericSerif;
+ case "sans-serif":
+ return System.Drawing.FontFamily.GenericSansSerif;
+ case "monospace":
+ return System.Drawing.FontFamily.GenericMonospace;
+ }
}
+
// No valid font family found from the list requested.
- return null;
+ return System.Drawing.FontFamily.GenericSansSerif;
}
}
diff --git a/Source/DataTypes/SvgUnitCollection.cs b/Source/DataTypes/SvgUnitCollection.cs
index 284dfe03d1cb24fbfbec531df6b191251887511b..4d831d54c801ab0306178e6cc3fd8e98712a4fd2 100644
--- a/Source/DataTypes/SvgUnitCollection.cs
+++ b/Source/DataTypes/SvgUnitCollection.cs
@@ -23,6 +23,12 @@ namespace Svg
return ret;
}
+
+ public static bool IsNullOrEmpty(SvgUnitCollection collection)
+ {
+ return collection == null || collection.Count < 1 ||
+ (collection.Count == 1 && (collection[0] == SvgUnit.Empty || collection[0] == SvgUnit.None));
+ }
}
///
diff --git a/Source/Document Structure/SvgGroup.cs b/Source/Document Structure/SvgGroup.cs
index 3dfc691f6f772c5a5e42f32deef0ce6316596390..f4826f80236a00f5a679bf23bd62a999955327e4 100644
--- a/Source/Document Structure/SvgGroup.cs
+++ b/Source/Document Structure/SvgGroup.cs
@@ -56,23 +56,25 @@ namespace Svg
}
}
- ///
- /// Renders the and contents to the specified object.
- ///
- /// The object to render to.
- protected override void Render(SvgRenderer renderer)
- {
- if (!Visible || !Displayable)
- return;
+ protected override bool Renderable { get { return false; } }
- if (this.PushTransforms(renderer))
- {
- this.SetClip(renderer);
- base.RenderChildren(renderer);
- this.ResetClip(renderer);
- this.PopTransforms(renderer);
- }
- }
+ /////
+ ///// Renders the and contents to the specified object.
+ /////
+ ///// The object to render to.
+ //protected override void Render(SvgRenderer renderer)
+ //{
+ // if (!Visible || !Displayable)
+ // return;
+
+ // if (this.PushTransforms(renderer))
+ // {
+ // this.SetClip(renderer);
+ // base.RenderChildren(renderer);
+ // this.ResetClip(renderer);
+ // this.PopTransforms(renderer);
+ // }
+ //}
public override SvgElement DeepCopy()
diff --git a/Source/Document Structure/SvgUse.cs b/Source/Document Structure/SvgUse.cs
index 147d0b915adef2685cf6e7acf2ee01d6242cc252..89dbc54767d5d597bb26c7fb7aace67559170b1d 100644
--- a/Source/Document Structure/SvgUse.cs
+++ b/Source/Document Structure/SvgUse.cs
@@ -77,6 +77,8 @@ namespace Svg
// }
// }
+ protected override bool Renderable { get { return false; } }
+
protected override void Render(SvgRenderer renderer)
{
if (!Visible || !Displayable)
diff --git a/Source/Extensibility/SvgForeignObject.cs b/Source/Extensibility/SvgForeignObject.cs
index 3fc4cdd485c95b3eb6950a98d0af67a3cc2fd134..d61a95484130fd9b1bcd05edc2e4bd433e5f0eac 100644
--- a/Source/Extensibility/SvgForeignObject.cs
+++ b/Source/Extensibility/SvgForeignObject.cs
@@ -56,21 +56,23 @@ namespace Svg
}
}
- ///
- /// Renders the and contents to the specified object.
- ///
- /// The object to render to.
- protected override void Render(SvgRenderer renderer)
- {
- if (!Visible || !Displayable)
- return;
+ protected override bool Renderable { get { return false; } }
- this.PushTransforms(renderer);
- this.SetClip(renderer);
- base.RenderChildren(renderer);
- this.ResetClip(renderer);
- this.PopTransforms(renderer);
- }
+ /////
+ ///// Renders the and contents to the specified object.
+ /////
+ ///// The object to render to.
+ //protected override void Render(SvgRenderer renderer)
+ //{
+ // if (!Visible || !Displayable)
+ // return;
+
+ // this.PushTransforms(renderer);
+ // this.SetClip(renderer);
+ // base.RenderChildren(renderer);
+ // this.ResetClip(renderer);
+ // this.PopTransforms(renderer);
+ //}
public override SvgElement DeepCopy()
{
diff --git a/Source/External/ExCSS/Parser.cs b/Source/External/ExCSS/Parser.cs
index b44db37cfb249ff5a992016d8a76d1406b811804..43b890016dd91db2108ef199dfa4d1684f3dd0ff 100644
--- a/Source/External/ExCSS/Parser.cs
+++ b/Source/External/ExCSS/Parser.cs
@@ -7,6 +7,28 @@ using ExCSS.Model.TextBlocks;
// ReSharper disable once CheckNamespace
using System;
+//The MIT License (MIT)
+
+//Copyright (c) [year] [fullname]
+
+//Permission is hereby granted, free of charge, to any person obtaining a copy
+//of this software and associated documentation files (the "Software"), to deal
+//in the Software without restriction, including without limitation the rights
+//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//copies of the Software, and to permit persons to whom the Software is
+//furnished to do so, subject to the following conditions:
+
+//The above copyright notice and this permission notice shall be included in all
+//copies or substantial portions of the Software.
+
+//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+//SOFTWARE.
+
namespace ExCSS
{
diff --git a/Source/Filter Effects/ImageBuffer.cs b/Source/Filter Effects/ImageBuffer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..5a8ec9983c45876fed0125bb7cdfc0d8cb639817
--- /dev/null
+++ b/Source/Filter Effects/ImageBuffer.cs
@@ -0,0 +1,215 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Drawing;
+using System.Drawing.Imaging;
+using System.Drawing.Drawing2D;
+
+namespace Svg.FilterEffects
+{
+ public class ImageBuffer : IDictionary
+ {
+ private const string BufferKey = "__!!BUFFER";
+
+ private Dictionary _images;
+ private RectangleF _bounds;
+ private SvgRenderer _renderer;
+ private Action _renderMethod;
+ private float _inflate;
+
+ public Matrix Transform { get; set; }
+
+ public Bitmap Buffer
+ {
+ get { return _images[BufferKey]; }
+ }
+ public int Count
+ {
+ get { return _images.Count; }
+ }
+ public Bitmap this[string key]
+ {
+ get
+ {
+ return ProcessResult(key, _images[ProcessKey(key)]);
+ }
+ set
+ {
+ _images[ProcessKey(key)] = value;
+ if (key != null) _images[BufferKey] = value;
+ }
+ }
+
+ public ImageBuffer(RectangleF bounds, float inflate, SvgRenderer renderer, Action renderMethod)
+ {
+ _bounds = bounds;
+ _inflate = inflate;
+ _renderer = renderer;
+ _renderMethod = renderMethod;
+ _images = new Dictionary();
+ _images[SvgFilterPrimitive.BackgroundAlpha] = null;
+ _images[SvgFilterPrimitive.BackgroundImage] = null;
+ _images[SvgFilterPrimitive.FillPaint] = null;
+ _images[SvgFilterPrimitive.SourceAlpha] = null;
+ _images[SvgFilterPrimitive.SourceGraphic] = null;
+ _images[SvgFilterPrimitive.StrokePaint] = null;
+ }
+
+ public void Add(string key, Bitmap value)
+ {
+ _images.Add(ProcessKey(key), value);
+ }
+ public bool ContainsKey(string key)
+ {
+ return _images.ContainsKey(ProcessKey(key));
+ }
+ public void Clear()
+ {
+ _images.Clear();
+ }
+ public IEnumerator> GetEnumerator()
+ {
+ return _images.GetEnumerator();
+ }
+ public bool Remove(string key)
+ {
+ switch (key)
+ {
+ case SvgFilterPrimitive.BackgroundAlpha:
+ case SvgFilterPrimitive.BackgroundImage:
+ case SvgFilterPrimitive.FillPaint:
+ case SvgFilterPrimitive.SourceAlpha:
+ case SvgFilterPrimitive.SourceGraphic:
+ case SvgFilterPrimitive.StrokePaint:
+ return false;
+ default:
+ return _images.Remove(ProcessKey(key));
+ }
+ }
+ public bool TryGetValue(string key, out Bitmap value)
+ {
+ if (_images.TryGetValue(ProcessKey(key), out value))
+ {
+ value = ProcessResult(key, value);
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ private Bitmap ProcessResult(string key, Bitmap curr)
+ {
+ if (curr == null)
+ {
+ switch (key)
+ {
+ case SvgFilterPrimitive.BackgroundAlpha:
+ case SvgFilterPrimitive.BackgroundImage:
+ case SvgFilterPrimitive.FillPaint:
+ case SvgFilterPrimitive.StrokePaint:
+ // Do nothing
+ return null;
+ case SvgFilterPrimitive.SourceAlpha:
+ _images[key] = CreateSourceAlpha();
+ return _images[key];
+ case SvgFilterPrimitive.SourceGraphic:
+ _images[key] = CreateSourceGraphic();
+ return _images[key];
+ }
+ }
+ return curr;
+ }
+ private string ProcessKey(string key)
+ {
+ if (string.IsNullOrEmpty(key)) return BufferKey;
+ return key;
+ }
+
+
+
+ private Bitmap CreateSourceGraphic()
+ {
+ var graphic = new Bitmap((int)(_bounds.Width + 2 * _inflate * _bounds.Width + _bounds.X),
+ (int)(_bounds.Height + 2 * _inflate * _bounds.Height + _bounds.Y));
+ using (var renderer = SvgRenderer.FromImage(graphic))
+ {
+ renderer.Boundable(_renderer.Boundable());
+ var transform = new Matrix();
+ transform.Translate(_bounds.Width * _inflate, _bounds.Height * _inflate);
+ renderer.Transform = transform;
+ //renderer.Transform = _renderer.Transform;
+ //renderer.Clip = _renderer.Clip;
+ _renderMethod.Invoke(renderer);
+ }
+ return graphic;
+ }
+
+ private Bitmap CreateSourceAlpha()
+ {
+ Bitmap source = this[SvgFilterPrimitive.SourceGraphic];
+
+ float[][] colorMatrixElements = {
+ new float[] {0, 0, 0, 0, 0}, // red
+ new float[] {0, 0, 0, 0, 0}, // green
+ new float[] {0, 0, 0, 0, 0}, // blue
+ new float[] {0, 0, 0, 1, 1}, // alpha
+ new float[] {0, 0, 0, 0, 0} }; // translations
+
+ var matrix = new ColorMatrix(colorMatrixElements);
+
+ ImageAttributes attributes = new ImageAttributes();
+ attributes.SetColorMatrix(matrix);
+
+ var sourceAlpha = new Bitmap(source.Width, source.Height);
+
+ using (var graphics = Graphics.FromImage(sourceAlpha))
+ {
+
+ graphics.DrawImage(source, new Rectangle(0, 0, source.Width, source.Height), 0, 0,
+ source.Width, source.Height, GraphicsUnit.Pixel, attributes);
+ graphics.Save();
+ }
+
+ return sourceAlpha;
+ }
+
+
+
+ bool ICollection>.IsReadOnly
+ {
+ get { return false; }
+ }
+ ICollection IDictionary.Keys
+ {
+ get { return _images.Keys; }
+ }
+ ICollection IDictionary.Values
+ {
+ get { return _images.Values; }
+ }
+
+ void ICollection>.Add(KeyValuePair item)
+ {
+ _images.Add(item.Key, item.Value);
+ }
+ bool ICollection>.Contains(KeyValuePair item)
+ {
+ return ((IDictionary)_images).Contains(item);
+ }
+ void ICollection>.CopyTo(KeyValuePair[] array, int arrayIndex)
+ {
+ ((IDictionary)_images).CopyTo(array, arrayIndex);
+ }
+ bool ICollection>.Remove(KeyValuePair item)
+ {
+ return _images.Remove(item.Key);
+ }
+ System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
+ {
+ return _images.GetEnumerator();
+ }
+ }
+}
diff --git a/Source/Filter Effects/SvgFilter.cs b/Source/Filter Effects/SvgFilter.cs
index 2ac722d17f4d952c3fa45beb773a39dff5413de6..51c3cccba086edb5af855a8609ec86f2d9a1f59b 100644
--- a/Source/Filter Effects/SvgFilter.cs
+++ b/Source/Filter Effects/SvgFilter.cs
@@ -72,15 +72,11 @@ namespace Svg.FilterEffects
set { this.Attributes["color-interpolation-filters"] = value; }
}
-
- internal Dictionary> Buffer { get; private set; }
-
///
/// Initializes a new instance of the class.
///
public SvgFilter()
{
- this.Buffer = new Dictionary>();
}
///
@@ -103,10 +99,32 @@ namespace Svg.FilterEffects
return (SvgFilter)this.MemberwiseClone();
}
- public void ApplyFilter(SvgVisualElement element, SvgRenderer renderer)
+ private Matrix GetTransform(SvgVisualElement element)
+ {
+ var transformMatrix = new Matrix();
+ foreach (var transformation in element.Transforms)
+ {
+ transformMatrix.Multiply(transformation.Matrix);
+ }
+ return transformMatrix;
+ }
+
+ private RectangleF GetPathBounds(SvgVisualElement element, SvgRenderer renderer, Matrix transform)
+ {
+ var bounds = element.Path(renderer).GetBounds();
+ var pts = new PointF[] { bounds.Location, new PointF(bounds.Right, bounds.Bottom) };
+ transform.TransformPoints(pts);
+
+ return new RectangleF(Math.Min(pts[0].X, pts[1].X), Math.Min(pts[0].Y, pts[1].Y),
+ Math.Abs(pts[0].X - pts[1].X), Math.Abs(pts[0].Y - pts[1].Y));
+ }
+
+ public void ApplyFilter(SvgVisualElement element, SvgRenderer renderer, Action renderMethod)
{
- this.Buffer.Clear();
- this.PopulateDefaults(element, renderer);
+ var inflate = 0.5f;
+ var transform = GetTransform(element);
+ var bounds = GetPathBounds(element, renderer, transform);
+ var buffer = new ImageBuffer(bounds, inflate, renderer, renderMethod) { Transform = transform };
IEnumerable primitives = this.Children.OfType();
@@ -114,21 +132,21 @@ namespace Svg.FilterEffects
{
foreach (var primitive in primitives)
{
- this.Buffer.Add(primitive.Result, (e, r) => primitive.Process());
+ primitive.Process(buffer);
}
// Render the final filtered image
- renderer.DrawImageUnscaled(this.Buffer.Last().Value(element, renderer), new Point(0, 0));
+ var bufferImg = buffer.Buffer;
+ bufferImg.Save(@"C:\test.png");
+ var imgDraw = RectangleF.Inflate(bounds, inflate * bounds.Width, inflate * bounds.Height);
+ var prevClip = renderer.Clip;
+ renderer.Clip = new Region(imgDraw);
+ renderer.DrawImage(bufferImg, imgDraw, new RectangleF(bounds.X, bounds.Y, imgDraw.Width, imgDraw.Height), GraphicsUnit.Pixel);
+ renderer.Clip = prevClip;
+ //renderer.DrawImage(bufferImg, bounds, bounds, GraphicsUnit.Pixel);
}
}
-
- private void PopulateDefaults(SvgVisualElement element, SvgRenderer renderer)
- {
- this.ResetDefaults();
-
- this.Buffer.Add(SvgFilterPrimitive.SourceGraphic, this.CreateSourceGraphic);
- this.Buffer.Add(SvgFilterPrimitive.SourceAlpha, this.CreateSourceAlpha);
- }
+
#region Defaults
@@ -147,58 +165,7 @@ namespace Svg.FilterEffects
}
}
- private Bitmap CreateSourceGraphic(SvgVisualElement element, SvgRenderer renderer)
- {
- if (this.sourceGraphic == null)
- {
- RectangleF bounds = element.Path(renderer).GetBounds();
- this.sourceGraphic = new Bitmap((int)bounds.Width, (int)bounds.Height);
-
- using (var graphics = Graphics.FromImage(this.sourceGraphic))
- {
- graphics.Clip = renderer.Clip;
- graphics.Transform = renderer.Transform;
-
- element.RenderElement(SvgRenderer.FromGraphics(graphics));
-
- graphics.Save();
- }
- }
-
- return this.sourceGraphic;
- }
-
- private Bitmap CreateSourceAlpha(SvgVisualElement element, SvgRenderer renderer)
- {
- if (this.sourceAlpha == null)
- {
- Bitmap source = this.Buffer[SvgFilterPrimitive.SourceGraphic](element, renderer);
-
- float[][] colorMatrixElements = {
- new float[] {0, 0, 0, 0, 0}, // red
- new float[] {0, 0, 0, 0, 0}, // green
- new float[] {0, 0, 0, 0, 0}, // blue
- new float[] {0, 0, 0, 1, 1}, // alpha
- new float[] {0, 0, 0, 0, 0} }; // translations
-
- var matrix = new ColorMatrix(colorMatrixElements);
-
- ImageAttributes attributes = new ImageAttributes();
- attributes.SetColorMatrix(matrix);
-
- this.sourceAlpha = new Bitmap(source.Width, source.Height);
-
- using (var graphics = Graphics.FromImage(this.sourceAlpha))
- {
-
- graphics.DrawImage(source, new Rectangle(0, 0, source.Width, source.Height), 0, 0,
- source.Width, source.Height, GraphicsUnit.Pixel, attributes);
- graphics.Save();
- }
- }
-
- return this.sourceAlpha;
- }
+
#endregion
diff --git a/Source/Filter Effects/SvgFilterPrimitive.cs b/Source/Filter Effects/SvgFilterPrimitive.cs
index b962c343d0c346490901b4809bb7eb6131eec83c..3055f27b5c507561298e9f769399fedae7a2dd7c 100644
--- a/Source/Filter Effects/SvgFilterPrimitive.cs
+++ b/Source/Filter Effects/SvgFilterPrimitive.cs
@@ -8,12 +8,12 @@ namespace Svg.FilterEffects
{
public abstract class SvgFilterPrimitive : SvgElement
{
- public static readonly string SourceGraphic = "SourceGraphic";
- public static readonly string SourceAlpha = "SourceAlpha";
- public static readonly string BackgroundImage = "BackgroundImage";
- public static readonly string BackgroundAlpha = "BackgroundAlpha";
- public static readonly string FillPaint = "FillPaint";
- public static readonly string StrokePaint = "StrokePaint";
+ public const string SourceGraphic = "SourceGraphic";
+ public const string SourceAlpha = "SourceAlpha";
+ public const string BackgroundImage = "BackgroundImage";
+ public const string BackgroundAlpha = "BackgroundAlpha";
+ public const string FillPaint = "FillPaint";
+ public const string StrokePaint = "StrokePaint";
[SvgAttribute("in")]
public string Input
@@ -34,6 +34,6 @@ namespace Svg.FilterEffects
get { return (SvgFilter)this.Parent; }
}
- public abstract Bitmap Process();
+ public abstract void Process(ImageBuffer buffer);
}
}
\ No newline at end of file
diff --git a/Source/Filter Effects/feColourMatrix/SvgColourMatrix.cs b/Source/Filter Effects/feColourMatrix/SvgColourMatrix.cs
index 9ae6f1b4e73e6bc52ddf0cf3282d42c0097e442b..e577ef3f23f59c9d88686babe84b2bcd1f0297c3 100644
--- a/Source/Filter Effects/feColourMatrix/SvgColourMatrix.cs
+++ b/Source/Filter Effects/feColourMatrix/SvgColourMatrix.cs
@@ -1,7 +1,8 @@
using System;
using System.Drawing;
using System.Collections.Generic;
-using Svg.Filter_Effects.feColourMatrix;
+using System.Linq;
+using System.Drawing.Imaging;
namespace Svg.FilterEffects
{
@@ -19,8 +20,7 @@ namespace Svg.FilterEffects
///
[SvgAttribute("type")]
public SvgColourMatrixType Type { get; set; }
-
-
+
///
/// list of s
@@ -29,13 +29,74 @@ namespace Svg.FilterEffects
///
[SvgAttribute("values")]
public string Values { get; set; }
+
+ public override void Process(ImageBuffer buffer)
+ {
+ var inputImage = buffer[this.Input];
+ float[][] colorMatrixElements;
+ float value;
+ switch (this.Type)
+ {
+ case SvgColourMatrixType.hueRotate:
+ value = (string.IsNullOrEmpty(this.Values) ? 0 : float.Parse(this.Values));
+ colorMatrixElements = new float[][] {
+ new float[] {(float)(0.213 + Math.Cos(value) * +0.787 + Math.Sin(value) * -0.213),
+ (float)(0.715 + Math.Cos(value) * -0.715 + Math.Sin(value) * -0.715),
+ (float)(0.072 + Math.Cos(value) * -0.072 + Math.Sin(value) * +0.928), 0, 0},
+ new float[] {(float)(0.213 + Math.Cos(value) * -0.213 + Math.Sin(value) * +0.143),
+ (float)(0.715 + Math.Cos(value) * +0.285 + Math.Sin(value) * +0.140),
+ (float)(0.072 + Math.Cos(value) * -0.072 + Math.Sin(value) * -0.283), 0, 0},
+ new float[] {(float)(0.213 + Math.Cos(value) * -0.213 + Math.Sin(value) * -0.787),
+ (float)(0.715 + Math.Cos(value) * -0.715 + Math.Sin(value) * +0.715),
+ (float)(0.072 + Math.Cos(value) * +0.928 + Math.Sin(value) * +0.072), 0, 0},
+ new float[] {0, 0, 0, 1, 0},
+ new float[] {0, 0, 0, 0, 1}
+ };
+ break;
+ case SvgColourMatrixType.luminanceToAlpha:
+ colorMatrixElements = new float[][] {
+ new float[] {0, 0, 0, 0, 0},
+ new float[] {0, 0, 0, 0, 0},
+ new float[] {0, 0, 0, 0, 0},
+ new float[] {0.2125f, 0.7154f, 0.0721f, 0, 0},
+ new float[] {0, 0, 0, 0, 1}
+ };
+ break;
+ case SvgColourMatrixType.saturate:
+ value = (string.IsNullOrEmpty(this.Values) ? 1 : float.Parse(this.Values));
+ colorMatrixElements = new float[][] {
+ new float[] {(float)(0.213+0.787*value), (float)(0.715-0.715*value), (float)(0.072-0.072*value), 0, 0},
+ new float[] {(float)(0.213-0.213*value), (float)(0.715+0.285*value), (float)(0.072-0.072*value), 0, 0},
+ new float[] {(float)(0.213-0.213*value), (float)(0.715-0.715*value), (float)(0.072+0.928*value), 0, 0},
+ new float[] {0, 0, 0, 1, 0},
+ new float[] {0, 0, 0, 0, 1}
+ };
+ break;
+ default: // Matrix
+ var parts = this.Values.Split(new char[] { ' ', '\t', '\n', '\r', ',' });
+ colorMatrixElements = new float[5][];
+ for (int i = 0; i < 4; i++)
+ {
+ colorMatrixElements[i] = parts.Skip(i * 5).Take(5).Select(v => float.Parse(v)).ToArray();
+ }
+ colorMatrixElements[4] = new float[] { 0, 0, 0, 0, 1 };
+ break;
+ }
+ var colorMatrix = new ColorMatrix(colorMatrixElements);
+ var imageAttrs = new ImageAttributes();
+ imageAttrs.SetColorMatrix(colorMatrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);
- public override Bitmap Process()
- {
- return null;
+ var result = new Bitmap(inputImage.Width, inputImage.Height);
+ using (var g = Graphics.FromImage(result))
+ {
+ g.DrawImage(inputImage, new Rectangle(0, 0, inputImage.Width, inputImage.Height),
+ 0, 0, inputImage.Width, inputImage.Height, GraphicsUnit.Pixel, imageAttrs);
+ g.Flush();
+ }
+ buffer[this.Result] = result;
}
diff --git a/Source/Filter Effects/feColourMatrix/SvgColourMatrixType.cs b/Source/Filter Effects/feColourMatrix/SvgColourMatrixType.cs
index 417c0ebbb0930d2e7bc526b821c6477b92d13476..9941f6373250723034b71ec479c815702ea7956b 100644
--- a/Source/Filter Effects/feColourMatrix/SvgColourMatrixType.cs
+++ b/Source/Filter Effects/feColourMatrix/SvgColourMatrixType.cs
@@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
-namespace Svg.Filter_Effects.feColourMatrix
+namespace Svg.FilterEffects
{
public enum SvgColourMatrixType
{
diff --git a/Source/Filter Effects/feGaussianBlur/SvgGaussianBlur.cs b/Source/Filter Effects/feGaussianBlur/SvgGaussianBlur.cs
index dc46d303d2e745fdef51a634ba3cf6925e2f60e7..d88631fa83b65279505ceaef6c46bf70f3c8aca4 100644
--- a/Source/Filter Effects/feGaussianBlur/SvgGaussianBlur.cs
+++ b/Source/Filter Effects/feGaussianBlur/SvgGaussianBlur.cs
@@ -67,7 +67,10 @@ namespace Svg.FilterEffects
public Bitmap Apply(Image inputImage)
{
- using (RawBitmap src = new RawBitmap(new Bitmap(inputImage)))
+ var bitmapSrc = inputImage as Bitmap;
+ if (bitmapSrc == null) bitmapSrc = new Bitmap(inputImage);
+
+ using (RawBitmap src = new RawBitmap(bitmapSrc))
{
using (RawBitmap dest = new RawBitmap(new Bitmap(inputImage.Width, inputImage.Height)))
{
@@ -250,11 +253,11 @@ namespace Svg.FilterEffects
- public override Bitmap Process()
+ public override void Process(ImageBuffer buffer)
{
- //Todo
-
- return null;
+ var inputImage = buffer[this.Input];
+ var result = Apply(inputImage);
+ buffer[this.Result] = result;
}
diff --git a/Source/Filter Effects/feMerge/SvgMerge.cs b/Source/Filter Effects/feMerge/SvgMerge.cs
index a18ffdc574531bd5b9605f749b4efe1bb9296a96..2be7eb95ac6244428fb6639debc05cb0ddf9705b 100644
--- a/Source/Filter Effects/feMerge/SvgMerge.cs
+++ b/Source/Filter Effects/feMerge/SvgMerge.cs
@@ -5,44 +5,34 @@ using System.Text;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
+using System.Linq;
namespace Svg.FilterEffects
{
-
[SvgElement("feMerge")]
public class SvgMerge : SvgFilterPrimitive
{
- public StringCollection MergeResults { get; private set; }
-
- public SvgMerge()
+ public override void Process(ImageBuffer buffer)
{
- MergeResults = new StringCollection();
+ var children = this.Children.OfType().ToList();
+ var inputImage = buffer[children.First().Input];
+ var result = new Bitmap(inputImage.Width, inputImage.Height);
+ using (var g = Graphics.FromImage(result))
+ {
+ foreach (var child in children)
+ {
+ g.DrawImage(buffer[child.Input], new Rectangle(0, 0, inputImage.Width, inputImage.Height),
+ 0, 0, inputImage.Width, inputImage.Height, GraphicsUnit.Pixel);
+ }
+ g.Flush();
+ }
+ result.Save(@"C:\test.png");
+ buffer[this.Result] = result;
}
- public override Bitmap Process()
- {
- //Todo
-
- //Bitmap merged = new Bitmap((int)this.Owner.Width.Value, (int)this.Owner.Height.Value);
- //Graphics mergedGraphics = Graphics.FromImage(merged);
-
- //foreach (string resultId in this.MergeResults)
- //{
- // mergedGraphics.DrawImageUnscaled(this.Owner.Results[resultId](), new Point(0, 0));
- //}
-
- //mergedGraphics.Save();
- //mergedGraphics.Dispose();
-
- //results.Add(this.Result, () => merged);
-
- return null;
- }
-
-
public override SvgElement DeepCopy()
{
- throw new NotImplementedException();
+ return DeepCopy();
}
}
diff --git a/Source/Filter Effects/feMerge/SvgMergeNode.cs b/Source/Filter Effects/feMerge/SvgMergeNode.cs
index a302a7a814818de9d6cf8689d5cf297d7dbb6bf4..c10ae201bb712e066864592749a8b6312a4298b1 100644
--- a/Source/Filter Effects/feMerge/SvgMergeNode.cs
+++ b/Source/Filter Effects/feMerge/SvgMergeNode.cs
@@ -10,13 +10,13 @@ namespace Svg.FilterEffects
{
[SvgElement("feMergeNode")]
- public class SvgMergeNode : SvgFilterPrimitive
+ public class SvgMergeNode : SvgElement
{
- public override Bitmap Process()
+ [SvgAttribute("in")]
+ public string Input
{
- //Todo
-
- return null;
+ get { return this.Attributes.GetAttribute("in"); }
+ set { this.Attributes["in"] = value; }
}
public override SvgElement DeepCopy()
diff --git a/Source/Filter Effects/feOffset/SvgOffset.cs b/Source/Filter Effects/feOffset/SvgOffset.cs
index 37680e72b2857a770e976066fb6186e143b95564..43ba4d06f9725925c49c091687175822e0eed3fb 100644
--- a/Source/Filter Effects/feOffset/SvgOffset.cs
+++ b/Source/Filter Effects/feOffset/SvgOffset.cs
@@ -1,7 +1,6 @@
using System;
using System.Drawing;
using System.Collections.Generic;
-using Svg.Filter_Effects.feColourMatrix;
namespace Svg.FilterEffects
{
@@ -28,13 +27,27 @@ namespace Svg.FilterEffects
/// Note: this is not used in calculations to bitmap - used only to allow for svg xml output
///
[SvgAttribute("dy")]
- public string Dy { get; set; }
+ public SvgUnit Dy { get; set; }
- public override Bitmap Process()
+ public override void Process(ImageBuffer buffer)
{
- return null;
+ var inputImage = buffer[this.Input];
+ var result = new Bitmap(inputImage.Width, inputImage.Height);
+
+ var pts = new PointF[] { new PointF(this.Dx.ToDeviceValue(null, UnitRenderingType.Horizontal, null),
+ this.Dy.ToDeviceValue(null, UnitRenderingType.Vertical, null)) };
+ buffer.Transform.TransformVectors(pts);
+
+ using (var g = Graphics.FromImage(result))
+ {
+ g.DrawImage(inputImage, new Rectangle((int)pts[0].X, (int)pts[0].Y,
+ inputImage.Width, inputImage.Height),
+ 0, 0, inputImage.Width, inputImage.Height, GraphicsUnit.Pixel);
+ g.Flush();
+ }
+ buffer[this.Result] = result;
}
diff --git a/Source/Painting/SvgGradientServer.cs b/Source/Painting/SvgGradientServer.cs
index f5c36617b1d64edcbc2a3fee4ca5a8cf3dfb4186..155286937ce3673cab778e910f597a808af0e964 100644
--- a/Source/Painting/SvgGradientServer.cs
+++ b/Source/Painting/SvgGradientServer.cs
@@ -111,7 +111,7 @@ namespace Svg
}
}
- private Matrix EffectiveGradientTransform
+ protected Matrix EffectiveGradientTransform
{
get
{
@@ -247,6 +247,15 @@ namespace Svg
return newVector[0];
}
+ protected float TransformDistance(float dist)
+ {
+ var newVector = new[] { new PointF(dist, 0) };
+
+ EffectiveGradientTransform.TransformVectors(newVector);
+
+ return (float)Math.Sqrt(Math.Pow(newVector[0].X, 2) + Math.Pow(newVector[0].Y, 2));
+ }
+
protected static double CalculateDistance(PointF first, PointF second)
{
return Math.Sqrt(Math.Pow(first.X - second.X, 2) + Math.Pow(first.Y - second.Y, 2));
diff --git a/Source/Painting/SvgLinearGradientServer.cs b/Source/Painting/SvgLinearGradientServer.cs
index d2356fd354bdff0cbb7139d8ea5a56f911f08a14..cc791efa9084c376d7aa8012b5637e174cc844c7 100644
--- a/Source/Painting/SvgLinearGradientServer.cs
+++ b/Source/Painting/SvgLinearGradientServer.cs
@@ -173,7 +173,7 @@ namespace Svg
if (boundable.Bounds.Contains(specifiedEnd))
{
- effectiveEnd = CalculateClosestIntersectionPoint(effectiveEnd, intersectionPoints);
+ effectiveEnd = CalculateClosestIntersectionPoint(expandedEnd, intersectionPoints);
effectiveEnd = MovePointAlongVector(effectiveEnd, specifiedUnitVector, 1);
}
@@ -296,48 +296,87 @@ namespace Svg
return result;
}
+ /// http://community.topcoder.com/tc?module=Static&d1=tutorials&d2=geometry2
private PointF? Intersection(LineF other)
{
- var a1 = Y2 - Y1;
- var b1 = X1 - X2;
- var c1 = X2 * Y1 - X1 * Y2;
+ const int precision = 8;
- var r3 = a1 * other.X1 + b1 * other.Y1 + c1;
- var r4 = a1 * other.X2 + b1 * other.Y2 + c1;
+ var a1 = (double)Y2 - Y1;
+ var b1 = (double)X1 - X2;
+ var c1 = a1 * X1 + b1 * Y1;
- if (r3 != 0 && r4 != 0 && Math.Sign(r3) == Math.Sign(r4))
+ var a2 = (double)other.Y2 - other.Y1;
+ var b2 = (double)other.X1 - other.X2;
+ var c2 = a2 * other.X1 + b2 * other.Y1;
+
+ var det = a1 * b2 - a2 * b1;
+ if (det == 0)
{
return null;
}
+ else
+ {
+ var xi = (b2 * c1 - b1 * c2) / det;
+ var yi = (a1 * c2 - a2 * c1) / det;
+
+ if (Math.Round(Math.Min(X1, X2), precision) <= Math.Round(xi, precision) &&
+ Math.Round(xi, precision) <= Math.Round(Math.Max(X1, X2), precision) &&
+ Math.Round(Math.Min(Y1, Y2), precision) <= Math.Round(yi, precision) &&
+ Math.Round(yi, precision) <= Math.Round(Math.Max(Y1, Y2), precision) &&
+ Math.Round(Math.Min(other.X1, other.X2), precision) <= Math.Round(xi, precision) &&
+ Math.Round(xi, precision) <= Math.Round(Math.Max(other.X1, other.X2), precision) &&
+ Math.Round(Math.Min(other.Y1, other.Y2), precision) <= Math.Round(yi, precision) &&
+ Math.Round(yi, precision) <= Math.Round(Math.Max(other.Y1, other.Y2), precision))
+ {
+ return new PointF((float)xi, (float)yi);
+ }
+ else
+ {
+ return null;
+ }
+ }
- var a2 = other.Y2 - other.Y1;
- var b2 = other.X1 - other.X2;
- var c2 = other.X2 * other.Y1 - other.X1 * other.Y2;
- var r1 = a2 * X1 + b2 * Y1 + c2;
- var r2 = a2 * X2 + b2 * Y2 + c2;
+ //var a1 = Y2 - Y1;
+ //var b1 = X1 - X2;
+ //var c1 = X2 * Y1 - X1 * Y2;
- if (r1 != 0 && r2 != 0 && Math.Sign(r1) == Math.Sign(r2))
- {
- return (null);
- }
+ //var r3 = a1 * other.X1 + b1 * other.Y1 + c1;
+ //var r4 = a1 * other.X2 + b1 * other.Y2 + c1;
- var denom = a1 * b2 - a2 * b1;
+ //if (r3 != 0 && r4 != 0 && Math.Sign(r3) == Math.Sign(r4))
+ //{
+ // return null;
+ //}
- if (denom == 0)
- {
- return null;
- }
+ //var a2 = other.Y2 - other.Y1;
+ //var b2 = other.X1 - other.X2;
+ //var c2 = other.X2 * other.Y1 - other.X1 * other.Y2;
+
+ //var r1 = a2 * X1 + b2 * Y1 + c2;
+ //var r2 = a2 * X2 + b2 * Y2 + c2;
+
+ //if (r1 != 0 && r2 != 0 && Math.Sign(r1) == Math.Sign(r2))
+ //{
+ // return (null);
+ //}
+
+ //var denom = a1 * b2 - a2 * b1;
+
+ //if (denom == 0)
+ //{
+ // return null;
+ //}
- var offset = denom < 0 ? -denom / 2 : denom / 2;
+ //var offset = denom < 0 ? -denom / 2 : denom / 2;
- var num = b1 * c2 - b2 * c1;
- var x = (num < 0 ? num - offset : num + offset) / denom;
+ //var num = b1 * c2 - b2 * c1;
+ //var x = (num < 0 ? num - offset : num + offset) / denom;
- num = a2 * c1 - a1 * c2;
- var y = (num < 0 ? num - offset : num + offset) / denom;
+ //num = a2 * c1 - a1 * c2;
+ //var y = (num < 0 ? num - offset : num + offset) / denom;
- return new PointF(x, y);
+ //return new PointF(x, y);
}
private static void AddIfIntersect(LineF first, LineF second, ICollection result)
diff --git a/Source/Painting/SvgRadialGradientServer.cs b/Source/Painting/SvgRadialGradientServer.cs
index 349c5dc1d4e20ffd2bf21874586dcf5a393be815..5a1a6cd443c7211079475a3df32870a719b086b7 100644
--- a/Source/Painting/SvgRadialGradientServer.cs
+++ b/Source/Painting/SvgRadialGradientServer.cs
@@ -1,6 +1,7 @@
using System;
using System.Diagnostics;
using System.Drawing;
+using System.Collections.Generic;
using System.Drawing.Drawing2D;
using System.Linq;
@@ -95,6 +96,8 @@ namespace Svg
Radius = new SvgUnit(SvgUnitType.Percentage, 50F);
}
+ private object _lockObj = new Object();
+
public override Brush GetBrush(SvgVisualElement renderingElement, SvgRenderer renderer, float opacity)
{
LoadStops(renderingElement);
@@ -102,20 +105,39 @@ namespace Svg
try
{
if (this.GradientUnits == SvgCoordinateUnits.ObjectBoundingBox) renderer.Boundable(renderingElement);
+
+ // Calculate the path and transform it appropriately
var origin = renderer.Boundable().Location;
- var centerPoint = CalculateCenterPoint(renderer, origin);
- var focalPoint = CalculateFocalPoint(renderer, origin);
-
- var specifiedRadius = CalculateRadius(renderer);
- var effectiveRadius = CalculateEffectiveRadius(renderingElement, centerPoint, specifiedRadius);
-
- var brush = new PathGradientBrush(CreateGraphicsPath(origin, centerPoint, effectiveRadius))
- {
- InterpolationColors = CalculateColorBlend(renderer, opacity, specifiedRadius, effectiveRadius),
- CenterPoint = focalPoint
- };
-
- Debug.Assert(brush.Rectangle.Contains(renderingElement.Bounds), "Brush rectangle does not contain rendering element bounds!");
+ var center = new PointF(origin.X + CenterX.ToDeviceValue(renderer, UnitRenderingType.HorizontalOffset, this),
+ origin.Y + CenterY.ToDeviceValue(renderer, UnitRenderingType.VerticalOffset, this));
+ var specifiedRadius = Radius.ToDeviceValue(renderer, UnitRenderingType.Other, this);
+ var path = new GraphicsPath();
+ path.AddEllipse(
+ origin.X + center.X - specifiedRadius, origin.Y + center.Y - specifiedRadius,
+ specifiedRadius * 2, specifiedRadius * 2
+ );
+ path.Transform(EffectiveGradientTransform);
+
+
+ // Calculate any required scaling
+ var scale = CalcScale(renderingElement.Bounds, path);
+
+ // Get the color blend and any tweak to the scaling
+ var blend = CalculateColorBlend(renderer, opacity, scale, out scale);
+
+ // Transform the path based on the scaling
+ var gradBounds = path.GetBounds();
+ var transCenter = new PointF(gradBounds.Left + gradBounds.Width / 2, gradBounds.Top + gradBounds.Height / 2);
+ var scaleMat = new Matrix();
+ scaleMat.Translate(-1 * transCenter.X, -1 * transCenter.Y, MatrixOrder.Append);
+ scaleMat.Scale(scale, scale, MatrixOrder.Append);
+ scaleMat.Translate(transCenter.X, transCenter.Y, MatrixOrder.Append);
+ path.Transform(scaleMat);
+
+ // calculate the brush
+ var brush = new PathGradientBrush(path);
+ brush.CenterPoint = CalculateFocalPoint(renderer, origin);
+ brush.InterpolationColors = blend;
return brush;
}
@@ -125,12 +147,38 @@ namespace Svg
}
}
- private PointF CalculateCenterPoint(SvgRenderer renderer, PointF origin)
+ ///
+ /// Determine how much (approximately) the path must be scaled to contain the rectangle
+ ///
+ /// Bounds that the path must contain
+ /// Path of the gradient
+ /// Scale factor
+ ///
+ /// This method continually transforms the rectangle (fewer points) until it is contained by the path
+ /// and returns the result of the search. The scale factor is set to a constant 95%
+ ///
+ private float CalcScale(RectangleF bounds, GraphicsPath path)
{
- var deviceCenterX = origin.X + CenterX.ToDeviceValue(renderer, UnitRenderingType.HorizontalOffset, this);
- var deviceCenterY = origin.Y + CenterY.ToDeviceValue(renderer, UnitRenderingType.VerticalOffset, this);
- var transformedCenterPoint = TransformPoint(new PointF(deviceCenterX, deviceCenterY));
- return transformedCenterPoint;
+ var points = new PointF[] {
+ new PointF(bounds.Left, bounds.Top),
+ new PointF(bounds.Right, bounds.Top),
+ new PointF(bounds.Right, bounds.Bottom),
+ new PointF(bounds.Left, bounds.Bottom)
+ };
+ var pathBounds = path.GetBounds();
+ var pathCenter = new PointF(pathBounds.X + pathBounds.Width / 2, pathBounds.Y + pathBounds.Height / 2);
+ var transform = new Matrix();
+ transform.Translate(-1 * pathCenter.X, -1 * pathCenter.Y, MatrixOrder.Append);
+ transform.Scale(.95f, .95f, MatrixOrder.Append);
+ transform.Translate(pathCenter.X, pathCenter.Y, MatrixOrder.Append);
+
+ var boundsTest = RectangleF.Inflate(bounds, 0, 0);
+ while (!(path.IsVisible(points[0]) && path.IsVisible(points[1]) &&
+ path.IsVisible(points[2]) && path.IsVisible(points[3])))
+ {
+ transform.TransformPoints(points);
+ }
+ return bounds.Height / (points[2].Y - points[1].Y);
}
private PointF CalculateFocalPoint(SvgRenderer renderer, PointF origin)
@@ -141,44 +189,6 @@ namespace Svg
return transformedFocalPoint;
}
- private float CalculateRadius(SvgRenderer renderer)
- {
- var radius = Radius.ToDeviceValue(renderer, UnitRenderingType.Other, this);
- var transformRadiusVector = TransformVector(new PointF(radius, 0));
- var transformedRadius = CalculateLength(transformRadiusVector);
- return transformedRadius;
- }
-
- private float CalculateEffectiveRadius(ISvgBoundable boundable, PointF centerPoint, float specifiedRadius)
- {
- if (SpreadMethod != SvgGradientSpreadMethod.Pad)
- {
- return specifiedRadius;
- }
-
- var topLeft = new PointF(boundable.Bounds.Left, boundable.Bounds.Top);
- var topRight = new PointF(boundable.Bounds.Right, boundable.Bounds.Top);
- var bottomRight = new PointF(boundable.Bounds.Right, boundable.Bounds.Bottom);
- var bottomLeft = new PointF(boundable.Bounds.Left, boundable.Bounds.Bottom);
-
- var effectiveRadius = (float)Math.Ceiling(
- Math.Max(
- Math.Max(
- CalculateDistance(centerPoint, topLeft),
- CalculateDistance(centerPoint, topRight)
- ),
- Math.Max(
- CalculateDistance(centerPoint, bottomRight),
- CalculateDistance(centerPoint, bottomLeft)
- )
- )
- );
-
- effectiveRadius = Math.Max(effectiveRadius, specifiedRadius);
-
- return effectiveRadius;
- }
-
private static GraphicsPath CreateGraphicsPath(PointF origin, PointF centerPoint, float effectiveRadius)
{
var path = new GraphicsPath();
@@ -193,23 +203,66 @@ namespace Svg
return path;
}
- private ColorBlend CalculateColorBlend(SvgRenderer renderer, float opacity, float specifiedRadius, float effectiveRadius)
+ private ColorBlend CalculateColorBlend(SvgRenderer renderer, float opacity, float scale, out float outScale)
{
var colorBlend = GetColorBlend(renderer, opacity, true);
+ float newScale;
+ List pos;
+ List colors;
- if (specifiedRadius >= effectiveRadius)
+ outScale = scale;
+ if (scale > 1)
{
- return colorBlend;
- }
-
- for (var i = 0; i < colorBlend.Positions.Length - 1; i++)
- {
- colorBlend.Positions[i] = 1 - (specifiedRadius / effectiveRadius) * (1 - colorBlend.Positions[i]);
+ switch (this.SpreadMethod)
+ {
+ case SvgGradientSpreadMethod.Reflect:
+ newScale = (float)Math.Ceiling(scale);
+ pos = (from p in colorBlend.Positions select p / newScale).ToList();
+ colors = colorBlend.Colors.ToList();
+
+ for (var i = 1; i < newScale; i++)
+ {
+ if (i % 2 == 1)
+ {
+ pos.AddRange(from p in colorBlend.Positions.Reverse().Skip(1) select (1 - p + i) / newScale);
+ colors.AddRange(colorBlend.Colors.Reverse().Skip(1));
+ }
+ else
+ {
+ pos.AddRange(from p in colorBlend.Positions.Skip(1) select (p + i) / newScale);
+ colors.AddRange(colorBlend.Colors.Skip(1));
+ }
+ }
+
+ colorBlend.Positions = pos.ToArray();
+ colorBlend.Colors = colors.ToArray();
+ outScale = newScale;
+ break;
+ case SvgGradientSpreadMethod.Repeat:
+ newScale = (float)Math.Ceiling(scale);
+ pos = (from p in colorBlend.Positions select p / newScale).ToList();
+ colors = colorBlend.Colors.ToList();
+
+ for (var i = 1; i < newScale; i++)
+ {
+ pos.AddRange(from p in colorBlend.Positions select (p <= 0 ? 0.001f : p) / newScale);
+ colors.AddRange(colorBlend.Colors);
+ }
+
+ break;
+ default:
+ for (var i = 0; i < colorBlend.Positions.Length - 1; i++)
+ {
+ colorBlend.Positions[i] = 1 - (1 - colorBlend.Positions[i]) / scale;
+ }
+
+ colorBlend.Positions = new[] { 0F }.Concat(colorBlend.Positions).ToArray();
+ colorBlend.Colors = new[] { colorBlend.Colors.First() }.Concat(colorBlend.Colors).ToArray();
+
+ break;
+ }
}
- colorBlend.Positions = new[] { 0F }.Concat(colorBlend.Positions).ToArray();
- colorBlend.Colors = new[] { colorBlend.Colors.First() }.Concat(colorBlend.Colors).ToArray();
-
return colorBlend;
}
diff --git a/Source/Svg.csproj b/Source/Svg.csproj
index b66ced056eb9248b51f1f15b62a0e9288c8196d5..7e2ee28c3dc68fcf372139d6c699a103bc2ed1da 100644
--- a/Source/Svg.csproj
+++ b/Source/Svg.csproj
@@ -101,6 +101,7 @@
+
diff --git a/Source/Text/SvgTextBase.cs b/Source/Text/SvgTextBase.cs
index 35040b7de16219e0691de54421ed191e005ffb1c..8729f8ed35e036ba2f7c2e5bdaaf9a3e75076459 100644
--- a/Source/Text/SvgTextBase.cs
+++ b/Source/Text/SvgTextBase.cs
@@ -275,6 +275,7 @@ namespace Svg
where (n is SvgContentNode || n is SvgTextBase) && !string.IsNullOrEmpty(n.Content)
select n).ToList();
+ // Individual character spacing
if (nodes.FirstOrDefault() is SvgContentNode && _x.Count > 1)
{
string ch;
@@ -298,7 +299,9 @@ namespace Svg
}
}
+ // Calculate the bounds of the text
ISvgNode node;
+ var accumulateDims = true;
for (var i = 0; i < nodes.Count; i++)
{
node = nodes[i];
@@ -316,10 +319,15 @@ namespace Svg
{
stringBounds = innerText.GetTextBounds(renderer).Bounds;
result.Nodes.Add(new NodeBounds() { Bounds = stringBounds, Node = node, xOffset = totalWidth });
- if (innerText.Dx.Count == 1) totalWidth += innerText.Dx[0].ToDeviceValue(renderer, UnitRenderingType.Horizontal, this);
+ accumulateDims = accumulateDims && SvgUnitCollection.IsNullOrEmpty(innerText.X) && SvgUnitCollection.IsNullOrEmpty(innerText.Y);
+ if (accumulateDims && innerText.Dx.Count == 1) totalWidth += innerText.Dx[0].ToDeviceValue(renderer, UnitRenderingType.Horizontal, this);
+ }
+
+ if (accumulateDims)
+ {
+ totalHeight = Math.Max(totalHeight, stringBounds.Height);
+ totalWidth += stringBounds.Width;
}
- totalHeight = Math.Max(totalHeight, stringBounds.Height);
- totalWidth += stringBounds.Width;
}
}
result.Bounds = new SizeF(totalWidth, totalHeight);
diff --git a/Tests/SvgW3CTestRunner/View.cs b/Tests/SvgW3CTestRunner/View.cs
index 8a09148032254e9ccf41adf9f51964f63d3d98f8..c39a31dc7e583087f42363bb97a91e77b8b81948 100644
--- a/Tests/SvgW3CTestRunner/View.cs
+++ b/Tests/SvgW3CTestRunner/View.cs
@@ -32,9 +32,16 @@ namespace SvgW3CTestRunner
try
{
var doc = SvgDocument.Open(_svgBasePath + fileName);
- var img = new Bitmap(480, 360);
- doc.Draw(img);
- picSvg.Image = img;
+ if (fileName.StartsWith("__"))
+ {
+ picSvg.Image = doc.Draw();
+ }
+ else
+ {
+ var img = new Bitmap(480, 360);
+ doc.Draw(img);
+ picSvg.Image = img;
+ }
}
catch (Exception ex)
{