diff --git a/src/ImageProcessor/Extensions/StringExtensions.cs b/src/ImageProcessor/Extensions/StringExtensions.cs
index 40380a1aa..9377ea2ed 100644
--- a/src/ImageProcessor/Extensions/StringExtensions.cs
+++ b/src/ImageProcessor/Extensions/StringExtensions.cs
@@ -123,7 +123,7 @@ namespace ImageProcessor.Extensions
throw new ArgumentNullException("expression");
}
- Regex regex = new Regex(@"\d+", RegexOptions.Compiled);
+ Regex regex = new Regex(@"[\d+]+(?=[,|])|[\d+]+(?![,|])", RegexOptions.Compiled);
MatchCollection matchCollection = regex.Matches(expression);
@@ -139,6 +139,35 @@ namespace ImageProcessor.Extensions
return matches;
}
+
+ ///
+ /// Creates an array of floats scraped from the String.
+ ///
+ /// The String instance that this method extends.
+ /// An array of floats scraped from the String.
+ public static float[] ToPositiveFloatArray(this string expression)
+ {
+ if (string.IsNullOrWhiteSpace(expression))
+ {
+ throw new ArgumentNullException("expression");
+ }
+
+ Regex regex = new Regex(@"[\d+\.]+(?=[,|])|[\d+\.]+(?![,|])", RegexOptions.Compiled);
+
+ MatchCollection matchCollection = regex.Matches(expression);
+
+ // Get the collections.
+ int count = matchCollection.Count;
+ float[] matches = new float[count];
+
+ // Loop and parse the int values.
+ for (int i = 0; i < count; i++)
+ {
+ matches[i] = float.Parse(matchCollection[i].Value);
+ }
+
+ return matches;
+ }
#endregion
#region Files and Paths
diff --git a/src/ImageProcessor/ImageFactory.cs b/src/ImageProcessor/ImageFactory.cs
index 3b610f444..4dfc2e297 100644
--- a/src/ImageProcessor/ImageFactory.cs
+++ b/src/ImageProcessor/ImageFactory.cs
@@ -395,7 +395,30 @@ namespace ImageProcessor
{
if (this.ShouldProcess)
{
- Crop crop = new Crop { DynamicParameter = rectangle };
+ CropLayer cropLayer = new CropLayer(rectangle.Left, rectangle.Top, rectangle.Right, rectangle.Bottom, CropMode.Pixels);
+
+ Crop crop = new Crop { DynamicParameter = cropLayer };
+
+ this.Image = crop.ProcessImage(this);
+ }
+
+ return this;
+ }
+
+ ///
+ /// Crops the current image to the given location and size.
+ ///
+ ///
+ /// The containing the coordinates and mode to crop the image with.
+ ///
+ ///
+ /// The current instance of the class.
+ ///
+ public ImageFactory Crop(CropLayer cropLayer)
+ {
+ if (this.ShouldProcess)
+ {
+ Crop crop = new Crop { DynamicParameter = cropLayer };
this.Image = crop.ProcessImage(this);
}
diff --git a/src/ImageProcessor/ImageProcessor.csproj b/src/ImageProcessor/ImageProcessor.csproj
index 5a5b7c98e..f75cf9ff9 100644
--- a/src/ImageProcessor/ImageProcessor.csproj
+++ b/src/ImageProcessor/ImageProcessor.csproj
@@ -66,6 +66,8 @@
+
+
diff --git a/src/ImageProcessor/Imaging/CropLayer.cs b/src/ImageProcessor/Imaging/CropLayer.cs
new file mode 100644
index 000000000..b9b163fb2
--- /dev/null
+++ b/src/ImageProcessor/Imaging/CropLayer.cs
@@ -0,0 +1,81 @@
+// --------------------------------------------------------------------------------------------------------------------
+//
+// Copyright (c) James South.
+// Licensed under the Apache License, Version 2.0.
+//
+//
+// Encapsulates the properties required to crop an image.
+//
+// --------------------------------------------------------------------------------------------------------------------
+
+namespace ImageProcessor.Imaging
+{
+ using System;
+
+ ///
+ /// Encapsulates the properties required to crop an image.
+ ///
+ public class CropLayer
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// The left coordinate of the crop layer.
+ ///
+ ///
+ /// The top coordinate of the crop layer.
+ ///
+ ///
+ /// The right coordinate of the crop layer.
+ ///
+ ///
+ /// The bottom coordinate of the crop layer.
+ ///
+ ///
+ /// The .
+ ///
+ ///
+ /// If the is set to CropMode.Percentage then the four coordinates
+ /// become percentages to reduce from each edge.
+ ///
+ public CropLayer(float left, float top, float right, float bottom, CropMode cropMode = CropMode.Percentage)
+ {
+ if (left < 0 || top < 0 || right < 0 || bottom < 0)
+ {
+ throw new ArgumentOutOfRangeException();
+ }
+
+ this.Left = left;
+ this.Top = top;
+ this.Right = right;
+ this.Bottom = bottom;
+ this.CropMode = cropMode;
+ }
+
+ ///
+ /// Gets or sets the left coordinate of the crop layer.
+ ///
+ public float Left { get; set; }
+
+ ///
+ /// Gets or sets the top coordinate of the crop layer.
+ ///
+ public float Top { get; set; }
+
+ ///
+ /// Gets or sets the right coordinate of the crop layer.
+ ///
+ public float Right { get; set; }
+
+ ///
+ /// Gets or sets the bottom coordinate of the crop layer.
+ ///
+ public float Bottom { get; set; }
+
+ ///
+ /// Gets or sets the .
+ ///
+ public CropMode CropMode { get; set; }
+ }
+}
diff --git a/src/ImageProcessor/Imaging/CropMode.cs b/src/ImageProcessor/Imaging/CropMode.cs
new file mode 100644
index 000000000..a0b9fe0d2
--- /dev/null
+++ b/src/ImageProcessor/Imaging/CropMode.cs
@@ -0,0 +1,28 @@
+// --------------------------------------------------------------------------------------------------------------------
+//
+// Copyright (c) James South.
+// Licensed under the Apache License, Version 2.0.
+//
+//
+// Enumerated cop modes to apply to cropped images.
+//
+// --------------------------------------------------------------------------------------------------------------------
+
+namespace ImageProcessor.Imaging
+{
+ ///
+ /// Enumerated cop modes to apply to cropped images.
+ ///
+ public enum CropMode
+ {
+ ///
+ /// Crops the image using the standard rectangle model of x, y, width, height.
+ ///
+ Pixels,
+
+ ///
+ /// Crops the image using percentages model left, top, right, bottom.
+ ///
+ Percentage
+ }
+}
diff --git a/src/ImageProcessor/Processors/Crop.cs b/src/ImageProcessor/Processors/Crop.cs
index 24123bb25..336885b80 100644
--- a/src/ImageProcessor/Processors/Crop.cs
+++ b/src/ImageProcessor/Processors/Crop.cs
@@ -15,9 +15,11 @@ namespace ImageProcessor.Processors
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
+ using System.Text;
using System.Text.RegularExpressions;
using ImageProcessor.Extensions;
+ using ImageProcessor.Imaging;
#endregion
@@ -30,7 +32,17 @@ namespace ImageProcessor.Processors
/// The regular expression to search strings for.
///
///
- private static readonly Regex QueryRegex = new Regex(@"crop=\d+[,-]\d+[,-]\d+[,-]\d+", RegexOptions.Compiled);
+ private static readonly Regex QueryRegex = new Regex(@"crop=\d+(.\d+)?[,-]\d+(.\d+)?[,-]\d+(.\d+)?[,-]\d+(.\d+)?|cropmode=(pixels|percent)", RegexOptions.Compiled);
+
+ ///
+ /// The coordinate regex.
+ ///
+ private static readonly Regex CoordinateRegex = new Regex(@"crop=\d+(.\d+)?[,-]\d+(.\d+)?[,-]\d+(.\d+)?[,-]\d+(.\d+)?", RegexOptions.Compiled);
+
+ ///
+ /// The mode regex.
+ ///
+ private static readonly Regex ModeRegex = new Regex(@"cropmode=(pixels|percent)", RegexOptions.Compiled);
#region IGraphicsProcessor Members
///
@@ -87,6 +99,9 @@ namespace ImageProcessor.Processors
// 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)
@@ -95,21 +110,26 @@ namespace ImageProcessor.Processors
{
// Set the index on the first instance only.
this.SortOrder = match.Index;
- int[] coordinates = match.Value.ToPositiveIntegerArray();
-
- int x = coordinates[0];
- int y = coordinates[1];
- int width = coordinates[2];
- int height = coordinates[3];
-
- Rectangle rectangle = new Rectangle(x, y, width, height);
- this.DynamicParameter = rectangle;
}
+ stringBuilder.Append(match.Value);
+
index += 1;
}
}
+ if (this.SortOrder < int.MaxValue)
+ {
+ // Match syntax
+ string toParse = stringBuilder.ToString();
+
+ float[] coordinates = this.ParseCoordinates(toParse);
+ CropMode cropMode = this.ParseMode(toParse);
+
+ CropLayer cropLayer = new CropLayer(coordinates[0], coordinates[1], coordinates[2], coordinates[3], cropMode);
+ this.DynamicParameter = cropLayer;
+ }
+
return this.SortOrder;
}
@@ -129,10 +149,27 @@ namespace ImageProcessor.Processors
Image image = factory.Image;
try
{
- Rectangle rectangle = this.DynamicParameter;
-
int sourceWidth = image.Width;
int sourceHeight = image.Height;
+ RectangleF rectangleF;
+ CropLayer cropLayer = this.DynamicParameter;
+
+ if (cropLayer.CropMode == CropMode.Percentage)
+ {
+ // Work out the percentages.
+ float left = cropLayer.Left * sourceWidth;
+ float top = cropLayer.Top * sourceWidth;
+ float right = (sourceWidth - (cropLayer.Right * sourceWidth)) - left;
+ float bottom = (sourceHeight - (cropLayer.Bottom * sourceHeight)) - top;
+
+ rectangleF = new RectangleF(left, top, right, bottom);
+ }
+ else
+ {
+ rectangleF = new RectangleF(cropLayer.Left, cropLayer.Top, cropLayer.Right, cropLayer.Bottom);
+ }
+
+ Rectangle rectangle = Rectangle.Round(rectangleF);
if (rectangle.X < sourceWidth && rectangle.Y < sourceHeight)
{
@@ -190,5 +227,55 @@ namespace ImageProcessor.Processors
return image;
}
#endregion
+
+ ///
+ /// Returns the correct for the given string.
+ ///
+ ///
+ /// The input string containing the value to parse.
+ ///
+ ///
+ /// The correct .
+ ///
+ private CropMode ParseMode(string input)
+ {
+ foreach (Match match in ModeRegex.Matches(input))
+ {
+ // Split on =
+ string mode = match.Value.Split('=')[1];
+
+ switch (mode)
+ {
+ case "percent":
+ return CropMode.Percentage;
+ case "pixels":
+ return CropMode.Pixels;
+ }
+ }
+
+ return CropMode.Pixels;
+ }
+
+
+ ///
+ /// Returns the correct for the given string.
+ ///
+ ///
+ /// The input string containing the value to parse.
+ ///
+ ///
+ /// The correct .
+ ///
+ private float[] ParseCoordinates(string input)
+ {
+ float[] floats = { };
+
+ foreach (Match match in CoordinateRegex.Matches(input))
+ {
+ floats = match.Value.ToPositiveFloatArray();
+ }
+
+ return floats;
+ }
}
}
diff --git a/src/ImageProcessor/Processors/Resize.cs b/src/ImageProcessor/Processors/Resize.cs
index 9cb1fbc5a..7989b6bb8 100644
--- a/src/ImageProcessor/Processors/Resize.cs
+++ b/src/ImageProcessor/Processors/Resize.cs
@@ -59,6 +59,7 @@ namespace ImageProcessor.Processors
/// The regular expression to search strings for the upscale attribute.
///
private static readonly Regex UpscaleRegex = new Regex(@"upscale=false", RegexOptions.Compiled);
+
#region IGraphicsProcessor Members
///
/// Gets the regular expression to search strings for.
diff --git a/src/TestWebsites/NET45/Test_Website_NET45/Views/Home/Index.cshtml b/src/TestWebsites/NET45/Test_Website_NET45/Views/Home/Index.cshtml
index 0cd6e38b3..1ea44b27b 100644
--- a/src/TestWebsites/NET45/Test_Website_NET45/Views/Home/Index.cshtml
+++ b/src/TestWebsites/NET45/Test_Website_NET45/Views/Home/Index.cshtml
@@ -14,7 +14,11 @@
Cropped
-

+

+
+
Cropped Percent
+

+
@@ -185,17 +189,17 @@
Color Profiles
@*
-
-
-
CMYK original jpg
-

-
-
-
sRGB original jpg
-

-
-
- *@
+
+
+
CMYK original jpg
+

+
+
+
sRGB original jpg
+

+
+
+ *@