Commit 780b5150 authored by Tebjan Halm's avatar Tebjan Halm
Browse files

Merge pull request #93 from erdomke/master

Initial Filter, Svg Font, and Text on a Path Support
parents 1bc4c0f3 4200d302
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Svg
{
public enum SvgTextPathMethod
{
align,
stretch
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Svg
{
public enum SvgTextPathSpacing
{
exact,
auto
}
}
......@@ -20,7 +20,7 @@ namespace Svg
/// <summary>
/// Gets and empty <see cref="SvgUnit"/>.
/// </summary>
public static readonly SvgUnit Empty = new SvgUnit(SvgUnitType.User, 0);
public static readonly SvgUnit Empty = new SvgUnit(SvgUnitType.User, 0) { _isEmpty = true };
/// <summary>
/// Gets an <see cref="SvgUnit"/> with a value of none.
......@@ -64,7 +64,7 @@ namespace Svg
/// </summary>
/// <param name="boundable">The container element used as the basis for calculations</param>
/// <returns>The representation of the current unit in a device value (usually pixels).</returns>
public float ToDeviceValue(SvgRenderer renderer, UnitRenderingType renderType, SvgElement owner)
public float ToDeviceValue(ISvgRenderer renderer, UnitRenderingType renderType, SvgElement owner)
{
// If it's already been calculated
if (this._deviceValue.HasValue)
......@@ -86,55 +86,39 @@ namespace Svg
var type = this.Type;
var value = this.Value;
// Deal with fractional pattern units
var coordElem = owner as ISvgSupportsCoordinateUnits;
if (coordElem != null && coordElem.GetUnits() == SvgCoordinateUnits.ObjectBoundingBox && type != SvgUnitType.Percentage)
{
type = SvgUnitType.Percentage;
value *= 100;
}
var element = owner as SvgElement;
if (element != null)
{
var pattern = element.Parents.OfType<SvgPatternServer>().FirstOrDefault();
if (pattern != null && pattern.PatternContentUnits == SvgCoordinateUnits.ObjectBoundingBox && type != SvgUnitType.Percentage)
{
type = SvgUnitType.Percentage;
value *= 100;
}
}
float points;
Font currFont;
switch (type)
{
case SvgUnitType.Em:
currFont = GetFont(renderer, owner);
if (currFont == null)
using (var currFont = GetFont(renderer, owner))
{
points = (float)(value * 9);
_deviceValue = (points / 72.0f) * ppi;
}
else
{
_deviceValue = value * (currFont.SizeInPoints / 72.0f) * ppi;
if (currFont == null)
{
points = (float)(value * 9);
_deviceValue = (points / 72.0f) * ppi;
}
else
{
_deviceValue = value * (currFont.SizeInPoints / 72.0f) * ppi;
}
}
break;
case SvgUnitType.Ex:
currFont = GetFont(renderer, owner);
if (currFont == null)
{
points = (float)(value * 9);
_deviceValue = (points * 0.5f / 72.0f) * ppi;
}
else
using (var currFont = GetFont(renderer, owner))
{
_deviceValue = value * 0.5f * (currFont.SizeInPoints / 72.0f) * ppi;
if (currFont == null)
{
points = (float)(value * 9);
_deviceValue = (points * 0.5f / 72.0f) * ppi;
}
else
{
_deviceValue = value * 0.5f * (currFont.SizeInPoints / 72.0f) * ppi;
}
break;
}
break;
case SvgUnitType.Centimeter:
_deviceValue = (float)((value / cmInInch) * ppi);
break;
......@@ -158,7 +142,7 @@ namespace Svg
break;
case SvgUnitType.Percentage:
// Can't calculate if there is no style owner
var boundable = (renderer == null ? (owner == null ? null : owner.OwnerDocument) : renderer.Boundable());
var boundable = (renderer == null ? (owner == null ? null : owner.OwnerDocument) : renderer.GetBoundable());
if (boundable == null)
{
_deviceValue = value;
......@@ -193,7 +177,7 @@ namespace Svg
return this._deviceValue.Value;
}
private Font GetFont(SvgRenderer renderer, SvgElement owner)
private IFontDefn GetFont(ISvgRenderer renderer, SvgElement owner)
{
if (owner == null) return null;
......@@ -317,9 +301,9 @@ namespace Svg
/// <param name="value">The value.</param>
public SvgUnit(SvgUnitType type, float value)
{
this._isEmpty = false;
this._type = type;
this._value = value;
this._isEmpty = (this._value == 0.0f);
this._deviceValue = null;
}
......@@ -329,24 +313,24 @@ namespace Svg
/// <param name="value">The value.</param>
public SvgUnit(float value)
{
this._isEmpty = false;
this._value = value;
this._type = SvgUnitType.User;
this._isEmpty = (this._value == 0.0f);
this._deviceValue = null;
}
public static System.Drawing.PointF GetDevicePoint(SvgUnit x, SvgUnit y, SvgRenderer renderer, SvgElement owner)
public static System.Drawing.PointF GetDevicePoint(SvgUnit x, SvgUnit y, ISvgRenderer renderer, SvgElement owner)
{
return new System.Drawing.PointF(x.ToDeviceValue(renderer, UnitRenderingType.Horizontal, owner),
y.ToDeviceValue(renderer, UnitRenderingType.Vertical, owner));
}
public static System.Drawing.PointF GetDevicePointOffset(SvgUnit x, SvgUnit y, SvgRenderer renderer, SvgElement owner)
public static System.Drawing.PointF GetDevicePointOffset(SvgUnit x, SvgUnit y, ISvgRenderer renderer, SvgElement owner)
{
return new System.Drawing.PointF(x.ToDeviceValue(renderer, UnitRenderingType.HorizontalOffset, owner),
y.ToDeviceValue(renderer, UnitRenderingType.VerticalOffset, owner));
}
public static System.Drawing.SizeF GetDeviceSize(SvgUnit width, SvgUnit height, SvgRenderer renderer, SvgElement owner)
public static System.Drawing.SizeF GetDeviceSize(SvgUnit width, SvgUnit height, ISvgRenderer renderer, SvgElement owner)
{
return new System.Drawing.SizeF(width.ToDeviceValue(renderer, UnitRenderingType.HorizontalOffset, owner),
height.ToDeviceValue(renderer, UnitRenderingType.VerticalOffset, owner));
......
......@@ -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));
}
}
/// <summary>
......
......@@ -35,7 +35,7 @@ namespace Svg
for (int i = 0; i < unit.Length; i++)
{
// If the character is a percent sign or a letter which is not an exponent 'e'
if (unit[i] == '%' || (char.IsLetter(unit[i]) && !(unit[i] == 'e' && i < unit.Length - 1 && !char.IsLetter(unit[i + 1]))))
if (unit[i] == '%' || (char.IsLetter(unit[i]) && !((unit[i] == 'e' || unit[i] == 'E') && i < unit.Length - 1 && !char.IsLetter(unit[i + 1]))))
{
identifierIndex = i;
break;
......
......@@ -4,6 +4,7 @@ using System.ComponentModel;
using System.Collections.Generic;
using System.Text;
using System.Globalization;
using System.Drawing.Drawing2D;
namespace Svg
{
......@@ -123,6 +124,85 @@ namespace Svg
}
#endregion
public void AddViewBoxTransform(SvgAspectRatio aspectRatio, ISvgRenderer renderer, SvgFragment frag)
{
var x = (frag == null ? 0 : frag.X.ToDeviceValue(renderer, UnitRenderingType.Horizontal, frag));
var y = (frag == null ? 0 : frag.Y.ToDeviceValue(renderer, UnitRenderingType.Vertical, frag));
if (this.Equals(SvgViewBox.Empty))
{
renderer.TranslateTransform(x, y);
return;
}
var width = (frag == null ? this.Width : frag.Width.ToDeviceValue(renderer, UnitRenderingType.Horizontal, frag));
var height = (frag == null ? this.Height : frag.Height.ToDeviceValue(renderer, UnitRenderingType.Vertical, frag));
var fScaleX = width / this.Width;
var fScaleY = height / this.Height; //(this.MinY < 0 ? -1 : 1) *
var fMinX = -this.MinX;
var fMinY = -this.MinY;
if (aspectRatio == null) aspectRatio = new SvgAspectRatio(SvgPreserveAspectRatio.xMidYMid, false);
if (aspectRatio.Align != SvgPreserveAspectRatio.none)
{
if (aspectRatio.Slice)
{
fScaleX = Math.Max(fScaleX, fScaleY);
fScaleY = Math.Max(fScaleX, fScaleY);
}
else
{
fScaleX = Math.Min(fScaleX, fScaleY);
fScaleY = Math.Min(fScaleX, fScaleY);
}
float fViewMidX = (this.Width / 2) * fScaleX;
float fViewMidY = (this.Height / 2) * fScaleY;
float fMidX = width / 2;
float fMidY = height / 2;
switch (aspectRatio.Align)
{
case SvgPreserveAspectRatio.xMinYMin:
break;
case SvgPreserveAspectRatio.xMidYMin:
fMinX += fMidX - fViewMidX;
break;
case SvgPreserveAspectRatio.xMaxYMin:
fMinX += width - this.Width * fScaleX;
break;
case SvgPreserveAspectRatio.xMinYMid:
fMinY += fMidY - fViewMidY;
break;
case SvgPreserveAspectRatio.xMidYMid:
fMinX += fMidX - fViewMidX;
fMinY += fMidY - fViewMidY;
break;
case SvgPreserveAspectRatio.xMaxYMid:
fMinX += width - this.Width * fScaleX;
fMinY += fMidY - fViewMidY;
break;
case SvgPreserveAspectRatio.xMinYMax:
fMinY += height - this.Height * fScaleY;
break;
case SvgPreserveAspectRatio.xMidYMax:
fMinX += fMidX - fViewMidX;
fMinY += height - this.Height * fScaleY;
break;
case SvgPreserveAspectRatio.xMaxYMax:
fMinX += width - this.Width * fScaleX;
fMinY += height - this.Height * fScaleY;
break;
default:
break;
}
}
renderer.SetClip(new Region(new RectangleF(x, y, width, height)), CombineMode.Intersect);
renderer.ScaleTransform(fScaleX, fScaleY, MatrixOrder.Prepend);
renderer.TranslateTransform(x, y);
renderer.TranslateTransform(fMinX, fMinY);
}
}
internal class SvgViewBoxConverter : TypeConverter
......
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Svg
{
public enum XmlSpaceHandling
{
@default,
inherit,
preserve
}
}
......@@ -18,10 +18,10 @@ namespace Svg
}
/// <summary>
/// Renders the <see cref="SvgElement"/> and contents to the specified <see cref="SvgRenderer"/> object.
/// Renders the <see cref="SvgElement"/> and contents to the specified <see cref="ISvgRenderer"/> object.
/// </summary>
/// <param name="renderer">The <see cref="SvgRenderer"/> object to render to.</param>
protected override void Render(SvgRenderer renderer)
/// <param name="renderer">The <see cref="ISvgRenderer"/> object to render to.</param>
protected override void Render(ISvgRenderer renderer)
{
// Do nothing. Children should NOT be rendered.
}
......
......@@ -31,10 +31,10 @@ namespace Svg
/// <summary>
/// Renders the <see cref="SvgElement"/> and contents to the specified <see cref="SvgRenderer"/> object.
/// Renders the <see cref="SvgElement"/> and contents to the specified <see cref="ISvgRenderer"/> object.
/// </summary>
/// <param name="renderer">The <see cref="SvgRenderer"/> object to render to.</param>
protected override void Render(SvgRenderer renderer)
/// <param name="renderer">The <see cref="ISvgRenderer"/> object to render to.</param>
protected override void Render(ISvgRenderer renderer)
{
// Do nothing. Children should NOT be rendered.
}
......
......@@ -148,87 +148,42 @@ namespace Svg
}
/// <summary>
/// Applies the required transforms to <see cref="SvgRenderer"/>.
/// Applies the required transforms to <see cref="ISvgRenderer"/>.
/// </summary>
/// <param name="renderer">The <see cref="SvgRenderer"/> to be transformed.</param>
protected internal override bool PushTransforms(SvgRenderer renderer)
/// <param name="renderer">The <see cref="ISvgRenderer"/> to be transformed.</param>
protected internal override bool PushTransforms(ISvgRenderer renderer)
{
if (!base.PushTransforms(renderer)) return false;
this.ViewBox.AddViewBoxTransform(this.AspectRatio, renderer, this);
return true;
}
if (!this.ViewBox.Equals(SvgViewBox.Empty))
protected override void Render(ISvgRenderer renderer)
{
switch (this.Overflow)
{
var width = this.Width.ToDeviceValue(renderer, UnitRenderingType.Horizontal, this);
var height = this.Height.ToDeviceValue(renderer, UnitRenderingType.Vertical, this);
var fScaleX = width / this.ViewBox.Width;
var fScaleY = height / this.ViewBox.Height;
var fMinX = -this.ViewBox.MinX;
var fMinY = -this.ViewBox.MinY;
if (AspectRatio.Align != SvgPreserveAspectRatio.none)
{
if (AspectRatio.Slice)
{
fScaleX = Math.Max(fScaleX, fScaleY);
fScaleY = Math.Max(fScaleX, fScaleY);
}
else
case SvgOverflow.auto:
case SvgOverflow.visible:
case SvgOverflow.scroll:
base.Render(renderer);
break;
default:
var prevClip = renderer.GetClip();
try
{
fScaleX = Math.Min(fScaleX, fScaleY);
fScaleY = Math.Min(fScaleX, fScaleY);
var size = (this.Parent == null ? renderer.GetBoundable().Bounds.Size : GetDimensions());
var clip = new RectangleF(this.X.ToDeviceValue(renderer, UnitRenderingType.Horizontal, this),
this.Y.ToDeviceValue(renderer, UnitRenderingType.Horizontal, this),
size.Width, size.Height);
renderer.SetClip(new Region(clip), CombineMode.Intersect);
base.Render(renderer);
}
float fViewMidX = (this.ViewBox.Width / 2) * fScaleX;
float fViewMidY = (this.ViewBox.Height / 2) * fScaleY;
float fMidX = width / 2;
float fMidY = height / 2;
switch (AspectRatio.Align)
finally
{
case SvgPreserveAspectRatio.xMinYMin:
break;
case SvgPreserveAspectRatio.xMidYMin:
fMinX += fMidX - fViewMidX;
break;
case SvgPreserveAspectRatio.xMaxYMin:
fMinX += width - this.ViewBox.Width * fScaleX;
break;
case SvgPreserveAspectRatio.xMinYMid:
fMinY += fMidY - fViewMidY;
break;
case SvgPreserveAspectRatio.xMidYMid:
fMinX += fMidX - fViewMidX;
fMinY += fMidY - fViewMidY;
break;
case SvgPreserveAspectRatio.xMaxYMid:
fMinX += width - this.ViewBox.Width * fScaleX;
fMinY += fMidY - fViewMidY;
break;
case SvgPreserveAspectRatio.xMinYMax:
fMinY += height - this.ViewBox.Height * fScaleY;
break;
case SvgPreserveAspectRatio.xMidYMax:
fMinX += fMidX - fViewMidX;
fMinY += height - this.ViewBox.Height * fScaleY;
break;
case SvgPreserveAspectRatio.xMaxYMax:
fMinX += width - this.ViewBox.Width * fScaleX;
fMinY += height - this.ViewBox.Height * fScaleY;
break;
default:
break;
renderer.SetClip(prevClip, CombineMode.Replace);
}
}
var x = _x.ToDeviceValue(renderer, UnitRenderingType.Horizontal, this);
var y = _y.ToDeviceValue(renderer, UnitRenderingType.Vertical, this);
renderer.AddClip(new Region(new RectangleF(x, y, width, height)));
renderer.ScaleTransform(fScaleX, fScaleY, MatrixOrder.Prepend);
renderer.TranslateTransform(x,y);
renderer.TranslateTransform(fMinX, fMinY);
break;
}
return true;
}
/// <summary>
......
......@@ -9,15 +9,11 @@ namespace Svg
[SvgElement("g")]
public class SvgGroup : SvgVisualElement
{
public SvgGroup()
{
}
/// <summary>
/// Gets the <see cref="GraphicsPath"/> for this element.
/// </summary>
/// <value></value>
public override System.Drawing.Drawing2D.GraphicsPath Path(SvgRenderer renderer)
public override System.Drawing.Drawing2D.GraphicsPath Path(ISvgRenderer renderer)
{
return GetPaths(this, renderer);
}
......@@ -56,25 +52,8 @@ namespace Svg
}
}
/// <summary>
/// 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))
{
this.SetClip(renderer);
base.RenderChildren(renderer);
this.ResetClip(renderer);
this.PopTransforms(renderer);
}
}
protected override bool Renderable { get { return false; } }
public override SvgElement DeepCopy()
{
return DeepCopy<SvgGroup>();
......
......@@ -17,7 +17,7 @@ namespace Svg
/// Gets the <see cref="GraphicsPath"/> for this element.
/// </summary>
/// <value></value>
public override System.Drawing.Drawing2D.GraphicsPath Path(SvgRenderer renderer)
public override System.Drawing.Drawing2D.GraphicsPath Path(ISvgRenderer renderer)
{
return GetPaths(this, renderer);
}
......@@ -60,7 +60,7 @@ namespace Svg
/// 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)
protected override void Render(ISvgRenderer renderer)
{
if (!Visible || !Displayable)
return;
......
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
namespace Svg.Document_Structure
{
/// <summary>
/// An element used to group SVG shapes.
/// </summary>
[SvgElement("symbol")]
public class SvgSymbol : SvgVisualElement
{
/// <summary>
/// Gets or sets the viewport of the element.
/// </summary>
/// <value></value>
[SvgAttribute("viewBox")]
public SvgViewBox ViewBox
{
get { return this.Attributes.GetAttribute<SvgViewBox>("viewBox"); }
set { this.Attributes["viewBox"] = value; }
}
/// <summary>
/// Gets or sets the aspect of the viewport.
/// </summary>
/// <value></value>
[SvgAttribute("preserveAspectRatio")]
public SvgAspectRatio AspectRatio
{
get { return this.Attributes.GetAttribute<SvgAspectRatio>("preserveAspectRatio"); }
set { this.Attributes["preserveAspectRatio"] = value; }
}
/// <summary>
/// Gets the <see cref="GraphicsPath"/> for this element.
/// </summary>
/// <value></value>
public override System.Drawing.Drawing2D.GraphicsPath Path(ISvgRenderer renderer)
{
return GetPaths(this, renderer);
}
/// <summary>
/// Gets the bounds of the element.
/// </summary>
/// <value>The bounds.</value>
public override System.Drawing.RectangleF Bounds
{
get
{
var r = new RectangleF();
foreach (var c in this.Children)
{
if (c is SvgVisualElement)
{
// First it should check if rectangle is empty or it will return the wrong Bounds.
// This is because when the Rectangle is Empty, the Union method adds as if the first values where X=0, Y=0
if (r.IsEmpty)
{
r = ((SvgVisualElement)c).Bounds;
}
else
{
var childBounds = ((SvgVisualElement)c).Bounds;
if (!childBounds.IsEmpty)
{
r = RectangleF.Union(r, childBounds);
}
}
}
}
return r;
}
}
protected override bool Renderable { get { return false; } }
/// <summary>
/// Applies the required transforms to <see cref="ISvgRenderer"/>.
/// </summary>
/// <param name="renderer">The <see cref="ISvgRenderer"/> to be transformed.</param>
protected internal override bool PushTransforms(ISvgRenderer renderer)
{
if (!base.PushTransforms(renderer)) return false;
this.ViewBox.AddViewBoxTransform(this.AspectRatio, renderer, null);
return true;
}
// Only render if the parent is set to a Use element
protected override void Render(ISvgRenderer renderer)
{
if (_parent is SvgUse) base.Render(renderer);
}
public override SvgElement DeepCopy()
{
return DeepCopy<SvgSymbol>();
}
public override SvgElement DeepCopy<T>()
{
var newObj = base.DeepCopy<T>() as SvgSymbol;
if (this.Fill != null)
newObj.Fill = this.Fill.DeepCopy() as SvgPaintServer;
return newObj;
}
}
}
......@@ -35,13 +35,13 @@ namespace Svg
}
/// <summary>
/// Applies the required transforms to <see cref="SvgRenderer"/>.
/// Applies the required transforms to <see cref="ISvgRenderer"/>.
/// </summary>
/// <param name="renderer">The <see cref="SvgRenderer"/> to be transformed.</param>
protected internal override bool PushTransforms(SvgRenderer renderer)
/// <param name="renderer">The <see cref="ISvgRenderer"/> to be transformed.</param>
protected internal override bool PushTransforms(ISvgRenderer renderer)
{
if (!base.PushTransforms(renderer)) return false;
renderer.TranslateTransform(this.X.ToDeviceValue(renderer, UnitRenderingType.Horizontal, this),
renderer.TranslateTransform(this.X.ToDeviceValue(renderer, UnitRenderingType.Horizontal, this),
this.Y.ToDeviceValue(renderer, UnitRenderingType.Vertical, this));
return true;
}
......@@ -55,7 +55,7 @@ namespace Svg
this.Y = 0;
}
public override System.Drawing.Drawing2D.GraphicsPath Path(SvgRenderer renderer)
public override System.Drawing.Drawing2D.GraphicsPath Path(ISvgRenderer renderer)
{
SvgVisualElement element = (SvgVisualElement)this.OwnerDocument.IdManager.GetElementById(this.ReferencedElement);
return (element != null) ? element.Path(renderer) : null;
......@@ -66,33 +66,26 @@ namespace Svg
get { return new System.Drawing.RectangleF(); }
}
// public override SvgElementCollection Children
// {
// get
// {
// SvgElement element = this.OwnerDocument.IdManager.GetElementById(this.ReferencedElement);
// SvgElementCollection elements = new SvgElementCollection(this, true);
// elements.Add(element);
// return elements;
// }
// }
protected override void Render(SvgRenderer renderer)
{
if (!Visible || !Displayable)
return;
this.PushTransforms(renderer);
protected override bool Renderable { get { return false; } }
SvgVisualElement element = (SvgVisualElement)this.OwnerDocument.IdManager.GetElementById(this.ReferencedElement);
// For the time of rendering we want the referenced element to inherit
// this elements transforms
SvgElement parent = element._parent;
element._parent = this;
element.RenderElement(renderer);
element._parent = parent;
this.PopTransforms(renderer);
protected override void Render(ISvgRenderer renderer)
{
if (this.Visible && this.Displayable && this.PushTransforms(renderer))
{
this.SetClip(renderer);
var element = this.OwnerDocument.IdManager.GetElementById(this.ReferencedElement) as SvgVisualElement;
if (element != null)
{
var origParent = element.Parent;
element._parent = this;
element.RenderElement(renderer);
element._parent = origParent;
}
this.ResetClip(renderer);
this.PopTransforms(renderer);
}
}
......
......@@ -17,7 +17,7 @@ namespace Svg
/// Gets the <see cref="GraphicsPath"/> for this element.
/// </summary>
/// <value></value>
public override System.Drawing.Drawing2D.GraphicsPath Path(SvgRenderer renderer)
public override System.Drawing.Drawing2D.GraphicsPath Path(ISvgRenderer renderer)
{
return GetPaths(this, renderer);
}
......@@ -56,21 +56,23 @@ namespace Svg
}
}
/// <summary>
/// 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;
protected override bool Renderable { get { return false; } }
this.PushTransforms(renderer);
this.SetClip(renderer);
base.RenderChildren(renderer);
this.ResetClip(renderer);
this.PopTransforms(renderer);
}
///// <summary>
///// 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);
// this.SetClip(renderer);
// base.RenderChildren(renderer);
// this.ResetClip(renderer);
// this.PopTransforms(renderer);
//}
public override SvgElement DeepCopy()
{
......
......@@ -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
{
......
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 ISvgRenderer _renderer;
private Action<ISvgRenderer> _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, ISvgRenderer renderer, Action<ISvgRenderer> 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 _images.ContainsKey(BufferKey) ? BufferKey : SvgFilterPrimitive.SourceGraphic;
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.SetBoundable(_renderer.GetBoundable());
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,22 +72,18 @@ namespace Svg.FilterEffects
set { this.Attributes["color-interpolation-filters"] = value; }
}
internal Dictionary<string, Func<SvgVisualElement, SvgRenderer, Bitmap>> Buffer { get; private set; }
/// <summary>
/// Initializes a new instance of the <see cref="SvgFilter"/> class.
/// </summary>
public SvgFilter()
{
this.Buffer = new Dictionary<string, Func<SvgVisualElement, SvgRenderer, Bitmap>>();
}
/// <summary>
/// Renders the <see cref="SvgElement"/> and contents to the specified <see cref="SvgRenderer"/> object.
/// Renders the <see cref="SvgElement"/> and contents to the specified <see cref="ISvgRenderer"/> object.
/// </summary>
/// <param name="renderer">The <see cref="SvgRenderer"/> object to render to.</param>
protected override void Render(SvgRenderer renderer)
/// <param name="renderer">The <see cref="ISvgRenderer"/> object to render to.</param>
protected override void Render(ISvgRenderer renderer)
{
base.RenderChildren(renderer);
}
......@@ -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, ISvgRenderer 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, ISvgRenderer renderer, Action<ISvgRenderer> 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<SvgFilterPrimitive> primitives = this.Children.OfType<SvgFilterPrimitive>();
......@@ -114,21 +132,20 @@ 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.GetClip();
renderer.SetClip(new Region(imgDraw));
renderer.DrawImage(bufferImg, imgDraw, new RectangleF(bounds.X, bounds.Y, imgDraw.Width, imgDraw.Height), GraphicsUnit.Pixel);
renderer.SetClip(prevClip);
}
}
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 +164,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
......
......@@ -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
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
/// </summary>
[SvgAttribute("type")]
public SvgColourMatrixType Type { get; set; }
/// <summary>
/// list of <number>s
......@@ -29,13 +29,76 @@ namespace Svg.FilterEffects
/// </summary>
[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);
using (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;
}
}
......
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