Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#482 - Masks are now rendered #825

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions Samples/SVGViewer/DebugRenderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -110,12 +110,33 @@ public Matrix Transform
}
}

public SizeF RenderSize
{
get
{
return new SizeF(0, 0);
}
}

public void Dispose()
{
if (_clip != null)
_clip.Dispose();
if (_transform != null)
_transform.Dispose();
}

public Bitmap GetMask()
{
return null;
}

public void SetMask(Bitmap mask)
{
}

public void DisposeMask()
{
}
}
}
49 changes: 47 additions & 2 deletions Source/Basic Shapes/SvgVisualElement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public abstract partial class SvgVisualElement : SvgElement, ISvgBoundable, ISvg
{
private bool? _requiresSmoothRendering;
private Region _previousClip;
private Bitmap _previousMask;

/// <summary>
/// Gets the <see cref="GraphicsPath"/> for this element.
Expand Down Expand Up @@ -64,6 +65,16 @@ public virtual Uri ClipPath
set { Attributes["clip-path"] = value; }
}

/// <summary>
/// Gets the associated <see cref="SvgMask"/> if one has been specified.
/// </summary>
[SvgAttribute("mask")]
public virtual Uri Mask
{
get { return GetAttribute<Uri>("mask", false); }
set { Attributes["mask"] = value; }
}

/// <summary>
/// Gets or sets the algorithm which is to be used to determine the clipping region.
/// </summary>
Expand Down Expand Up @@ -179,7 +190,15 @@ private void RenderInternal(ISvgRenderer renderer, Action<ISvgRenderer> renderMe
if (PushTransforms(renderer))
{
SetClip(renderer);

var isMaskSet = SetMask(renderer);
renderMethod.Invoke(renderer);

if (isMaskSet)
{
ResetMask(renderer);
}

ResetClip(renderer);
}
}
Expand Down Expand Up @@ -300,7 +319,7 @@ protected internal virtual bool RenderStroke(ISvgRenderer renderer)
strokeWidth = Math.Max(strokeWidth, 1f);

/* divide by stroke width - GDI uses stroke width as unit.*/
var dashPattern = strokeDashArray.Select(u => ((u.ToDeviceValue(renderer, UnitRenderingType.Other, this) <= 0f) ? 1f :
var dashPattern = strokeDashArray.Select(u => ((u.ToDeviceValue(renderer, UnitRenderingType.Other, this) <= 0f) ? 1f :
u.ToDeviceValue(renderer, UnitRenderingType.Other, this)) / strokeWidth).ToArray();
var length = dashPattern.Length;

Expand Down Expand Up @@ -362,7 +381,7 @@ protected internal virtual bool RenderStroke(ISvgRenderer renderer)

if (dashOffset != 0f)
{
pen.DashOffset = ((dashOffset.ToDeviceValue(renderer, UnitRenderingType.Other, this) <= 0f) ? 1f :
pen.DashOffset = ((dashOffset.ToDeviceValue(renderer, UnitRenderingType.Other, this) <= 0f) ? 1f :
dashOffset.ToDeviceValue(renderer, UnitRenderingType.Other, this)) / strokeWidth;
}
}
Expand Down Expand Up @@ -454,6 +473,32 @@ protected internal virtual void ResetClip(ISvgRenderer renderer)
}
}

protected internal virtual bool SetMask(ISvgRenderer renderer)
{
var maskPath = this.Mask.ReplaceWithNullIfNone();

if (maskPath != null)
{
this._previousMask = renderer.GetMask();

var element = this.OwnerDocument.GetElementById<SvgMask>(maskPath.ToString());
if (element != null)
{
renderer.SetMask(element.RenderMask(renderer));

return true;
}
}

return false;
}

protected internal virtual void ResetMask(ISvgRenderer renderer)
{
renderer.DisposeMask();
renderer.SetMask(this._previousMask);
}

/// <summary>
/// Sets the clipping region of the specified <see cref="ISvgRenderer"/>.
/// </summary>
Expand Down
28 changes: 28 additions & 0 deletions Source/Clipping and Masking/SvgMask.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
using System;
using System.Drawing;

namespace Svg
{
/// <summary>
Expand Down Expand Up @@ -70,5 +73,30 @@ public override SvgElement DeepCopy()
{
return DeepCopy<SvgMask>();
}

/// <summary>
/// Renders the mask to a bitmap.
/// </summary>
/// <param name="renderer">The current renderer.</param>
/// <returns>A <see cref="Bitmap"/> object.</returns>
public Bitmap RenderMask(ISvgRenderer renderer)
{
var boundable = renderer.GetBoundable();

var maskBitmap = new Bitmap((int)Math.Round(renderer.RenderSize.Width), (int)Math.Round(renderer.RenderSize.Height));

var graphics = Graphics.FromImage(maskBitmap);
graphics.Transform = renderer.Transform.Clone();
graphics.Clear(System.Drawing.Color.Black);

using (var maskRenderer = SvgRenderer.FromGraphics(graphics, renderer.RenderSize))
{
maskRenderer.SetBoundable(boundable);

RenderElement(maskRenderer);
}

return maskBitmap;
}
}
}
4 changes: 3 additions & 1 deletion Source/Document Structure/SvgImage.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
Expand Down Expand Up @@ -146,6 +146,7 @@ protected override void Render(ISvgRenderer renderer)
var destRect = destClip;
renderer.SetClip(new Region(destClip), CombineMode.Intersect);
SetClip(renderer);
SetMask(renderer);

var aspectRatio = AspectRatio;
if (aspectRatio.Align != SvgPreserveAspectRatio.none)
Expand Down Expand Up @@ -227,6 +228,7 @@ protected override void Render(ISvgRenderer renderer)
}
}

ResetMask(renderer);
ResetClip(renderer);
}
}
Expand Down
21 changes: 21 additions & 0 deletions Source/ExtensionMethods/PointFExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;

namespace Svg.ExtensionMethods
{
internal static class PointFExtensions
{
internal static RectangleF GetBounds(this IEnumerable<PointF> points)
{
var minX = points.Min(point => point.X);
var maxX = points.Max(point => point.X);
var minY = points.Min(point => point.Y);
var maxY = points.Max(point => point.Y);

return new RectangleF(minX, minY, maxX - minX, maxY - minY);
}
}
}
27 changes: 27 additions & 0 deletions Source/ExtensionMethods/RectangleFExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Text;

namespace Svg.ExtensionMethods
{
internal static class RectangleFExtensions
{
internal static PointF[] GetPoints(this RectangleF rectangle)
{
return new[]
{
rectangle.Location,
rectangle.Location + rectangle.Size
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here should consider five points like this, If there are only two points, there will have issue with the bounds when the transform contains rotation

 return new[]
            {
                rectangle.Location,
                rectangle.Location + rectangle.Size,
                new PointF(rectangle.X,rectangle.Y+rectangle.Height),
                new PointF(rectangle.X+rectangle.Width,rectangle.Y)
            };

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can verify this by this transform : "matrix(0.51 0.51 -0.51 0.51 776.90 96.03)"

};
}

internal static RectangleF Transform(this RectangleF rectangle, Matrix matrix)
{
var points = GetPoints(rectangle);
matrix.TransformPoints(points);
return points.GetBounds();
}
}
}
4 changes: 4 additions & 0 deletions Source/Rendering/ISvgRenderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,17 @@ public interface ISvgRenderer : IDisposable
void FillPath(Brush brush, GraphicsPath path);
ISvgBoundable GetBoundable();
Region GetClip();
Bitmap GetMask();
ISvgBoundable PopBoundable();
void RotateTransform(float fAngle, MatrixOrder order = MatrixOrder.Append);
void ScaleTransform(float sx, float sy, MatrixOrder order = MatrixOrder.Append);
void SetBoundable(ISvgBoundable boundable);
void SetClip(Region region, CombineMode combineMode = CombineMode.Replace);
void SetMask(Bitmap mask);
void DisposeMask();
SmoothingMode SmoothingMode { get; set; }
Matrix Transform { get; set; }
SizeF RenderSize { get; }
void TranslateTransform(float dx, float dy, MatrixOrder order = MatrixOrder.Append);
void DrawImage(Image image, RectangleF destRect, RectangleF srcRect, GraphicsUnit graphicsUnit, float opacity);
}
Expand Down
Loading