diff --git a/src/ImageProcessor.UnitTests/Images/masks/mask.png b/src/ImageProcessor.UnitTests/Images/imageprocessor/mask/mask.png
similarity index 100%
rename from src/ImageProcessor.UnitTests/Images/masks/mask.png
rename to src/ImageProcessor.UnitTests/Images/imageprocessor/mask/mask.png
diff --git a/src/ImageProcessor.UnitTests/Images/imageprocessor/overlay/monster.png.REMOVED.git-id b/src/ImageProcessor.UnitTests/Images/imageprocessor/overlay/monster.png.REMOVED.git-id
new file mode 100644
index 0000000000..c13b65e9eb
--- /dev/null
+++ b/src/ImageProcessor.UnitTests/Images/imageprocessor/overlay/monster.png.REMOVED.git-id
@@ -0,0 +1 @@
+4edf74a6857665c8efe2d3282c25907f5b20ca81
\ No newline at end of file
diff --git a/src/ImageProcessor.Web.UnitTests/RegularExpressionUnitTests.cs b/src/ImageProcessor.Web.UnitTests/RegularExpressionUnitTests.cs
index 963cd1b825..7df015d630 100644
--- a/src/ImageProcessor.Web.UnitTests/RegularExpressionUnitTests.cs
+++ b/src/ImageProcessor.Web.UnitTests/RegularExpressionUnitTests.cs
@@ -459,6 +459,20 @@ namespace ImageProcessor.Web.UnitTests
DropShadow = true,
FontFamily = new FontFamily("arial")
}
+ },
+ {
+ "watermark=watermark goodness&color=fff&fontsize=36&fontstyle=italic&fontopacity=80&watermark.position=30,150&textshadow=true&fontfamily=arial",
+ new TextLayer
+ {
+ Text = "watermark goodness",
+ FontColor = ColorTranslator.FromHtml("#" + "ffffff"),
+ FontSize = 36,
+ Style = FontStyle.Italic,
+ Opacity = 80,
+ Position = new Point(30, 150),
+ DropShadow = true,
+ FontFamily = new FontFamily("arial")
+ }
}
};
diff --git a/src/ImageProcessor.Web/Configuration/Resources/processing.config b/src/ImageProcessor.Web/Configuration/Resources/processing.config
index ab0eb300a6..8be8188c9c 100644
--- a/src/ImageProcessor.Web/Configuration/Resources/processing.config
+++ b/src/ImageProcessor.Web/Configuration/Resources/processing.config
@@ -30,10 +30,15 @@
-
+
+
+
+
+
+
diff --git a/src/ImageProcessor.Web/Helpers/ImageHelpers.cs b/src/ImageProcessor.Web/Helpers/ImageHelpers.cs
index 536b6f05db..cfa6186fd1 100644
--- a/src/ImageProcessor.Web/Helpers/ImageHelpers.cs
+++ b/src/ImageProcessor.Web/Helpers/ImageHelpers.cs
@@ -28,9 +28,9 @@ namespace ImageProcessor.Web.Helpers
///
/// The exclude regex for matching things to ignore when parsing image extensions.
- /// I'd like to make something more extensible than this.
+ /// TODO: This is hacky and awful and should go.
///
- private static readonly Regex ExcludeRegex = new Regex(@"mask=[\w+-]+.", RegexOptions.IgnoreCase);
+ private static readonly Regex ExcludeRegex = new Regex(@"(mask|overlay)=[\w+-]+.", RegexOptions.IgnoreCase);
///
/// The image format regex.
diff --git a/src/ImageProcessor.Web/ImageProcessor.Web.csproj b/src/ImageProcessor.Web/ImageProcessor.Web.csproj
index 7859ba25a1..1c7e29c403 100644
--- a/src/ImageProcessor.Web/ImageProcessor.Web.csproj
+++ b/src/ImageProcessor.Web/ImageProcessor.Web.csproj
@@ -50,6 +50,7 @@
+
diff --git a/src/ImageProcessor.Web/Processors/Mask.cs b/src/ImageProcessor.Web/Processors/Mask.cs
index f5ad7d454c..241cae8741 100644
--- a/src/ImageProcessor.Web/Processors/Mask.cs
+++ b/src/ImageProcessor.Web/Processors/Mask.cs
@@ -31,17 +31,17 @@ namespace ImageProcessor.Web.Processors
///
/// The regular expression to search strings for.
///
- private static readonly Regex QueryRegex = new Regex(@"(mask=|maskposition=)[^&]+", RegexOptions.Compiled);
+ private static readonly Regex QueryRegex = new Regex(@"(mask=|mask.\w+=)[^&]+", RegexOptions.Compiled);
///
/// The mask image regex.
///
- private static readonly Regex PixelRegex = new Regex(@"mask=[\w+-]+." + ImageHelpers.ExtensionRegexPattern);
+ private static readonly Regex ImageRegex = new Regex(@"mask=[\w+-]+." + ImageHelpers.ExtensionRegexPattern);
///
/// The point regex.
///
- private static readonly Regex PointRegex = new Regex(@"maskposition=\d+,\d+", RegexOptions.Compiled);
+ private static readonly Regex PointRegex = new Regex(@"mask.position=\d+,\d+", RegexOptions.Compiled);
///
/// Initializes a new instance of the class.
@@ -110,8 +110,8 @@ namespace ImageProcessor.Web.Processors
// Match syntax
string toParse = stringBuilder.ToString();
Image image = this.ParseImage(toParse);
- Point? rectangle = this.ParsePoint(toParse);
- this.Processor.DynamicParameter = new Tuple(image, rectangle);
+ Point? position = this.ParsePoint(toParse);
+ this.Processor.DynamicParameter = new Tuple(image, position);
}
return this.SortOrder;
@@ -136,7 +136,7 @@ namespace ImageProcessor.Web.Processors
if (!string.IsNullOrWhiteSpace(path) && path.StartsWith("~/"))
{
- Match match = PixelRegex.Match(input);
+ Match match = ImageRegex.Match(input);
if (match.Success)
{
@@ -168,8 +168,8 @@ namespace ImageProcessor.Web.Processors
private Point? ParsePoint(string input)
{
int[] dimensions = { };
-
- foreach (Match match in PointRegex.Matches(input))
+ Match match = PointRegex.Match(input);
+ if (match.Success)
{
dimensions = match.Value.ToPositiveIntegerArray();
}
diff --git a/src/ImageProcessor.Web/Processors/Overlay.cs b/src/ImageProcessor.Web/Processors/Overlay.cs
new file mode 100644
index 0000000000..b2121b6a8d
--- /dev/null
+++ b/src/ImageProcessor.Web/Processors/Overlay.cs
@@ -0,0 +1,244 @@
+// --------------------------------------------------------------------------------------------------------------------
+//
+// Copyright (c) James South.
+// Licensed under the Apache License, Version 2.0.
+//
+//
+// Adds an image overlay to the current image.
+// If the overlay is larger than the image it will be scaled to match 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.Imaging;
+ using ImageProcessor.Processors;
+ using ImageProcessor.Web.Extensions;
+ using ImageProcessor.Web.Helpers;
+
+ ///
+ /// Adds an image overlay to the current image.
+ /// If the overlay is larger than the image it will be scaled to match the image.
+ ///
+ public class Overlay : IWebGraphicsProcessor
+ {
+ ///
+ /// The regular expression to search strings for.
+ ///
+ private static readonly Regex QueryRegex = new Regex(@"(overlay=|overlay.\w+=)[^&]+", RegexOptions.Compiled);
+
+ ///
+ /// The overlay image regex.
+ ///
+ private static readonly Regex ImageRegex = new Regex(@"overlay=[\w+-]+." + ImageHelpers.ExtensionRegexPattern);
+
+ ///
+ /// The point regex.
+ ///
+ private static readonly Regex PointRegex = new Regex(@"overlay.position=\d+,\d+", RegexOptions.Compiled);
+
+ ///
+ /// The size regex.
+ ///
+ private static readonly Regex SizeRegex = new Regex(@"overlay.size=\d+,\d+", RegexOptions.Compiled);
+
+ ///
+ /// The opacity regex.
+ ///
+ private static readonly Regex OpacityRegex = new Regex(@"overlay.opacity=\d+", RegexOptions.Compiled);
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public Overlay()
+ {
+ this.Processor = new ImageProcessor.Processors.Overlay();
+ }
+
+ ///
+ /// 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();
+ this.Processor.DynamicParameter = new ImageLayer
+ {
+ Image = this.ParseImage(toParse),
+ Position = this.ParsePoint(toParse),
+ Opacity = this.ParseOpacity(toParse),
+ Size = this.ParseSize(toParse)
+ };
+ }
+
+ 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 = ImageRegex.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 = { };
+
+ Match match = PointRegex.Match(input);
+ if (match.Success)
+ {
+ dimensions = match.Value.ToPositiveIntegerArray();
+ }
+
+ if (dimensions.Length == 2)
+ {
+ return new Point(dimensions[0], dimensions[1]);
+ }
+
+ return null;
+ }
+
+ ///
+ /// Returns the correct for the given string.
+ ///
+ ///
+ /// The input string containing the value to parse.
+ ///
+ ///
+ /// The correct
+ ///
+ private int ParseOpacity(string input)
+ {
+ int opacity = 100;
+ Match match = OpacityRegex.Match(input);
+ if (match.Success)
+ {
+ opacity = Math.Abs(CommonParameterParserUtility.ParseIn100Range(match.Value.Split('=')[1]));
+ }
+
+ return opacity;
+ }
+
+ ///
+ /// Returns the correct for the given string.
+ ///
+ ///
+ /// The input string containing the value to parse.
+ ///
+ ///
+ /// The .
+ ///
+ private Size ParseSize(string input)
+ {
+ Size size = Size.Empty;
+ Match match = SizeRegex.Match(input);
+ if (match.Success)
+ {
+ int[] dimensions = match.Value.ToPositiveIntegerArray();
+ size = new Size(dimensions[0], dimensions[1]);
+ }
+
+ return size;
+ }
+ }
+}
diff --git a/src/ImageProcessor.Web/Processors/Resize.cs b/src/ImageProcessor.Web/Processors/Resize.cs
index a634c23277..59b3973585 100644
--- a/src/ImageProcessor.Web/Processors/Resize.cs
+++ b/src/ImageProcessor.Web/Processors/Resize.cs
@@ -112,6 +112,7 @@ namespace ImageProcessor.Web.Processors
if (match.Success)
{
// We don't want any resize carve or percentile crops requests to interfere.
+ // TODO: This is hacky and awful and should go.
if (match.Value.ToUpperInvariant().Contains("CARVE") || match.Value.ToUpperInvariant().Contains("PERCENT"))
{
break;
diff --git a/src/ImageProcessor.Web/Processors/Watermark.cs b/src/ImageProcessor.Web/Processors/Watermark.cs
index b61bf13b0c..92afc3228f 100644
--- a/src/ImageProcessor.Web/Processors/Watermark.cs
+++ b/src/ImageProcessor.Web/Processors/Watermark.cs
@@ -39,7 +39,7 @@ namespace ImageProcessor.Web.Processors
///
/// The regular expression to search strings for the position attribute.
///
- private static readonly Regex PositionRegex = new Regex(@"(text)?position(=|-)\d+[-,]\d+", RegexOptions.Compiled);
+ private static readonly Regex PositionRegex = new Regex(@"(watermark.position|textposition|[^.](&,=)?position)(=|-)\d+[-,]\d+", RegexOptions.Compiled);
///
/// The regular expression to search strings for the font size attribute.
@@ -59,7 +59,7 @@ namespace ImageProcessor.Web.Processors
///
/// The regular expression to search strings for the opacity attribute.
///
- private static readonly Regex OpacityRegex = new Regex(@"((font)?)opacity(=|-)(?:100|[1-9]?[0-9])", RegexOptions.Compiled);
+ private static readonly Regex OpacityRegex = new Regex(@"(watermark.opacity|fontopacity|[^.](&,=)?opacity)(=|-)(?:100|[1-9]?[0-9])", RegexOptions.Compiled);
///
/// The regular expression to search strings for the shadow attribute.
@@ -182,7 +182,8 @@ namespace ImageProcessor.Web.Processors
{
foreach (Match match in PositionRegex.Matches(input))
{
- int[] position = match.Value.ToPositiveIntegerArray();
+ // Chop off the leading legacy support '='
+ int[] position = match.Value.TrimStart('=').ToPositiveIntegerArray();
if (position != null)
{
diff --git a/src/ImageProcessor/ImageProcessor.csproj b/src/ImageProcessor/ImageProcessor.csproj
index dc77e9c2dc..0ad2b8a89d 100644
--- a/src/ImageProcessor/ImageProcessor.csproj
+++ b/src/ImageProcessor/ImageProcessor.csproj
@@ -159,7 +159,7 @@
-
+
diff --git a/src/ImageProcessor/Processors/Overlay.cs b/src/ImageProcessor/Processors/Overlay.cs
index d3fb2cce84..7551a95904 100644
--- a/src/ImageProcessor/Processors/Overlay.cs
+++ b/src/ImageProcessor/Processors/Overlay.cs
@@ -20,7 +20,8 @@ namespace ImageProcessor.Processors
using ImageProcessor.Imaging.Helpers;
///
- /// Adds an image overlay to the current image.
+ /// Adds an image overlay to the current image.
+ /// If the overlay is larger than the image it will be scaled to match the image.
///
public class Overlay : IGraphicsProcessor
{
@@ -77,8 +78,8 @@ namespace ImageProcessor.Processors
Size size = imageLayer.Size;
int width = image.Width;
int height = image.Height;
- int overlayWidth = Math.Min(image.Size.Width, size.Width);
- int overlayHeight = Math.Min(image.Size.Height, size.Height);
+ int overlayWidth = size != Size.Empty ? Math.Min(image.Size.Width, size.Width) : image.Size.Width;
+ int overlayHeight = size != Size.Empty ? Math.Min(image.Size.Height, size.Height) : image.Size.Height;
Point? position = imageLayer.Position;
int opacity = imageLayer.Opacity;