diff --git a/README.md b/README.md
index 28e4d1af6..a891a3a50 100644
--- a/README.md
+++ b/README.md
@@ -18,6 +18,7 @@ Core plugins at present include:
- Reset (Resets the image to its original loaded state)
- Resize
- Rotate (Rotate the image through 360º)
+ - RoundedCorners (Add rounded corners to each optional corner)
- Saturation
- Vignette (Adds a vignette effect to images)
- Watermark (Set a text watermark)
diff --git a/src/ImageProcessor.Tests/RegularExpressionUnitTests.cs b/src/ImageProcessor.Tests/RegularExpressionUnitTests.cs
index 40f00bb63..b305f540b 100644
--- a/src/ImageProcessor.Tests/RegularExpressionUnitTests.cs
+++ b/src/ImageProcessor.Tests/RegularExpressionUnitTests.cs
@@ -56,24 +56,44 @@ namespace ImageProcessor.Tests
Assert.AreEqual(Expected, actual);
}
- ///
- /// The contrast regex unit test.
- ///
- [TestMethod]
- public void TestContrastRegex()
- {
- const string Querystring = "contrast=56";
- const int Expected = 56;
-
- Contrast contrast = new Contrast();
- contrast.MatchRegexIndex(Querystring);
-
- int actual = contrast.DynamicParameter;
-
- Assert.AreEqual(Expected, actual);
- }
-
- ///
+ ///
+ /// The contrast regex unit test.
+ ///
+ [TestMethod]
+ public void TestContrastRegex()
+ {
+ const string Querystring = "contrast=56";
+ const int Expected = 56;
+
+ Contrast contrast = new Contrast();
+ contrast.MatchRegexIndex(Querystring);
+
+ int actual = contrast.DynamicParameter;
+
+ Assert.AreEqual(Expected, actual);
+ }
+
+ ///
+ /// The constrain regex unit test.
+ ///
+ [TestMethod]
+ public void TestConstrainRegex()
+ {
+ const string Querystring = "constrain=100,200";
+ const int ExpectedWidth = 100;
+ const int ExpectedHeight = 200;
+
+ Constrain contrast = new Constrain();
+ contrast.MatchRegexIndex(Querystring);
+
+ int actualWidth = contrast.DynamicParameter.Width;
+ int actualHeight = contrast.DynamicParameter.Height;
+
+ Assert.AreEqual(ExpectedWidth, actualWidth);
+ Assert.AreEqual(ExpectedHeight, actualHeight);
+ }
+
+ ///
/// The rotate regex unit test.
///
[TestMethod]
diff --git a/src/ImageProcessor/ImageProcessor.csproj b/src/ImageProcessor/ImageProcessor.csproj
index 038064bff..1efd252d1 100644
--- a/src/ImageProcessor/ImageProcessor.csproj
+++ b/src/ImageProcessor/ImageProcessor.csproj
@@ -81,7 +81,9 @@
+
+
diff --git a/src/ImageProcessor/Processors/Constrain.cs b/src/ImageProcessor/Processors/Constrain.cs
new file mode 100644
index 000000000..014fe1152
--- /dev/null
+++ b/src/ImageProcessor/Processors/Constrain.cs
@@ -0,0 +1,88 @@
+using System.Collections.Generic;
+using System.Drawing;
+using System.Text.RegularExpressions;
+using ImageProcessor.Helpers.Extensions;
+
+namespace ImageProcessor.Processors
+{
+ ///
+ /// Constrains an image to the given dimensions.
+ ///
+ public class Constrain : ResizeBase
+ {
+ private static readonly Regex QueryRegex = new Regex(@"constrain=\d+,\d+", RegexOptions.Compiled);
+
+ public override Regex RegexPattern
+ {
+ get
+ {
+ return QueryRegex;
+ }
+ }
+
+
+ public override dynamic DynamicParameter { get; set; }
+ public override int SortOrder { get; protected set; }
+ public override Dictionary Settings { get; set; }
+
+ public override int MatchRegexIndex(string queryString)
+ {
+ int index = 0;
+
+ // Set the sort order to max to allow filtering.
+ this.SortOrder = int.MaxValue;
+
+ 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;
+ int[] constraints = match.Value.ToPositiveIntegerArray();
+
+ int x = constraints[0];
+ int y = constraints[1];
+
+ this.DynamicParameter = new Size(x, y);
+ }
+
+ index += 1;
+ }
+ }
+
+ return this.SortOrder;
+ }
+
+ public override Image ProcessImage(ImageFactory factory)
+ {
+
+ double constrainedWidth = DynamicParameter.Width;
+ double constrainedHeight = DynamicParameter.Height;
+
+ var original = factory.Image;
+ double width = original.Width;
+ double height = original.Height;
+
+ if (width > constrainedWidth || height > constrainedHeight)
+ {
+
+ double constraintRatio = constrainedHeight / constrainedWidth;
+ double originalRatio = height / width;
+
+ Size newSize = originalRatio < constraintRatio
+ ? new Size((int)constrainedWidth, 0)
+ : new Size(0, (int)constrainedHeight);
+
+ int defaultMaxWidth;
+ int defaultMaxHeight;
+ int.TryParse(this.Settings["MaxWidth"], out defaultMaxWidth);
+ int.TryParse(this.Settings["MaxHeight"], out defaultMaxHeight);
+
+ return ResizeImage(factory, newSize.Width, newSize.Height, defaultMaxWidth, defaultMaxHeight);
+ }
+ return factory.Image;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageProcessor/Processors/Resize.cs b/src/ImageProcessor/Processors/Resize.cs
index 43cc489a4..0c8fbc267 100644
--- a/src/ImageProcessor/Processors/Resize.cs
+++ b/src/ImageProcessor/Processors/Resize.cs
@@ -20,7 +20,7 @@ namespace ImageProcessor.Processors
///
/// Resizes an image to the given dimensions.
///
- public class Resize : IGraphicsProcessor
+ public class Resize : ResizeBase
{
///
/// The regular expression to search strings for.
@@ -31,7 +31,7 @@ namespace ImageProcessor.Processors
///
/// Gets the regular expression to search strings for.
///
- public Regex RegexPattern
+ public override Regex RegexPattern
{
get
{
@@ -42,7 +42,7 @@ namespace ImageProcessor.Processors
///
/// Gets or sets DynamicParameter.
///
- public dynamic DynamicParameter
+ public override dynamic DynamicParameter
{
get;
set;
@@ -51,16 +51,16 @@ namespace ImageProcessor.Processors
///
/// Gets the order in which this processor is to be used in a chain.
///
- public int SortOrder
+ public override int SortOrder
{
get;
- private set;
+ protected set;
}
///
/// Gets or sets any additional settings required by the processor.
///
- public Dictionary Settings
+ public override Dictionary Settings
{
get;
set;
@@ -75,7 +75,7 @@ namespace ImageProcessor.Processors
///
/// The zero-based starting position in the original string where the captured substring was found.
///
- public int MatchRegexIndex(string queryString)
+ public override int MatchRegexIndex(string queryString)
{
int index = 0;
@@ -121,96 +121,20 @@ namespace ImageProcessor.Processors
///
/// The processed image from the current instance of the class.
///
- public Image ProcessImage(ImageFactory factory)
+ public override Image ProcessImage(ImageFactory factory)
{
- Bitmap newImage = null;
- Image image = factory.Image;
- try
- {
- int width = this.DynamicParameter.Width ?? 0;
- int height = this.DynamicParameter.Height ?? 0;
- int sourceWidth = image.Width;
- int sourceHeight = image.Height;
- int defaultMaxWidth;
- int defaultMaxHeight;
- int.TryParse(this.Settings["MaxWidth"], out defaultMaxWidth);
- int.TryParse(this.Settings["MaxHeight"], out defaultMaxHeight);
- int maxWidth = defaultMaxWidth > 0 ? defaultMaxWidth : int.MaxValue;
- int maxHeight = defaultMaxHeight > 0 ? defaultMaxHeight : int.MaxValue;
-
- // If height or width is not passed we assume that the standard ratio is to be kept.
- if (height == 0)
- {
- // Bit of simple fractional maths here.
- float percentWidth = Math.Abs(width / (float)sourceWidth);
- height = (int)Math.Floor(sourceHeight * percentWidth);
- }
-
- if (width == 0)
- {
- float percentHeight = Math.Abs(height / (float)sourceHeight);
- width = (int)Math.Floor(sourceWidth * percentHeight);
- }
-
- if (width > 0 && height > 0 && width <= maxWidth && height <= maxHeight)
- {
- // Dont use an object initializer here.
- newImage = new Bitmap(width, height, PixelFormat.Format32bppPArgb);
- newImage.Tag = image.Tag;
-
- using (Graphics graphics = Graphics.FromImage(newImage))
- {
- // We want to use two different blending algorithms for enlargement/shrinking.
- // Bicubic is better enlarging for whilst Bilinear is better for shrinking.
- // http://www.codinghorror.com/blog/2007/07/better-image-resizing.html
- if (image.Width < width && image.Height < height)
- {
- // We are making it larger.
- graphics.SmoothingMode = SmoothingMode.AntiAlias;
- graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
- graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
- graphics.CompositingQuality = CompositingQuality.HighQuality;
- }
- else
- {
- // We are making it smaller.
- graphics.SmoothingMode = SmoothingMode.None;
-
- // Contrary to everything I have read bicubic is producing the best results.
- graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
- graphics.PixelOffsetMode = PixelOffsetMode.None;
- graphics.CompositingQuality = CompositingQuality.HighSpeed;
- }
-
- // An unwanted border appears when using InterpolationMode.HighQualityBicubic to resize the image
- // as the algorithm appears to be pulling averaging detail from surFlooring pixels beyond the edge
- // of the image. Using the ImageAttributes class to specify that the pixels beyond are simply mirror
- // images of the pixels within solves this problem.
- using (ImageAttributes wrapMode = new ImageAttributes())
- {
- wrapMode.SetWrapMode(WrapMode.TileFlipXY);
- Rectangle destRect = new Rectangle(0, 0, width, height);
- graphics.DrawImage(image, destRect, 0, 0, sourceWidth, sourceHeight, GraphicsUnit.Pixel, wrapMode);
- }
+ int width = this.DynamicParameter.Width ?? 0;
+ int height = this.DynamicParameter.Height ?? 0;
- // Reassign the image.
- image.Dispose();
- image = newImage;
- }
- }
- }
- catch
- {
- if (newImage != null)
- {
- newImage.Dispose();
- }
- }
+ int defaultMaxWidth;
+ int defaultMaxHeight;
+ int.TryParse(this.Settings["MaxWidth"], out defaultMaxWidth);
+ int.TryParse(this.Settings["MaxHeight"], out defaultMaxHeight);
- return image;
+ return ResizeImage(factory, width, height, defaultMaxWidth, defaultMaxHeight);
}
- #endregion
+ #endregion
}
}
diff --git a/src/ImageProcessor/Processors/ResizeBase.cs b/src/ImageProcessor/Processors/ResizeBase.cs
new file mode 100644
index 000000000..23f9dbbde
--- /dev/null
+++ b/src/ImageProcessor/Processors/ResizeBase.cs
@@ -0,0 +1,104 @@
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Drawing.Drawing2D;
+using System.Drawing.Imaging;
+using System.Text.RegularExpressions;
+
+namespace ImageProcessor.Processors
+{
+ public abstract class ResizeBase : IGraphicsProcessor
+ {
+ public abstract Regex RegexPattern { get; }
+ public abstract dynamic DynamicParameter { get; set; }
+ public abstract int SortOrder { get; protected set; }
+ public abstract Dictionary Settings { get; set; }
+ public abstract int MatchRegexIndex(string queryString);
+ public abstract Image ProcessImage(ImageFactory factory);
+
+ protected Image ResizeImage(ImageFactory factory, int width, int height, int defaultMaxWidth, int defaultMaxHeight)
+ {
+ Bitmap newImage = null;
+ Image image = factory.Image;
+
+ try
+ {
+ int sourceWidth = image.Width;
+ int sourceHeight = image.Height;
+
+ int maxWidth = defaultMaxWidth > 0 ? defaultMaxWidth : int.MaxValue;
+ int maxHeight = defaultMaxHeight > 0 ? defaultMaxHeight : int.MaxValue;
+
+ // If height or width is not passed we assume that the standard ratio is to be kept.
+ if (height == 0)
+ {
+ // Bit of simple fractional maths here.
+ float percentWidth = Math.Abs(width/(float) sourceWidth);
+ height = (int) Math.Floor(sourceHeight*percentWidth);
+ }
+
+ if (width == 0)
+ {
+ float percentHeight = Math.Abs(height/(float) sourceHeight);
+ width = (int) Math.Floor(sourceWidth*percentHeight);
+ }
+
+ if (width > 0 && height > 0 && width <= maxWidth && height <= maxHeight)
+ {
+ // Dont use an object initializer here.
+ newImage = new Bitmap(width, height, PixelFormat.Format32bppPArgb);
+ newImage.Tag = image.Tag;
+
+ using (Graphics graphics = Graphics.FromImage(newImage))
+ {
+ // We want to use two different blending algorithms for enlargement/shrinking.
+ // Bicubic is better enlarging for whilst Bilinear is better for shrinking.
+ // http://www.codinghorror.com/blog/2007/07/better-image-resizing.html
+ if (image.Width < width && image.Height < height)
+ {
+ // We are making it larger.
+ graphics.SmoothingMode = SmoothingMode.AntiAlias;
+ graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
+ graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
+ graphics.CompositingQuality = CompositingQuality.HighQuality;
+ }
+ else
+ {
+ // We are making it smaller.
+ graphics.SmoothingMode = SmoothingMode.None;
+
+ // Contrary to everything I have read bicubic is producing the best results.
+ graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
+ graphics.PixelOffsetMode = PixelOffsetMode.None;
+ graphics.CompositingQuality = CompositingQuality.HighSpeed;
+ }
+
+ // An unwanted border appears when using InterpolationMode.HighQualityBicubic to resize the image
+ // as the algorithm appears to be pulling averaging detail from surFlooring pixels beyond the edge
+ // of the image. Using the ImageAttributes class to specify that the pixels beyond are simply mirror
+ // images of the pixels within solves this problem.
+ using (ImageAttributes wrapMode = new ImageAttributes())
+ {
+ wrapMode.SetWrapMode(WrapMode.TileFlipXY);
+ Rectangle destRect = new Rectangle(0, 0, width, height);
+ graphics.DrawImage(image, destRect, 0, 0, sourceWidth, sourceHeight, GraphicsUnit.Pixel, wrapMode);
+ }
+
+ // Reassign the image.
+ image.Dispose();
+ image = newImage;
+ }
+ }
+ }
+ catch
+ {
+ if (newImage != null)
+ {
+ newImage.Dispose();
+ }
+ }
+
+ return image;
+ }
+ }
+}
\ No newline at end of file