Commit 1a00f391 authored by Eric Domke's avatar Eric Domke
Browse files

Bug fixes: Gradients and Filters

- Support of generic fonts (serif, sans-serif, monospace)
- Initial support of filters
- Fixes to gradient rendering
parent 2187be3e
...@@ -99,34 +99,66 @@ namespace Svg ...@@ -99,34 +99,66 @@ namespace Svg
this._requiresSmoothRendering = false; this._requiresSmoothRendering = false;
} }
protected virtual bool Renderable { get { return true; } }
/// <summary> /// <summary>
/// Renders the <see cref="SvgElement"/> and contents to the specified <see cref="Graphics"/> object. /// Renders the <see cref="SvgElement"/> and contents to the specified <see cref="Graphics"/> object.
/// </summary> /// </summary>
/// <param name="renderer">The <see cref="SvgRenderer"/> object to render to.</param> /// <param name="renderer">The <see cref="SvgRenderer"/> object to render to.</param>
protected override void Render(SvgRenderer renderer) 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); bool renderNormal = true;
this.SetClip(renderer);
// If this element needs smoothing enabled turn anti-aliasing on if (renderFilter && this.Filter != null)
if (this.RequiresSmoothRendering)
{ {
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 (renderNormal)
if (this.RequiresSmoothRendering && renderer.SmoothingMode == SmoothingMode.AntiAlias)
{ {
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);
} }
} }
......
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Drawing;
using System.Text; using System.Text;
using System.Reflection; using System.Reflection;
using System.ComponentModel; using System.ComponentModel;
...@@ -53,6 +54,16 @@ namespace Svg ...@@ -53,6 +54,16 @@ namespace Svg
} }
} }
/// <summary>
/// Gets or sets the fill <see cref="SvgPaintServer"/> of this element.
/// </summary>
[SvgAttribute("enable-background")]
public virtual string EnableBackground
{
get { return this.Attributes["enable-background"] as string; }
set { this.Attributes["enable-background"] = value; }
}
/// <summary> /// <summary>
/// Gets or sets the fill <see cref="SvgPaintServer"/> of this element. /// Gets or sets the fill <see cref="SvgPaintServer"/> of this element.
/// </summary> /// </summary>
...@@ -361,26 +372,43 @@ namespace Svg ...@@ -361,26 +372,43 @@ namespace Svg
break; break;
} }
var family = ValidateFontFamily(this.FontFamily);
if (!family.IsStyleAvailable(fontStyle))
{
// Do Something
}
// Get the font-family // Get the font-family
string family = ValidateFontFamily(this.FontFamily) ?? DefaultFontFamily;
return new System.Drawing.Font(family, fontSize, fontStyle, System.Drawing.GraphicsUnit.Pixel); 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. // 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 fontParts = (fontFamilyList ?? "").Split(new[] { ',' }).Select(fontName => fontName.Trim(new[] { '"', ' ', '\'' }));
var families = System.Drawing.FontFamily.Families; var families = System.Drawing.FontFamily.Families;
FontFamily family;
// Find a the first font that exists in the list of installed font families. // Find a the first font that exists in the list of installed font families.
//styles from IE get sent through as lowercase. //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. // No valid font family found from the list requested.
return null; return System.Drawing.FontFamily.GenericSansSerif;
} }
} }
......
...@@ -23,6 +23,12 @@ namespace Svg ...@@ -23,6 +23,12 @@ namespace Svg
return ret; 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));
}
} }
/// <summary> /// <summary>
......
...@@ -56,23 +56,25 @@ namespace Svg ...@@ -56,23 +56,25 @@ namespace Svg
} }
} }
/// <summary> protected override bool Renderable { get { return false; } }
/// Renders the <see cref="SvgElement"/> and contents to the specified <see cref="Graphics"/> object.
/// </summary>
/// <param name="graphics">The <see cref="Graphics"/> object to render to.</param>
protected override void Render(SvgRenderer renderer)
{
if (!Visible || !Displayable)
return;
if (this.PushTransforms(renderer)) ///// <summary>
{ ///// Renders the <see cref="SvgElement"/> and contents to the specified <see cref="Graphics"/> object.
this.SetClip(renderer); ///// </summary>
base.RenderChildren(renderer); ///// <param name="graphics">The <see cref="Graphics"/> object to render to.</param>
this.ResetClip(renderer); //protected override void Render(SvgRenderer renderer)
this.PopTransforms(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() public override SvgElement DeepCopy()
......
...@@ -77,6 +77,8 @@ namespace Svg ...@@ -77,6 +77,8 @@ namespace Svg
// } // }
// } // }
protected override bool Renderable { get { return false; } }
protected override void Render(SvgRenderer renderer) protected override void Render(SvgRenderer renderer)
{ {
if (!Visible || !Displayable) if (!Visible || !Displayable)
......
...@@ -56,21 +56,23 @@ namespace Svg ...@@ -56,21 +56,23 @@ namespace Svg
} }
} }
/// <summary> protected override bool Renderable { get { return false; } }
/// Renders the <see cref="SvgElement"/> and contents to the specified <see cref="Graphics"/> object.
/// </summary>
/// <param name="renderer">The <see cref="Graphics"/> object to render to.</param>
protected override void Render(SvgRenderer renderer)
{
if (!Visible || !Displayable)
return;
this.PushTransforms(renderer); ///// <summary>
this.SetClip(renderer); ///// Renders the <see cref="SvgElement"/> and contents to the specified <see cref="Graphics"/> object.
base.RenderChildren(renderer); ///// </summary>
this.ResetClip(renderer); ///// <param name="renderer">The <see cref="Graphics"/> object to render to.</param>
this.PopTransforms(renderer); //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() public override SvgElement DeepCopy()
{ {
......
...@@ -7,6 +7,28 @@ using ExCSS.Model.TextBlocks; ...@@ -7,6 +7,28 @@ using ExCSS.Model.TextBlocks;
// ReSharper disable once CheckNamespace // ReSharper disable once CheckNamespace
using System; 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 namespace ExCSS
{ {
......
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<string, Bitmap>
{
private const string BufferKey = "__!!BUFFER";
private Dictionary<string, Bitmap> _images;
private RectangleF _bounds;
private SvgRenderer _renderer;
private Action<SvgRenderer> _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<SvgRenderer> renderMethod)
{
_bounds = bounds;
_inflate = inflate;
_renderer = renderer;
_renderMethod = renderMethod;
_images = new Dictionary<string, Bitmap>();
_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<KeyValuePair<string, Bitmap>> 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<KeyValuePair<string, Bitmap>>.IsReadOnly
{
get { return false; }
}
ICollection<string> IDictionary<string, Bitmap>.Keys
{
get { return _images.Keys; }
}
ICollection<Bitmap> IDictionary<string, Bitmap>.Values
{
get { return _images.Values; }
}
void ICollection<KeyValuePair<string, Bitmap>>.Add(KeyValuePair<string, Bitmap> item)
{
_images.Add(item.Key, item.Value);
}
bool ICollection<KeyValuePair<string, Bitmap>>.Contains(KeyValuePair<string, Bitmap> item)
{
return ((IDictionary<string, Bitmap>)_images).Contains(item);
}
void ICollection<KeyValuePair<string, Bitmap>>.CopyTo(KeyValuePair<string, Bitmap>[] array, int arrayIndex)
{
((IDictionary<string, Bitmap>)_images).CopyTo(array, arrayIndex);
}
bool ICollection<KeyValuePair<string, Bitmap>>.Remove(KeyValuePair<string, Bitmap> item)
{
return _images.Remove(item.Key);
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return _images.GetEnumerator();
}
}
}
...@@ -72,15 +72,11 @@ namespace Svg.FilterEffects ...@@ -72,15 +72,11 @@ namespace Svg.FilterEffects
set { this.Attributes["color-interpolation-filters"] = value; } set { this.Attributes["color-interpolation-filters"] = value; }
} }
internal Dictionary<string, Func<SvgVisualElement, SvgRenderer, Bitmap>> Buffer { get; private set; }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="SvgFilter"/> class. /// Initializes a new instance of the <see cref="SvgFilter"/> class.
/// </summary> /// </summary>
public SvgFilter() public SvgFilter()
{ {
this.Buffer = new Dictionary<string, Func<SvgVisualElement, SvgRenderer, Bitmap>>();
} }
/// <summary> /// <summary>
...@@ -103,10 +99,32 @@ namespace Svg.FilterEffects ...@@ -103,10 +99,32 @@ namespace Svg.FilterEffects
return (SvgFilter)this.MemberwiseClone(); 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<SvgRenderer> renderMethod)
{ {
this.Buffer.Clear(); var inflate = 0.5f;
this.PopulateDefaults(element, renderer); var transform = GetTransform(element);
var bounds = GetPathBounds(element, renderer, transform);
var buffer = new ImageBuffer(bounds, inflate, renderer, renderMethod) { Transform = transform };
IEnumerable<SvgFilterPrimitive> primitives = this.Children.OfType<SvgFilterPrimitive>(); IEnumerable<SvgFilterPrimitive> primitives = this.Children.OfType<SvgFilterPrimitive>();
...@@ -114,21 +132,21 @@ namespace Svg.FilterEffects ...@@ -114,21 +132,21 @@ namespace Svg.FilterEffects
{ {
foreach (var primitive in primitives) foreach (var primitive in primitives)
{ {
this.Buffer.Add(primitive.Result, (e, r) => primitive.Process()); primitive.Process(buffer);
} }
// Render the final filtered image // 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 #region Defaults
...@@ -147,58 +165,7 @@ namespace Svg.FilterEffects ...@@ -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 #endregion
......
...@@ -8,12 +8,12 @@ namespace Svg.FilterEffects ...@@ -8,12 +8,12 @@ namespace Svg.FilterEffects
{ {
public abstract class SvgFilterPrimitive : SvgElement public abstract class SvgFilterPrimitive : SvgElement
{ {
public static readonly string SourceGraphic = "SourceGraphic"; public const string SourceGraphic = "SourceGraphic";
public static readonly string SourceAlpha = "SourceAlpha"; public const string SourceAlpha = "SourceAlpha";
public static readonly string BackgroundImage = "BackgroundImage"; public const string BackgroundImage = "BackgroundImage";
public static readonly string BackgroundAlpha = "BackgroundAlpha"; public const string BackgroundAlpha = "BackgroundAlpha";
public static readonly string FillPaint = "FillPaint"; public const string FillPaint = "FillPaint";
public static readonly string StrokePaint = "StrokePaint"; public const string StrokePaint = "StrokePaint";
[SvgAttribute("in")] [SvgAttribute("in")]
public string Input public string Input
...@@ -34,6 +34,6 @@ namespace Svg.FilterEffects ...@@ -34,6 +34,6 @@ namespace Svg.FilterEffects
get { return (SvgFilter)this.Parent; } get { return (SvgFilter)this.Parent; }
} }
public abstract Bitmap Process(); public abstract void Process(ImageBuffer buffer);
} }
} }
\ No newline at end of file
using System; using System;
using System.Drawing; using System.Drawing;
using System.Collections.Generic; using System.Collections.Generic;
using Svg.Filter_Effects.feColourMatrix; using System.Linq;
using System.Drawing.Imaging;
namespace Svg.FilterEffects namespace Svg.FilterEffects
{ {
...@@ -19,8 +20,7 @@ namespace Svg.FilterEffects ...@@ -19,8 +20,7 @@ namespace Svg.FilterEffects
/// </summary> /// </summary>
[SvgAttribute("type")] [SvgAttribute("type")]
public SvgColourMatrixType Type { get; set; } public SvgColourMatrixType Type { get; set; }
/// <summary> /// <summary>
/// list of <number>s /// list of <number>s
...@@ -29,13 +29,74 @@ namespace Svg.FilterEffects ...@@ -29,13 +29,74 @@ namespace Svg.FilterEffects
/// </summary> /// </summary>
[SvgAttribute("values")] [SvgAttribute("values")]
public string Values { get; set; } 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() var result = new Bitmap(inputImage.Width, inputImage.Height);
{ using (var g = Graphics.FromImage(result))
return null; {
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;
} }
......
...@@ -3,7 +3,7 @@ using System.Collections.Generic; ...@@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
namespace Svg.Filter_Effects.feColourMatrix namespace Svg.FilterEffects
{ {
public enum SvgColourMatrixType public enum SvgColourMatrixType
{ {
......
...@@ -67,7 +67,10 @@ namespace Svg.FilterEffects ...@@ -67,7 +67,10 @@ namespace Svg.FilterEffects
public Bitmap Apply(Image inputImage) 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))) using (RawBitmap dest = new RawBitmap(new Bitmap(inputImage.Width, inputImage.Height)))
{ {
...@@ -250,11 +253,11 @@ namespace Svg.FilterEffects ...@@ -250,11 +253,11 @@ namespace Svg.FilterEffects
public override Bitmap Process() public override void Process(ImageBuffer buffer)
{ {
//Todo var inputImage = buffer[this.Input];
var result = Apply(inputImage);
return null; buffer[this.Result] = result;
} }
......
...@@ -5,44 +5,34 @@ using System.Text; ...@@ -5,44 +5,34 @@ using System.Text;
using System.Drawing; using System.Drawing;
using System.Drawing.Drawing2D; using System.Drawing.Drawing2D;
using System.Drawing.Imaging; using System.Drawing.Imaging;
using System.Linq;
namespace Svg.FilterEffects namespace Svg.FilterEffects
{ {
[SvgElement("feMerge")] [SvgElement("feMerge")]
public class SvgMerge : SvgFilterPrimitive public class SvgMerge : SvgFilterPrimitive
{ {
public StringCollection MergeResults { get; private set; } public override void Process(ImageBuffer buffer)
public SvgMerge()
{ {
MergeResults = new StringCollection(); var children = this.Children.OfType<SvgMergeNode>().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() public override SvgElement DeepCopy()
{ {
throw new NotImplementedException(); return DeepCopy<SvgMerge>();
} }
} }
......
...@@ -10,13 +10,13 @@ namespace Svg.FilterEffects ...@@ -10,13 +10,13 @@ namespace Svg.FilterEffects
{ {
[SvgElement("feMergeNode")] [SvgElement("feMergeNode")]
public class SvgMergeNode : SvgFilterPrimitive public class SvgMergeNode : SvgElement
{ {
public override Bitmap Process() [SvgAttribute("in")]
public string Input
{ {
//Todo get { return this.Attributes.GetAttribute<string>("in"); }
set { this.Attributes["in"] = value; }
return null;
} }
public override SvgElement DeepCopy() public override SvgElement DeepCopy()
......
using System; using System;
using System.Drawing; using System.Drawing;
using System.Collections.Generic; using System.Collections.Generic;
using Svg.Filter_Effects.feColourMatrix;
namespace Svg.FilterEffects namespace Svg.FilterEffects
{ {
...@@ -28,13 +27,27 @@ 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 /// Note: this is not used in calculations to bitmap - used only to allow for svg xml output
/// </summary> /// </summary>
[SvgAttribute("dy")] [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;
} }
......
...@@ -111,7 +111,7 @@ namespace Svg ...@@ -111,7 +111,7 @@ namespace Svg
} }
} }
private Matrix EffectiveGradientTransform protected Matrix EffectiveGradientTransform
{ {
get get
{ {
...@@ -247,6 +247,15 @@ namespace Svg ...@@ -247,6 +247,15 @@ namespace Svg
return newVector[0]; 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) 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)); return Math.Sqrt(Math.Pow(first.X - second.X, 2) + Math.Pow(first.Y - second.Y, 2));
......
...@@ -173,7 +173,7 @@ namespace Svg ...@@ -173,7 +173,7 @@ namespace Svg
if (boundable.Bounds.Contains(specifiedEnd)) if (boundable.Bounds.Contains(specifiedEnd))
{ {
effectiveEnd = CalculateClosestIntersectionPoint(effectiveEnd, intersectionPoints); effectiveEnd = CalculateClosestIntersectionPoint(expandedEnd, intersectionPoints);
effectiveEnd = MovePointAlongVector(effectiveEnd, specifiedUnitVector, 1); effectiveEnd = MovePointAlongVector(effectiveEnd, specifiedUnitVector, 1);
} }
...@@ -296,48 +296,87 @@ namespace Svg ...@@ -296,48 +296,87 @@ namespace Svg
return result; return result;
} }
/// <remarks>http://community.topcoder.com/tc?module=Static&d1=tutorials&d2=geometry2</remarks>
private PointF? Intersection(LineF other) private PointF? Intersection(LineF other)
{ {
var a1 = Y2 - Y1; const int precision = 8;
var b1 = X1 - X2;
var c1 = X2 * Y1 - X1 * Y2;
var r3 = a1 * other.X1 + b1 * other.Y1 + c1; var a1 = (double)Y2 - Y1;
var r4 = a1 * other.X2 + b1 * other.Y2 + c1; 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; 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 a1 = Y2 - Y1;
var r2 = a2 * X2 + b2 * Y2 + c2; //var b1 = X1 - X2;
//var c1 = X2 * Y1 - X1 * Y2;
if (r1 != 0 && r2 != 0 && Math.Sign(r1) == Math.Sign(r2)) //var r3 = a1 * other.X1 + b1 * other.Y1 + c1;
{ //var r4 = a1 * other.X2 + b1 * other.Y2 + c1;
return (null);
}
var denom = a1 * b2 - a2 * b1; //if (r3 != 0 && r4 != 0 && Math.Sign(r3) == Math.Sign(r4))
//{
// return null;
//}
if (denom == 0) //var a2 = other.Y2 - other.Y1;
{ //var b2 = other.X1 - other.X2;
return null; //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 num = b1 * c2 - b2 * c1;
var x = (num < 0 ? num - offset : num + offset) / denom; //var x = (num < 0 ? num - offset : num + offset) / denom;
num = a2 * c1 - a1 * c2; //num = a2 * c1 - a1 * c2;
var y = (num < 0 ? num - offset : num + offset) / denom; //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<PointF> result) private static void AddIfIntersect(LineF first, LineF second, ICollection<PointF> result)
......
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Drawing; using System.Drawing;
using System.Collections.Generic;
using System.Drawing.Drawing2D; using System.Drawing.Drawing2D;
using System.Linq; using System.Linq;
...@@ -95,6 +96,8 @@ namespace Svg ...@@ -95,6 +96,8 @@ namespace Svg
Radius = new SvgUnit(SvgUnitType.Percentage, 50F); Radius = new SvgUnit(SvgUnitType.Percentage, 50F);
} }
private object _lockObj = new Object();
public override Brush GetBrush(SvgVisualElement renderingElement, SvgRenderer renderer, float opacity) public override Brush GetBrush(SvgVisualElement renderingElement, SvgRenderer renderer, float opacity)
{ {
LoadStops(renderingElement); LoadStops(renderingElement);
...@@ -102,20 +105,39 @@ namespace Svg ...@@ -102,20 +105,39 @@ namespace Svg
try try
{ {
if (this.GradientUnits == SvgCoordinateUnits.ObjectBoundingBox) renderer.Boundable(renderingElement); if (this.GradientUnits == SvgCoordinateUnits.ObjectBoundingBox) renderer.Boundable(renderingElement);
// Calculate the path and transform it appropriately
var origin = renderer.Boundable().Location; var origin = renderer.Boundable().Location;
var centerPoint = CalculateCenterPoint(renderer, origin); var center = new PointF(origin.X + CenterX.ToDeviceValue(renderer, UnitRenderingType.HorizontalOffset, this),
var focalPoint = CalculateFocalPoint(renderer, origin); origin.Y + CenterY.ToDeviceValue(renderer, UnitRenderingType.VerticalOffset, this));
var specifiedRadius = Radius.ToDeviceValue(renderer, UnitRenderingType.Other, this);
var specifiedRadius = CalculateRadius(renderer); var path = new GraphicsPath();
var effectiveRadius = CalculateEffectiveRadius(renderingElement, centerPoint, specifiedRadius); path.AddEllipse(
origin.X + center.X - specifiedRadius, origin.Y + center.Y - specifiedRadius,
var brush = new PathGradientBrush(CreateGraphicsPath(origin, centerPoint, effectiveRadius)) specifiedRadius * 2, specifiedRadius * 2
{ );
InterpolationColors = CalculateColorBlend(renderer, opacity, specifiedRadius, effectiveRadius), path.Transform(EffectiveGradientTransform);
CenterPoint = focalPoint
};
// Calculate any required scaling
Debug.Assert(brush.Rectangle.Contains(renderingElement.Bounds), "Brush rectangle does not contain rendering element bounds!"); 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; return brush;
} }
...@@ -125,12 +147,38 @@ namespace Svg ...@@ -125,12 +147,38 @@ namespace Svg
} }
} }
private PointF CalculateCenterPoint(SvgRenderer renderer, PointF origin) /// <summary>
/// Determine how much (approximately) the path must be scaled to contain the rectangle
/// </summary>
/// <param name="bounds">Bounds that the path must contain</param>
/// <param name="path">Path of the gradient</param>
/// <returns>Scale factor</returns>
/// <remarks>
/// 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%
/// </remarks>
private float CalcScale(RectangleF bounds, GraphicsPath path)
{ {
var deviceCenterX = origin.X + CenterX.ToDeviceValue(renderer, UnitRenderingType.HorizontalOffset, this); var points = new PointF[] {
var deviceCenterY = origin.Y + CenterY.ToDeviceValue(renderer, UnitRenderingType.VerticalOffset, this); new PointF(bounds.Left, bounds.Top),
var transformedCenterPoint = TransformPoint(new PointF(deviceCenterX, deviceCenterY)); new PointF(bounds.Right, bounds.Top),
return transformedCenterPoint; 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) private PointF CalculateFocalPoint(SvgRenderer renderer, PointF origin)
...@@ -141,44 +189,6 @@ namespace Svg ...@@ -141,44 +189,6 @@ namespace Svg
return transformedFocalPoint; 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) private static GraphicsPath CreateGraphicsPath(PointF origin, PointF centerPoint, float effectiveRadius)
{ {
var path = new GraphicsPath(); var path = new GraphicsPath();
...@@ -193,23 +203,66 @@ namespace Svg ...@@ -193,23 +203,66 @@ namespace Svg
return path; 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); var colorBlend = GetColorBlend(renderer, opacity, true);
float newScale;
List<float> pos;
List<Color> colors;
if (specifiedRadius >= effectiveRadius) outScale = scale;
if (scale > 1)
{ {
return colorBlend; switch (this.SpreadMethod)
} {
case SvgGradientSpreadMethod.Reflect:
for (var i = 0; i < colorBlend.Positions.Length - 1; i++) newScale = (float)Math.Ceiling(scale);
{ pos = (from p in colorBlend.Positions select p / newScale).ToList();
colorBlend.Positions[i] = 1 - (specifiedRadius / effectiveRadius) * (1 - colorBlend.Positions[i]); 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; return colorBlend;
} }
......
...@@ -101,6 +101,7 @@ ...@@ -101,6 +101,7 @@
<Compile Include="Clipping and Masking\SvgMask.cs" /> <Compile Include="Clipping and Masking\SvgMask.cs" />
<Compile Include="DataTypes\ISvgSupportsCoordinateUnits.cs" /> <Compile Include="DataTypes\ISvgSupportsCoordinateUnits.cs" />
<Compile Include="DataTypes\SvgTextDecoration.cs" /> <Compile Include="DataTypes\SvgTextDecoration.cs" />
<Compile Include="Filter Effects\ImageBuffer.cs" />
<Compile Include="Painting\GenericBoundable.cs" /> <Compile Include="Painting\GenericBoundable.cs" />
<Compile Include="SvgNodeReader.cs" /> <Compile Include="SvgNodeReader.cs" />
<Compile Include="Css\CssQuery.cs" /> <Compile Include="Css\CssQuery.cs" />
......
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