Skip to content

Commit

Permalink
Add pattern border filter
Browse files Browse the repository at this point in the history
  • Loading branch information
hajduakos committed Apr 2, 2020
1 parent e46e9e6 commit 69f3f44
Show file tree
Hide file tree
Showing 8 changed files with 143 additions and 2 deletions.
22 changes: 21 additions & 1 deletion FilterLib.Tests/FilterTests/BorderTests.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using FilterLib.Filters.Border;
using FilterLib.Util;
using NUnit.Framework;
using System.Runtime.InteropServices;
using Bitmap = System.Drawing.Bitmap;

namespace FilterLib.Tests.FilterTests
{
Expand All @@ -25,6 +25,26 @@ public void TestFadeBorder()
new FadeBorderFilter(Size.Absolute(20), new RGB(255, 0, 0)), 1));
}

[Test]
public void TestPatternBorder()
{
using Bitmap pattern = new Bitmap(TestContext.CurrentContext.TestDirectory + "/TestImages/_input2.bmp");
Assert.IsTrue(Common.CheckFilter("_input.bmp", "_input.bmp",
new PatternBorderFilter(Size.Absolute(0), Size.Absolute(0), pattern, BorderPosition.Inside), 1));
Assert.IsTrue(Common.CheckFilter("_input.bmp", "PatternBorder_30px_0_Green_Inside.bmp",
new PatternBorderFilter(Size.Absolute(30), Size.Absolute(0), pattern, BorderPosition.Inside), 1));
}

[Test, Platform("Win")]
public void TestPatternBorderRadius()
{
using Bitmap pattern = new Bitmap(TestContext.CurrentContext.TestDirectory + "/TestImages/_input2.bmp");
Assert.IsTrue(Common.CheckFilter("_input.bmp", "PatternBorder_10pct_8px_Outside.bmp",
new PatternBorderFilter(Size.Relative(.1f), Size.Absolute(8), pattern, BorderPosition.Outside), 1));
Assert.IsTrue(Common.CheckFilter("_input.bmp", "PatternBorder_20px_10pct_Center.bmp",
new PatternBorderFilter(Size.Absolute(20), Size.Relative(.1f), pattern, BorderPosition.Center), 1));
}

[Test]
public void TestSimpleBorder()
{
Expand Down
2 changes: 1 addition & 1 deletion FilterLib.Tests/ReflectiveApiTests/ApiTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ public class ApiTests
{
[Test]
public void TestListFilters() =>
Assert.AreEqual(58, ReflectiveApi.GetFilterTypes().ToArray().Length);
Assert.AreEqual(59, ReflectiveApi.GetFilterTypes().ToArray().Length);

[Test]
public void TestListBlends() =>
Expand Down
19 changes: 19 additions & 0 deletions FilterLib.Tests/ReflectiveApiTests/BorderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,25 @@ public void TestFadeBorder()
[Test]
public void TestFadeBorderParCnt() => Assert.AreEqual(2, Common.ParamCount(typeof(FadeBorderFilter)));

[Test]
public void TestPatternBorder()
{
IFilter f = ReflectiveApi.ConstructFilterByName("PatternBorder");
Assert.IsInstanceOf<PatternBorderFilter>(f);
ReflectiveApi.SetFilterPropertyByName(f, "Width", "10%");
ReflectiveApi.SetFilterPropertyByName(f, "Radius", "8px");
ReflectiveApi.SetFilterPropertyByName(f, "Pattern", TestContext.CurrentContext.TestDirectory + "/TestImages/_input.bmp");
ReflectiveApi.SetFilterPropertyByName(f, "Position", "Outside");
PatternBorderFilter ff = f as PatternBorderFilter;
Assert.AreEqual(20, ff.Width.ToAbsolute(200));
Assert.AreEqual(8, ff.Radius.ToAbsolute(200));
Assert.AreEqual(160, ff.Pattern.Width);
Assert.AreEqual(BorderPosition.Outside, ff.Position);
}

[Test]
public void TestPatternBorderParCnt() => Assert.AreEqual(4, Common.ParamCount(typeof(PatternBorderFilter)));

[Test]
public void TestSimpleBorder()
{
Expand Down
Binary file not shown.
Binary file not shown.
Binary file not shown.
96 changes: 96 additions & 0 deletions FilterLib/Filters/Border/PatternBorderFilter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Text;
using FilterLib.Reporting;

namespace FilterLib.Filters.Border
{
/// <summary>
/// Pattern border filter.
/// </summary>
[Filter]
public sealed class PatternBorderFilter : IFilter
{
/// <summary>
/// Border width.
/// </summary>
[FilterParam]
public Util.Size Width { get; set; }

/// <summary>
/// Border radius.
/// </summary>
[FilterParam]
public Util.Size Radius { get; set; }

/// <summary>
/// Border pattern.
/// </summary>
[FilterParam]
public Bitmap Pattern { get; set; }

/// <summary>
/// Border position.
/// </summary>
[FilterParam]
public BorderPosition Position { get; set; }

/// <summary>
/// Constructor.
/// </summary>
public PatternBorderFilter() :
this(Util.Size.Absolute(0), Util.Size.Absolute(0), new Bitmap(1, 1), BorderPosition.Inside)
{ }

/// <summary>
/// Constructor.
/// </summary>
/// <param name="width">Border width</param>
/// <param name="radius">Border radius</param>
/// <param name="pattern">Border pattern</param>
/// <param name="position">Border position</param>
public PatternBorderFilter(Util.Size width, Util.Size radius, Bitmap pattern, BorderPosition position)
{
this.Width = width;
this.Radius = radius;
this.Pattern = pattern;
this.Position = position;
}

public Bitmap Apply(Bitmap image, IReporter reporter = null)
{
reporter?.Start();
int w = image.Width;
int h = image.Height;
int borderW = Width.ToAbsolute(Math.Max(w, h));
int borderRad = Radius.ToAbsolute(Math.Max(w, h));
if (Position == BorderPosition.Outside) { w += 2 * borderW; h += 2 * borderW; }
else if (Position == BorderPosition.Center) { w += borderW; h += borderW; }

Bitmap imageWithBorder = new Bitmap(w, h);
using (Graphics gfx = Graphics.FromImage(imageWithBorder))
using (Brush brush = new TextureBrush(Pattern))
{
gfx.FillRectangle(brush, 0, 0, w, h);
gfx.SmoothingMode = SmoothingMode.HighQuality;
gfx.SetClip(new Rectangle(borderW, borderW + borderRad, w - 2 * borderW, h - 2 * borderW - 2 * borderRad));
gfx.SetClip(new Rectangle(borderW + borderRad, borderW, w - 2 * borderW - 2 * borderRad, h - 2 * borderW), CombineMode.Union);
GraphicsPath clip = new GraphicsPath
{
FillMode = FillMode.Winding
};
clip.AddEllipse(borderW, borderW, borderRad * 2, borderRad * 2);
clip.AddEllipse(w - borderW - 2 * borderRad, borderW, 2 * borderRad, 2 * borderRad);
clip.AddEllipse(borderW, h - borderW - 2 * borderRad, 2 * borderRad, 2 * borderRad);
clip.AddEllipse(w - borderW - 2 * borderRad, h - borderW - 2 * borderRad, 2 * borderRad, 2 * borderRad);

gfx.SetClip(clip, CombineMode.Union);
gfx.DrawImage(image, (w - image.Width) / 2, (h - image.Height) / 2, image.Width, image.Height);
}
reporter?.Done();
return imageWithBorder;
}
}
}
6 changes: 6 additions & 0 deletions FilterLib/ReflectiveApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Globalization;
using System.Reflection;
using System.Linq;
using System.Drawing;

namespace FilterLib
{
Expand Down Expand Up @@ -101,6 +102,11 @@ private static object ParseStringToType(Type type, string value)
if (type == typeof(Boolean)) return Convert.ToBoolean(value);
if (type.IsEnum) return Enum.Parse(type, value);
if (type == typeof(Util.Size)) return Util.Size.FromString(value);
if (type == typeof(Bitmap))
{
using Bitmap b = new Bitmap(value);
return (Bitmap)b.Clone();
}

// Try string constructor
foreach (ConstructorInfo ci in type.GetConstructors())
Expand Down

0 comments on commit 69f3f44

Please sign in to comment.