Skip to content

Commit

Permalink
Fix: Zero matrix transformation issues (#537)
Browse files Browse the repository at this point in the history
* Small refactoring in PushTransforms and PopTransforms.
* Fix: In <text>, <image>, and <switch>, zero matrix transformation was ignored.
* Fix: Crash with zero matrix transformation.
* Add tests
  • Loading branch information
H1Gdev authored and mrbean-bremen committed Aug 3, 2019
1 parent d0816a8 commit 3247467
Show file tree
Hide file tree
Showing 14 changed files with 183 additions and 132 deletions.
29 changes: 14 additions & 15 deletions Source/Basic Shapes/SvgImage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -126,26 +126,24 @@ protected override void Render(ISvgRenderer renderer)
return;

var img = GetImage(Href);
if (img != null)
var bmp = img as Image;
var svg = img as SvgFragment;
if (bmp == null && svg == null)
return;
try
{
var bmp = img as Image;
var svg = img as SvgFragment;
try
if (PushTransforms(renderer))
{
RectangleF srcRect;
if (bmp != null)
srcRect = new RectangleF(0f, 0f, bmp.Width, bmp.Height);
else if (svg != null)
srcRect = new RectangleF(new PointF(0f, 0f), svg.GetDimensions());
else
return;
srcRect = new RectangleF(new PointF(0f, 0f), svg.GetDimensions());

var destClip = new RectangleF(Location.ToDeviceValue(renderer, this),
new SizeF(Width.ToDeviceValue(renderer, UnitRenderingType.Horizontal, this),
Height.ToDeviceValue(renderer, UnitRenderingType.Vertical, this)));
var destRect = destClip;

PushTransforms(renderer);
renderer.SetClip(new Region(destClip), CombineMode.Intersect);
SetClip(renderer);

Expand Down Expand Up @@ -225,14 +223,15 @@ protected override void Render(ISvgRenderer renderer)
}

ResetClip(renderer);
PopTransforms(renderer);
}
finally
{
if (bmp != null)
bmp.Dispose();
}
}
finally
{
PopTransforms(renderer);

if (bmp != null)
bmp.Dispose();
}
// TODO: cache images... will need a shared context for this
}

Expand Down
69 changes: 38 additions & 31 deletions Source/Basic Shapes/SvgVisualElement.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
using System;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using Svg.FilterEffects;
using System.Globalization;

namespace Svg
{
Expand Down Expand Up @@ -133,45 +133,53 @@ protected override void Render(ISvgRenderer renderer)

private void Render(ISvgRenderer renderer, bool renderFilter)
{
if (this.Visible && this.Displayable && this.PushTransforms(renderer) &&
(!this.Renderable || this.Path(renderer) != null))
if (Visible && Displayable && (!Renderable || Path(renderer) != null))
{
if (!(renderFilter && this.RenderFilter(renderer)))
if (!(renderFilter && RenderFilter(renderer)))
{
this.SetClip(renderer);

if (this.Renderable)
try
{
var opacity = Math.Min(Math.Max(this.Opacity, 0), 1);
if (opacity == 1f)
this.RenderFillAndStroke(renderer);
else
if (PushTransforms(renderer))
{
IsPathDirty = true;
var bounds = this.Bounds;
IsPathDirty = true;
SetClip(renderer);

using (var canvas = new Bitmap((int)Math.Ceiling(bounds.Width), (int)Math.Ceiling(bounds.Height)))
if (Renderable)
{
using (var canvasRenderer = SvgRenderer.FromImage(canvas))
var opacity = Math.Min(Math.Max(Opacity, 0), 1);
if (opacity == 1f)
RenderFillAndStroke(renderer);
else
{
canvasRenderer.SetBoundable(renderer.GetBoundable());
canvasRenderer.TranslateTransform(-bounds.X, -bounds.Y);
IsPathDirty = true;
var bounds = Bounds;
IsPathDirty = true;

using (var canvas = new Bitmap((int)Math.Ceiling(bounds.Width), (int)Math.Ceiling(bounds.Height)))
{
using (var canvasRenderer = SvgRenderer.FromImage(canvas))
{
canvasRenderer.SetBoundable(renderer.GetBoundable());
canvasRenderer.TranslateTransform(-bounds.X, -bounds.Y);

this.RenderFillAndStroke(canvasRenderer);
RenderFillAndStroke(canvasRenderer);
}
var srcRect = new RectangleF(0f, 0f, bounds.Width, bounds.Height);
renderer.DrawImage(canvas, bounds, srcRect, GraphicsUnit.Pixel, opacity);
}
}
var srcRect = new RectangleF(0f, 0f, bounds.Width, bounds.Height);
renderer.DrawImage(canvas, bounds, srcRect, GraphicsUnit.Pixel, opacity);
}
else
{
base.RenderChildren(renderer);
}

ResetClip(renderer);
}
}
else
finally
{
base.RenderChildren(renderer);
PopTransforms(renderer);
}

this.ResetClip(renderer);
this.PopTransforms(renderer);
}
}
}
Expand All @@ -180,16 +188,15 @@ private bool RenderFilter(ISvgRenderer renderer)
{
var rendered = false;

var filterPath = this.Filter;
var filterPath = Filter;
if (filterPath != null)
{
var element = this.OwnerDocument.IdManager.GetElementById(filterPath);
var element = OwnerDocument.IdManager.GetElementById(filterPath);
if (element is SvgFilter)
{
this.PopTransforms(renderer);
try
{
((SvgFilter)element).ApplyFilter(this, renderer, (r) => this.Render(r, false));
((SvgFilter)element).ApplyFilter(this, renderer, (r) => Render(r, false));
}
catch (Exception ex)
{
Expand Down
5 changes: 3 additions & 2 deletions Source/Document Structure/SvgFragment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -156,8 +156,9 @@ public override XmlSpaceHandling SpaceHandling
/// <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);
if (!base.PushTransforms(renderer))
return false;
ViewBox.AddViewBoxTransform(AspectRatio, renderer, this);
return true;
}

Expand Down
18 changes: 13 additions & 5 deletions Source/Document Structure/SvgSwitch.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,19 @@ protected override void Render(ISvgRenderer renderer)
if (!Visible || !Displayable)
return;

this.PushTransforms(renderer);
this.SetClip(renderer);
base.RenderChildren(renderer);
this.ResetClip(renderer);
this.PopTransforms(renderer);
try
{
if (PushTransforms(renderer))
{
SetClip(renderer);
base.RenderChildren(renderer);
ResetClip(renderer);
}
}
finally
{
PopTransforms(renderer);
}
}

public override SvgElement DeepCopy()
Expand Down
5 changes: 3 additions & 2 deletions Source/Document Structure/SvgSymbol.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,9 @@ public override System.Drawing.RectangleF Bounds
/// <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);
if (!base.PushTransforms(renderer))
return false;
ViewBox.AddViewBoxTransform(AspectRatio, renderer, null);
return true;
}

Expand Down
64 changes: 37 additions & 27 deletions Source/Document Structure/SvgUse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,10 @@ public virtual SvgUnit Height
/// <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),
this.Y.ToDeviceValue(renderer, UnitRenderingType.Vertical, this),
if (!base.PushTransforms(renderer))
return false;
renderer.TranslateTransform(X.ToDeviceValue(renderer, UnitRenderingType.Horizontal, this),
Y.ToDeviceValue(renderer, UnitRenderingType.Vertical, this),
MatrixOrder.Prepend);
return true;
}
Expand Down Expand Up @@ -146,37 +147,46 @@ public override System.Drawing.RectangleF Bounds

protected override void Render(ISvgRenderer renderer)
{
if (this.Visible && this.Displayable && this.ReferencedElement != null && !this.HasRecursiveReference() && this.PushTransforms(renderer))
if (Visible && Displayable && ReferencedElement != null && !HasRecursiveReference())
{
this.SetClip(renderer);

var element = this.OwnerDocument.IdManager.GetElementById(this.ReferencedElement) as SvgVisualElement;
if (element != null)
try
{
var ew = Width.ToDeviceValue(renderer, UnitRenderingType.Horizontal, this);
var eh = Height.ToDeviceValue(renderer, UnitRenderingType.Vertical, this);
if (ew > 0 && eh > 0)
if (PushTransforms(renderer))
{
var viewBox = element.Attributes.GetAttribute<SvgViewBox>("viewBox");
if (viewBox != SvgViewBox.Empty && Math.Abs(ew - viewBox.Width) > float.Epsilon && Math.Abs(eh - viewBox.Height) > float.Epsilon)
SetClip(renderer);

var element = OwnerDocument.IdManager.GetElementById(ReferencedElement) as SvgVisualElement;
if (element != null)
{
var sw = ew / viewBox.Width;
var sh = eh / viewBox.Height;
renderer.ScaleTransform(sw, sh, MatrixOrder.Prepend);
var ew = Width.ToDeviceValue(renderer, UnitRenderingType.Horizontal, this);
var eh = Height.ToDeviceValue(renderer, UnitRenderingType.Vertical, this);
if (ew > 0 && eh > 0)
{
var viewBox = element.Attributes.GetAttribute<SvgViewBox>("viewBox");
if (viewBox != SvgViewBox.Empty && Math.Abs(ew - viewBox.Width) > float.Epsilon && Math.Abs(eh - viewBox.Height) > float.Epsilon)
{
var sw = ew / viewBox.Width;
var sh = eh / viewBox.Height;
renderer.ScaleTransform(sw, sh, MatrixOrder.Prepend);
}
}

var origParent = element.Parent;
element._parent = this;
// as the new parent may have other styles that are inherited,
// we have to redraw the paths for the children
element.InvalidateChildPaths();
element.RenderElement(renderer);
element._parent = origParent;
}
}

var origParent = element.Parent;
element._parent = this;
// as the new parent may have other styles that are inherited,
// we have to redraw the paths for the children
element.InvalidateChildPaths();
element.RenderElement(renderer);
element._parent = origParent;
ResetClip(renderer);
}
}
finally
{
PopTransforms(renderer);
}

this.ResetClip(renderer);
this.PopTransforms(renderer);
}
}

Expand Down
Loading

0 comments on commit 3247467

Please sign in to comment.