diff --git a/src/ImageProcessor.UnitTests/Images/masks/mask.png b/src/ImageProcessor.UnitTests/Images/masks/mask.png
new file mode 100644
index 0000000000..39a5b05c5c
--- /dev/null
+++ b/src/ImageProcessor.UnitTests/Images/masks/mask.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b76febb5bd44ae5624851482c5368a32f87ee1124a332115dd4f5421e19b269c
+size 3284
diff --git a/src/ImageProcessor.Web/Configuration/Resources/processing.config b/src/ImageProcessor.Web/Configuration/Resources/processing.config
index a1fed5fbdd..9d4a463090 100644
--- a/src/ImageProcessor.Web/Configuration/Resources/processing.config
+++ b/src/ImageProcessor.Web/Configuration/Resources/processing.config
@@ -12,7 +12,7 @@
-
+
@@ -27,6 +27,11 @@
+
+
+
+
+
diff --git a/src/ImageProcessor.Web/Helpers/ImageHelpers.cs b/src/ImageProcessor.Web/Helpers/ImageHelpers.cs
index 2b7946d779..29d7953a40 100644
--- a/src/ImageProcessor.Web/Helpers/ImageHelpers.cs
+++ b/src/ImageProcessor.Web/Helpers/ImageHelpers.cs
@@ -24,7 +24,13 @@ namespace ImageProcessor.Web.Helpers
///
/// The regex pattern.
///
- private static readonly string ExtensionRegexPattern = BuildExtensionRegexPattern();
+ public static readonly string ExtensionRegexPattern = BuildExtensionRegexPattern();
+
+ ///
+ /// The exclude regex for matching things to ignore when parsing image extensions.
+ /// I'd like to make something more extensible than this.
+ ///
+ private static readonly Regex ExcludeRegex = new Regex(@"mask=[\w+-]+.", RegexOptions.IgnoreCase);
///
/// The image format regex.
@@ -57,6 +63,12 @@ namespace ImageProcessor.Web.Helpers
///
public static string GetExtension(string input)
{
+ // First filter out any troublesome elements.
+ foreach (Match exlude in ExcludeRegex.Matches(input))
+ {
+ input = input.Replace(exlude.Value, string.Empty);
+ }
+
Match match = FormatRegex.Match(input);
if (match.Success)
diff --git a/src/ImageProcessor.Web/ImageProcessor.Web.csproj b/src/ImageProcessor.Web/ImageProcessor.Web.csproj
index c8171d4ba2..98f0385af3 100644
--- a/src/ImageProcessor.Web/ImageProcessor.Web.csproj
+++ b/src/ImageProcessor.Web/ImageProcessor.Web.csproj
@@ -48,6 +48,7 @@
+
diff --git a/src/ImageProcessor.Web/Processors/Mask.cs b/src/ImageProcessor.Web/Processors/Mask.cs
new file mode 100644
index 0000000000..f5ad7d454c
--- /dev/null
+++ b/src/ImageProcessor.Web/Processors/Mask.cs
@@ -0,0 +1,185 @@
+// --------------------------------------------------------------------------------------------------------------------
+//
+// Copyright (c) James South.
+// Licensed under the Apache License, Version 2.0.
+//
+//
+// Applies a mask to the given image. If the mask is not the same size as the image
+// it will be centered against the image.
+//
+// --------------------------------------------------------------------------------------------------------------------
+
+namespace ImageProcessor.Web.Processors
+{
+ using System;
+ using System.Drawing;
+ using System.IO;
+ using System.Text;
+ using System.Text.RegularExpressions;
+ using System.Web.Hosting;
+
+ using ImageProcessor.Processors;
+ using ImageProcessor.Web.Extensions;
+ using ImageProcessor.Web.Helpers;
+
+ ///
+ /// Applies a mask to the given image. If the mask is not the same size as the image
+ /// it will be centered against the image.
+ ///
+ public class Mask : IWebGraphicsProcessor
+ {
+ ///
+ /// The regular expression to search strings for.
+ ///
+ private static readonly Regex QueryRegex = new Regex(@"(mask=|maskposition=)[^&]+", RegexOptions.Compiled);
+
+ ///
+ /// The mask image regex.
+ ///
+ private static readonly Regex PixelRegex = new Regex(@"mask=[\w+-]+." + ImageHelpers.ExtensionRegexPattern);
+
+ ///
+ /// The point regex.
+ ///
+ private static readonly Regex PointRegex = new Regex(@"maskposition=\d+,\d+", RegexOptions.Compiled);
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public Mask()
+ {
+ this.Processor = new ImageProcessor.Processors.Mask();
+ }
+
+ ///
+ /// Gets the regular expression to search strings for.
+ ///
+ public Regex RegexPattern
+ {
+ get
+ {
+ return QueryRegex;
+ }
+ }
+
+ ///
+ /// Gets the order in which this processor is to be used in a chain.
+ ///
+ public int SortOrder { get; private set; }
+
+ ///
+ /// Gets the associated graphics processor.
+ ///
+ public IGraphicsProcessor Processor { get; private set; }
+
+ ///
+ /// The position in the original string where the first character of the captured substring was found.
+ ///
+ /// The query string to search.
+ ///
+ /// The zero-based starting position in the original string where the captured substring was found.
+ ///
+ public int MatchRegexIndex(string queryString)
+ {
+ int index = 0;
+
+ // Set the sort order to max to allow filtering.
+ this.SortOrder = int.MaxValue;
+
+ // First merge the matches so we can parse .
+ StringBuilder stringBuilder = new StringBuilder();
+
+ foreach (Match match in this.RegexPattern.Matches(queryString))
+ {
+ if (match.Success)
+ {
+ if (index == 0)
+ {
+ // Set the index on the first instance only.
+ this.SortOrder = match.Index;
+ }
+
+ stringBuilder.Append(match.Value);
+
+ index += 1;
+ }
+ }
+
+ if (this.SortOrder < int.MaxValue)
+ {
+ // Match syntax
+ string toParse = stringBuilder.ToString();
+ Image image = this.ParseImage(toParse);
+ Point? rectangle = this.ParsePoint(toParse);
+ this.Processor.DynamicParameter = new Tuple(image, rectangle);
+ }
+
+ return this.SortOrder;
+ }
+
+ ///
+ /// Returns the correct size of pixels.
+ ///
+ ///
+ /// The input containing the value to parse.
+ ///
+ ///
+ /// The representing the pixel size.
+ ///
+ public Image ParseImage(string input)
+ {
+ Image image = null;
+
+ // Correctly parse the path.
+ string path;
+ this.Processor.Settings.TryGetValue("VirtualPath", out path);
+
+ if (!string.IsNullOrWhiteSpace(path) && path.StartsWith("~/"))
+ {
+ Match match = PixelRegex.Match(input);
+
+ if (match.Success)
+ {
+ string imagePath = HostingEnvironment.MapPath(path);
+ if (imagePath != null)
+ {
+ imagePath = Path.Combine(imagePath, match.Value.Split('=')[1]);
+ using (ImageFactory factory = new ImageFactory())
+ {
+ factory.Load(imagePath);
+ image = new Bitmap(factory.Image);
+ }
+ }
+ }
+ }
+
+ return image;
+ }
+
+ ///
+ /// Returns the correct for the given string.
+ ///
+ ///
+ /// The input string containing the value to parse.
+ ///
+ ///
+ /// The correct
+ ///
+ private Point? ParsePoint(string input)
+ {
+ int[] dimensions = { };
+
+ foreach (Match match in PointRegex.Matches(input))
+ {
+ dimensions = match.Value.ToPositiveIntegerArray();
+ }
+
+ if (dimensions.Length == 2)
+ {
+ return new Point(dimensions[0], dimensions[1]);
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/src/TestWebsites/MVC/Web.config b/src/TestWebsites/MVC/Web.config
index 9bef44eafd..5aae58e7ed 100644
--- a/src/TestWebsites/MVC/Web.config
+++ b/src/TestWebsites/MVC/Web.config
@@ -5,7 +5,7 @@
-->
-
+