Commit d5c659a5 authored by Eric Domke's avatar Eric Domke
Browse files

Refactoring while working through W3C tests

- Adding W3C test cases and a test fixture
- Fixed support for CSS stylesheets (particularly when class names are
referenced)
- Refactoring unit calculations so that percentages and fractions
calculate more accurately
- SvgImage:
- Support PreserveAspectRatio attribute
- Support for referencing svg images
- Refactored text rendering to use the AttributeCollection inheritance
scheme
- Initial attempt at 'ex' unit support
- Added support for system color names
- Changed parsing of entities to support XML entities
- Supporting loading of a svg document directly from a XmlDocument with
requiring serializing the document as a string first.
- ...
parent 3aedd8e8
......@@ -17,17 +17,9 @@ namespace Svg
/// Gets the <see cref="GraphicsPath"/> for this element.
/// </summary>
/// <value></value>
public override System.Drawing.Drawing2D.GraphicsPath Path
public override System.Drawing.Drawing2D.GraphicsPath Path(SvgRenderer renderer)
{
get
{
//var path = new GraphicsPath();
//AddPaths(this, path);
return GetPaths(this);
}
protected set
{ }
return GetPaths(this, renderer);
}
/// <summary>
......
......@@ -38,10 +38,12 @@ namespace Svg
/// Applies the required transforms to <see cref="SvgRenderer"/>.
/// </summary>
/// <param name="renderer">The <see cref="SvgRenderer"/> to be transformed.</param>
protected internal override void PushTransforms(SvgRenderer renderer)
protected internal override bool PushTransforms(SvgRenderer renderer)
{
base.PushTransforms(renderer);
renderer.TranslateTransform(this.X.ToDeviceValue(this), this.Y.ToDeviceValue(this, true));
if (!base.PushTransforms(renderer)) return false;
renderer.TranslateTransform(this.X.ToDeviceValue(renderer, UnitRenderingType.Horizontal, this),
this.Y.ToDeviceValue(renderer, UnitRenderingType.Vertical, this));
return true;
}
/// <summary>
......@@ -53,15 +55,10 @@ namespace Svg
this.Y = 0;
}
public override System.Drawing.Drawing2D.GraphicsPath Path
{
get
public override System.Drawing.Drawing2D.GraphicsPath Path(SvgRenderer renderer)
{
SvgVisualElement element = (SvgVisualElement)this.OwnerDocument.IdManager.GetElementById(this.ReferencedElement);
return (element != null) ? element.Path : null;
}
protected set
{ }
return (element != null) ? element.Path(renderer) : null;
}
public override System.Drawing.RectangleF Bounds
......
......@@ -17,17 +17,9 @@ namespace Svg
/// Gets the <see cref="GraphicsPath"/> for this element.
/// </summary>
/// <value></value>
public override System.Drawing.Drawing2D.GraphicsPath Path
public override System.Drawing.Drawing2D.GraphicsPath Path(SvgRenderer renderer)
{
get
{
//var path = new GraphicsPath();
//AddPaths(this, path);
return GetPaths(this);
}
protected set
{ }
return GetPaths(this, renderer);
}
/// <summary>
......
......@@ -151,7 +151,7 @@ namespace Svg.FilterEffects
{
if (this.sourceGraphic == null)
{
RectangleF bounds = element.Path.GetBounds();
RectangleF bounds = element.Path(renderer).GetBounds();
this.sourceGraphic = new Bitmap((int)bounds.Width, (int)bounds.Height);
using (var graphics = Graphics.FromImage(this.sourceGraphic))
......
<?xml version="1.0" encoding="UTF-8"?>
<TestSettings name="Local" id="616d39c5-86e4-40f8-97e9-b8a423ce7322" xmlns="http://microsoft.com/schemas/VisualStudio/TeamTest/2010">
<Description>These are default test settings for a local test run.</Description>
<Deployment enabled="false" />
<Execution>
<TestTypeSpecific />
<AgentRule name="Execution Agents">
</AgentRule>
</Execution>
</TestSettings>
\ No newline at end of file
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
namespace Svg
{
internal class GenericBoundable : ISvgBoundable
{
private RectangleF _rect;
public GenericBoundable(RectangleF rect)
{
_rect = rect;
}
public GenericBoundable(float x, float y, float width, float height)
{
_rect = new RectangleF(x, y, width, height);
}
public System.Drawing.PointF Location
{
get { return _rect.Location; }
}
public System.Drawing.SizeF Size
{
get { return _rect.Size; }
}
public System.Drawing.RectangleF Bounds
{
get { return _rect; }
}
}
}
......@@ -19,6 +19,6 @@ namespace Svg
float StrokeMiterLimit { get; set; }
SvgUnitCollection StrokeDashArray { get; set; }
SvgUnit StrokeDashOffset { get; set; }
GraphicsPath Path { get; }
GraphicsPath Path(SvgRenderer renderer);
}
}
\ No newline at end of file
......@@ -95,6 +95,37 @@ namespace Svg
return base.ConvertFrom(context, culture, colour);
}
switch (colour.ToLowerInvariant())
{
case "activeborder": return SystemColors.ActiveBorder;
case "activecaption": return SystemColors.ActiveCaption;
case "appworkspace": return SystemColors.AppWorkspace;
case "background": return SystemColors.Desktop;
case "buttonface": return SystemColors.Control;
case "buttonhighlight": return SystemColors.ControlLightLight;
case "buttonshadow": return SystemColors.ControlDark;
case "buttontext": return SystemColors.ControlText;
case "captiontext": return SystemColors.ActiveCaptionText;
case "graytext": return SystemColors.GrayText;
case "highlight": return SystemColors.Highlight;
case "highlighttext": return SystemColors.HighlightText;
case "inactiveborder": return SystemColors.InactiveBorder;
case "inactivecaption": return SystemColors.InactiveCaption;
case "inactivecaptiontext": return SystemColors.InactiveCaptionText;
case "infobackground": return SystemColors.Info;
case "infotext": return SystemColors.InfoText;
case "menu": return SystemColors.Menu;
case "menutext": return SystemColors.MenuText;
case "scrollbar": return SystemColors.ScrollBar;
case "threeddarkshadow": return SystemColors.ControlDarkDark;
case "threedface": return SystemColors.Control;
case "threedhighlight": return SystemColors.ControlLight;
case "threedlightshadow": return SystemColors.ControlLightLight;
case "window": return SystemColors.Window;
case "windowframe": return SystemColors.WindowFrame;
case "windowtext": return SystemColors.WindowText;
}
Thread.CurrentThread.CurrentCulture = oldCulture;
}
......
......@@ -11,9 +11,14 @@ namespace Svg
/// <summary>
/// An unspecified <see cref="SvgPaintServer"/>.
/// </summary>
public static readonly SvgPaintServer NotSet = new SvgColourServer();
public static readonly SvgPaintServer NotSet = new SvgColourServer(System.Drawing.Color.Black);
/// <summary>
/// A <see cref="SvgPaintServer"/> that should inherit from its parent.
/// </summary>
public static readonly SvgPaintServer Inherit = new SvgColourServer(System.Drawing.Color.Black);
public SvgColourServer() : this(Color.Black)
public SvgColourServer()
: this(System.Drawing.Color.Black)
{
}
......@@ -30,13 +35,13 @@ namespace Svg
set { this._colour = value; }
}
public override Brush GetBrush(SvgVisualElement styleOwner, float opacity)
public override Brush GetBrush(SvgVisualElement styleOwner, SvgRenderer renderer, float opacity)
{
//is none?
if (this == SvgPaintServer.None) return new SolidBrush(Color.Transparent);
if (this == SvgPaintServer.None) return new SolidBrush(System.Drawing.Color.Transparent);
int alpha = (int)((opacity * (this.Colour.A/255.0f) ) * 255);
Color colour = Color.FromArgb(alpha, this.Colour);
Color colour = System.Drawing.Color.FromArgb(alpha, this.Colour);
return new SolidBrush(colour);
}
......
......@@ -24,19 +24,30 @@ namespace Svg
this.DeferredId = id;
}
private void EnsureServer()
public void EnsureServer(SvgElement styleOwner)
{
if (!_serverLoaded)
{
if (this.DeferredId == "currentColor" && styleOwner != null)
{
var colorElement = (from e in styleOwner.ParentsAndSelf.OfType<SvgElement>()
where e.Color != SvgPaintServer.None && e.Color != SvgColourServer.NotSet &&
e.Color != SvgColourServer.Inherit && e.Color != SvgColourServer.None
select e).FirstOrDefault();
_concreteServer = (colorElement == null ? SvgPaintServer.None : colorElement.Color);
}
else
{
_concreteServer = this.Document.IdManager.GetElementById(this.DeferredId) as SvgPaintServer;
}
_serverLoaded = true;
}
}
public override System.Drawing.Brush GetBrush(SvgVisualElement styleOwner, float opacity)
public override System.Drawing.Brush GetBrush(SvgVisualElement styleOwner, SvgRenderer renderer, float opacity)
{
EnsureServer();
return _concreteServer.GetBrush(styleOwner, opacity);
EnsureServer(styleOwner);
return _concreteServer.GetBrush(styleOwner, renderer, opacity);
}
public override SvgElement DeepCopy()
......@@ -72,7 +83,7 @@ namespace Svg
return (_serverLoaded ? _serverLoaded.ToString() : string.Format("deferred: {0}", this.DeferredId));
}
public static T TryGet<T>(SvgPaintServer server) where T : SvgPaintServer
public static T TryGet<T>(SvgPaintServer server, SvgElement parent) where T : SvgPaintServer
{
var deferred = server as SvgDeferredPaintServer;
if (deferred == null)
......@@ -81,7 +92,7 @@ namespace Svg
}
else
{
deferred.EnsureServer();
deferred.EnsureServer(parent);
return deferred._concreteServer as T;
}
}
......
......@@ -9,7 +9,7 @@ namespace Svg
/// <summary>
/// Provides the base class for all paint servers that wish to render a gradient.
/// </summary>
public abstract class SvgGradientServer : SvgPaintServer
public abstract class SvgGradientServer : SvgPaintServer, ISvgSupportsCoordinateUnits
{
private SvgCoordinateUnits _gradientUnits;
private SvgGradientSpreadMethod _spreadMethod;
......@@ -130,7 +130,7 @@ namespace Svg
/// </summary>
/// <param name="owner">The parent <see cref="SvgVisualElement"/>.</param>
/// <param name="opacity">The opacity of the colour blend.</param>
protected ColorBlend GetColorBlend(SvgVisualElement owner, float opacity, bool radial)
protected ColorBlend GetColorBlend(SvgRenderer renderer, float opacity, bool radial)
{
int colourBlends = this.Stops.Count;
bool insertStart = false;
......@@ -179,18 +179,19 @@ namespace Svg
int actualStops = 0;
float mergedOpacity = 0.0f;
float position = 0.0f;
Color colour = Color.Black;
Color colour = System.Drawing.Color.Black;
for (int i = 0; i < colourBlends; i++)
{
var currentStop = this.Stops[radial ? this.Stops.Count - 1 - actualStops : actualStops];
var boundWidth = renderer.Boundable().Bounds.Width;
mergedOpacity = opacity * currentStop.Opacity;
position =
radial
? 1 - (currentStop.Offset.ToDeviceValue(owner) / owner.Bounds.Width)
: (currentStop.Offset.ToDeviceValue(owner) / owner.Bounds.Width);
colour = Color.FromArgb((int)(mergedOpacity * 255), currentStop.Colour);
? 1 - (currentStop.Offset.ToDeviceValue(renderer, UnitRenderingType.Horizontal, this) / boundWidth)
: (currentStop.Offset.ToDeviceValue(renderer, UnitRenderingType.Horizontal, this) / boundWidth);
colour = System.Drawing.Color.FromArgb((int)(mergedOpacity * 255), currentStop.GetColor(this));
actualStops++;
......@@ -219,20 +220,15 @@ namespace Svg
return blend;
}
protected void LoadStops()
protected void LoadStops(SvgVisualElement parent)
{
var core = SvgDeferredPaintServer.TryGet<SvgGradientServer>(_inheritGradient);
var core = SvgDeferredPaintServer.TryGet<SvgGradientServer>(_inheritGradient, parent);
if (this.Stops.Count == 0 && core != null)
{
_stops.AddRange(core.Stops);
}
}
protected ISvgBoundable CalculateBoundable(SvgVisualElement renderingElement)
{
return (this.GradientUnits == SvgCoordinateUnits.ObjectBoundingBox) ? (ISvgBoundable)renderingElement : renderingElement.OwnerDocument;
}
protected PointF TransformPoint(PointF originalPoint)
{
var newPoint = new[] { originalPoint };
......@@ -272,5 +268,10 @@ namespace Svg
return newObj;
}
public SvgCoordinateUnits GetUnits()
{
return _gradientUnits;
}
}
}
\ No newline at end of file
......@@ -13,7 +13,7 @@ namespace Svg
public class SvgGradientStop : SvgElement
{
private SvgUnit _offset;
private Color _colour;
private SvgPaintServer _colour;
private float _opacity;
/// <summary>
......@@ -58,8 +58,8 @@ namespace Svg
/// Gets or sets the colour of the gradient stop.
/// </summary>
[SvgAttribute("stop-color")]
[TypeConverter(typeof(SvgColourConverter))]
public Color Colour
[TypeConverter(typeof(SvgPaintServerFactory))]
public SvgPaintServer Colour
{
get { return this._colour; }
set { this._colour = value; }
......@@ -81,7 +81,7 @@ namespace Svg
public SvgGradientStop()
{
this._offset = new SvgUnit(0.0f);
this._colour = Color.Transparent;
this._colour = SvgColourServer.NotSet;
this._opacity = 1.0f;
}
......@@ -93,10 +93,16 @@ namespace Svg
public SvgGradientStop(SvgUnit offset, Color colour)
{
this._offset = offset;
this._colour = colour;
this._colour = new SvgColourServer(colour);
this._opacity = 1.0f;
}
public Color GetColor(SvgElement parent)
{
var core = SvgDeferredPaintServer.TryGet<SvgColourServer>(_colour, parent);
if (core == null) throw new InvalidOperationException("Invalid paint server for gradient stop detected.");
return core.Colour;
}
public override SvgElement DeepCopy()
{
......
......@@ -79,18 +79,20 @@ namespace Svg
Y2 = new SvgUnit(SvgUnitType.Percentage, 0F);
}
public override Brush GetBrush(SvgVisualElement renderingElement, float opacity)
public override Brush GetBrush(SvgVisualElement renderingElement, SvgRenderer renderer, float opacity)
{
LoadStops();
LoadStops(renderingElement);
if (IsInvalid)
{
return null;
}
var boundable = CalculateBoundable(renderingElement);
try
{
if (this.GradientUnits == SvgCoordinateUnits.ObjectBoundingBox) renderer.Boundable(renderingElement);
var specifiedStart = CalculateStart(boundable);
var specifiedEnd = CalculateEnd(boundable);
var specifiedStart = CalculateStart(renderer);
var specifiedEnd = CalculateEnd(renderer);
var effectiveStart = specifiedStart;
var effectiveEnd = specifiedEnd;
......@@ -102,21 +104,26 @@ namespace Svg
effectiveEnd = expansion.EndPoint;
}
return new LinearGradientBrush(effectiveStart, effectiveEnd, Color.Transparent, Color.Transparent)
return new LinearGradientBrush(effectiveStart, effectiveEnd, System.Drawing.Color.Transparent, System.Drawing.Color.Transparent)
{
InterpolationColors = CalculateColorBlend(renderingElement, opacity, specifiedStart, effectiveStart, specifiedEnd, effectiveEnd),
InterpolationColors = CalculateColorBlend(renderer, opacity, specifiedStart, effectiveStart, specifiedEnd, effectiveEnd),
WrapMode = WrapMode.TileFlipX
};
}
finally
{
if (this.GradientUnits == SvgCoordinateUnits.ObjectBoundingBox) renderer.PopBoundable();
}
}
private PointF CalculateStart(ISvgBoundable boundable)
private PointF CalculateStart(SvgRenderer renderer)
{
return TransformPoint(new PointF(this.X1.ToDeviceValue(boundable), this.Y1.ToDeviceValue(boundable, true)));
return TransformPoint(SvgUnit.GetDevicePointOffset(this.X1, this.Y1, renderer, this));
}
private PointF CalculateEnd(ISvgBoundable boundable)
private PointF CalculateEnd(SvgRenderer renderer)
{
return TransformPoint(new PointF(this.X2.ToDeviceValue(boundable), this.Y2.ToDeviceValue(boundable, true)));
return TransformPoint(SvgUnit.GetDevicePointOffset(this.X2, this.Y2, renderer, this));
}
private bool NeedToExpandGradient(ISvgBoundable boundable, PointF specifiedStart, PointF specifiedEnd)
......@@ -174,9 +181,9 @@ namespace Svg
return new GradientPoints(effectiveStart, effectiveEnd);
}
private ColorBlend CalculateColorBlend(SvgVisualElement owner, float opacity, PointF specifiedStart, PointF effectiveStart, PointF specifiedEnd, PointF effectiveEnd)
private ColorBlend CalculateColorBlend(SvgRenderer renderer, float opacity, PointF specifiedStart, PointF effectiveStart, PointF specifiedEnd, PointF effectiveEnd)
{
var colorBlend = GetColorBlend(owner, opacity, false);
var colorBlend = GetColorBlend(renderer, opacity, false);
var startDelta = CalculateDistance(specifiedStart, effectiveStart);
var endDelta = CalculateDistance(specifiedEnd, effectiveEnd);
......
......@@ -87,26 +87,19 @@ namespace Svg
Overflow = SvgOverflow.hidden;
}
public override System.Drawing.Drawing2D.GraphicsPath Path
{
get
public override System.Drawing.Drawing2D.GraphicsPath Path(SvgRenderer renderer)
{
var path = this.Children.FirstOrDefault(x => x is SvgPath);
if (path != null)
return (path as SvgPath).Path;
return (path as SvgPath).Path(renderer);
return null;
}
protected set
{
// No-op
}
}
public override System.Drawing.RectangleF Bounds
{
get
{
var path = this.Path;
var path = this.Path(null);
if (path != null)
return path.GetBounds();
return new System.Drawing.RectangleF();
......@@ -177,7 +170,7 @@ namespace Svg
/// <param name="pMarkerPoint"></param>
private void RenderPart2(float fAngle, SvgRenderer pRenderer, SvgPath pOwner, PointF pMarkerPoint)
{
Pen pRenderPen = CreatePen(pOwner);
Pen pRenderPen = CreatePen(pOwner, pRenderer);
GraphicsPath markerPath = GetClone(pOwner);
......@@ -190,10 +183,14 @@ namespace Svg
switch (MarkerUnits)
{
case SvgMarkerUnits.strokeWidth:
transMatrix.Translate(AdjustForViewBoxWidth(-RefX * pOwner.StrokeWidth), AdjustForViewBoxHeight(-RefY * pOwner.StrokeWidth));
transMatrix.Translate(AdjustForViewBoxWidth(-RefX.ToDeviceValue(pRenderer, UnitRenderingType.Horizontal, this) *
pOwner.StrokeWidth.ToDeviceValue(pRenderer, UnitRenderingType.Other, this)),
AdjustForViewBoxHeight(-RefY.ToDeviceValue(pRenderer, UnitRenderingType.Vertical, this) *
pOwner.StrokeWidth.ToDeviceValue(pRenderer, UnitRenderingType.Other, this)));
break;
case SvgMarkerUnits.userSpaceOnUse:
transMatrix.Translate(-RefX, -RefY);
transMatrix.Translate(-RefX.ToDeviceValue(pRenderer, UnitRenderingType.Horizontal, this),
-RefY.ToDeviceValue(pRenderer, UnitRenderingType.Vertical, this));
break;
}
markerPath.Transform(transMatrix);
......@@ -205,7 +202,7 @@ namespace Svg
if (pFill != null)
{
Brush pBrush = pFill.GetBrush(this, fOpacity);
Brush pBrush = pFill.GetBrush(this, pRenderer, fOpacity);
pRenderer.FillPath(pBrush, markerPath);
pBrush.Dispose();
}
......@@ -219,17 +216,18 @@ namespace Svg
/// </summary>
/// <param name="pStroke"></param>
/// <returns></returns>
private Pen CreatePen(SvgPath pPath)
private Pen CreatePen(SvgPath pPath, SvgRenderer renderer)
{
Brush pBrush = pPath.Stroke.GetBrush(this, Opacity);
Brush pBrush = pPath.Stroke.GetBrush(this, renderer, Opacity);
switch (MarkerUnits)
{
case SvgMarkerUnits.strokeWidth:
return (new Pen(pBrush, StrokeWidth * pPath.StrokeWidth));
return (new Pen(pBrush, StrokeWidth.ToDeviceValue(renderer, UnitRenderingType.Other, this) *
pPath.StrokeWidth.ToDeviceValue(renderer, UnitRenderingType.Other, this)));
case SvgMarkerUnits.userSpaceOnUse:
return (new Pen(pBrush, StrokeWidth));
return (new Pen(pBrush, StrokeWidth.ToDeviceValue(renderer, UnitRenderingType.Other, this)));
}
return (new Pen(pBrush, StrokeWidth));
return (new Pen(pBrush, StrokeWidth.ToDeviceValue(renderer, UnitRenderingType.Other, this)));
}
/// <summary>
......@@ -238,7 +236,7 @@ namespace Svg
/// <returns></returns>
private GraphicsPath GetClone(SvgPath pPath)
{
GraphicsPath pRet = Path.Clone() as GraphicsPath;
GraphicsPath pRet = Path(null).Clone() as GraphicsPath;
switch (MarkerUnits)
{
case SvgMarkerUnits.strokeWidth:
......
......@@ -39,7 +39,7 @@ namespace Svg
/// </summary>
/// <param name="styleOwner">The owner <see cref="SvgVisualElement"/>.</param>
/// <param name="opacity">The opacity of the brush.</param>
public abstract Brush GetBrush(SvgVisualElement styleOwner, float opacity);
public abstract Brush GetBrush(SvgVisualElement styleOwner, SvgRenderer renderer, float opacity);
/// <summary>
/// Returns a <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
......
......@@ -27,6 +27,14 @@ namespace Svg
{
return SvgColourServer.NotSet;
}
else if (value == "inherit")
{
return SvgColourServer.Inherit;
}
else if (value == "currentColor")
{
return new SvgDeferredPaintServer(document, value);
}
else if (value.IndexOf("url(#") > -1)
{
Match match = _urlRefPattern.Match(value);
......
......@@ -13,13 +13,15 @@ namespace Svg
/// A pattern is used to fill or stroke an object using a pre-defined graphic object which can be replicated ("tiled") at fixed intervals in x and y to cover the areas to be painted.
/// </summary>
[SvgElement("pattern")]
public sealed class SvgPatternServer : SvgPaintServer, ISvgViewPort
public sealed class SvgPatternServer : SvgPaintServer, ISvgViewPort, ISvgSupportsCoordinateUnits
{
private SvgUnit _width;
private SvgUnit _height;
private SvgUnit _x;
private SvgUnit _y;
private SvgViewBox _viewBox;
private SvgCoordinateUnits _patternUnits;
private SvgCoordinateUnits _patternContentUnits;
[SvgAttribute("overflow")]
public SvgOverflow Overflow
......@@ -61,6 +63,26 @@ namespace Svg
set { this._width = value; }
}
/// <summary>
/// Gets or sets the width of the pattern.
/// </summary>
[SvgAttribute("patternUnits")]
public SvgCoordinateUnits PatternUnits
{
get { return this._patternUnits; }
set { this._patternUnits = value; }
}
/// <summary>
/// Gets or sets the width of the pattern.
/// </summary>
[SvgAttribute("patternUnits")]
public SvgCoordinateUnits PatternContentUnits
{
get { return this._patternContentUnits; }
set { this._patternContentUnits = value; }
}
/// <summary>
/// Gets or sets the height of the pattern.
/// </summary>
......@@ -107,7 +129,7 @@ namespace Svg
/// </summary>
/// <param name="renderingElement">The owner <see cref="SvgVisualElement"/>.</param>
/// <param name="opacity">The opacity of the brush.</param>
public override Brush GetBrush(SvgVisualElement renderingElement, float opacity)
public override Brush GetBrush(SvgVisualElement renderingElement, SvgRenderer renderer, float opacity)
{
// If there aren't any children, return null
if (this.Children.Count == 0)
......@@ -117,18 +139,21 @@ namespace Svg
if (this._width.Value == 0.0f || this._height.Value == 0.0f)
return null;
float width = this._width.ToDeviceValue(renderingElement);
float height = this._height.ToDeviceValue(renderingElement, true);
Bitmap image = new Bitmap((int)width, (int)height);
using (SvgRenderer renderer = SvgRenderer.FromImage(image))
try
{
Matrix patternMatrix = new Matrix();
if (this.PatternUnits == SvgCoordinateUnits.ObjectBoundingBox) renderer.Boundable(renderingElement);
float width = this._width.ToDeviceValue(renderer, UnitRenderingType.Horizontal, this);
float height = this._height.ToDeviceValue(renderer, UnitRenderingType.Vertical, this);
Matrix patternMatrix = new Matrix();
// Apply a translate if needed
if (this._x.Value > 0.0f || this._y.Value > 0.0f)
{
patternMatrix.Translate(this._x.ToDeviceValue(renderingElement) + -1.0f, this._y.ToDeviceValue(renderingElement, true) + -1.0f);
float x = this._x.ToDeviceValue(renderer, UnitRenderingType.HorizontalOffset, this);
float y = this._y.ToDeviceValue(renderer, UnitRenderingType.VerticalOffset, this);
patternMatrix.Translate(x + -1.0f, y + -1.0f);
}
else
{
......@@ -137,27 +162,40 @@ namespace Svg
if (this.ViewBox.Height > 0 || this.ViewBox.Width > 0)
{
patternMatrix.Scale(this.Width.ToDeviceValue() / this.ViewBox.Width, this.Height.ToDeviceValue() / this.ViewBox.Height);
patternMatrix.Scale(this.Width.ToDeviceValue(renderer, UnitRenderingType.Horizontal, this) / this.ViewBox.Width,
this.Height.ToDeviceValue(renderer, UnitRenderingType.Vertical, this) / this.ViewBox.Height);
}
renderer.Transform = patternMatrix;
renderer.CompositingQuality = CompositingQuality.HighQuality;
renderer.SmoothingMode = SmoothingMode.AntiAlias;
renderer.PixelOffsetMode = PixelOffsetMode.Half;
Bitmap image = new Bitmap((int)width, (int)height);
using (SvgRenderer iRenderer = SvgRenderer.FromImage(image))
{
iRenderer.Boundable((_patternContentUnits == SvgCoordinateUnits.ObjectBoundingBox) ? new GenericBoundable(0, 0, width, height) : renderer.Boundable());
iRenderer.Transform = patternMatrix;
iRenderer.CompositingQuality = CompositingQuality.HighQuality;
iRenderer.SmoothingMode = SmoothingMode.AntiAlias;
iRenderer.PixelOffsetMode = PixelOffsetMode.Half;
foreach (SvgElement child in this.Children)
{
child.RenderElement(renderer);
child.RenderElement(iRenderer);
}
renderer.Save();
iRenderer.Save();
}
image.Save(string.Format(@"C:\test{0:D3}.png", imgNumber++));
TextureBrush textureBrush = new TextureBrush(image);
return textureBrush;
}
finally
{
if (this.PatternUnits == SvgCoordinateUnits.ObjectBoundingBox) renderer.PopBoundable();
}
}
private static int imgNumber = 0;
......@@ -180,5 +218,10 @@ namespace Svg
return newObj;
}
public SvgCoordinateUnits GetUnits()
{
return _patternUnits;
}
}
}
\ No newline at end of file
......@@ -95,21 +95,23 @@ namespace Svg
Radius = new SvgUnit(SvgUnitType.Percentage, 50F);
}
public override Brush GetBrush(SvgVisualElement renderingElement, float opacity)
public override Brush GetBrush(SvgVisualElement renderingElement, SvgRenderer renderer, float opacity)
{
LoadStops();
var origin = CalculateOrigin(renderingElement);
LoadStops(renderingElement);
var centerPoint = CalculateCenterPoint(renderingElement, origin);
var focalPoint = CalculateFocalPoint(renderingElement, origin);
try
{
if (this.GradientUnits == SvgCoordinateUnits.ObjectBoundingBox) renderer.Boundable(renderingElement);
var origin = renderer.Boundable().Location;
var centerPoint = CalculateCenterPoint(renderer, origin);
var focalPoint = CalculateFocalPoint(renderer, origin);
var specifiedRadius = CalculateRadius(renderingElement);
var specifiedRadius = CalculateRadius(renderer);
var effectiveRadius = CalculateEffectiveRadius(renderingElement, centerPoint, specifiedRadius);
var brush = new PathGradientBrush(CreateGraphicsPath(origin, centerPoint, effectiveRadius))
{
InterpolationColors = CalculateColorBlend(renderingElement, opacity, specifiedRadius, effectiveRadius),
InterpolationColors = CalculateColorBlend(renderer, opacity, specifiedRadius, effectiveRadius),
CenterPoint = focalPoint
};
......@@ -117,31 +119,31 @@ namespace Svg
return brush;
}
private PointF CalculateOrigin(SvgVisualElement renderingElement)
finally
{
return CalculateBoundable(renderingElement).Location;
if (this.GradientUnits == SvgCoordinateUnits.ObjectBoundingBox) renderer.PopBoundable();
}
}
private PointF CalculateCenterPoint(ISvgBoundable boundable, PointF origin)
private PointF CalculateCenterPoint(SvgRenderer renderer, PointF origin)
{
var deviceCenterX = origin.X + CenterX.ToDeviceValue(boundable);
var deviceCenterY = origin.Y + CenterY.ToDeviceValue(boundable, true);
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;
}
private PointF CalculateFocalPoint(ISvgBoundable boundable, PointF origin)
private PointF CalculateFocalPoint(SvgRenderer renderer, PointF origin)
{
var deviceFocalX = origin.X + FocalX.ToDeviceValue(boundable);
var deviceFocalY = origin.Y + FocalY.ToDeviceValue(boundable, true);
var deviceFocalX = origin.X + FocalX.ToDeviceValue(renderer, UnitRenderingType.HorizontalOffset, this);
var deviceFocalY = origin.Y + FocalY.ToDeviceValue(renderer, UnitRenderingType.VerticalOffset, this);
var transformedFocalPoint = TransformPoint(new PointF(deviceFocalX, deviceFocalY));
return transformedFocalPoint;
}
private float CalculateRadius(ISvgBoundable boundable)
private float CalculateRadius(SvgRenderer renderer)
{
var radius = Radius.ToDeviceValue(boundable);
var radius = Radius.ToDeviceValue(renderer, UnitRenderingType.Other, this);
var transformRadiusVector = TransformVector(new PointF(radius, 0));
var transformedRadius = CalculateLength(transformRadiusVector);
return transformedRadius;
......@@ -191,9 +193,9 @@ namespace Svg
return path;
}
private ColorBlend CalculateColorBlend(SvgVisualElement renderingElement, float opacity, float specifiedRadius, float effectiveRadius)
private ColorBlend CalculateColorBlend(SvgRenderer renderer, float opacity, float specifiedRadius, float effectiveRadius)
{
var colorBlend = GetColorBlend(renderingElement, opacity, true);
var colorBlend = GetColorBlend(renderer, opacity, true);
if (specifiedRadius >= effectiveRadius)
{
......
......@@ -81,9 +81,7 @@ namespace Svg
/// <summary>
/// Gets the <see cref="GraphicsPath"/> for this element.
/// </summary>
public override GraphicsPath Path
{
get
public override GraphicsPath Path(SvgRenderer renderer)
{
if (this._path == null || this.IsPathDirty)
{
......@@ -98,11 +96,6 @@ namespace Svg
}
return _path;
}
protected set
{
_path = value;
}
}
internal void OnPathUpdated()
{
......@@ -124,7 +117,7 @@ namespace Svg
/// <value>The bounds.</value>
public override System.Drawing.RectangleF Bounds
{
get { return this.Path.GetBounds(); }
get { return this.Path(null).GetBounds(); }
}
/// <summary>
......@@ -145,8 +138,8 @@ namespace Svg
{
if (this.Stroke != null)
{
float strokeWidth = this.StrokeWidth.ToDeviceValue(this);
using (Pen pen = new Pen(this.Stroke.GetBrush(this, this.StrokeOpacity), strokeWidth))
float strokeWidth = this.StrokeWidth.ToDeviceValue(renderer, UnitRenderingType.Other, this);
using (Pen pen = new Pen(this.Stroke.GetBrush(this, renderer, this.StrokeOpacity), strokeWidth))
{
if (this.StrokeDashArray != null && this.StrokeDashArray.Count > 0)
{
......@@ -154,25 +147,26 @@ namespace Svg
pen.DashPattern = this.StrokeDashArray.ConvertAll(u => u.Value / ((strokeWidth <= 0) ? 1 : strokeWidth)).ToArray();
}
renderer.DrawPath(pen, this.Path);
var path = this.Path(renderer);
renderer.DrawPath(pen, path);
if (this.MarkerStart != null)
{
SvgMarker marker = this.OwnerDocument.GetElementById<SvgMarker>(this.MarkerStart.ToString());
marker.RenderMarker(renderer, this, Path.PathPoints[0], Path.PathPoints[0], Path.PathPoints[1]);
marker.RenderMarker(renderer, this, path.PathPoints[0], path.PathPoints[0], path.PathPoints[1]);
}
if (this.MarkerMid != null)
{
SvgMarker marker = this.OwnerDocument.GetElementById<SvgMarker>(this.MarkerMid.ToString());
for (int i = 1; i <= Path.PathPoints.Length - 2; i++)
marker.RenderMarker(renderer, this, Path.PathPoints[i], Path.PathPoints[i - 1], Path.PathPoints[i], Path.PathPoints[i + 1]);
for (int i = 1; i <= path.PathPoints.Length - 2; i++)
marker.RenderMarker(renderer, this, path.PathPoints[i], path.PathPoints[i - 1], path.PathPoints[i], path.PathPoints[i + 1]);
}
if (this.MarkerEnd != null)
{
SvgMarker marker = this.OwnerDocument.GetElementById<SvgMarker>(this.MarkerEnd.ToString());
marker.RenderMarker(renderer, this, Path.PathPoints[Path.PathPoints.Length - 1], Path.PathPoints[Path.PathPoints.Length - 2], Path.PathPoints[Path.PathPoints.Length - 1]);
marker.RenderMarker(renderer, this, path.PathPoints[path.PathPoints.Length - 1], path.PathPoints[path.PathPoints.Length - 2], path.PathPoints[path.PathPoints.Length - 1]);
}
}
}
......
......@@ -36,3 +36,5 @@ using System.Runtime.InteropServices;
//[assembly: AssemblyFileVersion("1.0.1.*")]
[assembly: CLSCompliant(true)]
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Svg.UnitTests")]
\ No newline at end of file
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